Files

29 KiB

VD-25: Server State Cache lifecycle

  • 상태: Accepted — reference bound-query/input-aware mutation baseline composed, lifecycle delta pending
  • 결정일: 2026-07-28
  • TanStack Query memory runtime: COMPOSED
  • reference bound-query/profile/input-aware duplicate coordination: COMPOSED
  • session-generation/identity lifecycle: COMPOSED
  • conditional sidecar/optimistic layer/cursor runtime: AVAILABLE_NOT_COMPOSED
  • account projection/infinite-query/effect reconciliation delta: DESIGNED_NOT_IMPLEMENTED
  • normalized graph cache: NOT_SELECTED
  • query persistence product selection: NOT_SELECTED
  • 관련 결정: VD-13, VD-23, VD-24, VD-26, VD-27, VD-29, VD-30
  • 상세 설계: API contract, Schema, Mapper와 Server State

배경

현재 production bootstrap은 runtime별 QueryClient와 invalidation coordinator를 실제로 조립한다. reference presentation은 application input을 useApplicationQuery()/useApplicationMutation()에 연결하며 다음을 제공한다.

  • AbortSignal cancellation
  • finite global stale/gc default
  • Query network retry off
  • stale-degraded 표시
  • exact-key optimistic snapshot/rollback
  • conflict 표면
  • mutation topic lease와 cross-context invalidate-only hint

reference list/detail은 bound definition이 key, executor와 profile을 함께 제공한다. strict canonical codec은 depth/node/string/encoded-byte, cycle/shared reference, undefined/NaN/negative-zero, sparse array, accessor와 non-plain object를 닫고 runtime-private opaque identity만 query key에 넣는다. profile의 stale/gc/ refetch/retry owner와 result item/byte admission이 실제 hook에 적용된다.

current invalidation definition은 namespace당 singular topic이고 같은 topic의 multi-namespace fan-out을 compose하지 못한다. 아래 bounded topic-set registry는 target delta이며 현재 runtime 증거가 아니다.

production scope runtime은 session transition 즉시 old generation을 fence하고 QueryClient cancel/clear 뒤 새 opaque scope를 발급한다. identity registry는 active lease, refcount, bounded LRU, canonical-byte/entry ceiling과 token collision 검사를 scope별로 소유한다. mutation duplicate baseline도 scope exact semantic input만 join하고 late result를 폐기한다.

ordered optimistic layer, conditional validator CAS sidecar와 bounded cursor chain은 실행 가능한 test runtime까지 존재하지만 reference backend/definition에는 아직 연결하지 않았다. 남은 부분은 account identity projection, logical-key serialization, HTTP 304 transaction, product optimistic membership/revision, infinite-query binding과 effect certainty reconcile이다.

소유권

VD-13:

  • CacheScopeSnapshot
  • ORIGIN/ACCOUNT/SESSION projection
  • QueryClient generation/fence/remount
  • strict canonical key codec 공통 구현
  • cross-tab wire와 durable namespace epoch
  • optional IndexedDB persistence와 restore
  • logout/account switch purge

VD-25:

  • operation/application query definition binding
  • per-query freshness/gc/refetch/result budget
  • cache admission과 mapped value contract
  • cursor/infinite pagination
  • conditional revalidation integration
  • mutation concurrency, optimistic patch와 reconciliation
  • invalidation/seed policy
  • transport-independent error/stale behavior

VD-25는 VD-13의 scope snapshot/key codec을 소비하고 다른 epoch/fingerprint protocol을 만들지 않는다.

결정

1. TanStack Query가 유일한 기본 Server State owner다

REST, GraphQL과 gRPC-Web unary result는 transport-independent application projection으로 mapping된 뒤 TanStack Query memory cache에 들어갈 수 있다.

기본적으로 설치하지 않는다.

  • Redux/Zustand server entity copy
  • Apollo/urql normalized cache
  • raw HTTP response cache wrapper
  • generated client SDK cache
  • custom Map singleton

GraphQL normalized cache가 실제로 필요하면 bounded context에서 TanStack operation-result cache를 대체하는 별도 ADR을 승인한다. 두 cache에 같은 entity를 동시 write하지 않는다.

browser HTTP cache/Cache Storage, TanStack Query memory와 IndexedDB query persistence는 서로 다른 owner다.

