Skip to content

Simplify instantiate_record_from_json with a cached per-class lookup table - #130

Closed
tungleduyxyz wants to merge 1 commit into
killbill:masterfrom
tungleduyxyz:issue_126_simplify_instantiate_record
Closed

tungleduyxyz wants to merge 1 commit into
killbill:masterfrom
tungleduyxyz:issue_126_simplify_instantiate_record

Conversation

@tungleduyxyz

Copy link
Copy Markdown
Contributor

Addresses #126 (partially; see "Not covered" below).

What

Resource.instantiate_record_from_json now resolves each JSON key once per class into [setter, cardinality, type] and caches it, instead of per key/record doing an ancestors walk, two Utils.underscore calls, string interpolation and rescue nil for unknown fields.

  • Copy-on-write cache: readers never lock, writers are serialized. Cleared on has_one, has_many, attribute and create_alias.
  • Unknown keys are cached and skipped without raising NoMethodError.
  • Method split into instantiate_record_from_json, assign_json_attribute, build_many and lookup helpers.
  • Behaviour is unchanged, including inheritance of has_one/has_many and leniency: a setter that raises is still swallowed (rescue StandardError, free unless raised).

Tests

New spec/killbill_client/instantiate_record_from_json_spec.rb (14 examples): nested has_one/has_many, inherited metadata, no leak to parent, unknown fields ignored without exceptions, shape mismatches, nulls, cache invalidation, threads, and a real AccountTimeline. All but the no-exceptions example also pass on the old code. Full unit suite: 68 examples, 0 failures on CRuby 2.6.10 and JRuby 9.4.9.

Benchmark

New benchmark/from_json.rb (benchmark-ips, allocation and exception counts, Utils.underscore). Use PAYLOAD_DIR for real captures; otherwise it generates synthetic payloads.

Synthetic payloads, iterations/s:

Payload CRuby before after JRuby 9.4 before after
Account 9.9k 77k 18.2k 53.9k
Invoice list x50 31.8 273 60.6 187
Payment list x50 46.3 364 85.6 252
AccountTimeline (300 invoices/payments) 3.0 22.8 5.8 17.0

CRuby allocations for the timeline: ~2.0M -> ~171k objects per parse.

Not covered (still open on #126)

  • Results on real Kill Bill payloads and the rescue nil hit rate (synthetic payloads inject unknown fields, so the old-code exception counts are an artifact).
  • JRuby 10 numbers and the follow-up JFR profile of Kaui.

Utils.underscore is untouched; #125 stacks on top of this.

…table

Resolve each JSON key once per class into [setter, cardinality, type]
(copy-on-write cache, cleared when attributes are declared) instead of
walking ancestors, underscoring twice and rescuing NoMethodError for
every key of every record. Unknown keys are cached and skipped without
raising. Split the method into smaller pieces.

Add specs for nested has_one/has_many, inherited metadata, unknown fields
and cache invalidation, plus benchmark/from_json.rb (benchmark-ips).

Refs killbill#126

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tungleduyxyz

Copy link
Copy Markdown
Contributor Author

Superseded by #131 (same commit, opened from a branch in this repository instead of a fork).

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