chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
# Client cache and Web Storage recovery
|
||||
|
||||
- 적용 대상: production-composed TanStack memory cache, registered Web Storage,
|
||||
cross-tab invalidate-only runtime
|
||||
- 비대상: query persistence와 IndexedDB query cache
|
||||
- 기준일: 2026-07-28
|
||||
|
||||
현재 query persistence는 정책과 runtime에서 `disabled`다. 기존 IndexedDB
|
||||
reference runtime은 query cache에 조립되지 않았으므로 query-cache incident에서
|
||||
database 삭제, migration 또는 hydration 조치를 수행하지 않는다.
|
||||
session/account Query lifecycle, strict query policy와 Web Storage v2 lifecycle도
|
||||
현재 `DESIGNED_NOT_IMPLEMENTED`다. 아래 목표 절차를 현재 runtime의 보장으로
|
||||
해석하지 않는다.
|
||||
|
||||
## 1. 변경할 수 없는 복구 원칙
|
||||
|
||||
- 서버가 server state와 authorization의 source of truth다.
|
||||
- cross-tab event는 invalidate hint다. delivery나 server commit 증명이 아니다.
|
||||
- remote event로 `queryClient.clear()`, account logout 또는 credential 폐기를
|
||||
수행하지 않는다.
|
||||
- `BroadcastChannel` 실패 후 localStorage fallback도 실패하면
|
||||
`DEGRADED_LOCAL_ONLY`가 정상 fallback이다. 현재 tab의 local invalidation과
|
||||
server request는 계속 동작해야 한다.
|
||||
- origin 전체 `localStorage.clear()`나 `deleteDatabase()`를 복구 명령으로
|
||||
사용하지 않는다.
|
||||
- query key, storage value, event/topic/epoch/source ID, user/account/tenant ID를
|
||||
incident log에 복사하지 않는다.
|
||||
- incident 대응 중 `QUERY_PERSISTENCE`를 켜거나 server response를 Web Storage에
|
||||
저장하지 않는다.
|
||||
|
||||
## 2. 최초 확인
|
||||
|
||||
1. 영향 release ID, browser family/version과 최초 발생 시각을 확인한다.
|
||||
2. `cache.operation.failed`와 `storage.operation.failed`의 allowlisted
|
||||
operation/outcome/reason만으로 범위를 좁힌다.
|
||||
3. runtime status가 `ACTIVE_BROADCAST`, `ACTIVE_STORAGE_FALLBACK`,
|
||||
`DEGRADED_LOCAL_ONLY`, `CLOSED` 중 무엇인지 확인한다.
|
||||
4. 현재 tab의 mutation 성공 후 local namespace가 stale 처리되는지 확인한다.
|
||||
5. 다른 tab의 오래된 화면이 단순 stale 표시인지, 실제 server authorization
|
||||
우회인지 분리한다. authorization 우회는 cache incident가 아니라 P1 security
|
||||
incident다.
|
||||
6. affected release의 query registry topic/version과 release cache epoch가
|
||||
일치하는지 확인한다.
|
||||
|
||||
## 3. Cross-tab invalidation degradation
|
||||
|
||||
### 신호
|
||||
|
||||
- `BROADCAST_OPEN_FAILED`
|
||||
- `BROADCAST_PUBLISH_FAILED`
|
||||
- `STORAGE_LISTENER_FAILED`
|
||||
- `STORAGE_PUBLISH_FAILED`
|
||||
- runtime status `DEGRADED_LOCAL_ONLY`
|
||||
|
||||
### 확인
|
||||
|
||||
1. browser policy, embedded/sandbox context 또는 privacy mode가
|
||||
BroadcastChannel/Web Storage를 제한하는지 확인한다.
|
||||
2. BroadcastChannel publish 실패 뒤 fixed localStorage pulse key로 정확히 한 번
|
||||
fallback하는지 확인한다.
|
||||
3. publishing tab은 `storage` event를 받지 않으므로 local invalidation을
|
||||
coordinator가 직접 수행했는지 확인한다.
|
||||
4. 다른 tab은 focus/reconnect 또는 명시적 refresh에서 server를 다시 읽는지
|
||||
확인한다.
|
||||
|
||||
### 안전한 조치
|
||||
|
||||
- local-only 상태에서는 사용자에게 현재 tab refresh action을 유지한다.
|
||||
- fallback regression이 특정 release에서 시작됐으면 이전 compatible release로
|
||||
rollback한다.
|
||||
- event payload에 query key/data를 추가하거나 TTL/size/source tracking limit을
|
||||
임시 확대하지 않는다.
|
||||
- 현재 runtime에는 별도 dynamic kill switch가 없다. 존재하지 않는 flag로
|
||||
복구됐다고 선언하지 않는다.
|
||||
|
||||
## 4. Duplicate, sequence gap과 refetch 증가
|
||||
|
||||
### 신호
|
||||
|
||||
- `DUPLICATE`, `STALE`, sequence-gap observation 증가
|
||||
- 여러 namespace의 active refetch 동시 증가
|
||||
- backend read traffic 증가
|
||||
|
||||
### 확인
|
||||
|
||||
1. 같은 source/epoch의 sequence gap인지 새 page epoch의 정상 sequence reset인지
|
||||
구분한다.
|
||||
2. duplicate가 BroadcastChannel과 storage fallback 양쪽에서 들어온 것인지
|
||||
확인한다.
|
||||
3. mutation lease가 유지되는 동안 remote hint가 topic별로 coalesce되는지
|
||||
확인한다.
|
||||
4. gap에서 inactive query까지 즉시 refetch하거나 remote hint를 다시 publish하는
|
||||
echo가 없는지 확인한다.
|
||||
|
||||
### 안전한 조치
|
||||
|
||||
- gap은 등록 namespace를 stale 처리하고 active query만 refetch한다.
|
||||
- backend가 압박을 받으면 API degradation runbook의 server read 보호 정책을
|
||||
적용한다. client query와 HTTP 양쪽 retry를 동시에 늘리지 않는다.
|
||||
- persistent event queue나 localStorage counter를 급히 추가하지 않는다.
|
||||
|
||||
## 5. Web Storage quota, corruption과 denial
|
||||
|
||||
### 신호
|
||||
|
||||
- `STORAGE_QUOTA_EXCEEDED`
|
||||
- `SIZE_LIMIT_EXCEEDED`
|
||||
- `VALUE_REJECTED`
|
||||
- `STORAGE_UNAVAILABLE`
|
||||
|
||||
### 확인
|
||||
|
||||
1. 실패 key가 registry backend/value codec/schema/TTL과 일치하는지 확인한다.
|
||||
2. value가 기본 16,384-byte hard cap 안인지 확인한다.
|
||||
3. corrupt/expired record가 exact physical key에서만 제거되고 다른 application
|
||||
key는 유지되는지 확인한다.
|
||||
4. `COLOR_SCHEME` quota fallback이 동일 serialized envelope와 TTL 규칙을 쓰는지
|
||||
확인한다.
|
||||
5. native exception message나 value가 diagnostics에 포함되지 않았는지 확인한다.
|
||||
|
||||
### 안전한 조치
|
||||
|
||||
- preference persistence 실패는 memory fallback 또는 safe default로 degrade한다.
|
||||
- exact corrupt/expired registered key만 제거한다.
|
||||
- credential key를 임시 storage key로 재등록하지 않는다.
|
||||
- origin 전체 clear, arbitrary key enumeration과 query-cache persistence 전환을
|
||||
금지한다.
|
||||
|
||||
## 6. Logout, account 전환과 stale data
|
||||
|
||||
현재 구현은 release cache epoch만 제공하고 session/account epoch와 opaque account
|
||||
partition은 아직 없다. 따라서 다음을 과장해 보장하지 않는다.
|
||||
|
||||
- cross-tab hint가 모든 tab의 logout을 완료했다.
|
||||
- old in-flight result가 새 account runtime에 기록되지 않는다.
|
||||
- persisted query data가 account별로 분리된다.
|
||||
|
||||
실제 account-switching 제품을 배포하기 전에 session owner 기반 local admission
|
||||
fence, old QueryClient cancel/clear, late-result generation fence와 account epoch를
|
||||
구현해야 한다. 그 전에는 external auth owner와 서버 authorization을
|
||||
authoritative하게 유지하고, 의심되는 stale personal data incident는 security
|
||||
owner에게 escalation한다.
|
||||
|
||||
VD-13 lifecycle이 구현된 제품의 안전한 전환 순서는 다음과 같다.
|
||||
|
||||
```text
|
||||
local session authority revoke
|
||||
-> old generation FENCED
|
||||
-> new query/mutation admission 거절
|
||||
-> in-flight cancel
|
||||
-> provider/listener detach
|
||||
-> old QueryClient clear + dispose
|
||||
-> old Web Storage/optional persistence partition purge policy
|
||||
-> 새 opaque scope와 새 QueryClient remount
|
||||
```
|
||||
|
||||
각 async callback은 terminal cache write 직전에 captured generation을 다시
|
||||
검증한다. broadcast logout 수신 여부와 무관하게 local 전환이 완료되어야 한다.
|
||||
optional query persistence를 선택한 제품은 durable namespace epoch/CAS와 old
|
||||
record resurrection fault까지 통과한 경우에만 restore를 다시 연다.
|
||||
|
||||
## 7. Cleanup과 종료
|
||||
|
||||
정상 dispose는 coordinator subscription, BroadcastChannel, storage listener,
|
||||
dedupe/high-watermark state와 pending remote set을 idempotent하게 정리한다.
|
||||
|
||||
확인:
|
||||
|
||||
- HMR/unmount 반복 뒤 listener/channel 수가 증가하지 않음
|
||||
- dispose 뒤 event가 query invalidation을 실행하지 않음
|
||||
- fixed pulse key가 publish 후 best-effort 제거됨
|
||||
- cleanup failure가 cached value나 identifier를 log하지 않음
|
||||
|
||||
## 8. 복구 완료 조건
|
||||
|
||||
- current tab의 committed mutation 후 local namespace invalidation 성공
|
||||
- available transport에서는 two-tab remote invalidation 성공
|
||||
- unavailable transport에서는 explicit `DEGRADED_LOCAL_ONLY`와 manual/focus
|
||||
revalidation 성공
|
||||
- duplicate/self/stale event가 추가 refetch를 만들지 않음
|
||||
- sequence gap은 bounded active-query reconciliation으로 종료
|
||||
- Web Storage corrupt/quota scenario가 exact-key cleanup 또는 documented fallback
|
||||
으로 종료
|
||||
- raw key/value/query/user identifier가 diagnostic evidence에 없음
|
||||
- affected browser의 manual two-tab smoke 기록 보존
|
||||
|
||||
two-page native cross-context transport suite는 존재하지만 현재 보존 evidence는
|
||||
Chromium/Firefox에 한정되고, production QueryClient/coordinator의 account
|
||||
lifecycle과 late-result fence까지 연결한 end-to-end case는 아직 없다. 따라서
|
||||
manual smoke나 transport-only suite를 production promotion evidence의 영구
|
||||
대체물로 사용하지 않는다. Chromium/Firefox/WebKit에서 scope 전환을 포함한
|
||||
동일 case set이 통과할 때 이 runbook의 promotion close criteria를 충족한다.
|
||||
|
||||
## 9. 관련 문서
|
||||
|
||||
- [API contract와 Server State recovery](./api-contract-and-server-state-recovery.md)
|
||||
- [VD-25 Server State Cache lifecycle](../architecture/decisions/VD-25-server-state-cache-lifecycle.md)
|
||||
- [Client cache and browser storage platform](../architecture/client-cache-and-storage.md)
|
||||
- [Browser data capability completion ledger](../architecture/browser-data-capability-completion-ledger.md)
|
||||
- [VD-13 Client cache scope and persistence](../architecture/decisions/VD-13-client-cache-scope-and-persistence.md)
|
||||
- [TypeScript, 상태 소유권, 데이터 흐름](../architecture/typescript-state-and-data-flow.md)
|
||||
- [Backend API degradation](../runbooks/FE-RB-003.md)
|
||||
- [Release, cache, and rollback contract](./release-cache-rollback.md)
|
||||
Reference in New Issue
Block a user