@cat-factory/caching 0.17.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,25 +31,80 @@ consume through the kernel `AppCaches` port, implemented on
31
31
  only the in-memory machinery. The Redis notification classes are loaded dynamically
32
32
  by the Node facade alone, behind `REDIS_URL`.
33
33
 
34
- ## The Cloudflare Worker profile (`ISOLATE_SAFE_APP_CACHES_PROFILE`)
34
+ ## The Cloudflare Worker profiles
35
35
 
36
- A Worker isolate has no cross-isolate invalidation bus and no Redis, so a TTL'd
36
+ A Worker isolate has no cross-isolate invalidation bus and no Redis, so a bare TTL'd
37
37
  in-isolate cache over **mutable cross-instance state** would serve stale data after a
38
- write processed by another isolate: a correctness bug, not an optimization. The
39
- Worker therefore wires the isolate-safe profile: caches of mutable state are
40
- configured **pass-through** (`enabled: false`; every read runs its load), and only
41
- caches of immutable or self-verifying entries (sha-pinned repo reads, static
42
- catalogs) get real TTLs. Distributed invalidation is a
43
- genuine Node-only concern, not a facade-parity gap: the Worker's cross-instance state
44
- already lives in globally-addressed Durable Objects / D1. Revisit only if a
45
- per-isolate staleness bug actually surfaces.
38
+ write processed by another isolate: a correctness bug, not an optimization. Push is
39
+ structurally unavailable there (an isolate holds no subscription between requests), so
40
+ the Worker has two stances, selected by whether its `CACHE_GENERATIONS` Durable Object
41
+ binding exists:
42
+
43
+ - **`ISOLATE_SAFE_APP_CACHES_PROFILE`** (the fallback, and prior behaviour): caches of
44
+ mutable state are **pass-through** (`enabled: false`; every read runs its load), and
45
+ only caches of immutable or self-verifying entries (sha-pinned repo reads, external
46
+ documents re-validated by a version probe) get real TTLs.
47
+ - **`ISOLATE_COHERENT_APP_CACHES_PROFILE`**: the isolate-safe profile plus
48
+ **pull-coherent** caches. A coherent cache keeps a real TTL, and its profile entry
49
+ carries a `coherencyWindowMsecs`: a read whose group snapshot is older than the
50
+ window re-reads the injected `CacheGenerationStore` (one monotonic counter per
51
+ (cache, group), one round trip per group serving every coherent cache) and, on a
52
+ moved counter, applies layered-loader 16.1's local, fencing, non-publishing
53
+ `applyRemoteInvalidation*` primitives before serving. Every invalidation site bumps
54
+ the directory right after its local invalidation, awaited by the write path.
55
+ Cross-isolate staleness is bounded by the window (5s on the pilot), instead of
56
+ indefinite (a bare TTL) or zero-at-the-cost-of-every-read (pass-through).
57
+
58
+ Error posture, deliberately asymmetric: a probe failure fails CLOSED (the read
59
+ invalidates locally and loads fresh, so a directory outage degrades to pass-through
60
+ performance, never staleness); a bump failure fails OPEN (the write and its local
61
+ invalidation already happened; peers heal at the TTL, and
62
+ `cache.coherency_bump_failure` is the visible trace). `createAppCaches` refuses a
63
+ profile that sets a window on an enabled cache with no `generationStore` wired.
64
+
65
+ A coherent cache also declares whether it ever invalidates CACHE-WIDE, with
66
+ `cacheWideInvalidation`. Only a cache that does needs the reserved `'*'` epoch counter
67
+ probed beside its own group, and that shard is ONE globally placed Durable Object, so a
68
+ cache with no `invalidateAll` call site would be paying a cross-colo round trip that
69
+ structurally cannot return news. It is declared rather than inferred, and `invalidateAll`
70
+ on a coherent cache that did not declare it THROWS: dropping the entries here while every
71
+ peer keeps serving theirs to the TTL is exactly the hole the flag exists to close.
72
+
73
+ ## Isolate runtimes: nothing may cross an invocation
74
+
75
+ Two rules, both from the same fact: on Cloudflare, I/O is scoped to the invocation that
76
+ created it, and an invocation that touches another's I/O is **destroyed** with "Cannot
77
+ perform I/O on behalf of a different request", at the runtime level, where no `catch` in
78
+ the joining code can see it.
79
+
80
+ - **Background work is adopted by the CURRENT invocation.** Pass `scheduleBackgroundWork`
81
+ and hand the promise to `ctx.waitUntil`, resolving the context at call time rather than
82
+ closing over one (the Worker reads the ambient ExecutionContext off an
83
+ AsyncLocalStorage; see `runtimes/cloudflare/src/infrastructure/appCachesHost.ts`).
84
+ - **In-flight promises are never shared between invocations.** This is what
85
+ `currentInvocation` is for. The bag is one per ISOLATE, which is what makes its entries
86
+ caches rather than per-invocation memos, but it also puts layered-loader's in-flight
87
+ load map (and the coherency probe's) in reach of a second concurrent invocation on every
88
+ same-key miss. Supplying `currentInvocation` moves the MISS path onto per-invocation
89
+ loads: reads still serve from the isolate-scoped in-memory tier and a hit still schedules
90
+ its preemptive refresh, but a miss loads for itself rather than joining a promise it
91
+ cannot safely await. Within one invocation the coalescing is kept, and an invocation the
92
+ runtime cannot name (a Workflows step, which has no ExecutionContext) coalesces with
93
+ nothing, because two loads that cannot be told apart must be assumed to be different
94
+ contexts. Node supplies nothing here and keeps layered-loader's own load path unchanged.
95
+
96
+ Because a miss now publishes outside the loader, that publish is fenced locally: an
97
+ invalidation landing while a load is in flight discards the late write instead of
98
+ resurrecting the entry it dropped (the caller still receives the value it loaded).
46
99
 
47
100
  `fragmentDocumentBody` is the first self-verifying cache that stays **enabled** on the
48
- Worker: its entries are external Confluence/Notion/GitHub/… page content re-validated
49
- by the source's cheap version probe (`ttlLeftBeforeRefreshInMsecs` + `isStillCurrent`),
50
- so a peer isolate's cached body self-heals within the refresh window without an
51
- invalidation bus: its staleness is bounded by the probe, exactly like a sha-pinned
52
- read. Only `fragmentCatalog`, which mirrors our own mutable D1 rows, passes through.
101
+ Worker even without the directory: its entries are external Confluence/Notion/GitHub/…
102
+ page content re-validated by the source's cheap version probe
103
+ (`ttlLeftBeforeRefreshInMsecs` + `isStillCurrent`), so a peer isolate's cached body
104
+ self-heals within the refresh window without an invalidation bus.
105
+ `workspaceSettings` is the pull-coherency pilot (one invalidation site, no
106
+ `invalidateAll`, hot on the Worker); further flips are one profile row each, in their
107
+ own slice.
53
108
 
54
109
  ## Named caches
55
110
 
@@ -68,8 +123,14 @@ import { createAppCaches } from '@cat-factory/caching'
68
123
  // Node facade (multi-node): inject the Redis-backed notification pair factory.
69
124
  const caches = createAppCaches({ notificationPairFactory, logger })
70
125
 
71
- // Cloudflare Worker: the isolate-safe profile.
72
- const caches = createAppCaches({ profile: ISOLATE_SAFE_APP_CACHES_PROFILE })
126
+ // Cloudflare Worker: the module-scope host picks the coherent profile when the
127
+ // CACHE_GENERATIONS Durable Object is bound, else the isolate-safe fallback
128
+ // (runtimes/cloudflare/src/infrastructure/appCachesHost.ts).
129
+ const caches = createAppCaches({
130
+ profile: ISOLATE_COHERENT_APP_CACHES_PROFILE,
131
+ generationStore, // DO-backed on the Worker; any CacheGenerationStore elsewhere
132
+ scheduleBackgroundWork, // adopt detached refreshes onto ctx.waitUntil
133
+ })
73
134
 
74
135
  // A consuming service reads through its named handle…
75
136
  const catalog = await caches.fragmentCatalog.get(key, workspaceId, () => loadCatalog())
@@ -1,5 +1,6 @@
1
1
  import type { AppCaches, Logger, OperationalMetrics } from '@cat-factory/kernel';
2
- import type { AbstractNotificationConsumer, GroupNotificationPublisher, InMemoryGroupCache } from 'layered-loader/core';
2
+ import type { AbstractNotificationConsumer, BackgroundWorkScheduler, GroupNotificationPublisher, InMemoryGroupCache } from 'layered-loader/core';
3
+ import type { CacheGenerationStore } from './generationCoherency.js';
3
4
  /** Per-cache tuning knobs; a facade passes a profile so TTLs can differ per runtime. */
4
5
  export interface GroupCacheProfile {
5
6
  /**
@@ -25,6 +26,30 @@ export interface GroupCacheProfile {
25
26
  * cost as much as the load).
26
27
  */
27
28
  ttlLeftBeforeRefreshInMsecs?: number;
29
+ /**
30
+ * Pull-coherency probe cadence, for an enabled cache of our own mutable state on a runtime
31
+ * with no push bus (the Worker): a read whose group snapshot in the injected
32
+ * {@link CacheGenerationStore} is older than this re-reads the directory before serving,
33
+ * applying the group invalidation locally on a moved counter. Bounds cross-isolate staleness
34
+ * at roughly this window. Requires `generationStore`: {@link createAppCaches} REFUSES a
35
+ * profile that sets it on an enabled cache without one, because an enabled TTL'd cache of
36
+ * mutable state with no coherency mechanism is the exact bug the isolate-safe profile
37
+ * exists to prevent.
38
+ */
39
+ coherencyWindowMsecs?: number;
40
+ /**
41
+ * Declares that this cache's owning service calls {@link GroupCacheHandle.invalidateAll}.
42
+ *
43
+ * Only meaningful together with `coherencyWindowMsecs`, and it costs something real: a
44
+ * cache-wide invalidation rides the reserved epoch counter, whose shard is ONE globally
45
+ * placed Durable Object, so declaring this puts a cross-colo probe on the cache's read path.
46
+ * A cache with no `invalidateAll` call site leaves it off and probes only its own group.
47
+ *
48
+ * Getting it wrong cannot go quiet: `invalidateAll` on a coherent cache that did NOT declare
49
+ * it THROWS, because dropping the entries locally while peers keep serving them for a full
50
+ * TTL is the failure this flag exists to prevent.
51
+ */
52
+ cacheWideInvalidation?: boolean;
28
53
  }
29
54
  /** One profile entry per named cache in the kernel {@link AppCaches} bag. */
30
55
  export interface AppCachesProfile {
@@ -62,8 +87,32 @@ export declare const DEFAULT_APP_CACHES_PROFILE: AppCachesProfile;
62
87
  * lets sha-pinned reads keep a TTL on the Worker) — its staleness is bounded by the
63
88
  * probe, not indefinite. Only `fragmentCatalog`, which mirrors our own mutable D1
64
89
  * state, must pass through.
90
+ *
91
+ * Every ENABLED entry below is read against ONE FACT that the numbers on the Node profile were
92
+ * not chosen for: the Worker's bag is one per ISOLATE (`appCachesHost.ts`), so an entry lives
93
+ * its whole TTL across requests. It used to be rebuilt per invocation, which quietly capped
94
+ * every one of these at the length of a single wake. The two probe-backed slices therefore
95
+ * widen their refresh window to cover the full TTL here (see each entry); the two that have no
96
+ * probe keep their TTL as the entire bound, which their own numbers were already sized as:
97
+ * `linkedDocumentVersion` at 60s, and `ssoDiscovery` at 15 minutes of EXTERNAL state that
98
+ * additionally self-heals on an unknown `kid`.
65
99
  */
66
100
  export declare const ISOLATE_SAFE_APP_CACHES_PROFILE: AppCachesProfile;
101
+ /**
102
+ * The isolate-safe profile plus the caches the generation directory makes coherent: selected
103
+ * by the Worker ONLY when its `CACHE_GENERATIONS` Durable Object binding exists (and so a
104
+ * {@link CacheGenerationStore} is injected); with no binding the Worker keeps the pass-through
105
+ * stance above. Flipping a cache here means giving it a real TTL on the Worker with a
106
+ * generation probe bounding its cross-isolate staleness at `coherencyWindowMsecs`; the cache's
107
+ * EVERY invalidation site then also bumps the directory (the handle does both together).
108
+ *
109
+ * `workspaceSettings` is the pilot: exactly one invalidation site
110
+ * (`WorkspaceSettingsService.update`), no `invalidateAll`, and hot on the Worker (read per
111
+ * recorded LLM call, per task-limit guard, per pricing resolution, each a live D1 read
112
+ * today). Further flips are one profile row each, in their own slice
113
+ * (docs/initiatives/caching-layer.md).
114
+ */
115
+ export declare const ISOLATE_COHERENT_APP_CACHES_PROFILE: AppCachesProfile;
67
116
  /**
68
117
  * A per-cache invalidation-notification pair (layered-loader's group publisher +
69
118
  * consumer). Produced by the facade's factory — Redis-backed in a multi-node Node
@@ -93,6 +142,30 @@ export interface CreateAppCachesOptions {
93
142
  * two states with identical latency graphs and opposite fixes. Absent ⇒ uncounted.
94
143
  */
95
144
  operationalMetrics?: OperationalMetrics;
145
+ /**
146
+ * The shared generation directory backing every profile entry with a
147
+ * `coherencyWindowMsecs` (see that field's doc). One directory serves the whole bag, so all
148
+ * coherent caches share each group's probe. Absent ⇒ no entry may set a window.
149
+ */
150
+ generationStore?: CacheGenerationStore;
151
+ /**
152
+ * Adopter for the work the loaders start and do not await (preemptive refreshes, staleness
153
+ * probes, notification publishes). On Node the default detached run is correct; an isolate
154
+ * runtime hands the promise to the current request's `ctx.waitUntil` instead, because I/O
155
+ * there is scoped to the request that created it. The promise always settles fulfilled.
156
+ */
157
+ scheduleBackgroundWork?: BackgroundWorkScheduler;
158
+ /**
159
+ * Supplied ONLY by an isolate runtime (the Worker): returns an object identifying the
160
+ * invocation currently being served, or `undefined` outside a bracketed entry point.
161
+ *
162
+ * Its presence switches every cache MISS off layered-loader's shared load path and onto the
163
+ * per-invocation one in {@link InvocationScopedLoads}, because the bag is one per ISOLATE
164
+ * and workerd destroys, uncatchably, any invocation that awaits a promise another one
165
+ * created. Absent (Node, where one process serves every request out of one I/O context) ⇒
166
+ * the loader's own coalescing is kept, unchanged.
167
+ */
168
+ currentInvocation?: () => object | undefined;
96
169
  }