2. Bound Query Definition

caller가 query key와 executor를 독립적으로 조립하지 않는다.

QueryDefinition<Input, Value>
  definitionId
  owner
  operationId
  inputCodec
  keyCodecId
  serverStateProfileId
  scopePersistencePolicyId
  resultContractId
  execute(validatedInput, executionContext)

binding:

bindQuery(definition, rawInput, CacheScopeSnapshot)
  -> validate/canonicalize input
  -> derive branded query key
  -> resolve immutable policy
  -> freeze execute closure and captured generation
  -> BoundQuery<Value>

presentation API:

useApplicationQuery(boundQuery)

queryKey, queryFn, stale/gc/retry와 arbitrary TanStack option을 feature page에서 따로 넘기지 않는다. escape hatch가 필요하면 새 profile을 먼저 등록한다.

3. Semantic query identity

query key는 VD-13의 단일 normative layout을 그대로 사용한다.

[
  "query",
  keySchemaVersion,
  scopeProjectionFingerprint,
  namespaceName,
  namespaceVersion,
  queryDefinitionVersion,
  canonicalSemanticInput
]
  • namespace/key schema/version과 scope projection은 VD-13 profile-owned
  • query definition version과 semantic input projection은 VD-25 definition-owned
  • scope projection은 VD-13의 exact profile output
  • input은 strict codec의 plain immutable representation
  • REST URL/query string, ETag와 cursor raw value를 root identity에 넣지 않음
  • GraphQL document/persisted hash를 넣지 않음
  • protobuf bytes/generated message를 넣지 않음
  • presentation locale/formatted string을 넣지 않음

transport migration이 use-case/result meaning을 보존하면 semantic query family를 유지할 수 있다. schema/mapper meaning, scope나 output identity가 바뀌면 query-definition/release epoch를 바꾼다.

strict key codec은 다음을 거절한다.

  • undefined, sparse array
  • NaN, Infinity, negative zero policy mismatch
  • bigint/symbol/function
  • Date/Map/Set/RegExp/typed array/native/class instance
  • accessor/proxy/prototype pollution key
  • cycle/shared-reference ambiguity
  • non-plain object
  • depth/node/part/string/encoded-byte ceiling 초과

query key에 PII/business ID를 직접 넣지 않는다. 필요한 resource identity는 feature policy가 발급한 opaque bounded token으로 투영한다.

cursor와 command 동일성은 raw value나 충돌 가능 digest만으로 판정하지 않는다.

RuntimeIdentityTokenCodecV1
  canonicalCodecVersion
  maxCanonicalBytes
  maxInternEntries
  maxInternCanonicalBytes
  tokenEntropyBits >= 128
  lifetime = RUNTIME_SCOPE
  • strict length-prefixed typed canonical encoding이 exact equality source다.
  • runtime-private intern table이 canonical bytes를 opaque random token에 일대일로 binding하고 token collision을 reverse map으로 검사한다.
  • 같은 token 후보가 다른 canonical bytes와 충돌하면 새 token을 발급한다. bounded 재시도 후에도 해결되지 않으면 IDENTITY_TOKEN_COLLISION으로 admission/join을 fail-closed한다.
  • intern row는 lease/refcount를 가진다. Query entry가 설치된 동안, active observer/fetch와 mutation/join이 진행되는 동안 해당 token을 eviction하지 않는다.
  • Query removal/GC에서 query token lease를, mutation terminal/join waiter settlement에서 command token lease를 exact once release한다. runtime/scope close는 남은 table을 전부 폐기한다.
  • refcount 0 row만 bounded LRU eviction할 수 있다. entry 수 또는 total canonical bytes ceiling을 active lease 때문에 회수할 수 없으면 IDENTITY_INTERN_LIMIT_EXCEEDED로 신규 cache/command admission을 fail-closed한다.
  • canonical bytes/raw cursor/command input은 query key, diagnostics, cross-context wire와 persistence에 넣지 않고 runtime/scope close에서 폐기한다.
  • token은 backend idempotency key, authorization proof나 durable identity가 아니다.

4. ServerStateProfile

