Skip to content

refactor: deepen CacheItem to own the singleton get-or-create invariant - #314

Merged
lesnik512 merged 5 commits into
mainfrom
deepen-cacheitem-get-or-create
Jul 14, 2026
Merged

lesnik512 merged 5 commits into
mainfrom
deepen-cacheitem-get-or-create

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Summary

Internal deepening — no public-API or behaviour change. The singleton double-checked-lock (DCL) + memoization moves off Factory.resolve onto a new CacheItem.get_or_create method, so Factory.resolve stops reaching into cache_item.cache and container._lock field-by-field. This is Candidate 2 from the 2026-07-13 architecture review (Candidate 1 was the integration kit).

Design/rationale: planning/changes/2026-07-14.02-cacheitem-get-or-create.md.

What changes

  • CacheItem.get_or_create(lock, resolve, create) -> (value, created) (registries/cache_registry.py) owns the full two-phase dance: fast-read of self.cache, an unlocked resolve() (recursive kwargs resolution must not hold the lock), then acquire the lock, double-check, create(resolved), store, release. Returns created so the caller knows whether to register for finalization.
  • Factory.resolve (providers/factory.py) delegates to it and only drives mark_created via the created flag — it no longer acquires/releases the lock or writes the cache field.
  • Error-trace parity: because CreatorCallError/ArgumentResolutionError are ResolutionError subclasses, the resolve-time prepend_step decoration moves into _resolve_kwargs (its only caller), so both thunks self-decorate and get_or_create catches nothing — no double-prepended resolution step. Traces render byte-identically.

Untouched: the close/finalize half of CacheItem; the nogil-sensitive wiring_plan publication (stays unlocked); find_container/reopen; the non-caching path; mark_created orchestration (stays a registry concern, no back-reference).

Test plan

  • just test-ci — 100% line coverage, 379 tests. New direct unit tests for get_or_create (miss / hit / post-lock double-check / lock-release) in tests/registries/test_cache_registry.py; zero changes to existing tests.
  • just lint-ci — ruff (select=ALL), ty, check-planning clean.
  • Concurrency: test_singleton_threading_concurrency (4-thread race) drives the inner double-check; reentrancy/deadlock and use_lock=False tests pass unchanged (same RLock passed through).
  • Error-trace parity: test_dependency_path + test_error_rendering pass unmodified.
  • Per-task spec+quality review (3 tasks) + adversarial whole-branch review (concurrency + error-trace parity traced against baseline; no Critical/Important findings).

🤖 Generated with Claude Code

@lesnik512
lesnik512 merged commit 16f99a8 into main Jul 14, 2026
7 checks passed
@lesnik512
lesnik512 deleted the deepen-cacheitem-get-or-create branch July 14, 2026 11:41
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