Skip to content

docs(cache): ADR 0007 — selection-set-aware cache reads (PR-009a) - #1003

Merged
AnthonyMDev merged 1 commit into
cache-rewrite/phase-1-planfrom
cache-rewrite/phase-0-adr-007-selection-aware-reads
May 29, 2026
Merged

docs(cache): ADR 0007 — selection-set-aware cache reads (PR-009a)#1003
AnthonyMDev merged 1 commit into
cache-rewrite/phase-1-planfrom
cache-rewrite/phase-0-adr-007-selection-aware-reads

Conversation

@AnthonyMDev

@AnthonyMDev AnthonyMDev commented May 28, 2026

Copy link
Copy Markdown
Contributor

Goal

Lock the architectural decision behind the projection-aware sub-phase 1A.5 (PR-009a through PR-009h): cache reads under 3.0 carry per-field projection info, including each field's storage-shape type and the scalar-vs-list discriminator from ADR 0006, end-to-end from executor to SQL. The whole-record selectRecords(forKeys:) shape that PR-009 initially drafted is removed from the public protocol (already done in PR #1001's slim) and replaced with a projection-aware read API.

Why this is needed

PR-008 + the ADR-0006 amendment (PR-008b in the §8 list) produce a row-per-element schema with six type-specific columns and a position discriminator. Each scalar field is one row at position = -1; each list element is one row at position = 0..N-1. Five of the six value columns are NULL on every row, and list reads pull N rows where N is the list length. The schema's design property is that each field (and each list element) has its storage column determined by its GraphQL type, known at codegen time — but the current executor never communicates that type info to the cache.

Closing the loop requires changes to NormalizedCache, ApolloStore.load, CacheDataExecutionSource, and SQLiteNormalizedCache. That's a breaking-protocol-shape change to the public NormalizedCache contract — well within scope for the 3.0 major version bump per ADR 0001, but a one-way ratchet under semver. Doing it later means waiting for 4.0.

What this ADR commits to

Seven principles (full text in the file). Of particular note after ADR 0006's row-per-element decision:

  • Principle 1 — per-field type info now also covers the scalar-vs-list discriminator. A scalar Int field projects int_value filtered to position = -1; a [String] field projects string_value filtered to position >= 0 ordered by position.
  • Principle 3 — SQL-level projection now includes position predicates per field.
  • Principle 5 — upfront declaration by CacheDataExecutionSource is the highest-risk PR in the sub-phase; may split if the executor's lazy resolution pattern can't migrate in one shot.
  • Principle 7 — custom scalars expose their storage column-shape statically; mechanism fixed in PR-009b.

Non-goals captured:

  • The list-storage layout itself — that's ADR 0006's decision; this ADR consumes that layout's position discriminator and synthetic sub-record indirection for nested lists.
  • TTL evaluation during projection (handled separately by ADR 0003).
  • Any compatibility shim for the pre-3.0 NormalizedCache contract.

Alternatives rejected

Alternative Why rejected
Status quo — whole-record reads Leaves the schema's central optimization on the table permanently under semver
Field-name projection without type info Half a fix; would change the public contract twice within one major
Lazy field resolution via cache callback Turns one SELECT into N×M round trips per selection set
Eager whole-record + projection cache Helps steady-state but not first-read latency; complexity comparable to upstream plumbing
Single-column generic value with type tag Re-introduces JSON-blob's decode overhead; undoes the typed-column schema's benefit

Implementation sequence (post-ADR-0006 §8)

Slot Title Status
PR-008b feat(sqlite): position-keyed v4 schema per ADR 0006; not yet opened
PR-009 feat(sqlite): row-per-element CRUD against position-keyed schema open at #1001; pending PR-008b + rebase to amended scope
PR-009a docs(cache): ADR 0007 — selection-set-aware cache reads this PR
PR-009b refactor(cache): introduce FieldProjection types next, after this ADR + PR-008b + PR-009 land
PR-009c refactor(cache): NormalizedCache adopts field projection
PR-009d refactor(executor): CacheDataExecutionSource declares field reads upfront highest risk; may split
PR-009e refactor(cache): ApolloStore.load propagates field projection
PR-009f refactor(cache): GraphQLDependencyTracker consumes field projections
PR-009g feat(sqlite): field-aware selectFields with column projection retires the internal-test selectRecords from PR-009
PR-009h refactor(sqlite): SQLiteNormalizedCache switches + drop-and-rebuild migration was original PR-010

Phase 1A grows from the post-ADR-0006 count of 11 PRs to ~17 PRs, adding 4–6 weeks.

Acceptance criteria

  • ADR matches the file conventions of ADRs 0001–0006 (Status / Date / Phase 1 PR / Engineering plan reference header; Context / Decision / Alternatives / Migration / References sections).
  • Decision principles are stated such that PR-009b through PR-009h can each be reviewed against them.
  • Non-goals explicitly cross-reference ADR 0006 so the ADR doesn't get re-litigated when adjacent design questions surface.
  • Rollback / risk section captures the one-way-ratchet cost.
  • Branch rebased onto the post-ADR-0006 plan branch tip.

Stacks on

cache-rewrite/phase-1-plan at 4dfb34e4d (the §8 amendment commit). Doc-only — no code dependencies on PR #1001 or future PR-008b.

Followup

After this merges:

  1. PR-008b opens (v4 DDL change per ADR 0006; ~150 LoC).
  2. PR feat(sqlite): row-per-element CRUD against position-keyed schema (PR-009) #1001's amended PR-009 rebases onto PR-008b once that merges.
  3. PR-009b drafts FieldProjection types.

@apollo-librarian

apollo-librarian Bot commented May 28, 2026

Copy link
Copy Markdown

✅ Docs preview has no changes

The preview was not built because there were no changes.

Build ID: bcfdcd0a695d6e2dd8d7ae93
Build Logs: View logs


✅ AI Style Review — No Changes Detected

No MDX files were changed in this pull request.

Review Log: View detailed log

This review is AI-generated. Please use common sense when accepting these suggestions, as they may not always be accurate or appropriate for your specific context.

Captures the architectural shift to per-field column projection for
the row-per-field SQLite schema. The whole-record `selectRecords`
shape that PR-009 initially drafted is the wrong public contract —
shipping it under semver locks out the schema's central optimization
permanently. The fix is to thread per-field projection info, including
each field's storage-shape type, from the executor down to the cache.

Decision principles fixed by this ADR:
- The NormalizedCache protocol changes its read shape — breaking
  change accepted under ADR 0001.
- The executor declares field reads upfront, not lazily.
- SQL-level column projection is the implementation target for
  ApolloSQLiteDatabase.
- InMemoryNormalizedCache filters fields client-side.
- Custom scalars expose their storage column statically; mechanism
  fixed in PR-009b.

Implementation sequence: eight new sub-phase 1A.5 PRs (009a–009h)
between the slimmed PR-009 and SQLiteNormalizedCache's switchover.
Phase 1A grows from 10 to ~16 PRs, adding 4–6 weeks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AnthonyMDev
AnthonyMDev force-pushed the cache-rewrite/phase-0-adr-007-selection-aware-reads branch from 115922d to 0c1d2a2 Compare May 29, 2026 16:54
@AnthonyMDev
AnthonyMDev merged commit 9b2dbb3 into cache-rewrite/phase-1-plan May 29, 2026
17 checks passed
@AnthonyMDev
AnthonyMDev deleted the cache-rewrite/phase-0-adr-007-selection-aware-reads branch May 29, 2026 17:49
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