ServerStateProfileV1
  profileId
  classification
  scopePersistencePolicyId
  staleTimeMs
  gcTimeMs
  refetchOnMount
  refetchOnFocus
  refetchOnReconnect
  networkMode
  retryOwner = TRANSPORT | QUERY | NONE
  maxResultItems
  maxEstimatedResultBytes
  paginationProfileId | null
  conditionalProfileId | null
  placeholderPolicy
  initialFailurePolicy
  refreshFailurePolicy
  authorizationFailurePolicy
  contractFailurePolicy
  invalidationTopicRefs[] = { topicId, topicVersion }
  owner

implementation ceilings:

  • query invalidationTopicRefs set/version은 joined VD-13 QueryScopePersistencePolicy와 exact match
  • gcTimeMs는 inactive retention이고 staleTimeMs는 freshness이므로 staleTimeMs <= gcTimeMs를 일반 불변조건으로 강제하지 않는다.
  • VD-13 persistence를 선택한 경우에만 restore maxAgeMs, retention과 gcTimeMs의 join compatibility를 검증한다.
  • finite gc 기본, Infinity는 explicit immortal-static profile만
  • stale/gc 최대값
  • result item/estimated byte 상한
  • maximum pages
  • refetch trigger storm coalescing
  • foreground/background concurrency

현재 global 30초/5분은 reference default이지 모든 product query의 production 정책이 아니다.

5. Retry owner

한 network operation에는 retry owner가 정확히 하나다.

상황 기본 owner
installed REST REST transport
persisted GraphQL GraphQL transport
Connect unary Connect adapter 또는 selected edge 중 exact one
gRPC-Web unary gRPC-Web transport
pure local query computation Query 또는 none

transport retry가 있는 definition은 TanStack retry=false다. Query retry callback이 same gateway call을 다시 실행해 transport attempts를 배가하지 않는다.

manual UI retry는 새 logical query execution이다. keyed command의 ambiguous outcome을 query retry처럼 재실행하지 않는다.

6. Cache admission

query cache에 admission 가능한 값:

  • VD-24 mapper가 만든 immutable plain application read model
  • exact result contract/version
  • current scope/runtime generation
  • item/estimated-byte ceiling 안
  • complete result 또는 operation이 허용한 typed partial result

금지:

  • raw JSON/GraphQL envelope
  • generated protobuf message/client
  • Response/ReadableStream
  • auth/CSRF/idempotency/cursor/validator/trace metadata
  • thrown Error/AppFailure detail payload
  • function/class/domain service/native object

admission 순서:

transport success
  -> schema
  -> mapper
  -> result budget
  -> scope/generation fence
  -> Query commit

어느 단계든 실패하면 cache write 0회다.

7. Result size

maxEstimatedResultBytes는 memory reservation이 아니라 hard admission guard다.

  • mapper output plain data를 bounded estimator로 측정
  • string UTF-8 bytes, key overhead, array/object node count를 보수적으로 합산
  • cycle/class/native/accessor는 측정 전에 거절
  • 측정 자체가 deadline/node ceiling을 넘으면 admission 거절
  • result cap을 넘겨도 transport success를 unbounded UI state로 반환하지 않고 RESULT_LIMIT_EXCEEDED 또는 server pagination requirement로 닫음

large binary/collection은 streaming/file 또는 cursor page capability로 이동한다.

8. Query lifecycle

IDLE
  -> LOADING
  -> SUCCESS | EMPTY | TERMINAL_ERROR

SUCCESS | EMPTY
  -> REFRESHING
  -> SUCCESS | EMPTY
  -> STALE_DEGRADED
  -> TERMINAL/REAUTH when policy forbids stale visibility

current AsyncState의 base와 overlay 구분을 유지한다.

failure policy:

failure default
transient network/5xx refresh failure valid previous data + stale-degraded
caller/navigation abort terminal error로 표시하지 않음
auth required sensitive/account query는 stale 숨김, reauth
forbidden/account switch current scope data 즉시 숨김/clear
schema/mapper/contract mismatch cache write 금지, default stale 숨김 또는 explicit safe-static exception
rate limit previous data 정책 + retry-after UX
not found feature policy에 따라 empty/remove/tombstone

query profile이 sensitive stale data를 계속 보여 주는 결정을 global fallback으로 상속하지 않는다.

9. Freshness와 refetch

