@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 +78 -17
- package/dist/appCaches.d.ts +74 -1
- package/dist/appCaches.d.ts.map +1 -1
- package/dist/appCaches.js +245 -35
- package/dist/appCaches.js.map +1 -1
- package/dist/generationCoherency.d.ts +116 -0
- package/dist/generationCoherency.d.ts.map +1 -0
- package/dist/generationCoherency.js +188 -0
- package/dist/generationCoherency.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/invocationScopedLoads.d.ts +38 -0
- package/dist/invocationScopedLoads.d.ts.map +1 -0
- package/dist/invocationScopedLoads.js +87 -0
- package/dist/invocationScopedLoads.js.map +1 -0
- package/package.json +3 -3
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
|
|
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.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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/…
|
|
49
|
-
by the source's cheap version probe
|
|
50
|
-
so a peer isolate's cached body
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
72
|
-
|
|
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())
|
package/dist/appCaches.d.ts
CHANGED
|
@@ -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
|
package/dist/appCaches.d.ts.map
CHANGED
|
@@ -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;
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
332
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/appCaches.js.map
CHANGED
|
@@ -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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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.
|
|
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.
|
|
28
|
-
"@cat-factory/kernel": "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",
|