97
170
  /**
98
171
  * Build the app-owned cache bag. Called once per process by a facade's
@@ -1 +1 @@
1
- {"version":3,"file":"appCaches.d.ts","sourceRoot":"","sources":["../src/appCaches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,SAAS,EAOT,MAAM,EAUN,kBAAkB,EACnB,MAAM,qBAAqB,CAAA;AAO5B,OAAO,KAAK,EACV,4BAA4B,EAC5B,0BAA0B,EAC1B,kBAAkB,EAEnB,MAAM,qBAAqB,CAAA;AAwB5B,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAA;IACjB,6CAA6C;IAC7C,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;;;;OAQG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAA;CACrC;AAED,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,iBAAiB,CAAA;IAClC,YAAY,EAAE,iBAAiB,CAAA;IAC/B,0BAA0B,EAAE,iBAAiB,CAAA;IAC7C,oBAAoB,EAAE,iBAAiB,CAAA;IACvC,qBAAqB,EAAE,iBAAiB,CAAA;IACxC,cAAc,EAAE,iBAAiB,CAAA;IACjC,SAAS,EAAE,iBAAiB,CAAA;IAC5B,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,eAAe,EAAE,iBAAiB,CAAA;IAClC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,eAAe,EAAE,iBAAiB,CAAA;IAClC,WAAW,EAAE,iBAAiB,CAAA;IAC9B,oBAAoB,EAAE,iBAAiB,CAAA;IACvC,UAAU,EAAE,iBAAiB,CAAA;IAC7B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,eAAe,EAAE,iBAAiB,CAAA;IAClC,qBAAqB,EAAE,iBAAiB,CAAA;IACxC,YAAY,EAAE,iBAAiB,CAAA;CAChC;AAED,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,EAAE,gBAiJxC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,EAAE,gBA2E7C,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,SAAS,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAA;IACxC,QAAQ,EAAE,4BAA4B,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAA;CACjE;AAED;;;;;GAKG;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,EAC3C,SAAS,EAAE,MAAM,KACd,uBAAuB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;AAE3C,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACnC,2EAA2E;IAC3E,uBAAuB,CAAC,EAAE,4BAA4B,CAAA;IACtD,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;CACxC;AAmHD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,SAAS,CAqI/E"}
1
+ {"version":3,"file":"appCaches.d.ts","sourceRoot":"","sources":["../src/appCaches.ts"],"names":[],"mappings":"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"}
package/dist/appCaches.js CHANGED
@@ -4,6 +4,8 @@ import { noopOperationalMetrics } from '@cat-factory/kernel';
4
4
  // notification wiring — the Worker imports this package too. The package root re-exports
5
5
  // the Redis surface as well, so it is NOT interchangeable with this import; keep it `/core`.
6
6
  import { GroupLoader } from 'layered-loader/core';
7
+ import { CACHE_EPOCH_GROUP, GroupGenerationTracker } from './generationCoherency.js';
8
+ import { InvocationScopedLoads } from './invocationScopedLoads.js';
7
9
  /**
8
10
  * layered-loader logs its background failures through a pino-shaped `error(obj, msg?)`.
9
11
  * The platform's own convention is message-first (`kernel`'s {@link Logger}), so the
@@ -23,7 +25,15 @@ function asLayeredLoaderLogger(logger) {
23
25
  export const DEFAULT_APP_CACHES_PROFILE = {
24
26
  // One merged catalog per workspace; the key varies only when the workspace's
25
27
  // account changes, so a small per-group bound is plenty.
26
- fragmentCatalog: { enabled: true, ttlInMsecs: 5 * 60_000, maxGroups: 500, maxItemsPerGroup: 4 },
28
+ fragmentCatalog: {
29
+ enabled: true,
30
+ ttlInMsecs: 5 * 60_000,
31
+ maxGroups: 500,
32
+ maxItemsPerGroup: 4,
33
+ // `FragmentLibraryService` drops the whole cache on an ACCOUNT-tier write (it spans every
34
+ // workspace in the account), so this cache needs the epoch counter wherever it runs coherent.
35
+ cacheWideInvalidation: true,
36
+ },
27
37
  // One repo-sourced skill catalog per account, keyed by account id (one entry per group).
28
38
  // Invalidation-driven (the skill-source sync drops the group after a change); no version
29
39
  // probe — a DB read as the probe would cost as much as the DB read as the load.
@@ -36,6 +46,8 @@ export const DEFAULT_APP_CACHES_PROFILE = {
36
46
  ttlInMsecs: 5 * 60_000,
37
47
  maxGroups: 500,
38
48
  maxItemsPerGroup: 1,
49
+ // Same account-tier blast radius as `fragmentCatalog`, which it mirrors.
50
+ cacheWideInvalidation: true,
39
51
  },
40
52
  // The live external body of a document-backed fragment, grouped by workspace and
41
53
  // keyed per document. Self-verifying: an entry entering the last minute of its TTL
@@ -145,13 +157,22 @@ export const DEFAULT_APP_CACHES_PROFILE = {
145
157
  // invalidation-driven, no version probe. A SHORT 60s TTL: it's the freshness backstop only (the
146
158
  // roster/access-mode/account-membership writes invalidate on commit), and it bounds how long a
147
159
  // just-revoked member keeps read access on the rare path an invalidation is missed.
148
- workspaceAccess: { enabled: true, ttlInMsecs: 60_000, maxGroups: 2000, maxItemsPerGroup: 256 },
160
+ workspaceAccess: {
161
+ enabled: true,
162
+ ttlInMsecs: 60_000,
163
+ maxGroups: 2000,
164
+ maxItemsPerGroup: 256,
165
+ // An account-membership change alters access to potentially every board, so `AccountService`
166
+ // drops the whole cache rather than enumerating them.
167
+ cacheWideInvalidation: true,
168
+ },
149
169
  // One session generation per user, grouped AND keyed by user id (one entry per group), read on
150
170
  // every authenticated request. `maxGroups` is therefore sized against CONCURRENTLY ACTIVE users
151
171
  // rather than registered ones, and each entry is a single small number. The 60s TTL is the
152
172
  // freshness backstop only, exactly as on `workspaceAccess` above: a bump invalidates the key on
153
173
  // commit, and the TTL bounds how long a revoked bearer survives on the rare path where an
154
- // invalidation is missed (a peer replica with no notification bus wired).
174
+ // invalidation is missed (a peer replica with no notification bus wired). Every invalidation
175
+ // site is per-user, so it needs no cache-wide epoch probe.
155
176
  userSessionGeneration: {
156
177
  enabled: true,
157
178
  ttlInMsecs: 60_000,
@@ -178,6 +199,15 @@ export const DEFAULT_APP_CACHES_PROFILE = {
178
199
  * lets sha-pinned reads keep a TTL on the Worker) — its staleness is bounded by the
179
200
  * probe, not indefinite. Only `fragmentCatalog`, which mirrors our own mutable D1
180
201
  * state, must pass through.
202
+ *
203
+ * Every ENABLED entry below is read against ONE FACT that the numbers on the Node profile were
204
+ * not chosen for: the Worker's bag is one per ISOLATE (`appCachesHost.ts`), so an entry lives
205
+ * its whole TTL across requests. It used to be rebuilt per invocation, which quietly capped
206
+ * every one of these at the length of a single wake. The two probe-backed slices therefore
207
+ * widen their refresh window to cover the full TTL here (see each entry); the two that have no
208
+ * probe keep their TTL as the entire bound, which their own numbers were already sized as:
209
+ * `linkedDocumentVersion` at 60s, and `ssoDiscovery` at 15 minutes of EXTERNAL state that
210
+ * additionally self-heals on an unknown `kid`.
181
211
  */
