Skip to content

perf(backend): clean up hot-path allocations and bound the count cache (WI-1917) - #437

Merged
stefan-ernst merged 1 commit into
mainfrom
wi-1917-backend-cleanup
Nov 2, 2026
Merged

stefan-ernst merged 1 commit into
mainfrom
wi-1917-backend-cleanup

Conversation

@stefan-ernst

Copy link
Copy Markdown
Contributor

Why

P2 cleanup from docs/backend-performance-scaling-review.md. The effective-project cache was split into WI-1964, so this is the mechanical remainder.

What

  • GetTimeRollup (item_hierarchy.go): drop the redundant second loop that re-assigned the args slice it had just built.
  • NewItemRepository(db) per call: HierarchyService allocated one in 12 methods and PermissionService.GetItemWorkspaceID in one. Both now hold a single *repository.ItemRepository.
  • itemListCountCache bound (item_count_cache.go): the generation now lives in the entry (as a tombstone after invalidation) instead of a second unbounded map, and an opportunistic sweep keeps the map count-bounded. A tombstone is only dropped once it has been expired longer than a load can still be in flight, so an invalidated in-flight load can never store its stale count.
  • SQLite pool lifetimes (database.go): the read and write pools now set SetConnMaxLifetime(30m) / SetConnMaxIdleTime(5m), matching Postgres.

Findings recorded (no code change)

  • Rate-limiter buckets are already bounded (internal/ratelimit/limiter.go): 64 shards, per-shard cap, 10 min idle sweep, and makeRoom LRU eviction.
  • Migrations are already cheap: runPendingMigrations loads applied versions in one query and skips them in memory; the per-migration idempotency predicates only run for migrations that are not stamped. A test now pins that.

Verification

  • go build ./internal/..., go vet — pass
  • golangci-lint on the changed packages — 0 issues
  • internal/repository/... — pass (31.5s)
  • internal/database/... — pass, except the pre-existing TestCompactCatalogContainsExactlyRetainedMigrations (fails on main too: catalog length 120 vs want 115)
  • internal/services/... — pass, except the pre-existing TestItemWorkspaceMovePreviewsAndCommitsExactPolicy (fails on main too)
  • New tests: count-cache over-bound eviction, grace expiry, generation tombstone; migration check-skip

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

…e (WI-1917)

- Remove the redundant second arg-building loop in GetTimeRollup.
- Hoist the ItemRepository onto HierarchyService and PermissionService
  instead of allocating one per call in hot helpers.
- Fold the item-list count cache generation into its entry and add an
  opportunistic sweep, so the per-workspace map is count-bounded and
  cannot grow under workspace churn. A generation tombstone is only
  dropped once it has been expired longer than a load can be in flight.
- Give the SQLite read and write pools the same connection lifetimes as
  Postgres (30m lifetime / 5m idle).

Recorded findings: the rate-limiter buckets are already bounded
(sharded, per-shard cap, idle sweep, LRU eviction), and the migration
runner loads applied versions in one query — per-migration idempotency
checks only run for unapplied migrations. A test now pins that.
@stefan-ernst
stefan-ernst merged commit 9b140a9 into main Nov 2, 2026
18 checks passed
@stefan-ernst
stefan-ernst deleted the wi-1917-backend-cleanup branch November 2, 2026 22:12
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