staleTime은 business correctness/authorization TTL이 아니다.

  • focus/reconnect/mount refetch는 profile별
  • simultaneous trigger는 one in-flight query로 coalesce
  • minimum refetch interval과 deadline 적용
  • visibility offline state는 hint이며 server revision을 대체하지 않음
  • response age/cache-control을 staleTime으로 자동 변환하지 않음
  • backend push/invalidation은 stale hint이며 authoritative refetch를 시작

freshness-sensitive command/read-after-write는 mutation receipt/revision 또는 authoritative refetch contract를 사용한다.

10. Conditional revalidation

REST app-managed ETag profile만 internal validator metadata를 사용할 수 있다.

ValidatorBinding
  query definition/fingerprint
  scope fingerprint
  runtime generation
  representation version
  opaque validator
  • raw validator는 query value/key/diagnostics에 넣지 않음
  • 304는 exact binding + existing mapped cache value가 있을 때만 fresh transition
  • 304 freshness transition은 같은 query-entry cache revision에 CAS가 성공할 때만 commit하며, concurrent 200/removal 뒤의 late 304를 폐기
  • value가 없거나 wrong generation이면 304 success로 만들지 않음
  • validator mismatch/full 200은 normal schema/mapper/admission을 다시 수행하고 mapped value commit과 validator install/update를 하나의 entry transaction으로 취급
  • Query removal/GC, scope/logout/account switch, release/contract/schema/mapper epoch 변경과 incompatible cache clear에서 validator sidecar도 함께 폐기
  • ordinary invalidation 때 validator를 conditional refetch까지 보존할지 즉시 폐기할지는 profile이 고정하며 query entry와 독립적으로 남기지 않음

GraphQL/gRPC metadata를 arbitrary ETag로 해석하지 않는다. application revision field를 schema/mapper가 명시적으로 제공한 경우 별도 revalidation policy가 사용한다.

11. Cursor pagination

PaginationProfile
  CURSOR_SINGLE_PAGE
  CURSOR_INFINITE
  OFFSET_STABLE

cursor page:

CursorPage<T>
  items
  nextCursor | null
  hasMore
  snapshotToken | null

page invariant:

  • hasMore === (nextCursor !== null)을 codec에서 강제한다.
  • page item count는 requested/implementation ceiling 이하다.
  • chain 안의 snapshot token은 provider profile이 허용한 null/동일 값만 사용한다.
  • hasMore=true인 empty/non-progress page는 explicit sparse-page profile이 없으면 contract failure다.

CURSOR_INFINITE root key:

  • filters/sort/page size semantics
  • scope
  • page definition version
  • cursor 제외

CURSOR_INFINITE page parameter:

  • adapter-private/opaque bounded cursor
  • previous page/snapshot binding
  • raw cursor를 diagnostics/URL state/persistence에 임의 저장하지 않음

CURSOR_SINGLE_PAGE:

  • first page의 null marker 또는 current cursor의 runtime-scoped non-reversible identity token을 canonicalSemanticInput에 포함한다.
  • raw cursor는 bound executor closure에만 두며 query key/value/diagnostics에 넣지 않는다.
  • runtime-private exact equality guard/token binding이 실패하면 single-page cache admission을 끄고 closed failure로 끝낸다.
  • runtime-scoped token을 쓰는 CURSOR_SINGLE_PAGEMEMORY_ONLY다.

infinite policy:

  • max pages
  • max total items
  • max estimated bytes
  • repeated cursor/non-progress/loop detection
  • page eviction direction
  • refresh strategy: first page only, visible window 또는 complete bounded chain
  • item stable identity/duplicate/revision conflict policy
  • snapshot changed 시 old/new page를 섞지 않고 restart

pagination persistence는 기본 disabled다. CURSOR_SINGLE_PAGE를 durable하게 만들려면 stable partition-bound keyed codec/key lifecycle을 별도 ADR로 승인해야 하며 runtime token을 persistence key로 재사용하지 않는다. CURSOR_INFINITE persistence를 선택하려면 VD-13 profile이 cursor와 snapshot의 classification/expiry, maximum persisted pages/bytes, restored pageParams 사용 여부를 명시적으로 승인해야 한다. raw sensitive cursor 또는 만료 후 page parameter를 IndexedDB에 저장하지 않는다.

offset pagination은 insert/delete drift를 허용하는 dataset에서 사용하지 않는다.

12. Mutation Definition

