Skip to content

perf: precompute and cache field metadata per class (closes #77)#78

Merged
jeyben merged 3 commits intomasterfrom
features/77_cache-field-metadata-per-class
Apr 17, 2026
Merged

perf: precompute and cache field metadata per class (closes #77)#78
jeyben merged 3 commits intomasterfrom
features/77_cache-field-metadata-per-class

Conversation

@jeyben
Copy link
Copy Markdown
Owner

@jeyben jeyben commented Apr 17, 2026

Summary

  • Introduces ClassMetadataCache and FieldDescriptor so annotation scanning, FormatContext/FormatInstructions building, setter resolution, and formatter instantiation happen at most once per class — not on every load()/export() call
  • Hardens thread safety: helper objects are local to each cache-build invocation, FormatContext fields and ByTypeFormatter.context are now final
  • Adds a dedicated concurrency test suite (5 new tests) that proved the cache is race-free and would catch any regression reintroducing shared mutable state

Test plan

  • TestClassMetadataCache — unit tests: correct descriptor count, non-null metadata, @Fields expansion, repeating/nested-record handling, custom-formatter flag
  • TestClassMetadataCacheConcurrency — 20-thread tests: same-class access returns identical list instance; different-class concurrent builds produce correct per-class counts; no cross-contamination
  • TestFixedFormatManagerConcurrency — 20-thread tests: concurrent load() returns correctly-populated POJOs; concurrent export() returns correct record strings
  • Full suite: 386 tests, 0 failures

🤖 Generated with Claude Code

jeyben and others added 3 commits April 17, 2026 09:15
Introduces ClassMetadataCache and FieldDescriptor so that annotation
scanning, FormatContext/FormatInstructions building, setter resolution,
and formatter instantiation happen at most once per class rather than
on every load() and export() call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move helper instances (AnnotationScanner, FormatInstructionsBuilder,
  RepeatingFieldSupport) from ClassMetadataCache singleton fields to local
  variables inside build(), eliminating shared mutable state between
  concurrent builds of different classes
- Mark FormatContext.offset/dataType/formatter fields as final, enforcing
  immutability at the type level for objects cached and shared across threads
- Mark ByTypeFormatter.context as final for the same reason

No behavioral change; all 381 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Impl

Adds two test classes covering four scenarios:

TestClassMetadataCacheConcurrency:
- Same-class concurrent access: 20 threads verified to receive the identical
  cached List<FieldDescriptor> instance (proves single build, no duplicate work)
- Different-class concurrent builds: interleaved access to MyRecord,
  MultibleFieldsRecord, and RepeatingFieldRecord verified to produce the
  correct per-class descriptor counts with no cross-contamination

TestFixedFormatManagerConcurrency:
- Concurrent load(): 20 threads parse the same record string and each get a
  correctly-populated POJO (stringData, booleanData, integerData, longData)
- Concurrent export(): 20 threads serialise the same pre-built MyRecord and
  each get the correct fixed-width string

Regression verification: replacing computeIfAbsent with a racy put/get caused
sameConcurrentAccessReturnsSameListInstance to fail, confirming the test detects
the class of race condition it was written to guard against.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jeyben jeyben merged commit 23997ca into master Apr 17, 2026
5 checks passed
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