182
212
  export const ISOLATE_SAFE_APP_CACHES_PROFILE = {
183
213
  fragmentCatalog: { ...DEFAULT_APP_CACHES_PROFILE.fragmentCatalog, enabled: false },
@@ -189,7 +219,18 @@ export const ISOLATE_SAFE_APP_CACHES_PROFILE = {
189
219
  ...DEFAULT_APP_CACHES_PROFILE.foundationalServiceCatalog,
190
220
  enabled: false,
191
221
  },
192
- fragmentDocumentBody: { ...DEFAULT_APP_CACHES_PROFILE.fragmentDocumentBody },
222
+ fragmentDocumentBody: {
223
+ ...DEFAULT_APP_CACHES_PROFILE.fragmentDocumentBody,
224
+ // The refresh window covers the WHOLE TTL here, unlike the Node profile's last minute of
225
+ // five. The bag is one per ISOLATE, so an entry now lives its full TTL across requests
226
+ // rather than being rebuilt per wake as it was when these numbers were chosen; with a
227
+ // window of one minute, the first four served unprobed. Since the claim that keeps this
228
+ // cache enabled on the Worker at all is "a peer isolate's copy self-heals within the
229
+ // refresh window", the window has to BE the lifetime. The probe is the source's cheap
230
+ // version check, deduped per (group, key) while one is in flight, so a read pays at most
231
+ // one of them and never waits on it.
232
+ ttlLeftBeforeRefreshInMsecs: DEFAULT_APP_CACHES_PROFILE.fragmentDocumentBody.ttlInMsecs,
233
+ },
193
234
  // Stays ENABLED here for the same reason, one step further: the entry IS an external source's
194
235
  // version token, so it is neither our own mutable state nor in need of a probe to re-validate — a
195
236
  // peer isolate's copy self-heals when its 60s TTL lapses. Passing through instead would put a live
@@ -206,7 +247,15 @@ export const ISOLATE_SAFE_APP_CACHES_PROFILE = {
206
247
  // file self-heals within the refresh window without an invalidation bus — its staleness is
207
248
  // bounded by the probe, not indefinite. The same reasoning that keeps `fragmentDocumentBody`
208
249
  // on; only caches of our own mutable D1 state (`fragmentCatalog`/`repoProjection`) pass through.
209
- repoFiles: { ...DEFAULT_APP_CACHES_PROFILE.repoFiles },
250
+ repoFiles: {
251
+ ...DEFAULT_APP_CACHES_PROFILE.repoFiles,
252
+ // Same reasoning as `fragmentDocumentBody` above, and this is the slice where it bites
253
+ // hardest: the entries are repo FILE CONTENT, so an unprobed window is a post-op reading
254
+ // pre-commit files that the per-invocation bag made impossible. The `headSha` probe is one
255
+ // cheap call per branch, so covering the whole TTL with it is what makes "bounded by the
256
+ // probe, not indefinite" true now that entries outlive the request that loaded them.
257
+ ttlLeftBeforeRefreshInMsecs: DEFAULT_APP_CACHES_PROFILE.repoFiles.ttlInMsecs,
258
+ },
210
259
  // Pass-through for the same reason: the account policy is our own mutable D1 state
211
260
  // with no cross-isolate invalidation bus on the Worker.
212
261
  accountModelPolicy: { ...DEFAULT_APP_CACHES_PROFILE.accountModelPolicy, enabled: false },
@@ -255,13 +304,56 @@ export const ISOLATE_SAFE_APP_CACHES_PROFILE = {
255
304
  // them.
256
305
  ssoDiscovery: { ...DEFAULT_APP_CACHES_PROFILE.ssoDiscovery },
257
306
  };
307
+ /**
308
+ * The isolate-safe profile plus the caches the generation directory makes coherent: selected
309
+ * by the Worker ONLY when its `CACHE_GENERATIONS` Durable Object binding exists (and so a
310
+ * {@link CacheGenerationStore} is injected); with no binding the Worker keeps the pass-through
311
+ * stance above. Flipping a cache here means giving it a real TTL on the Worker with a
312
+ * generation probe bounding its cross-isolate staleness at `coherencyWindowMsecs`; the cache's
313
+ * EVERY invalidation site then also bumps the directory (the handle does both together).
314
+ *
315
+ * `workspaceSettings` is the pilot: exactly one invalidation site
316
+ * (`WorkspaceSettingsService.update`), no `invalidateAll`, and hot on the Worker (read per
317
+ * recorded LLM call, per task-limit guard, per pricing resolution, each a live D1 read
318
+ * today). Further flips are one profile row each, in their own slice
319
+ * (docs/initiatives/caching-layer.md).
320
+ */
321
+ export const ISOLATE_COHERENT_APP_CACHES_PROFILE = {
322
+ ...ISOLATE_SAFE_APP_CACHES_PROFILE,
323
+ workspaceSettings: {
324
+ ...DEFAULT_APP_CACHES_PROFILE.workspaceSettings,
325
+ coherencyWindowMsecs: 5_000,
326
+ // The TTL is SHORTER here than the 5 minutes the Node profile carries, and it is sized for
327
+ // a specific job: it is the backstop for a FAILED bump. The probe window bounds staleness
328
+ // at ~5s only while the directory is reachable; when a writer's bump fails the mechanism
329
+ // fails OPEN (the write already committed, so refusing it would be worse) and peers learn
330
+ // nothing until their entry expires, so the TTL, not the window, is the real bound in
331
+ // that case. This row carries `allowInitiatorPat`, `storeAgentContext` and the spend caps,
332
+ // where the failure mode is a revoked permission still being honoured, so the honest
333
+ // number is the one an operator would accept for a REVOCATION, not for a settings edit.
334
+ ttlInMsecs: 60_000,
335
+ },
336
+ };
258
337
  class LayeredGroupCacheHandle {
259
338
  name;
260
- metrics;
261
339
  loader;
262
- constructor(name, profile, notifications, logger, metrics = noopOperationalMetrics) {
340
+ /** Set only for an enabled cache whose profile carries a `coherencyWindowMsecs`. */
341
+ coherency;
342
+ metrics;
343
+ /** Set only on an isolate runtime; see {@link CreateAppCachesOptions.currentInvocation}. */
344
+ invocationLoads;
345
+ enabled;
346
+ /** See {@link GroupCacheProfile.cacheWideInvalidation}. */
347
+ cacheWideInvalidation;
348
+ constructor(name, profile, deps) {
263
349
  this.name = name;
264
- this.metrics = metrics;
350
+ const { notifications, logger, coherency, scheduleBackgroundWork } = deps;
351
+ this.metrics = deps.metrics ?? noopOperationalMetrics;
352
+ this.enabled = profile.enabled;
353
+ this.cacheWideInvalidation = profile.cacheWideInvalidation ?? false;
354
+ this.invocationLoads = deps.currentInvocation
355
+ ? new InvocationScopedLoads(deps.currentInvocation)
356
+ : undefined;
265
357
  this.loader = new GroupLoader({
266
358
  inMemoryCache: profile.enabled
267
359
  ? {
@@ -307,6 +399,26 @@ class LayeredGroupCacheHandle {
307
399
  }
308
400
  : {}),
309
401
  ...(logger ? { logger: asLayeredLoaderLogger(logger) } : {}),
402
+ ...(scheduleBackgroundWork ? { scheduleBackgroundWork } : {}),
403
+ });
404
+ this.coherency = coherency?.tracker;
405
+ // The 16.1 apply-remote primitives are handed over BOUND: synchronous, purely local,
406
+ // non-publishing, and fencing (an in-flight load cannot write its pre-invalidation
407
+ // snapshot back), which is exactly what a probe-detected peer change must apply.
408
+ coherency?.tracker.register({
409
+ cacheName: name,
410
+ windowMsecs: coherency.windowMsecs,
411
+ probesEpoch: this.cacheWideInvalidation,
412
+ applyRemoteInvalidationForGroup: (group) => {
413
+ // The invocation-scoped load path publishes outside the loader, so it needs the same
414
+ // news the loader's own fences get: a peer's invalidation must void an in-flight load.
415
+ this.invocationLoads?.noteInvalidation();
416
+ this.loader.applyRemoteInvalidationForGroup(group);
417
+ },
418
+ applyRemoteInvalidation: () => {
419
+ this.invocationLoads?.noteInvalidation();
420
+ this.loader.applyRemoteInvalidation();
421
+ },
310
422
  });
311
423
  }
312
424
  async get(key, group, load, isStillCurrent) {
@@ -316,6 +428,15 @@ class LayeredGroupCacheHandle {
316
428
  // that lands inside a probe-refresh window can trigger a BACKGROUND reload, and if that
317
429
  // reload's load closure runs before this await resolves it is attributed to this read. So
318
430
  // the hit rate on a probe-refreshed cache is a floor, never an overstatement.
431
+ // Pull coherency runs BEFORE the loader read: inside the window it resolves with no I/O,
432
+ // past it one shared probe re-reads the generation directory and locally invalidates what
433
+ // moved, so the loader read below never serves an entry a peer already invalidated more
434
+ // than the window ago. Never rejects (probe failure fails closed inside the tracker).
435
+ if (this.coherency)
436
+ await this.coherency.ensureFresh(this.name, group);
437
+ if (this.invocationLoads) {
438
+ return this.getWithoutCrossInvocationJoin(this.invocationLoads, key, group, load, isStillCurrent);
439
+ }
319
440
  let loaderRan = false;
320
441
  const countedLoad = () => {
321
442
  loaderRan = true;
@@ -328,14 +449,64 @@ class LayeredGroupCacheHandle {
328
449
  this.metrics.increment(loaderRan ? 'cache.miss' : 'cache.hit', { cache: this.name });
329
450
  return value;
330
451
  }
331
- invalidate(key, group) {
332
- return this.loader.invalidateCacheFor(key, group);
452
+ /**
453
+ * The isolate-runtime read: the same two tiers as the loader's own `get`, but split so the
454
+ * MISS half never enters layered-loader's shared load path, whose in-flight map is the one
455
+ * piece of the isolate-scoped bag that would hand this invocation a promise created by
456
+ * another (see {@link InvocationScopedLoads}).
457
+ *
458
+ * The HIT half still goes through the loader, so an entry entering its refresh window keeps
459
+ * scheduling the preemptive reload / staleness probe exactly as before: that read only
460
+ * INSPECTS the in-flight map, it never awaits what it finds there.
461
+ */
462
+ async getWithoutCrossInvocationJoin(invocationLoads, key, group, load, isStillCurrent) {
463
+ const params = { key, load, ...(isStillCurrent ? { isStillCurrent } : {}) };
464
+ // `undefined` is the miss; `null` is a legitimately cached resolved-but-empty value.
465
+ const cached = this.loader.getInMemoryOnly(params, group);
466
+ if (cached !== undefined) {
467
+ this.metrics.increment('cache.hit', { cache: this.name });
468
+ return cached;
469
+ }
470
+ // NUL-separated, so no group/key pair can ever spell another pair's composite.
471
+ const composite = `${group}\u0000${key}`;
472
+ const { value, loaded } = await invocationLoads.run(composite, load, (fresh) =>
473
+ // A pass-through cache has no in-memory tier to publish into: every read loads, which is
474
+ // what `enabled: false` means. Publishing would be a no-op through the loader's noop
475
+ // cache, but saying so here keeps the pass-through stance readable.
476
+ this.enabled ? this.loader.forceSetValueForGroup(key, fresh, group) : Promise.resolve());
477
+ this.metrics.increment(loaded ? 'cache.miss' : 'cache.hit', { cache: this.name });
478
+ return value;
479
+ }
480
+ // Each invalidation bumps the generation directory AFTER the local invalidation resolves,
481
+ // and the write path awaits both, so "invalidate right after the write commits" spans the
482
+ // peers' view too. A single-key invalidation bumps its whole GROUP's counter: coarser than
483
+ // the local drop (peers re-load the group once), but the directory then never needs per-key
484
+ // state, and every coherent cache today keys group == key anyway. A bump failure fails open
485
+ // inside the tracker (peers heal at the TTL), so none of these can turn a committed write
486
+ // into a thrown request.
487
+ async invalidate(key, group) {
488
+ this.invocationLoads?.noteInvalidation();
489
+ await this.loader.invalidateCacheFor(key, group);
490
+ await this.coherency?.noteLocalInvalidation(this.name, group);
333
491
  }
334
- invalidateGroup(group) {
335
- return this.loader.invalidateCacheForGroup(group);
492
+ async invalidateGroup(group) {
493
+ this.invocationLoads?.noteInvalidation();
494
+ await this.loader.invalidateCacheForGroup(group);
495
+ await this.coherency?.noteLocalInvalidation(this.name, group);
336
496
  }
337
- invalidateAll() {
338
- return this.loader.invalidateCache();
497
+ async invalidateAll() {
498
+ if (this.coherency && !this.cacheWideInvalidation) {
499
+ // Refused rather than half-applied: without the declaration peers never probe the epoch
500
+ // counter, so this call would drop the entries on THIS instance while every other one
501
+ // kept serving them until their TTL, a coherency hole that looks like a working
502
+ // invalidation from the caller's side.
503
+ throw new Error(`cache '${this.name}' called invalidateAll() but its profile does not set ` +
504
+ `cacheWideInvalidation, so peer instances never probe the epoch counter this would ` +
505
+ `bump. Set it on the profile entry (it turns the epoch probe on for this cache).`);
506
+ }
507
+ this.invocationLoads?.noteInvalidation();
508
+ await this.loader.invalidateCache();
509
+ await this.coherency?.noteLocalInvalidation(this.name, CACHE_EPOCH_GROUP);
339
510
  }
340
511
  /** Releases the notification pair's resources along with the loader. */
341
512
  close() {
@@ -350,25 +521,36 @@ class LayeredGroupCacheHandle {
350
521
  */
351
522
  export function createAppCaches(options = {}) {
352
523
  const profile = { ...DEFAULT_APP_CACHES_PROFILE, ...options.profile };
353
- const fragmentCatalog = buildGroupCache('fragment-catalog', profile.fragmentCatalog, options);
354
- const skillCatalog = buildGroupCache('skill-catalog', profile.skillCatalog, options);
355
- const foundationalServiceCatalog = buildGroupCache('foundational-service-catalog', profile.foundationalServiceCatalog, options);
356
- const fragmentDocumentBody = buildGroupCache('fragment-document-body', profile.fragmentDocumentBody, options);
357
- const linkedDocumentVersion = buildGroupCache('linked-document-version', profile.linkedDocumentVersion, options);
358
- const repoProjection = buildGroupCache('repo-projection', profile.repoProjection, options);
359
- const repoFiles = buildGroupCache('repo-files', profile.repoFiles, options);
360
- const accountModelPolicy = buildGroupCache('account-model-policy', profile.accountModelPolicy, options);
361
- const accountSettings = buildGroupCache('account-settings', profile.accountSettings, options);
362
- const workspaceSettings = buildGroupCache('workspace-settings', profile.workspaceSettings, options);
363
- const accountBudgetLimit = buildGroupCache('account-budget-limit', profile.accountBudgetLimit, options);
364
- const userBudgetLimit = buildGroupCache('user-budget-limit', profile.userBudgetLimit, options);
365
- const viewerRepos = buildGroupCache('viewer-repos', profile.viewerRepos, options);
366
- const patInstallationRepos = buildGroupCache('pat-installation-repos', profile.patInstallationRepos, options);
367
- const riskPolicy = buildGroupCache('risk-policy', profile.riskPolicy, options);
368
- const modelPreset = buildGroupCache('model-preset', profile.modelPreset, options);
369
- const workspaceAccess = buildGroupCache('workspace-access', profile.workspaceAccess, options);
370
- const userSessionGeneration = buildGroupCache('user-session-generation', profile.userSessionGeneration, options);
371
- const ssoDiscovery = buildGroupCache('sso-discovery', profile.ssoDiscovery, options);
524
+ assertCoherencyWirable(profile, options);
525
+ // One tracker serves the whole bag, so every coherent cache shares each group's probe.
526
+ const tracker = options.generationStore
527
+ ? new GroupGenerationTracker(options.generationStore, {
528
+ ...(options.logger ? { logger: options.logger } : {}),
529
+ ...(options.operationalMetrics ? { metrics: options.operationalMetrics } : {}),
530
+ // The probe is a Durable Object round trip, so its in-flight promise is subject to the
531
+ // same cross-invocation rule as a load: coalesce within one invocation, never across.
532
+ ...(options.currentInvocation ? { currentInvocation: options.currentInvocation } : {}),
533
+ })
534
+ : undefined;
535
+ const fragmentCatalog = buildGroupCache('fragment-catalog', profile.fragmentCatalog, options, tracker);
536
+ const skillCatalog = buildGroupCache('skill-catalog', profile.skillCatalog, options, tracker);
537
+ const foundationalServiceCatalog = buildGroupCache('foundational-service-catalog', profile.foundationalServiceCatalog, options, tracker);
538
+ const fragmentDocumentBody = buildGroupCache('fragment-document-body', profile.fragmentDocumentBody, options, tracker);
539
+ const linkedDocumentVersion = buildGroupCache('linked-document-version', profile.linkedDocumentVersion, options, tracker);
540
+ const repoProjection = buildGroupCache('repo-projection', profile.repoProjection, options, tracker);
541
+ const repoFiles = buildGroupCache('repo-files', profile.repoFiles, options, tracker);
542
+ const accountModelPolicy = buildGroupCache('account-model-policy', profile.accountModelPolicy, options, tracker);
543
+ const accountSettings = buildGroupCache('account-settings', profile.accountSettings, options, tracker);
544
+ const workspaceSettings = buildGroupCache('workspace-settings', profile.workspaceSettings, options, tracker);
545
+ const accountBudgetLimit = buildGroupCache('account-budget-limit', profile.accountBudgetLimit, options, tracker);
546
+ const userBudgetLimit = buildGroupCache('user-budget-limit', profile.userBudgetLimit, options, tracker);
547
+ const viewerRepos = buildGroupCache('viewer-repos', profile.viewerRepos, options, tracker);
548
+ const patInstallationRepos = buildGroupCache('pat-installation-repos', profile.patInstallationRepos, options, tracker);
549
+ const riskPolicy = buildGroupCache('risk-policy', profile.riskPolicy, options, tracker);
550
+ const modelPreset = buildGroupCache('model-preset', profile.modelPreset, options, tracker);
551
+ const workspaceAccess = buildGroupCache('workspace-access', profile.workspaceAccess, options, tracker);
552
+ const userSessionGeneration = buildGroupCache('user-session-generation', profile.userSessionGeneration, options, tracker);
553
+ const ssoDiscovery = buildGroupCache('sso-discovery', profile.ssoDiscovery, options, tracker);
372
554
  return {
373
555
  fragmentCatalog,
374
556
  skillCatalog,
@@ -414,8 +596,36 @@ export function createAppCaches(options = {}) {
414
596
  },
415
597
  };
416
598
  }
417
- function buildGroupCache(name, profile, options) {
599
+ /**
600
+ * An enabled cache with a coherency window but no directory to probe would be a TTL'd cache
601
+ * of mutable state with no invalidation reaching it, the exact bug the isolate-safe profile
602
+ * exists to prevent, so the combination is refused at construction, not degraded.
603
+ */
604
+ function assertCoherencyWirable(profile, options) {
605
+ if (options.generationStore)
606
+ return;
607
+ for (const [name, entry] of Object.entries(profile)) {
608
+ if (entry.enabled && entry.coherencyWindowMsecs !== undefined) {
609
+ throw new Error(`cache '${name}' sets coherencyWindowMsecs but createAppCaches got no generationStore; ` +
610
+ `wire one or drop the window (an enabled TTL'd cache of mutable state with no ` +
611
+ `coherency mechanism would serve stale data after a peer's write)`);
612
+ }
613
+ }
614
+ }
615
+ function buildGroupCache(name, profile, options, tracker) {
418
616
  const notifications = profile.enabled ? options.notificationPairFactory?.(name) : undefined;
419
- return new LayeredGroupCacheHandle(name, profile, notifications, options.logger, options.operationalMetrics);
617
+ const coherency = tracker && profile.enabled && profile.coherencyWindowMsecs !== undefined
618
+ ? { tracker, windowMsecs: profile.coherencyWindowMsecs }
619
+ : undefined;
620
+ return new LayeredGroupCacheHandle(name, profile, {
621
+ ...(notifications ? { notifications } : {}),
622
+ ...(options.logger ? { logger: options.logger } : {}),
623
+ ...(options.operationalMetrics ? { metrics: options.operationalMetrics } : {}),
624
+ ...(coherency ? { coherency } : {}),
625
+ ...(options.scheduleBackgroundWork
626
+ ? { scheduleBackgroundWork: options.scheduleBackgroundWork }
627
+ : {}),
628
+ ...(options.currentInvocation ? { currentInvocation: options.currentInvocation } : {}),
629
+ });
420
630
  }
421
631
  //# sourceMappingURL=appCaches.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"appCaches.js","sourceRoot":"","sources":["../src/appCaches.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"appCaches.js","sourceRoot":"","sources":["../src/appCaches.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import type { Logger, OperationalMetrics } from '@cat-factory/kernel';
2
+ /**
3
+ * One shared, cross-instance generation directory. The contract every implementation must
4
+ * honour: counters are MONOTONIC per (cache, group) and a group's counters are readable in
5
+ * ONE round trip: the batched read is what lets every coherent cache share a single probe
6
+ * per group per window. On the Worker this is a Durable Object namespace sharded by group;
7
+ * tests inject an in-memory fake.
8
+ */
9
+ export interface CacheGenerationStore {
10
+ /**
11
+ * Every cache's generation counter for one group. A cache absent from the result has
12
+ * generation 0 (never bumped).
13
+ */
14
+ getGenerations(group: string): Promise<Record<string, number>>;
15
+ /** Increment one cache's counter for a group; resolves to the NEW generation. */
16
+ bump(cacheName: string, group: string): Promise<number>;
17
+ }
18
+ /**
19
+ * The reserved group that carries cache-WIDE invalidation (`invalidateAll`) as an epoch
20
+ * counter, so the store interface stays two methods. It is probed beside the real group on
21
+ * its own timestamp, and a moved epoch maps to a full local clear.
22
+ */
23
+ export declare const CACHE_EPOCH_GROUP = "*";
24
+ /** What one coherent cache hands the tracker: its identity, cadence, and the 16.1 primitives. */
25
+ export interface CoherentCacheRegistration {
26
+ cacheName: string;
27
+ /** Probe cadence; a group snapshot older than this re-reads the directory before serving. */
28
+ windowMsecs: number;
29
+ /**
30
+ * Whether this cache ever invalidates CACHE-WIDE, and so needs the reserved epoch group
31
+ * probed beside its own.
32
+ *
33
+ * Declared per cache because the epoch shard is ONE globally-placed Durable Object: probing
34
+ * it is a cross-colo round trip awaited on the read path of every isolate, and a cache with
35
+ * no `invalidateAll` call site can never move that counter, so it would be a round trip that
36
+ * structurally cannot return news. Declared rather than inferred from call sites, and the
37
+ * handle REFUSES an undeclared `invalidateAll` rather than dropping entries locally while
38
+ * peers keep serving them.
39
+ */
40
+ probesEpoch: boolean;
41
+ /** layered-loader's local, fencing, non-publishing group invalidation. */
42
+ applyRemoteInvalidationForGroup(group: string): void;
43
+ /** Its cache-wide form, for a moved epoch. */
44
+ applyRemoteInvalidation(): void;
45
+ }
46
+ export interface GroupGenerationTrackerOptions {
47
+ logger?: Logger;
48
+ metrics?: OperationalMetrics;
49
+ now?: () => number;
50
+ /**
51
+ * Supplied ONLY by an isolate runtime; see `CreateAppCachesOptions.currentInvocation`. It
52
+ * decides which in-flight probes may be joined. Absent ⇒ every probe coalesces, which is
53
+ * correct wherever one I/O context serves everything.
54
+ */
55
+ currentInvocation?: () => object | undefined;
56
+ /**
57
+ * LRU bound on per-group bookkeeping. Evicting a group loses its baselines, which the next
58
+ * probe treats as "possibly missed a bump" and re-invalidates (an over-invalidation, never
59
+ * a stale serve), so the bound trades a little re-loading for bounded memory.
60
+ */
61
+ maxTrackedGroups?: number;
62
+ }
63
+ /**
64
+ * The per-instance bookkeeping half of the scheme: which generation each (cache, group) was
65
+ * last seen at, when each group was last probed, and the application of a moved counter onto
66
+ * the registered caches. One tracker serves the whole cache bag, so all coherent caches share
67
+ * each group's probe.
68
+ *
69
+ * Error posture, deliberately asymmetric:
70
+ * - a PROBE failure fails CLOSED: the affected caches are locally invalidated and the read
71
+ * loads fresh, so a directory outage degrades to pass-through performance, never to serving
72
+ * stale (`container.ts`'s "a stale-serving cache would be a correctness bug" stance);
73
+ * - a BUMP failure fails OPEN: the write and its local invalidation already happened, peers
74
+ * heal at the cache TTL, and the counter + warn line are the visible trace.
75
+ */
76
+ export declare class GroupGenerationTracker {
77
+ private readonly store;
78
+ private readonly registrations;
79
+ private readonly groups;
80
+ /** Held outside the LRU map: the epoch baseline must never be evicted. */
81
+ private readonly epoch;
82
+ private readonly log;
83
+ private readonly metrics;
84
+ private readonly now;
85
+ private readonly maxTrackedGroups;
86
+ private readonly currentInvocation;
87
+ constructor(store: CacheGenerationStore, options?: GroupGenerationTrackerOptions);
88
+ register(registration: CoherentCacheRegistration): void;
89
+ /**
90
+ * Called by a coherent handle before its read. Resolves without I/O while the group's (and
91
+ * the epoch's) snapshot is inside the calling cache's window; otherwise one coalesced store
92
+ * round trip refreshes every registered cache's view of the group. Never rejects.
93
+ */
94
+ ensureFresh(cacheName: string, group: string): Promise<void>;
95
+ /**
96
+ * Called by a coherent handle AFTER its local invalidation, awaited by the write path so
97
+ * "invalidate right after the write commits" spans the directory too. The returned
98
+ * generation is max-merged into the baseline, so the writing instance's next probe does not
99
+ * read its own bump as a peer's change and re-invalidate what it just reloaded.
100
+ */
101
+ noteLocalInvalidation(cacheName: string, group: string): Promise<void>;
102
+ private isStale;
103
+ /** LRU-touch the group's state, evicting the oldest when over the bound. */
104
+ private touchGroup;
105
+ private probe;
106
+ private runProbe;
107
+ /**
108
+ * The caches one probe speaks for. A GROUP probe speaks for every registered cache (they
109
+ * share the group's shard, which is what makes the batched read worth doing); an EPOCH probe
110
+ * speaks only for those that declared a cache-wide invalidation path, so a cache that opted
111
+ * out is neither invalidated by it nor has its baseline moved by it.
112
+ */
113
+ private affectedBy;
114
+ private applyInvalidation;
115
+ }
116
+ //# sourceMappingURL=generationCoherency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generationCoherency.d.ts","sourceRoot":"","sources":["../src/generationCoherency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAgBrE;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC9D,iFAAiF;IACjF,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxD;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC,iGAAiG;AACjG,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;;;;;OAUG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB,0EAA0E;IAC1E,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD,8CAA8C;IAC9C,uBAAuB,IAAI,IAAI,CAAA;CAChC;AAuBD,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IAC5C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAsB;IAgB/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAfxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+C;IAC7E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAIrB;IACD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0B;IAE5D,YACmB,KAAK,EAAE,oBAAoB,EAC5C,OAAO,GAAE,6BAAkC,EAO5C;IAED,QAAQ,CAAC,YAAY,EAAE,yBAAyB,GAAG,IAAI,CAEtD;IAED;;;;OAIG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYjE;IAED;;;;;OAKG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa3E;IAED,OAAO,CAAC,OAAO;IAIf,4EAA4E;IAC5E,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,KAAK;YAgBC,QAAQ;IAoCtB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,iBAAiB;CAQ1B"}
@@ -0,0 +1,188 @@
1
+ import { describeError, noopOperationalMetrics } from '@cat-factory/kernel';
2
+ /**
3
+ * The reserved group that carries cache-WIDE invalidation (`invalidateAll`) as an epoch
4
+ * counter, so the store interface stays two methods. It is probed beside the real group on
5
+ * its own timestamp, and a moved epoch maps to a full local clear.
6
+ */
7
+ export const CACHE_EPOCH_GROUP = '*';
8
+ /**
9
+ * The stand-in invocation for a runtime that has no such concept: one process serving every
10
+ * request out of one I/O context, so every probe belongs to the same "invocation" and
11
+ * coalescing behaves exactly as it did before the distinction existed.
12
+ */
13
+ const SINGLE_INVOCATION = { runtime: 'single-context' };
14
+ /**
15
+ * The per-instance bookkeeping half of the scheme: which generation each (cache, group) was
16
+ * last seen at, when each group was last probed, and the application of a moved counter onto
17
+ * the registered caches. One tracker serves the whole cache bag, so all coherent caches share
18
+ * each group's probe.
19
+ *
20
+ * Error posture, deliberately asymmetric:
21
+ * - a PROBE failure fails CLOSED: the affected caches are locally invalidated and the read
22
+ * loads fresh, so a directory outage degrades to pass-through performance, never to serving
23
+ * stale (`container.ts`'s "a stale-serving cache would be a correctness bug" stance);
24
+ * - a BUMP failure fails OPEN: the write and its local invalidation already happened, peers
25
+ * heal at the cache TTL, and the counter + warn line are the visible trace.
26
+ */
27
+ export class GroupGenerationTracker {
28
+ store;
29
+ registrations = new Map();
30
+ groups = new Map();
31
+ /** Held outside the LRU map: the epoch baseline must never be evicted. */
32
+ epoch = {
33
+ lastProbeAt: undefined,
34
+ generations: new Map(),
35
+ inflight: undefined,
36
+ };
37
+ log;
38
+ metrics;
39
+ now;
40
+ maxTrackedGroups;
41
+ currentInvocation;
42
+ constructor(store, options = {}) {
43
+ this.store = store;
44
+ this.log = options.logger;
45
+ this.metrics = options.metrics ?? noopOperationalMetrics;
46
+ this.now = options.now ?? Date.now;
47
+ this.maxTrackedGroups = options.maxTrackedGroups ?? 2000;
48
+ this.currentInvocation = options.currentInvocation ?? (() => SINGLE_INVOCATION);
49
+ }
50
+ register(registration) {
51
+ this.registrations.set(registration.cacheName, registration);
52
+ }
53
+ /**
54
+ * Called by a coherent handle before its read. Resolves without I/O while the group's (and
55
+ * the epoch's) snapshot is inside the calling cache's window; otherwise one coalesced store
56
+ * round trip refreshes every registered cache's view of the group. Never rejects.
57
+ */
58
+ async ensureFresh(cacheName, group) {
59
+ const registration = this.registrations.get(cacheName);
60
+ if (!registration)
61
+ return;
62
+ const probes = [];
63
+ const groupState = this.touchGroup(group);
64
+ if (this.isStale(groupState, registration.windowMsecs)) {
65
+ probes.push(this.probe(groupState, group));
66
+ }
67
+ if (registration.probesEpoch && this.isStale(this.epoch, registration.windowMsecs)) {
68
+ probes.push(this.probe(this.epoch, CACHE_EPOCH_GROUP));
69
+ }
70
+ if (probes.length > 0)
71
+ await Promise.all(probes);
72
+ }
73
+ /**
74
+ * Called by a coherent handle AFTER its local invalidation, awaited by the write path so
75
+ * "invalidate right after the write commits" spans the directory too. The returned
76
+ * generation is max-merged into the baseline, so the writing instance's next probe does not
77
+ * read its own bump as a peer's change and re-invalidate what it just reloaded.
78
+ */
79
+ async noteLocalInvalidation(cacheName, group) {
80
+ try {
81
+ const generation = await this.store.bump(cacheName, group);
82
+ const state = group === CACHE_EPOCH_GROUP ? this.epoch : this.touchGroup(group);
83
+ mergeGeneration(state, cacheName, generation);
84
+ }
85
+ catch (error) {
86
+ this.metrics.increment('cache.coherency_bump_failure', { cache: cacheName });
87
+ this.log?.warn('cache coherency bump failed; peers heal at the cache TTL', {
88
+ cache: cacheName,
89
+ group,
90
+ ...describeError(error),
91
+ });
92
+ }
93
+ }
94
+ isStale(state, windowMsecs) {
95
+ return state.lastProbeAt === undefined || this.now() - state.lastProbeAt >= windowMsecs;
96
+ }
97
+ /** LRU-touch the group's state, evicting the oldest when over the bound. */
98
+ touchGroup(group) {
99
+ let state = this.groups.get(group);
100
+ if (state) {
101
+ this.groups.delete(group);
102
+ }
103
+ else {
104
+ state = { lastProbeAt: undefined, generations: new Map(), inflight: undefined };
105
+ }
106
+ this.groups.set(group, state);
107
+ if (this.groups.size > this.maxTrackedGroups) {
108
+ const oldest = this.groups.keys().next().value;
109
+ if (oldest !== undefined)
110
+ this.groups.delete(oldest);
111
+ }
112
+ return state;
113
+ }
114
+ probe(state, group) {
115
+ const invocation = this.currentInvocation();
116
+ const inflight = state.inflight;
117
+ // `undefined` never matches, not even itself: an invocation the runtime cannot name is one
118
+ // this probe cannot prove it shares an I/O context with, and joining wrongly is fatal where
119
+ // starting a second probe merely costs a round trip.
120
+ if (inflight && invocation !== undefined && inflight.invocation === invocation) {
121
+ return inflight.promise;
122
+ }
123
+ const promise = this.runProbe(state, group).finally(() => {
124
+ if (state.inflight?.promise === promise)
125
+ state.inflight = undefined;
126
+ });
127
+ state.inflight = { invocation, promise };
128
+ return promise;
129
+ }
130
+ async runProbe(state, group) {
131
+ const scope = group === CACHE_EPOCH_GROUP ? 'epoch' : 'group';
132
+ try {
133
+ const fetched = await this.store.getGenerations(group);
134
+ this.metrics.increment('cache.coherency_probe', { scope });
135
+ for (const registration of this.affectedBy(scope)) {
136
+ const generation = fetched[registration.cacheName] ?? 0;
137
+ // Counters are monotonic, so only a HIGHER generation is a peer's change: a lower one
138
+ // can only be a store read racing this instance's own concurrent bump. An evicted or
139
+ // never-seen baseline reads as 0, so any bump that ever happened re-invalidates,
140
+ // over-invalidation, never a stale serve.
141
+ const changed = generation > (state.generations.get(registration.cacheName) ?? 0);
142
+ if (changed) {
143
+ this.applyInvalidation(registration, scope, group);
144
+ this.metrics.increment('cache.coherency_invalidation', {
145
+ cache: registration.cacheName,
146
+ });
147
+ }
148
+ mergeGeneration(state, registration.cacheName, generation);
149
+ }
150
+ state.lastProbeAt = this.now();
151
+ }
152
+ catch (error) {
153
+ // Fail CLOSED: invalidate locally and leave the window stale, so reads load fresh (and
154
+ // re-probe) until the directory answers again: pass-through performance, zero staleness.
155
+ this.metrics.increment('cache.coherency_probe_failure', { scope });
156
+ for (const registration of this.affectedBy(scope)) {
157
+ this.applyInvalidation(registration, scope, group);
158
+ }
159
+ this.log?.warn('cache coherency probe failed; affected reads load fresh', {
160
+ scope,
161
+ group,
162
+ ...describeError(error),
163
+ });
164
+ }
165
+ }
166
+ /**
167
+ * The caches one probe speaks for. A GROUP probe speaks for every registered cache (they
168
+ * share the group's shard, which is what makes the batched read worth doing); an EPOCH probe
169
+ * speaks only for those that declared a cache-wide invalidation path, so a cache that opted
170
+ * out is neither invalidated by it nor has its baseline moved by it.
171
+ */
172
+ affectedBy(scope) {
173
+ const all = [...this.registrations.values()];
174
+ return scope === 'epoch' ? all.filter((registration) => registration.probesEpoch) : all;
175
+ }
176
+ applyInvalidation(registration, scope, group) {
177
+ if (scope === 'epoch')
178
+ registration.applyRemoteInvalidation();
179
+ else
180
+ registration.applyRemoteInvalidationForGroup(group);
181
+ }
182
+ }
183
+ function mergeGeneration(state, cacheName, generation) {
184
+ const current = state.generations.get(cacheName) ?? 0;
185
+ if (generation > current)
186
+ state.generations.set(cacheName, generation);
187
+ }
188
+ //# sourceMappingURL=generationCoherency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generationCoherency.js","sourceRoot":"","sources":["../src/generationCoherency.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
- export { createAppCaches, DEFAULT_APP_CACHES_PROFILE, ISOLATE_SAFE_APP_CACHES_PROFILE, } from './appCaches.js';
1
+ export { createAppCaches, DEFAULT_APP_CACHES_PROFILE, ISOLATE_COHERENT_APP_CACHES_PROFILE, ISOLATE_SAFE_APP_CACHES_PROFILE, } from './appCaches.js';
2
2
  export type { AppCachesProfile, CreateAppCachesOptions, GroupCacheNotifications, GroupCacheProfile, GroupNotificationPairFactory, } from './appCaches.js';
3
+ export { CACHE_EPOCH_GROUP } from './generationCoherency.js';
4
+ export type { CacheGenerationStore } from './generationCoherency.js';
5
+ export type { BackgroundWorkMeta, BackgroundWorkScheduler } from 'layered-loader/core';
3
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,+BAA+B,GAChC,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,mCAAmC,EACnC,+BAA+B,GAChC,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAIpE,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- export { createAppCaches, DEFAULT_APP_CACHES_PROFILE, ISOLATE_SAFE_APP_CACHES_PROFILE, } from './appCaches.js';
1
+ export { createAppCaches, DEFAULT_APP_CACHES_PROFILE, ISOLATE_COHERENT_APP_CACHES_PROFILE, ISOLATE_SAFE_APP_CACHES_PROFILE, } from './appCaches.js';
2
+ export { CACHE_EPOCH_GROUP } from './generationCoherency.js';
2
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,+BAA+B,GAChC,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,mCAAmC,EACnC,+BAA+B,GAChC,MAAM,gBAAgB,CAAA;AAQvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,38 @@
1
+ /** The outcome of one guarded read, so the caller keeps owning its hit/miss accounting. */
2
+ export interface InvocationScopedLoadResult<T> {
3
+ value: T;
4
+ /** Whether THIS caller ran the load, as opposed to joining one already in flight. */
5
+ loaded: boolean;
6
+ }
7
+ /**
8
+ * Per-invocation coalescing of cache-miss loads, plus the local fence their publish needs.
9
+ *
10
+ * One instance per cache handle. `currentInvocation` returns an object identifying the
11
+ * invocation being served, or `undefined` outside any bracketed entry point (a Workflows step,
12
+ * which has no ExecutionContext). An absent invocation coalesces with NOTHING, because
13
+ * two loads that cannot be told apart must be assumed to belong to different contexts.
14
+ */
15
+ export declare class InvocationScopedLoads<T> {
16
+ private readonly currentInvocation;
17
+ /**
18
+ * In-flight loads, partitioned by invocation so a lookup can never return another
19
+ * invocation's promise. Weak, so an invocation's partition is collected with it: an entry
20
+ * point that ends mid-load leaks neither the map nor the promise.
21
+ */
22
+ private readonly inFlight;
23
+ /** Monotonic; every invalidation of this cache bumps it. See {@link loadAndPublish}. */
24
+ private invalidations;
25
+ constructor(currentInvocation: () => object | undefined);
26
+ /**
27
+ * Record that this cache was invalidated, from any source: its own write path, or a
28
+ * coherency probe applying a peer's. Breaks the fence for every load currently in flight.
29
+ */
30
+ noteInvalidation(): void;
31
+ /**
32
+ * Run (or join, within this same invocation only) the load for one cache key, publishing the
33
+ * result through `publish` when the fence holds.
34
+ */
35
+ run(cacheKey: string, load: () => Promise<T>, publish: (value: T) => Promise<void>): Promise<InvocationScopedLoadResult<T>>;
36
+ private loadAndPublish;
37
+ }
38
+ //# sourceMappingURL=invocationScopedLoads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocationScopedLoads.d.ts","sourceRoot":"","sources":["../src/invocationScopedLoads.ts"],"names":[],"mappings":"AAmBA,2FAA2F;AAC3F,MAAM,WAAW,0BAA0B,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,CAAA;IACR,qFAAqF;IACrF,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,qBAAqB,CAAC,CAAC;IAUtB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAT9C;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiD;IAC1E,wFAAwF;IACxF,OAAO,CAAC,aAAa,CAAI;IAEzB,YAA6B,iBAAiB,EAAE,MAAM,MAAM,GAAG,SAAS,EAAI;IAE5E;;;OAGG;IACH,gBAAgB,IAAI,IAAI,CAEvB;IAED;;;OAGG;IACG,GAAG,CACP,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAqBxC;YAEa,cAAc;CAc7B"}
@@ -0,0 +1,87 @@
1
+ // Load scoping for ISOLATE runtimes, where a promise may not outlive the invocation that
2
+ // created it.
3
+ //
4
+ // On Cloudflare Workers every invocation gets its own I/O context, and a promise created while
5
+ // serving one may never be awaited by another: workerd destroys the joining invocation with
6
+ // "Cannot perform I/O on behalf of a different request". That fault is raised at the RUNTIME
7
+ // level, so the joining code cannot catch it: its work simply disappears, which is why this
8
+ // cannot be handled where it surfaces and has to be made impossible instead.
9
+ //
10
+ // The app cache bag is one per ISOLATE (that is what makes the entries caches rather than
11
+ // per-invocation memos), so layered-loader's in-flight load coalescing (`runningLoads`) would
12
+ // hand exactly such a promise to a second concurrent invocation on every same-key MISS. This
13
+ // guard keeps the coalescing where it is safe (within ONE invocation, which is the common case
14
+ // it exists for: a batch read issuing the same key twice) and removes it where it is not.
15
+ //
16
+ // Nothing here is needed on Node, where one process serves every request out of one I/O
17
+ // context; a facade that supplies no `currentInvocation` keeps layered-loader's own load path
18
+ // unchanged.
19
+ /**
20
+ * Per-invocation coalescing of cache-miss loads, plus the local fence their publish needs.
21
+ *
22
+ * One instance per cache handle. `currentInvocation` returns an object identifying the
23
+ * invocation being served, or `undefined` outside any bracketed entry point (a Workflows step,
24
+ * which has no ExecutionContext). An absent invocation coalesces with NOTHING, because
25
+ * two loads that cannot be told apart must be assumed to belong to different contexts.
26
+ */
27
+ export class InvocationScopedLoads {
28
+ currentInvocation;
29
+ /**
30
+ * In-flight loads, partitioned by invocation so a lookup can never return another
31
+ * invocation's promise. Weak, so an invocation's partition is collected with it: an entry
32
+ * point that ends mid-load leaks neither the map nor the promise.
33
+ */
34
+ inFlight = new WeakMap();
35
+ /** Monotonic; every invalidation of this cache bumps it. See {@link loadAndPublish}. */
36
+ invalidations = 0;
37
+ constructor(currentInvocation) {
38
+ this.currentInvocation = currentInvocation;
39
+ }
40
+ /**
41
+ * Record that this cache was invalidated, from any source: its own write path, or a
42
+ * coherency probe applying a peer's. Breaks the fence for every load currently in flight.
43
+ */
44
+ noteInvalidation() {
45
+ this.invalidations += 1;
46
+ }
47
+ /**
48
+ * Run (or join, within this same invocation only) the load for one cache key, publishing the
49
+ * result through `publish` when the fence holds.
50
+ */
51
+ async run(cacheKey, load, publish) {
52
+ const invocation = this.currentInvocation();
53
+ if (!invocation) {
54
+ return { value: await this.loadAndPublish(load, publish), loaded: true };
55
+ }
56
+ let partition = this.inFlight.get(invocation);
57
+ if (!partition) {
58
+ partition = new Map();
59
+ this.inFlight.set(invocation, partition);
60
+ }
61
+ const joined = partition.get(cacheKey);
62
+ // Safe to await: this promise was created while serving THIS invocation.
63
+ if (joined)
64
+ return { value: await joined, loaded: false };
65
+ const loading = this.loadAndPublish(load, publish);
66
+ partition.set(cacheKey, loading);
67
+ try {
68
+ return { value: await loading, loaded: true };
69
+ }
70
+ finally {
71
+ partition.delete(cacheKey);
72
+ }
73
+ }
74
+ async loadAndPublish(load, publish) {
75
+ const fence = this.invalidations;
76
+ const value = await load();
77
+ // Publish only when no invalidation landed while the load was running, or a late write
78
+ // would resurrect the entry that invalidation dropped. layered-loader fences its own load
79
+ // path this way (`backgroundWriteFences`); this path is ours, so the fence is ours too.
80
+ // The counter is cache-WIDE rather than per key, so an unrelated invalidation costs one
81
+ // skipped cache write: over-invalidation, never a stale serve.
82
+ if (this.invalidations === fence)
83
+ await publish(value);
84
+ return value;
85
+ }
86
+ }
87
+ //# sourceMappingURL=invocationScopedLoads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocationScopedLoads.js","sourceRoot":"","sources":["../src/invocationScopedLoads.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,cAAc;AACd,EAAE;AACF,+FAA+F;AAC/F,4FAA4F;AAC5F,6FAA6F;AAC7F,4FAA4F;AAC5F,6EAA6E;AAC7E,EAAE;AACF,0FAA0F;AAC1F,8FAA8F;AAC9F,6FAA6F;AAC7F,+FAA+F;AAC/F,0FAA0F;AAC1F,EAAE;AACF,wFAAwF;AACxF,8FAA8F;AAC9F,aAAa;AASb;;;;;;;GAOG;AACH,MAAM,OAAO,qBAAqB;IAUH,iBAAiB;IAT9C;;;;OAIG;IACc,QAAQ,GAAG,IAAI,OAAO,EAAmC,CAAA;IAC1E,wFAAwF;IAChF,aAAa,GAAG,CAAC,CAAA;IAEzB,YAA6B,iBAA2C;iCAA3C,iBAAiB;IAA6B,CAAC;IAE5E;;;OAGG;IACH,gBAAgB;QACd,IAAI,CAAC,aAAa,IAAI,CAAC,CAAA;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CACP,QAAgB,EAChB,IAAsB,EACtB,OAAoC;QAEpC,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC3C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAC1E,CAAC;QACD,IAAI,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS,GAAG,IAAI,GAAG,EAAE,CAAA;YACrB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC1C,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACtC,yEAAyE;QACzE,IAAI,MAAM;YAAE,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QAEzD,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAClD,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAChC,IAAI,CAAC;YACH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;QAC/C,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAC1B,IAAsB,EACtB,OAAoC;QAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAA;QAChC,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,CAAA;QAC1B,uFAAuF;QACvF,0FAA0F;QAC1F,wFAAwF;QACxF,wFAAwF;QACxF,+DAA+D;QAC/D,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK;YAAE,MAAM,OAAO,CAAC,KAAK,CAAC,CAAA;QACtD,OAAO,KAAK,CAAA;IACd,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/caching",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "description": "The app-level caching seam for the Agent Architecture Board (docs/initiatives/caching-layer.md): createAppCaches builds the named, typed in-memory read-through caches (layered-loader) the services consume via the kernel AppCaches port, with optional distributed invalidation over an injected Redis notification pair. Redis is only ever an invalidation bus, never a data tier.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,8 +24,8 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "layered-loader": "^16.0.0",
28
- "@cat-factory/kernel": "0.260.0"
27
+ "layered-loader": "^16.1.0",
28
+ "@cat-factory/kernel": "0.261.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^26.1.2",