MutationDefinition<Input, Value>
  definitionId
  operationId
  inputCodec
  logicalKeyCodec
  commandIdentityTokenCodecId
  concurrencyPolicy
  duplicatePolicy
  optimisticPolicyId | null
  invalidationTopicRefs[] = { topicId, topicVersion }
  seedPolicyId | null
  conflictPolicyId
  effectCertaintyPolicy
  owner

presentation:

useApplicationMutation(boundMutation)

caller는 raw optimistic query key/update function과 invalidation topic을 조립하지 않는다.

mutation topic ref는 unique 0..16개이고 모두 VD-13 global topic registry의 exact version으로 resolve돼야 한다. 한 ref가 가리키는 bounded namespace set만 invalidate하며 caller가 runtime에 topic을 추가하지 못한다.

13. Mutation concurrency

ConcurrencyPolicy
  PARALLEL
  SERIAL_BY_LOGICAL_KEY
  SUPERSEDE_PENDING_READ_BY_LOGICAL_KEY
  REJECT_WHILE_ACTIVE_BY_LOGICAL_KEY

DuplicatePolicy
  JOIN_IDENTICAL
  REJECT_DUPLICATE
  ALLOW_INDEPENDENT
  • logical key는 validated input의 approved opaque identity
  • command identity token은 operation/definition version, current scope partition과 전체 validated semantic inputRuntimeIdentityTokenCodecV1으로 intern해 만든다. UI transient field와 transport bytes/idempotency key는 canonical equality source에 포함하지 않는다.
  • logical key는 serialization/conflict group이고 exact canonical equality + identity token은 동일 command 판별 값이다. 두 값을 서로 대체하지 않는다.
  • JOIN_IDENTICAL은 exact equality guard도 통과한 같은 identity token의 기존 in-flight Promise와 terminal result를 공유하며 transport/optimistic layer를 추가하지 않는다.
  • REJECT_DUPLICATE는 exact-identical token이 active이면 fetch 0회와 closed DUPLICATE_IN_FLIGHT를 반환한다.
  • ALLOW_INDEPENDENT는 같은 exact identity도 독립 command로 실행한다. backend replay/idempotency와 UX가 이를 명시적으로 허용한 operation에만 등록한다.
  • distinct input을 같은 Promise에 join하지 않음
  • same hook, two hooks, two routes의 coordinator가 동일 policy를 사용
  • coordinator는 hook-local singleton이 아니라 runtime/scope 수명의 registry-owned service이며 scope generation 전환에서 신규 admission을 닫고 late commit을 fence
  • SUPERSEDE는 이미 server로 보낸 non-replayable command를 cancel/rollback했다고 가정하지 않음
  • local serialization은 server idempotency/concurrency authority가 아님
  • scope/generation change는 pending result commit을 fence

14. Optimistic patch

snapshot 전체 restore만 사용하지 않는다.

OptimisticLayer
  mutationId
  logicalKey
  commandIdentityToken
  baseCacheRevision
  expectedEntityRevision | null
  patch
  inversePatch
  affectedQueryDefinitions

선택 가능한 구현:

  • cache entry revision CAS
  • ordered optimistic layer log
  • operation-specific compare-and-apply patch

공통:

  1. affected exact queries cancel
  2. bounded current revision/value 확인
  3. registered pure patch 적용
  4. other mutation layer와 ordering 보존
  5. failure에서 자기 layer만 제거/역적용
  6. success result/revision과 reconcile
  7. invalidation/refetch

old whole snapshot을 복원해 다른 mutation commit을 덮지 않는다.

optimistic update를 하지 않는 조건:

  • snapshot/patch/result byte ceiling 초과
  • cache entry missing/wrong revision
  • non-deterministic merge
  • high-conflict command
  • scope/generation transition
  • unknown effect certainty

그 경우 pending UX만 보여 주고 server response/refetch를 기다린다.

15. Effect certainty와 conflict

MutationEffect
  NOT_APPLIED
  COMMITTED
  UNKNOWN
  • timeout/cancel/network failure가 NOT_APPLIED를 자동 의미하지 않음
  • keyed backend status/receipt가 있어야 ambiguous command reconcile 가능
  • UNKNOWN은 새 idempotency key로 자동 retry 금지
  • 409 business conflict, REST 412, GraphQL safe conflict code, gRPC ABORTED를 common conflict surface로 mapping하되 의미 차이는 feature policy가 소유
  • server revision/merge/overwrite decision은 cache가 아니라 use case 소유

