Skip to content

fix: stream NVD mirror cache writes#409

Open
johnou wants to merge 1 commit into
jeremylong:mainfrom
johnou:bugfix/stream-nvd-mirror-cache-writes
Open

fix: stream NVD mirror cache writes#409
johnou wants to merge 1 commit into
jeremylong:mainfrom
johnou:bugfix/stream-nvd-mirror-cache-writes

Conversation

@johnou

@johnou johnou commented Jun 22, 2026

Copy link
Copy Markdown

As of late the NVD service has been returning 503 responses despite valid API keys. While setting up an NVD proxy, our build node also hit Java heap OOMs with max heap set to 2G..

 Caused by: java.lang.OutOfMemoryError: Java heap space
 	at java.base/java.lang.StringUTF16.compress(StringUTF16.java:161)
 	at java.base/java.lang.String.<init>(String.java:4501)
 	at java.base/java.lang.String.<init>(String.java:300)
 	at com.fasterxml.jackson.core.util.TextBuffer.setCurrentAndReturn(TextBuffer.java:996)
 	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishAndReturnString(UTF8StreamJsonParser.java:2512)
 	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.getText(UTF8StreamJsonParser.java:294)
 	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:42)
 	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
 	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:543)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:585)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:447)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1497)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
 	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:543)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:585)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:447)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1497)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
 	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:543)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:585)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:447)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1497)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
 	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
 	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)

MAT analysis showed the mirror update path materialized full yearly NVD cache files into Java objects, then copied them into additional map/list structures before writing. This made memory scale with full cache size, not update size.

Before, synthetic 120k CVE cache update:

  • -Xmx128m: OOM in Jackson PropertyValueBuffer
  • live merge heap dump: 258MB
  • MAT retained set: 194.6MB
  • retained objects: 3,938,347
  • retained CveItem: 120,000

After, same synthetic 120k CVE cache update:

  • -Xmx64m: completed
  • live heap dump: 14MB
  • MAT retained set: 7.6MB
  • retained objects: 114,347
  • no full-year CveItem graph retained


@SuppressFBWarnings(value = "DM_GC", justification = "Without calling System.gc() the JVM uses 2+GB of memory, calling gc profiling results in less than 1GB")
private void writeJsonMirror(Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, DefCveItem>> cves,
@SuppressFBWarnings(value = "DM_GC", justification = "Manual collection preserves existing mirror memory behavior.")

@johnou johnou Jun 22, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

system.gc is probably redundant now, but it may reduce RSS between yearly file writes..

@johnou

johnou commented Jun 23, 2026

Copy link
Copy Markdown
Author

@jeremylong ptal when you get a chance, not a blocker but would be a nice to have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant