audit-tools 0.33.9 → 0.33.11
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/dist/audit/orchestrator/rollingDispatch.js +1 -1
- package/dist/audit/orchestrator/rollingDispatch.js.map +1 -1
- package/dist/remediate/steps/nextStep.js +1 -1
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/shared/dispatch/admissionLeaseReconcile.js +1 -1
- package/dist/shared/dispatch/admissionLeaseReconcile.js.map +1 -1
- package/dist/shared/dispatch/admissionLoop.d.ts +42 -10
- package/dist/shared/dispatch/admissionLoop.d.ts.map +1 -1
- package/dist/shared/dispatch/admissionLoop.js +74 -7
- package/dist/shared/dispatch/admissionLoop.js.map +1 -1
- package/dist/shared/dispatch/dispatchQuotaContract.d.ts +82 -7
- package/dist/shared/dispatch/dispatchQuotaContract.d.ts.map +1 -1
- package/dist/shared/dispatch/hostDispatchWall.d.ts.map +1 -1
- package/dist/shared/dispatch/hostDispatchWall.js +8 -0
- package/dist/shared/dispatch/hostDispatchWall.js.map +1 -1
- package/dist/shared/dispatch/rollingDispatch.d.ts +17 -10
- package/dist/shared/dispatch/rollingDispatch.d.ts.map +1 -1
- package/dist/shared/dispatch/rollingDispatch.js +49 -33
- package/dist/shared/dispatch/rollingDispatch.js.map +1 -1
- package/dist/shared/dispatch/unifiedRolling.d.ts +3 -2
- package/dist/shared/dispatch/unifiedRolling.d.ts.map +1 -1
- package/dist/shared/dispatch/unifiedRolling.js +12 -4
- package/dist/shared/dispatch/unifiedRolling.js.map +1 -1
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +5 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/providers/providerConfirmation.d.ts +1 -1
- package/dist/shared/providers/providerConfirmation.d.ts.map +1 -1
- package/dist/shared/providers/providerConfirmation.js +11 -2
- package/dist/shared/providers/providerConfirmation.js.map +1 -1
- package/dist/shared/providers/sharedProviderConfirmation.d.ts +20 -5
- package/dist/shared/providers/sharedProviderConfirmation.d.ts.map +1 -1
- package/dist/shared/providers/sharedProviderConfirmation.js +108 -34
- package/dist/shared/providers/sharedProviderConfirmation.js.map +1 -1
- package/dist/shared/quota/accountId.d.ts +70 -0
- package/dist/shared/quota/accountId.d.ts.map +1 -1
- package/dist/shared/quota/accountId.js +82 -5
- package/dist/shared/quota/accountId.js.map +1 -1
- package/dist/shared/quota/apiPool.d.ts +2 -1
- package/dist/shared/quota/apiPool.d.ts.map +1 -1
- package/dist/shared/quota/apiPool.js +14 -2
- package/dist/shared/quota/apiPool.js.map +1 -1
- package/dist/shared/quota/capacity.d.ts +82 -0
- package/dist/shared/quota/capacity.d.ts.map +1 -1
- package/dist/shared/quota/capacity.js +42 -1
- package/dist/shared/quota/capacity.js.map +1 -1
- package/dist/shared/quota/claudeOAuthQuotaSource.d.ts.map +1 -1
- package/dist/shared/quota/claudeOAuthQuotaSource.js +46 -8
- package/dist/shared/quota/claudeOAuthQuotaSource.js.map +1 -1
- package/dist/shared/quota/quotaSource.d.ts +73 -2
- package/dist/shared/quota/quotaSource.d.ts.map +1 -1
- package/dist/shared/quota/quotaSource.js +57 -2
- package/dist/shared/quota/quotaSource.js.map +1 -1
- package/dist/shared/quota/reservationLedger.d.ts +98 -26
- package/dist/shared/quota/reservationLedger.d.ts.map +1 -1
- package/dist/shared/quota/reservationLedger.js +109 -37
- package/dist/shared/quota/reservationLedger.js.map +1 -1
- package/dist/shared/quota/scheduler.d.ts +25 -1
- package/dist/shared/quota/scheduler.d.ts.map +1 -1
- package/dist/shared/quota/scheduler.js +115 -10
- package/dist/shared/quota/scheduler.js.map +1 -1
- package/dist/shared/quota/state.d.ts +24 -9
- package/dist/shared/quota/state.d.ts.map +1 -1
- package/dist/shared/quota/state.js +43 -16
- package/dist/shared/quota/state.js.map +1 -1
- package/dist/shared/quota/types.d.ts +57 -6
- package/dist/shared/quota/types.d.ts.map +1 -1
- package/dist/shared/quota/types.js.map +1 -1
- package/dist/shared/quota/windowConstraints.d.ts +56 -0
- package/dist/shared/quota/windowConstraints.d.ts.map +1 -0
- package/dist/shared/quota/windowConstraints.js +71 -0
- package/dist/shared/quota/windowConstraints.js.map +1 -0
- package/dist/shared/types/providerConfirmation.d.ts +31 -1
- package/dist/shared/types/providerConfirmation.d.ts.map +1 -1
- package/docs/audit-pkg/operator-guide.md +4 -1
- package/package.json +1 -1
|
@@ -4,27 +4,93 @@ import { z } from "zod";
|
|
|
4
4
|
* that scale on DIFFERENT denominators (e.g. Claude's 5-hour `session` vs 7-day
|
|
5
5
|
* `weekly`; Codex's primary vs secondary). The same N tokens is a large percent
|
|
6
6
|
* of a small window and a tiny percent of a big one, so tokens→percent slope is
|
|
7
|
-
* learned per (pool,
|
|
8
|
-
*
|
|
7
|
+
* learned per (pool, {@link windowSlopeKey}) and each window meters against its own
|
|
8
|
+
* allowance. `label` is an AGNOSTIC string ("session","weekly",…) — never a
|
|
9
9
|
* provider/model identity — so any source can add its own windows.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Whether a window's allowance is shared by every model on the credential, or
|
|
13
|
+
* belongs to one model alone. This is the METERING PARTITION and it is decided
|
|
14
|
+
* HERE, by the producer that knows — never re-derived downstream from pool
|
|
15
|
+
* identity, which is what four refused repair attempts each tried and got wrong.
|
|
16
|
+
*
|
|
17
|
+
* - `account` — one allowance shared across all of the credential's models
|
|
18
|
+
* (Claude's `five_hour`/`seven_day`, an unscoped `limits[]` entry). Meters on
|
|
19
|
+
* `(accountKey, label)`: N models on one credential draw down ONE budget.
|
|
20
|
+
* - `model` — applies to this model alone (a `limits[]` entry carrying
|
|
21
|
+
* `scope.model`). Meters on `(poolId, label)`: sharing it would falsely
|
|
22
|
+
* throttle siblings that the limit does not cover.
|
|
23
|
+
*
|
|
24
|
+
* REQUIRED, with no default, on every window from every source. A source that
|
|
25
|
+
* cannot distinguish the two emits `account` for all of its windows — that IS
|
|
26
|
+
* the fallback ("provider exposes only account-wide quota ⇒ it applies to all
|
|
27
|
+
* their models"), stated rather than assumed. The absence of a default is
|
|
28
|
+
* deliberate: a default is how the next provider gets silently special-cased.
|
|
29
|
+
*
|
|
30
|
+
* Orthogonal to the learned slope. `tokens_per_pct` stays per (pool, window)
|
|
31
|
+
* unconditionally — even under an account-scoped window, where N models share
|
|
32
|
+
* one percent allowance but each converts it to tokens at its OWN rate. Scope
|
|
33
|
+
* partitions the ALLOWANCE; the slope prices it per model.
|
|
34
|
+
*/
|
|
35
|
+
export declare const QuotaWindowScopeSchema: z.ZodEnum<["account", "model"]>;
|
|
36
|
+
export type QuotaWindowScope = z.infer<typeof QuotaWindowScopeSchema>;
|
|
37
|
+
/**
|
|
38
|
+
* A `tokens_per_pct` map key, as produced by {@link windowSlopeKey}. Branded so the
|
|
39
|
+
* slope map cannot be read or written with a bare window label: the two key spaces
|
|
40
|
+
* are both strings, and an unbranded parameter left the guarantee to whoever
|
|
41
|
+
* remembered it — which a test promptly got wrong, writing under `"weekly"` where
|
|
42
|
+
* production only ever reads `"account:weekly"`.
|
|
43
|
+
*/
|
|
44
|
+
export type WindowSlopeKey = string & {
|
|
45
|
+
readonly __windowSlopeKey: unique symbol;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The key a window's learned tokens-per-percent slope is stored under, within a
|
|
49
|
+
* pool's quota-state entry. **The single resolution point for slope lookup** — the
|
|
50
|
+
* only way to obtain a {@link WindowSlopeKey}, and every read and write of
|
|
51
|
+
* `tokens_per_pct` requires one, so the map cannot be keyed one way by the producer
|
|
52
|
+
* and another by the consumer.
|
|
53
|
+
*
|
|
54
|
+
* Keyed by `(scope, label)`, not label alone: an account-scoped and a model-scoped
|
|
55
|
+
* window can share a group name (both `session`) while pricing different allowances,
|
|
56
|
+
* and collapsing them onto one entry would silently blend two exchange rates into a
|
|
57
|
+
* single wrong one.
|
|
58
|
+
*
|
|
59
|
+
* ⚠ Changing this key ORPHANS previously-learned slopes (they were stored under the
|
|
60
|
+
* bare label). That is deliberate and self-healing: an orphaned window reads as
|
|
61
|
+
* uncalibrated, which routes the pool through the cold-start probe path and re-learns
|
|
62
|
+
* within a few dispatches. A migration shim would instead carry a slope forward onto a
|
|
63
|
+
* partition it was never measured against.
|
|
64
|
+
*
|
|
65
|
+
* This is also the seam for the deferred sibling-derived slope prior (design of
|
|
66
|
+
* record, "Deferred seam"): a prior can be injected at this one lookup point without
|
|
67
|
+
* touching admission.
|
|
68
|
+
*/
|
|
69
|
+
export declare function windowSlopeKey(scope: QuotaWindowScope, label: string): WindowSlopeKey;
|
|
11
70
|
export declare const QuotaWindowSchema: z.ZodObject<{
|
|
12
71
|
label: z.ZodString;
|
|
72
|
+
scope: z.ZodEnum<["account", "model"]>;
|
|
13
73
|
remaining_pct: z.ZodNullable<z.ZodNumber>;
|
|
14
74
|
reset_at: z.ZodNullable<z.ZodString>;
|
|
15
75
|
tokens_remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
16
76
|
}, "strict", z.ZodTypeAny, {
|
|
17
77
|
label: string;
|
|
78
|
+
scope: "model" | "account";
|
|
18
79
|
remaining_pct: number | null;
|
|
19
80
|
reset_at: string | null;
|
|
20
81
|
tokens_remaining?: number | null | undefined;
|
|
21
82
|
}, {
|
|
22
83
|
label: string;
|
|
84
|
+
scope: "model" | "account";
|
|
23
85
|
remaining_pct: number | null;
|
|
24
86
|
reset_at: string | null;
|
|
25
87
|
tokens_remaining?: number | null | undefined;
|
|
26
88
|
}>;
|
|
27
89
|
export type QuotaWindow = z.infer<typeof QuotaWindowSchema>;
|
|
90
|
+
/** Whether a window carries a usable metering scope. */
|
|
91
|
+
export declare function hasWindowScope(window: {
|
|
92
|
+
scope?: unknown;
|
|
93
|
+
}): boolean;
|
|
28
94
|
export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
|
|
29
95
|
remaining_pct: z.ZodNullable<z.ZodNumber>;
|
|
30
96
|
reset_at: z.ZodNullable<z.ZodString>;
|
|
@@ -40,16 +106,19 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
|
|
|
40
106
|
*/
|
|
41
107
|
windows: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
42
108
|
label: z.ZodString;
|
|
109
|
+
scope: z.ZodEnum<["account", "model"]>;
|
|
43
110
|
remaining_pct: z.ZodNullable<z.ZodNumber>;
|
|
44
111
|
reset_at: z.ZodNullable<z.ZodString>;
|
|
45
112
|
tokens_remaining: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
46
113
|
}, "strict", z.ZodTypeAny, {
|
|
47
114
|
label: string;
|
|
115
|
+
scope: "model" | "account";
|
|
48
116
|
remaining_pct: number | null;
|
|
49
117
|
reset_at: string | null;
|
|
50
118
|
tokens_remaining?: number | null | undefined;
|
|
51
119
|
}, {
|
|
52
120
|
label: string;
|
|
121
|
+
scope: "model" | "account";
|
|
53
122
|
remaining_pct: number | null;
|
|
54
123
|
reset_at: string | null;
|
|
55
124
|
tokens_remaining?: number | null | undefined;
|
|
@@ -63,6 +132,7 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
|
|
|
63
132
|
source: string;
|
|
64
133
|
windows?: {
|
|
65
134
|
label: string;
|
|
135
|
+
scope: "model" | "account";
|
|
66
136
|
remaining_pct: number | null;
|
|
67
137
|
reset_at: string | null;
|
|
68
138
|
tokens_remaining?: number | null | undefined;
|
|
@@ -76,6 +146,7 @@ export declare const QuotaUsageSnapshotSchema: z.ZodObject<{
|
|
|
76
146
|
source: string;
|
|
77
147
|
windows?: {
|
|
78
148
|
label: string;
|
|
149
|
+
scope: "model" | "account";
|
|
79
150
|
remaining_pct: number | null;
|
|
80
151
|
reset_at: string | null;
|
|
81
152
|
tokens_remaining?: number | null | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotaSource.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"quotaSource.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,sBAAsB,iCAA+B,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAErF;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;EAQnB,CAAC;AACZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAEnE;AAED,eAAO,MAAM,wBAAwB;;;;;;;IAQjC;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGI,CAAC;AACZ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAChF;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjE;;;;;;;OAOG;IACH,gBAAgB,CAAC,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
|
|
@@ -4,18 +4,73 @@ import { z } from "zod";
|
|
|
4
4
|
* that scale on DIFFERENT denominators (e.g. Claude's 5-hour `session` vs 7-day
|
|
5
5
|
* `weekly`; Codex's primary vs secondary). The same N tokens is a large percent
|
|
6
6
|
* of a small window and a tiny percent of a big one, so tokens→percent slope is
|
|
7
|
-
* learned per (pool,
|
|
8
|
-
*
|
|
7
|
+
* learned per (pool, {@link windowSlopeKey}) and each window meters against its own
|
|
8
|
+
* allowance. `label` is an AGNOSTIC string ("session","weekly",…) — never a
|
|
9
9
|
* provider/model identity — so any source can add its own windows.
|
|
10
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Whether a window's allowance is shared by every model on the credential, or
|
|
13
|
+
* belongs to one model alone. This is the METERING PARTITION and it is decided
|
|
14
|
+
* HERE, by the producer that knows — never re-derived downstream from pool
|
|
15
|
+
* identity, which is what four refused repair attempts each tried and got wrong.
|
|
16
|
+
*
|
|
17
|
+
* - `account` — one allowance shared across all of the credential's models
|
|
18
|
+
* (Claude's `five_hour`/`seven_day`, an unscoped `limits[]` entry). Meters on
|
|
19
|
+
* `(accountKey, label)`: N models on one credential draw down ONE budget.
|
|
20
|
+
* - `model` — applies to this model alone (a `limits[]` entry carrying
|
|
21
|
+
* `scope.model`). Meters on `(poolId, label)`: sharing it would falsely
|
|
22
|
+
* throttle siblings that the limit does not cover.
|
|
23
|
+
*
|
|
24
|
+
* REQUIRED, with no default, on every window from every source. A source that
|
|
25
|
+
* cannot distinguish the two emits `account` for all of its windows — that IS
|
|
26
|
+
* the fallback ("provider exposes only account-wide quota ⇒ it applies to all
|
|
27
|
+
* their models"), stated rather than assumed. The absence of a default is
|
|
28
|
+
* deliberate: a default is how the next provider gets silently special-cased.
|
|
29
|
+
*
|
|
30
|
+
* Orthogonal to the learned slope. `tokens_per_pct` stays per (pool, window)
|
|
31
|
+
* unconditionally — even under an account-scoped window, where N models share
|
|
32
|
+
* one percent allowance but each converts it to tokens at its OWN rate. Scope
|
|
33
|
+
* partitions the ALLOWANCE; the slope prices it per model.
|
|
34
|
+
*/
|
|
35
|
+
export const QuotaWindowScopeSchema = z.enum(["account", "model"]);
|
|
36
|
+
/**
|
|
37
|
+
* The key a window's learned tokens-per-percent slope is stored under, within a
|
|
38
|
+
* pool's quota-state entry. **The single resolution point for slope lookup** — the
|
|
39
|
+
* only way to obtain a {@link WindowSlopeKey}, and every read and write of
|
|
40
|
+
* `tokens_per_pct` requires one, so the map cannot be keyed one way by the producer
|
|
41
|
+
* and another by the consumer.
|
|
42
|
+
*
|
|
43
|
+
* Keyed by `(scope, label)`, not label alone: an account-scoped and a model-scoped
|
|
44
|
+
* window can share a group name (both `session`) while pricing different allowances,
|
|
45
|
+
* and collapsing them onto one entry would silently blend two exchange rates into a
|
|
46
|
+
* single wrong one.
|
|
47
|
+
*
|
|
48
|
+
* ⚠ Changing this key ORPHANS previously-learned slopes (they were stored under the
|
|
49
|
+
* bare label). That is deliberate and self-healing: an orphaned window reads as
|
|
50
|
+
* uncalibrated, which routes the pool through the cold-start probe path and re-learns
|
|
51
|
+
* within a few dispatches. A migration shim would instead carry a slope forward onto a
|
|
52
|
+
* partition it was never measured against.
|
|
53
|
+
*
|
|
54
|
+
* This is also the seam for the deferred sibling-derived slope prior (design of
|
|
55
|
+
* record, "Deferred seam"): a prior can be injected at this one lookup point without
|
|
56
|
+
* touching admission.
|
|
57
|
+
*/
|
|
58
|
+
export function windowSlopeKey(scope, label) {
|
|
59
|
+
return `${scope}:${label}`;
|
|
60
|
+
}
|
|
11
61
|
export const QuotaWindowSchema = z
|
|
12
62
|
.object({
|
|
13
63
|
label: z.string(),
|
|
64
|
+
scope: QuotaWindowScopeSchema,
|
|
14
65
|
remaining_pct: z.number().nullable(),
|
|
15
66
|
reset_at: z.string().nullable(),
|
|
16
67
|
tokens_remaining: z.number().int().nullable().optional(),
|
|
17
68
|
})
|
|
18
69
|
.strict();
|
|
70
|
+
/** Whether a window carries a usable metering scope. */
|
|
71
|
+
export function hasWindowScope(window) {
|
|
72
|
+
return window.scope === "account" || window.scope === "model";
|
|
73
|
+
}
|
|
19
74
|
export const QuotaUsageSnapshotSchema = z
|
|
20
75
|
.object({
|
|
21
76
|
remaining_pct: z.number().nullable(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotaSource.js","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACzD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AA2DZ;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,CAAC,MAAM,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,MAAM,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"quotaSource.js","sourceRoot":"","sources":["../../../src/shared/quota/quotaSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;AAYnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAuB,EAAE,KAAa;IACnE,OAAO,GAAG,KAAK,IAAI,KAAK,EAAoB,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,sBAAsB;IAC7B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACzD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,wDAAwD;AACxD,MAAM,UAAU,cAAc,CAAC,MAA2B;IACxD,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AA2DZ;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,CAAC,MAAM,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAmB,EACnB,gBAAwB;IAExB,IAAI,MAAM,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
|
|
@@ -19,25 +19,85 @@ export interface ReservationLease {
|
|
|
19
19
|
/** Epoch ms after which the lease no longer counts toward outstanding. */
|
|
20
20
|
expiresAt: number;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* One metered allowance a dispatch must fit inside. A packet is admitted only if it
|
|
24
|
+
* clears EVERY constraint that applies to it — an account-wide window shared with
|
|
25
|
+
* every sibling model on the credential, plus any window scoped to this model alone.
|
|
26
|
+
*
|
|
27
|
+
* ⚠ **`budget` and `cost` are in the WINDOW's own unit, and the caller owns the
|
|
28
|
+
* conversion.** There is no unit shared across windows: a 5-hour `session` and a
|
|
29
|
+
* 7-day `weekly` scale on different denominators, so the same N tokens is a
|
|
30
|
+
* different fraction of each. The ledger is deliberately unit-agnostic — it only
|
|
31
|
+
* compares `cost` against `budget - Σ outstanding` for one key — which is exactly
|
|
32
|
+
* what lets an account-scoped window meter in the shared percent while each sibling
|
|
33
|
+
* converts its own tokens at its own learned rate.
|
|
34
|
+
*/
|
|
35
|
+
export interface AdmitConstraint {
|
|
36
|
+
/** The metered allowance's identity — `(accountKey, label)` for an account-wide window, `(poolId, label)` for a model-scoped one. */
|
|
37
|
+
resourceKey: string;
|
|
38
|
+
/** Caller-computed live remaining allowance for `resourceKey`. Non-finite ⇒ optimistic (unbounded). */
|
|
39
|
+
budget: number;
|
|
40
|
+
/** This dispatch's draw against `resourceKey`, in the same unit as `budget`. */
|
|
41
|
+
cost: number;
|
|
42
|
+
}
|
|
43
|
+
/** How one constraint evaluated during an admission attempt. */
|
|
44
|
+
export interface ConstraintOutcome {
|
|
45
|
+
resourceKey: string;
|
|
46
|
+
/** Live budget minus `outstandingBefore` — so it carries that field's carve-out too. */
|
|
47
|
+
headroomBefore: number;
|
|
48
|
+
/**
|
|
49
|
+
* Outstanding (non-expired) leases counted against this key when it was
|
|
50
|
+
* evaluated. This is everyone else's in-flight total — PLUS any draw an earlier
|
|
51
|
+
* constraint in this same attempt already made on the same key (see the
|
|
52
|
+
* duplicate-key note on {@link ReservationLedger.admit}), so it is what this
|
|
53
|
+
* constraint was actually measured against rather than a pure "before" reading.
|
|
54
|
+
*/
|
|
55
|
+
outstandingBefore: number;
|
|
56
|
+
/** The cost this attempt tried to reserve against this key (clamped to >= 0). */
|
|
57
|
+
cost: number;
|
|
58
|
+
/** Whether this constraint alone had room. Admission requires ALL to be true. */
|
|
59
|
+
cleared: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Outcome of an admission attempt against every constraint that applies. */
|
|
23
62
|
export interface AdmitDecision {
|
|
24
63
|
admitted: boolean;
|
|
25
64
|
/** Minted only when `admitted`; pass back to `reconcile` on completion. */
|
|
26
65
|
leaseId: string | null;
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
/** Every constraint's evaluation, in input order — the per-admission explain record. */
|
|
67
|
+
constraints: ConstraintOutcome[];
|
|
68
|
+
/**
|
|
69
|
+
* The TIGHTEST constraint — the one closest to blocking this dispatch, by the
|
|
70
|
+
* dimensionless ratio `cost / headroomBefore`. This is the "binding window" a
|
|
71
|
+
* report should name. Null only when no constraints were supplied.
|
|
72
|
+
*
|
|
73
|
+
* ⚠ Deliberately a RATIO, not the smallest `headroomBefore`. Constraints meter in
|
|
74
|
+
* different windows' units, so raw headroom is not comparable across them — "40
|
|
75
|
+
* left of a session window" vs "80 left of a weekly one" says nothing about which
|
|
76
|
+
* binds. The ratio is dimensionless and therefore is comparable. A blocked
|
|
77
|
+
* constraint always outranks a cleared one (its ratio exceeds 1), so this is also
|
|
78
|
+
* independent of the order the caller supplied the constraints in.
|
|
79
|
+
*/
|
|
80
|
+
binding: ConstraintOutcome | null;
|
|
81
|
+
/**
|
|
82
|
+
* Whether ANY constraint had outstanding leases against it.
|
|
83
|
+
*
|
|
84
|
+
* This exists because `binding.outstandingBefore` is one key's total and must
|
|
85
|
+
* never be read as "is anything in flight anywhere" — the liveness backstop in
|
|
86
|
+
* the rolling dispatcher needs exactly that aggregate to decide whether waiting
|
|
87
|
+
* could ever help. Given as a computed boolean rather than left to the consumer
|
|
88
|
+
* to derive, so the predicate cannot be reconstructed wrongly from a per-key
|
|
89
|
+
* number (it was, and a blocked model window with an idle account window would
|
|
90
|
+
* have force-dispatched into overshoot).
|
|
91
|
+
*/
|
|
92
|
+
anyOutstanding: boolean;
|
|
33
93
|
}
|
|
34
94
|
export interface AdmitInput {
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Every allowance this dispatch draws against. Admission is ALL-OR-NOTHING: one
|
|
97
|
+
* lease id is recorded under each key, or none are. An empty array is admitted
|
|
98
|
+
* unmetered (a lease is still minted so completion reconciles symmetrically).
|
|
99
|
+
*/
|
|
100
|
+
constraints: AdmitConstraint[];
|
|
41
101
|
/** Pool id for diagnostics. */
|
|
42
102
|
poolId: string;
|
|
43
103
|
/** Lease lifetime in ms; defaults to the ledger's TTL. */
|
|
@@ -64,24 +124,36 @@ export declare class ReservationLedger {
|
|
|
64
124
|
private readonly defaultTtlMs;
|
|
65
125
|
constructor(ledgerPath: string, now?: () => number, defaultTtlMs?: number);
|
|
66
126
|
/**
|
|
67
|
-
* Attempt to reserve
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* (
|
|
72
|
-
* is
|
|
73
|
-
*
|
|
74
|
-
*
|
|
127
|
+
* Attempt to reserve against EVERY constraint that applies to this dispatch.
|
|
128
|
+
* Each is admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, and
|
|
129
|
+
* the dispatch is admitted iff every one clears — **all-or-nothing**: either one
|
|
130
|
+
* lease id is recorded under each key, or nothing is written. A partial
|
|
131
|
+
* reservation (fits the account window, overruns its model window) is therefore
|
|
132
|
+
* unrepresentable, which is what makes `reconcile` by lease id total.
|
|
133
|
+
*
|
|
134
|
+
* The whole evaluation runs atomically under the lock, so concurrent admitters
|
|
135
|
+
* serialize and each sees the others' in-flight reservations across every key.
|
|
136
|
+
* A non-finite `budget` is unbounded (optimistic start — the reactive floor still
|
|
137
|
+
* corrects); a non-positive `cost` always clears its own constraint so completion
|
|
138
|
+
* still reconciles symmetrically. Every constraint is evaluated even after one
|
|
139
|
+
* blocks, so the explain record shows the full picture rather than the first
|
|
140
|
+
* failure.
|
|
141
|
+
*
|
|
142
|
+
* Two constraints on the same `resourceKey` accumulate within the attempt — the
|
|
143
|
+
* second sees the first's draw as outstanding — so a caller that supplies a key
|
|
144
|
+
* twice cannot double-spend one allowance.
|
|
75
145
|
*/
|
|
76
146
|
admit(input: AdmitInput): Promise<AdmitDecision>;
|
|
77
147
|
/**
|
|
78
148
|
* Free a reservation on completion (success OR failure — the request is no longer
|
|
79
|
-
* in flight either way). Token-checked by `leaseId
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
149
|
+
* in flight either way). Token-checked by `leaseId` and swept across EVERY key, so
|
|
150
|
+
* a multi-constraint reservation is released in full or not at all — the caller
|
|
151
|
+
* cannot leak one window's reservation by forgetting which keys it took. A no-op
|
|
152
|
+
* if the lease is already gone (expired / reconciled). Returns whether anything was
|
|
153
|
+
* removed. The lease's real cost surfaces in the provider's next quota snapshot;
|
|
154
|
+
* the ledger's job is only to stop reserving it.
|
|
83
155
|
*/
|
|
84
|
-
reconcile(
|
|
156
|
+
reconcile(leaseId: string): Promise<boolean>;
|
|
85
157
|
/** Sum of outstanding (non-expired) reservations for `resourceKey`. Read under the lock. */
|
|
86
158
|
outstanding(resourceKey: string): Promise<number>;
|
|
87
159
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reservationLedger.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED
|
|
1
|
+
{"version":3,"file":"reservationLedger.d.ts","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,qIAAqI;IACrI,WAAW,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wFAAwF;IACxF,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC;;;;;;;;;;OAUG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAmHpD;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAGnB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,MAAyB,EACrD,YAAY,GAAE,MAAsB;IAMtC;;;;;;;;;;;;;;;;;;;OAmBG;IACG,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAqDtD;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBlD,4FAA4F;IACtF,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQvD;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IASvC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,SAAS,CAAC;CAOrC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAUjD;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D"}
|
|
@@ -110,6 +110,44 @@ function sumOutstanding(leases, now) {
|
|
|
110
110
|
}
|
|
111
111
|
return total;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* How close a constraint came to blocking, as a dimensionless ratio of the draw to
|
|
115
|
+
* the room available for it. Comparable ACROSS windows that meter in different
|
|
116
|
+
* units, which raw headroom is not. Higher = tighter; > 1 means it blocked.
|
|
117
|
+
*/
|
|
118
|
+
function tightness(outcome) {
|
|
119
|
+
// Order matters. A zero-cost constraint ALWAYS clears — including on an exhausted
|
|
120
|
+
// window — so it must rank loosest, not tightest. Testing headroom first returned
|
|
121
|
+
// +Infinity for `cost 0, headroom 0` and let a constraint that cleared outrank one
|
|
122
|
+
// that actually blocked. With this order the invariant below is exact: cleared
|
|
123
|
+
// ⇒ tightness <= 1 (cost <= 0 ⇒ 0, else headroom >= cost ⇒ ratio <= 1), and
|
|
124
|
+
// blocked ⇒ > 1 or +Infinity.
|
|
125
|
+
if (outcome.cost <= 0)
|
|
126
|
+
return 0;
|
|
127
|
+
if (outcome.headroomBefore <= 0)
|
|
128
|
+
return Number.POSITIVE_INFINITY;
|
|
129
|
+
return outcome.cost / outcome.headroomBefore;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The constraint closest to blocking — what a report should name as the binding
|
|
133
|
+
* window. Replaces the MIN-collapse that used to happen before the ledger was
|
|
134
|
+
* reached. Ranked by {@link tightness}, so the result does not depend on the order
|
|
135
|
+
* the caller listed the constraints in, and a blocked constraint always wins.
|
|
136
|
+
*/
|
|
137
|
+
function pickBinding(outcomes) {
|
|
138
|
+
if (outcomes.length === 0)
|
|
139
|
+
return null;
|
|
140
|
+
return outcomes.reduce((tightest, o) => {
|
|
141
|
+
const a = tightness(o);
|
|
142
|
+
const b = tightness(tightest);
|
|
143
|
+
if (a !== b)
|
|
144
|
+
return a > b ? o : tightest;
|
|
145
|
+
// Ties are reachable (two exhausted windows both score +Infinity; equal ratios).
|
|
146
|
+
// Break them on the key so the answer is a property of the ledger state, not of
|
|
147
|
+
// the order the caller happened to list the constraints in.
|
|
148
|
+
return o.resourceKey < tightest.resourceKey ? o : tightest;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
113
151
|
/** Drop expired leases from every key, returning the trimmed map and how many were dropped. */
|
|
114
152
|
function pruneExpired(ledger, now) {
|
|
115
153
|
let dropped = 0;
|
|
@@ -147,65 +185,99 @@ export class ReservationLedger {
|
|
|
147
185
|
this.defaultTtlMs = defaultTtlMs;
|
|
148
186
|
}
|
|
149
187
|
/**
|
|
150
|
-
* Attempt to reserve
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* (
|
|
155
|
-
* is
|
|
156
|
-
*
|
|
157
|
-
*
|
|
188
|
+
* Attempt to reserve against EVERY constraint that applies to this dispatch.
|
|
189
|
+
* Each is admitted iff `budget - Σ outstanding_leases(resourceKey) >= cost`, and
|
|
190
|
+
* the dispatch is admitted iff every one clears — **all-or-nothing**: either one
|
|
191
|
+
* lease id is recorded under each key, or nothing is written. A partial
|
|
192
|
+
* reservation (fits the account window, overruns its model window) is therefore
|
|
193
|
+
* unrepresentable, which is what makes `reconcile` by lease id total.
|
|
194
|
+
*
|
|
195
|
+
* The whole evaluation runs atomically under the lock, so concurrent admitters
|
|
196
|
+
* serialize and each sees the others' in-flight reservations across every key.
|
|
197
|
+
* A non-finite `budget` is unbounded (optimistic start — the reactive floor still
|
|
198
|
+
* corrects); a non-positive `cost` always clears its own constraint so completion
|
|
199
|
+
* still reconciles symmetrically. Every constraint is evaluated even after one
|
|
200
|
+
* blocks, so the explain record shows the full picture rather than the first
|
|
201
|
+
* failure.
|
|
202
|
+
*
|
|
203
|
+
* Two constraints on the same `resourceKey` accumulate within the attempt — the
|
|
204
|
+
* second sees the first's draw as outstanding — so a caller that supplies a key
|
|
205
|
+
* twice cannot double-spend one allowance.
|
|
158
206
|
*/
|
|
159
207
|
async admit(input) {
|
|
160
208
|
const ttl = input.leaseTtlMs ?? this.defaultTtlMs;
|
|
161
209
|
return withFileLock(this.lockPath, async () => {
|
|
162
210
|
const now = this.now();
|
|
163
211
|
const { ledger } = pruneExpired(await readLedger(this.ledgerPath), now);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
212
|
+
// Draws already committed by EARLIER constraints in this same attempt, so a
|
|
213
|
+
// repeated resourceKey is metered once against its real allowance.
|
|
214
|
+
const pendingByKey = new Map();
|
|
215
|
+
const outcomes = [];
|
|
216
|
+
let anyOutstanding = false;
|
|
217
|
+
for (const constraint of input.constraints) {
|
|
218
|
+
const pending = pendingByKey.get(constraint.resourceKey) ?? 0;
|
|
219
|
+
const priorOutstanding = sumOutstanding(ledger[constraint.resourceKey], now);
|
|
220
|
+
if (priorOutstanding > 0)
|
|
221
|
+
anyOutstanding = true;
|
|
222
|
+
const outstandingBefore = priorOutstanding + pending;
|
|
223
|
+
const budget = Number.isFinite(constraint.budget) ? constraint.budget : Number.POSITIVE_INFINITY;
|
|
224
|
+
const headroomBefore = budget - outstandingBefore;
|
|
225
|
+
// Normalize ONCE: the value validated against headroom, accumulated for a
|
|
226
|
+
// repeated key, and persisted on the lease must be the same number. A
|
|
227
|
+
// negative cost reaching the lease would subtract from the key's outstanding
|
|
228
|
+
// total and manufacture headroom that does not exist for every peer.
|
|
229
|
+
const cost = Number.isFinite(constraint.cost) ? Math.max(0, constraint.cost) : 0;
|
|
230
|
+
outcomes.push({
|
|
231
|
+
resourceKey: constraint.resourceKey,
|
|
232
|
+
headroomBefore,
|
|
233
|
+
outstandingBefore,
|
|
234
|
+
cost,
|
|
235
|
+
cleared: cost <= 0 || headroomBefore >= cost,
|
|
236
|
+
});
|
|
237
|
+
pendingByKey.set(constraint.resourceKey, pending + cost);
|
|
238
|
+
}
|
|
239
|
+
const admitted = outcomes.every((o) => o.cleared);
|
|
240
|
+
const binding = pickBinding(outcomes);
|
|
169
241
|
if (!admitted) {
|
|
170
242
|
// Persist the prune even on a blocked admission so expired leases don't
|
|
171
243
|
// linger and depress headroom for the next attempt.
|
|
172
244
|
await writeLedger(this.ledgerPath, ledger);
|
|
173
|
-
return { admitted: false, leaseId: null,
|
|
245
|
+
return { admitted: false, leaseId: null, constraints: outcomes, binding, anyOutstanding };
|
|
174
246
|
}
|
|
175
247
|
const leaseId = mintLeaseId();
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
cost,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
182
|
-
ledger[input.resourceKey] = [...(ledger[input.resourceKey] ?? []), lease];
|
|
248
|
+
const expiresAt = now + ttl;
|
|
249
|
+
for (const outcome of outcomes) {
|
|
250
|
+
const lease = { leaseId, cost: outcome.cost, poolId: input.poolId, expiresAt };
|
|
251
|
+
ledger[outcome.resourceKey] = [...(ledger[outcome.resourceKey] ?? []), lease];
|
|
252
|
+
}
|
|
183
253
|
await writeLedger(this.ledgerPath, ledger);
|
|
184
|
-
return { admitted: true, leaseId,
|
|
254
|
+
return { admitted: true, leaseId, constraints: outcomes, binding, anyOutstanding };
|
|
185
255
|
});
|
|
186
256
|
}
|
|
187
257
|
/**
|
|
188
258
|
* Free a reservation on completion (success OR failure — the request is no longer
|
|
189
|
-
* in flight either way). Token-checked by `leaseId
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
259
|
+
* in flight either way). Token-checked by `leaseId` and swept across EVERY key, so
|
|
260
|
+
* a multi-constraint reservation is released in full or not at all — the caller
|
|
261
|
+
* cannot leak one window's reservation by forgetting which keys it took. A no-op
|
|
262
|
+
* if the lease is already gone (expired / reconciled). Returns whether anything was
|
|
263
|
+
* removed. The lease's real cost surfaces in the provider's next quota snapshot;
|
|
264
|
+
* the ledger's job is only to stop reserving it.
|
|
193
265
|
*/
|
|
194
|
-
async reconcile(
|
|
266
|
+
async reconcile(leaseId) {
|
|
195
267
|
return withFileLock(this.lockPath, async () => {
|
|
196
268
|
const now = this.now();
|
|
197
269
|
const { ledger } = pruneExpired(await readLedger(this.ledgerPath), now);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
270
|
+
let removed = false;
|
|
271
|
+
for (const [resourceKey, leases] of Object.entries(ledger)) {
|
|
272
|
+
const remaining = leases.filter((l) => l.leaseId !== leaseId);
|
|
273
|
+
if (remaining.length === leases.length)
|
|
274
|
+
continue;
|
|
275
|
+
removed = true;
|
|
276
|
+
if (remaining.length > 0)
|
|
277
|
+
ledger[resourceKey] = remaining;
|
|
278
|
+
else
|
|
279
|
+
delete ledger[resourceKey];
|
|
202
280
|
}
|
|
203
|
-
const remaining = leases.filter((l) => l.leaseId !== leaseId);
|
|
204
|
-
const removed = remaining.length !== leases.length;
|
|
205
|
-
if (remaining.length > 0)
|
|
206
|
-
ledger[resourceKey] = remaining;
|
|
207
|
-
else
|
|
208
|
-
delete ledger[resourceKey];
|
|
209
281
|
await writeLedger(this.ledgerPath, ledger);
|
|
210
282
|
return removed;
|
|
211
283
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reservationLedger.js","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,6EAA6E;AAC7E,+EAA+E;AAC/E,4EAA4E;AAC5E,mFAAmF;AACnF,mFAAmF;AACnF,mFAAmF;AACnF,mFAAmF;AACnF,mFAAmF;AACnF,gFAAgF;AAChF,EAAE;AACF,kFAAkF;AAClF,6EAA6E;AAC7E,oFAAoF;AACpF,mFAAmF;AACnF,kFAAkF;AAClF,gFAAgF;AAChF,iFAAiF;AACjF,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,4EAA4E;AAC5E,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,mEAAmE;AAEnE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"reservationLedger.js","sourceRoot":"","sources":["../../../src/shared/quota/reservationLedger.ts"],"names":[],"mappings":"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"}
|