refactor(registry): clear memos on mutation; drop per-resolve version stamp#347
Merged
Merged
Conversation
…n (A2) Replace ProvidersRegistry's per-lookup version-stamp on the resolver/plan memos with clear-on-mutation, and fold validation freshness to a _validated bool — dropping the per-resolve version compare from the hot path. Simplification-first (no perf kill-gate; perf is a reported bonus), licensed by the explicit build->resolve->close lifecycle contract. Deletes the internal version/ validated_version machinery (treated as non-public). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… stamp _plans/_resolvers become plain dicts cleared by register/add_providers/remove (via _invalidate); the per-lookup version compare leaves the resolve hot path. Validation freshness folds from the _version/validated_version counter to a _validated bool. Deletes the internal version property/counter (non-public). Same invalidation breadth as the old version bump; sound because mutation is a single-threaded configure-phase operation per the lifecycle contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Updates resolution/providers/containers/validation/concurrency capability docs off version-stamping onto clear-on-mutation + the _validated flag, records the sanctioned free-threaded trade in concurrency.md, fixes one stale test comment, and finalizes the change summary with the measured (bonus) perf delta. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: 891231d | Previous: c16b80a | Ratio |
|---|---|---|---|
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
435700.78326729283 iter/sec (stddev: 0.00004462224328092711) |
433935.3954375655 iter/sec (stddev: 0.00005460575872291772) |
1.00 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle |
65224.830496207855 iter/sec (stddev: 0.000012609462792397555) |
63693.167227274425 iter/sec (stddev: 0.0000131713654431779) |
0.98 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
1242427.4849290976 iter/sec (stddev: 3.910800928945715e-7) |
1163016.5186574862 iter/sec (stddev: 4.0302918185877904e-7) |
0.94 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
2371543.497703308 iter/sec (stddev: 6.491227467209881e-8) |
2176586.2148827524 iter/sec (stddev: 7.792856151735141e-8) |
0.92 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
518832.5108892471 iter/sec (stddev: 5.922256330265779e-7) |
502324.995757382 iter/sec (stddev: 5.815581860954582e-7) |
0.97 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
316904.1776352059 iter/sec (stddev: 6.097230802332533e-7) |
310669.6238494726 iter/sec (stddev: 6.455572929657008e-7) |
0.98 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
1102952.4181309878 iter/sec (stddev: 3.534874420146444e-7) |
1012348.7177749319 iter/sec (stddev: 4.0550288620064454e-7) |
0.92 |
This comment was automatically generated by workflow using github-action-benchmark.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to the dropped C2 memo-swap (#346): instead of adding a bypass layer, remove per-resolve work. Simplification-first — the hot-path perf win is a reported bonus, not gated.
What changes
ProvidersRegistry's resolver/plan memos (_resolvers/_plans) were(version, obj)tuples checked against a monotonic_versioncounter on every lookup. They become plain dicts cleared on mutation via a_invalidate()helper (register/add_providers/_remove_providers). Validation freshness folds from the counter to a_validated: bool. The internalversionproperty /validated_version/_versionare deleted.The resolve hot path (
resolver_for, per resolve and per dependency edge) drops the per-lookup tuple-unpack + int-compare. The invariant collapses to: a memo is valid until the registry changes; changing it clears the memos.Licensed by the explicit lifecycle contract (from #346): the registry mutates only during single-threaded configure, so
clear()never races the concurrent resolve path in a contract-abiding program — andclear()'s invalidation breadth is identical to the old version bump.Not a behavior change
is_validated()semantics preserved;add_providers→rebuild preserved (the twotest_add_providers_rebuilds_stale_wiring_plan_*tests pass unchanged).versionis treated as an internal, non-public detail (no consumers in source/tests/integration-kit/user-docs) — removed without a deprecation cycle.Measured (bonus, no kill-gate)
Guard g1 −4%, g2 (cached singleton) −9%/−12%; comparative median C1 −7.6%, C2 −4.8%, C3 −3.3%; C4 flat (construction-dominated). No regression beyond noise.
Full rationale + free-threaded soundness argument in the change bundle. Architecture docs (resolution/providers/containers/validation/concurrency) promoted in the same PR.
🤖 Generated with Claude Code