Skip to content

Add object cache for ID lookups#3

Merged
colemancda merged 4 commits into
masterfrom
feature/objectCache
Jul 14, 2026
Merged

Add object cache for ID lookups#3
colemancda merged 4 commits into
masterfrom
feature/objectCache

Conversation

@colemancda

Copy link
Copy Markdown
Member

Summary

Adds an in-memory object cache to SQLiteDatabase, mirroring CoreData's row cache. Repeated fetch(_:for:) calls return the cached ModelData without touching SQLite, skipping both the row query and the per-relationship queries that derive to-many values. Predicate fetches (fetch(_ fetchRequest:)) also register their results, so ID lookups following a list fetch are free.

Design

  • The cache is actor-isolated state ([EntityName: [ObjectID: ModelData]]), so no additional synchronization is needed.
  • Invalidation: a write to entity E touches E's own table, the foreign key columns of E's relationship destinations, and their shared join tables. Derived to-many values are computed only from those same foreign keys and join tables, so every write (insert/delete) drops the cached objects of E plus E's destination entities. Whole entity caches are dropped rather than single objects because a write can affect sibling rows of the same entity — e.g. reassigning a person to a new team also changes the old team's derived members.
  • The cache assumes the SQLiteDatabase actor is the database's only writer — the same coherency contract as separate CoreData stacks sharing one store file. SQLiteViewContext reads its own connection directly and always observes committed state.

Benchmarks

Measured against a production-scale model (13 entities, ~11K objects, file-backed database) with 100 single-object fetches by ID, two passes over the same IDs (M-series Mac):

Pass Before After
Cold (first fetch per ID) ~0.04–0.12s unchanged
Warm (repeat fetch per ID) same as cold ~0.001–0.002s

Warm ID lookups — the shape detail screens hit when reopening records — go from re-querying every time to effectively free, and are competitive with CoreData's row cache for the equivalent workload.

Tests

CacheTests covers cache population (by-ID and via predicate fetch registration) and every invalidation path: direct updates, inverse to-many changes from the foreign-key side, reassignment side effects on sibling rows, deletes, and delete-nullification of cached references. Full suite passes (50 tests).

@colemancda colemancda merged commit 0ff2be3 into master Jul 14, 2026
6 checks passed
@colemancda colemancda deleted the feature/objectCache branch July 14, 2026 01:13
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