Skip to content

perf(backend): wire the effective-project cache and move it off BigCache (WI-1964) - #443

Merged
stefan-ernst merged 1 commit into
mainfrom
wi-1964-effective-project-cache
Nov 3, 2026
Merged

stefan-ernst merged 1 commit into
mainfrom
wi-1964-effective-project-cache

Conversation

@stefan-ernst

Copy link
Copy Markdown
Contributor

Why

ItemCacheService exists to cache the resolved effective project_id for an item so a read doesn't re-walk the parent chain, but it was never wired:

  • GetEffectiveProjectForItem (the only reader/writer of the cache) had no caller.
  • The live read path — ItemApplicationService.Get → ItemCRUDService.GetWithEffectiveProject — called ResolveEffectiveProject directly, running the recursive CTE (up to 10 levels) on every item-detail read.
  • WarmCache was a no-op (_ = itemID).

What

  • Store: replace the BigCache with cacheutil.TypedCache[effectiveProjectCacheEntry] — sharded, per-entry TTL with sweep, count-bounded, registered for /admin/diagnostics/cache-memory. Same call WI-1906 made for the permission snapshot cache: cardinality is items and the value is a small struct, so the byte API only added marshal/copy overhead.
  • Wiring: ItemCRUDService gains an optional cache + WithCache; GetWithEffectiveProject resolves through it. ItemApplicationService.WithCache passes the shared cache down to s.crud. Without a cache the uncached resolver is used, and both share ResolveEffectiveProject as the single source of truth.
  • WarmCache: now actually populates entries (bounded by WarmupBatchSize, still backgrounded).
  • Cleanup: dropped the unused GetItemHierarchy/SetItemHierarchy/ItemHierarchyCache surface and the per-call repo allocation; NewItemCacheService no longer returns an error.

Invalidation is unchanged — mutation paths already invalidate the item and its descendants.

Acceptance

  • A warm item-detail read issues no recursive project-resolution query; a cold read resolves once and caches (covered by the new tests, which mutate the row behind the cache and assert the cached value is served until invalidation).
  • Changing project_id/inherit_project/parent_id makes affected items resolve fresh (existing invalidation, asserted in the new tests).
  • Count-bounded and TTL-expired via TypedCache.

Verification

  • go build ./internal/..., go vet, golangci-lint on the changed packages — 0 issues
  • internal/services/... — pass except the pre-existing TestItemWorkspaceMovePreviewsAndCommitsExactPolicy (fails on main)
  • internal/handlers/... — pass except two pre-existing TestNeutralInferenceEndpoint... failures (fail on main)
  • New tests in core-tests#36

Note: verification ran from a clean core-tests worktree because the shared core-tests checkout has another session's uncommitted email_reply_service changes (which reference a core field not yet on main).

Pairs with Windshiftapp/core-tests-internal#36.

…e (WI-1964)

ItemCacheService existed to cache the resolved effective project for an
item but was never wired: GetEffectiveProjectForItem had no caller, and
the item read path ran the recursive parent-chain CTE on every detail
read. WarmCache was a no-op.

- Replace the BigCache store with cacheutil.TypedCache (sharded, TTL,
  count-bounded, registered for /admin/diagnostics/cache-memory), the same
  call WI-1906 made for the permission snapshot cache. No JSON on the read
  path.
- Wire ItemCRUDService.GetWithEffectiveProject through the cache when one
  is injected; ItemApplicationService.WithCache passes the shared cache
  down. The uncached resolver stays the fallback and shares
  ResolveEffectiveProject as the single source of truth.
- Make WarmCache actually populate entries (bounded by WarmupBatchSize,
  still backgrounded) instead of a no-op loop.
- Drop the unused GetItemHierarchy/SetItemHierarchy/ItemHierarchyCache
  surface and the per-call repo allocation.

Invalidation is unchanged: mutation paths already invalidate the item and
its descendants.
@stefan-ernst
stefan-ernst merged commit 6329599 into main Nov 3, 2026
18 checks passed
@stefan-ernst
stefan-ernst deleted the wi-1964-effective-project-cache branch November 3, 2026 11:32
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