16. Mutation success, seed와 invalidation

server commit 뒤:

  • exact returned result를 schema/mapper/fence/budget 검증
  • registered detail seed policy가 있으면 exact current entity/revision만 write
  • list/aggregate는 default invalidate
  • list patch는 deterministic sort/filter/membership policy가 있을 때만
  • invalidation topic은 query key가 아닌 opaque registry identity
  • current mutation lease 동안 remote hints coalesce

local invalidation failure는 committed command를 failure로 바꾸지 않는다. cache health를 degraded로 기록하고 bounded authoritative refetch를 예약한다.

17. Cross-context

현재 cross-tab wire는 invalidate-only다. 유지한다.

  • query data/key/input/cursor/validator를 broadcast하지 않음
  • remote event는 authority가 아니라 stale hint
  • account/scope/version 검증은 VD-13
  • mutation ordering/optimistic layer를 tab 간 복제하지 않음
  • sequence gap은 모든 registered namespace를 stale 처리하되 active query만 bounded refetch한다. inactive query는 다음 mount/focus에서 revalidate하고, persistence가 선택된 경우 durable ledger refresh는 VD-13 절차를 따른다.

18. GraphQL과 normalized cache

persisted GraphQL query도 mapped operation result를 TanStack에 cache한다.

  • query key는 semantic application input
  • GraphQL document/hash는 key/value에 없음
  • GraphQL SDK cache는 no-cache/disabled
  • partial data default reject
  • approved partial result는 completeness metadata를 application contract가 소유
  • missing/error field를 previous complete value와 자동 merge하지 않음

normalized entity cache가 필요하면:

  • bounded context 하나가 TanStack operation cache를 대체
  • key fields/typename, eviction, pagination merge, optimistic layer, logout/scope, persistence와 removal을 별도 ADR
  • dual write/read 금지

현재 NOT_SELECTED다.

19. Connect/gRPC-Web server stream

ordinary Query는 terminal operation 결과를 전제로 한다.

  • unary는 normal query 가능
  • finite server stream을 complete aggregate로 쓸 경우 staging buffer에 bounded accumulate하고 valid Connect EndStream 또는 gRPC-Web terminal status, schema/mapper/fence 뒤 atomic cache commit
  • long-running stream은 ServerStreamPort와 registered reducer/invalidation owner
  • frame마다 query cache를 append하여 unbounded event history를 만들지 않음
  • stream reconnect를 Query retry로 하지 않음
  • gap/overflow는 current snapshot 폐기 또는 authoritative query refetch

20. Persistence, SSR와 offline

  • memory cache runtime은 COMPOSED
  • IndexedDB query persister reference는 VD-13 기준 DESIGNED_NOT_IMPLEMENTED
  • product persistence는 NOT_SELECTED
  • SSR hydration은 NOT_SELECTED
  • offline mutation queue는 NOT_SELECTED

VD-25 profile은 persistence를 직접 켜지 않는다. joined VD-13 scope/persistence profile, reference runtime과 제품 allowlist/retention/scope가 모두 구현·선택된 query만 IndexedDB persistence를 사용할 수 있다.

server-state policy를 이유로 Web Storage에 query payload를 넣지 않는다.

21. Security와 privacy

  • authorization result를 staleTime/cache hit으로 대체하지 않음
  • account/logout transition에서 sensitive data를 즉시 fence/hide
  • query key에 raw PII/account/resource ID/URL/document/message 금지
  • cache value에 credential/header/validator/trace/error raw data 금지
  • optimistic layer에도 command token/raw body를 저장하지 않음
  • command identity token/logical key를 diagnostics, cross-context wire나 persistence에 넣지 않음
  • developer tools/diagnostics production exposure policy
  • cache poisoning 방지를 위해 schema/mapper/result contract와 generation 검증
  • cross-context event에 data 없음

22. Observability

허용:

  • query/mutation definition/profile ID
  • hit/miss/stale/fresh/refresh/evict outcome
  • result item/estimated-byte/page bucket
  • runtime identity intern entry/canonical-byte/active-lease bucket
  • focus/reconnect/invalidation refetch reason
  • mutation concurrency/duplicate/optimistic/rollback/conflict/effect bucket
  • invalidation/seed/degraded recovery outcome
  • scope/profile version의 low-cardinality bucket

금지:

  • query key/input/value
  • command identity token/logical key
  • identity intern canonical bytes/token actual value
  • cursor/snapshot/validator/revision actual value
  • resource/account/tenant ID
  • GraphQL/protobuf/REST DTO
  • optimistic patch/snapshot

23. Testing

query definition/key:

  • definition/input/key/executor type/runtime binding
  • VD-13↔VD-25 topic set/version exact join과 bounded many-to-many fan-out
  • runtime identity token same-input stability, random-token collision regeneration과 bounded failure의 cache/join 0회
  • intern entry/total-byte ceiling, active non-eviction, Query GC/mutation terminal lease release와 runtime-close leak 0
  • undefined/NaN/Date/class/accessor/cycle/sparse/oversize collision fixture
  • same semantic input stable identity
  • protocol wire identity 변화가 key에 들어가지 않음
  • per-profile stale/gc/refetch/retry owner

cache admission:

  • mapped immutable plain value only
  • item/estimated byte cap
  • schema/mapper/generation failure write 0
  • auth/contract failure stale visibility

mutation:

  • same semantic command identity, same logical key의 distinct input
  • same hook/two hooks/two routes
  • runtime/scope coordinator의 logical-key serial/parallel과 join/reject/independent duplicate 결과
  • out-of-order success/failure
  • optimistic layer/CAS rollback without overwriting other commit
  • effect NOT_APPLIED/COMMITTED/UNKNOWN
  • server response detail seed + list invalidate
  • invalidation failure after commit
  • account switch during pending command

pagination:

  • null/repeated/cyclic cursor
  • single-page cursor identity-token collision/isolation과 memory-only enforcement
  • hasMore/nextCursor 불일치와 snapshot drift
  • snapshot change
  • max page/item/byte
  • page eviction/refetch
  • duplicate identity/revision policy
  • cancellation and late page

integration:

  • REST/GraphQL/gRPC unary → schema → mapper → cache → UI
  • focus/reconnect/offline/stale-degraded
  • conditional 304 exact binding
  • cross-tab invalidation/mutation lease
  • logout/account/release generation
  • finite stream atomic commit and overflow

24. Rollout

  1. VD-13 strict key codec/scope snapshot interface를 확정한다.
  2. current useApplicationQuery({queryKey, execute}) 뒤에 bound definition adapter를 추가한다.
  3. reference queries를 shadow key/policy로 비교하되 secondary cache write 금지.
  4. per-profile policy/result ceiling을 read query에 canary한다.
  5. typed mutation definition과 input-aware coordinator를 도입한다.
  6. optimistic layer/CAS를 low-conflict command에만 canary한다.
  7. cursor page reference vertical을 구현한다.
  8. arbitrary key/executor와 raw optimistic callback API를 제거한다.
  9. account/generation browser evidence와 runbook drill 뒤 traffic을 올린다.

rollback:

  • 신규 query/mutation admission/optimistic patch를 kill switch로 닫음
  • current scope queries cancel
  • unsafe/incompatible memory cache clear
  • pending command effect certainty reconcile
  • current basic facade 또는 no-optimistic authoritative refetch로 downgrade
  • schema/mapper/query definition/backend artifact를 coherent set으로 복구

규범 기준

완료 기준

  • caller가 arbitrary key/executor/TanStack option을 조합할 수 없다.
  • strict key codec과 VD-13 scope projection이 모든 query에 적용된다.
  • VD-13 normative key layout과 scope/persistence profile을 재정의하지 않고 exact join한다.
  • mapped/bounded/current-generation value만 cache에 들어간다.
  • transport와 Query retry owner가 중복되지 않는다.
  • cursor page가 next/snapshot/loop/page/item/byte ceiling을 갖는다.
  • runtime-private exact equality guard까지 통과한 command identity만 declared join되고 distinct input이 같은 Promise로 잘못 join되지 않는다.
  • concurrent optimistic rollback이 다른 committed update를 덮지 않는다.
  • effect certainty, conflict, seed와 invalidation owner가 operation별로 닫힌다.
  • GraphQL normalized dual cache와 unbounded stream cache가 없다.
  • scope/logout/provider fault와 rollback/removal evidence가 통과한다.