@serfab/cadre-core 1.7.0 → 1.9.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 +8 -6
- package/dist/cadre-node.d.ts +527 -206
- package/dist/cadre-node.js +1084 -528
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +2 -2
- package/dist/control-authorization.js +1 -0
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +6 -0
- package/dist/control-cohort.js +6 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +46 -8
- package/dist/control-database.js +97 -18
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +37 -14
- package/dist/control-formation-recorder.js +60 -10
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-read-retry.d.ts +13 -12
- package/dist/control-read-retry.js +14 -12
- package/dist/control-read-retry.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +67 -5
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +9 -1
- package/dist/control-stream.js +24 -1
- package/dist/control-stream.js.map +1 -1
- package/dist/control-write-retry.d.ts +12 -2
- package/dist/control-write-retry.js +13 -2
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +11 -8
- package/dist/delegate-admission.js +19 -11
- package/dist/delegate-admission.js.map +1 -1
- package/dist/diagnostics/connection-path.d.ts +0 -7
- package/dist/diagnostics/connection-path.js +0 -7
- package/dist/diagnostics/connection-path.js.map +1 -1
- package/dist/formation-approval.d.ts +7 -0
- package/dist/formation-approval.js +9 -3
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +11 -2
- package/dist/fs-atomic.js +13 -3
- package/dist/fs-atomic.js.map +1 -1
- package/dist/hibernation-manager.d.ts +111 -8
- package/dist/hibernation-manager.js +234 -49
- package/dist/hibernation-manager.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +25 -20
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +90 -25
- package/dist/joined-strand-store.js +223 -55
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +14 -5
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +367 -69
- package/dist/link-budget.js +375 -72
- package/dist/link-budget.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +30 -43
- package/dist/membership-connection-gater.js +31 -42
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-local-snapshot.d.ts +6 -4
- package/dist/node-local-snapshot.js +7 -3
- package/dist/node-local-snapshot.js.map +1 -1
- package/dist/peer-dial.d.ts +24 -4
- package/dist/peer-dial.js +74 -5
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +2 -10
- package/dist/peer-join-backfill.js +8 -8
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/push-fanout.d.ts +6 -0
- package/dist/push-fanout.js +6 -0
- package/dist/push-fanout.js.map +1 -1
- package/dist/relay-reservation.d.ts +3 -1
- package/dist/relay-reservation.js +3 -1
- package/dist/relay-reservation.js.map +1 -1
- package/dist/relay-server.d.ts +79 -0
- package/dist/relay-server.js +74 -0
- package/dist/relay-server.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +57 -4
- package/dist/seed-bootstrap.js +107 -83
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/storage-scope.d.ts +33 -17
- package/dist/storage-scope.js +44 -23
- package/dist/storage-scope.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +58 -21
- package/dist/strand-addr-protocol.js +133 -85
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-database.d.ts +20 -2
- package/dist/strand-database.js +42 -5
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +59 -21
- package/dist/strand-first-sync-gate.js +58 -19
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +151 -0
- package/dist/strand-formation-deadlines.js +171 -0
- package/dist/strand-formation-deadlines.js.map +1 -0
- package/dist/strand-formation-manager.d.ts +63 -30
- package/dist/strand-formation-manager.js +67 -41
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +45 -46
- package/dist/strand-formation-protocol.js +62 -126
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +54 -0
- package/dist/strand-fret-addrs.js +108 -0
- package/dist/strand-fret-addrs.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +122 -44
- package/dist/strand-instance-manager.js +270 -114
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +6 -0
- package/dist/strand-membership-writer.js +11 -0
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-network-state-file.d.ts +12 -0
- package/dist/strand-network-state-file.js +38 -0
- package/dist/strand-network-state-file.js.map +1 -0
- package/dist/strand-network-state.d.ts +111 -0
- package/dist/strand-network-state.js +191 -0
- package/dist/strand-network-state.js.map +1 -0
- package/dist/strand-revocation-enforcer.js +2 -1
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +36 -20
- package/dist/strand-solicitation.js +4 -4
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-protocol.d.ts +80 -23
- package/dist/strand-wake-protocol.js +89 -64
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +148 -61
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +11 -10
- package/src/cadre-node.ts +8400 -7776
- package/src/control-authorization.ts +2 -1
- package/src/control-cohort.ts +6 -0
- package/src/control-database.ts +122 -18
- package/src/control-formation-recorder.ts +354 -305
- package/src/control-read-retry.ts +14 -12
- package/src/control-schema.ts +67 -5
- package/src/control-stream.ts +25 -1
- package/src/control-write-retry.ts +13 -2
- package/src/delegate-admission.ts +23 -13
- package/src/diagnostics/connection-path.ts +0 -7
- package/src/formation-approval.ts +610 -604
- package/src/fs-atomic.ts +14 -4
- package/src/hibernation-manager.ts +638 -437
- package/src/index.ts +43 -65
- package/src/joined-strand-store.ts +247 -57
- package/src/key-store-file.ts +14 -5
- package/src/link-budget.ts +393 -74
- package/src/membership-connection-gater.ts +31 -45
- package/src/node-local-snapshot.ts +12 -7
- package/src/peer-dial.ts +81 -7
- package/src/peer-join-backfill.ts +10 -9
- package/src/push-fanout.ts +6 -0
- package/src/relay-reservation.ts +3 -1
- package/src/relay-server.ts +98 -0
- package/src/seed-bootstrap.ts +133 -91
- package/src/storage-scope.ts +44 -23
- package/src/strand-addr-protocol.ts +547 -458
- package/src/strand-database.ts +287 -242
- package/src/strand-first-sync-gate.ts +58 -19
- package/src/strand-formation-deadlines.ts +227 -0
- package/src/strand-formation-manager.ts +700 -647
- package/src/strand-formation-protocol.ts +106 -154
- package/src/strand-fret-addrs.ts +145 -0
- package/src/strand-instance-manager.ts +307 -140
- package/src/strand-membership-writer.ts +12 -0
- package/src/strand-network-state-file.ts +40 -0
- package/src/strand-network-state.ts +243 -0
- package/src/strand-revocation-enforcer.ts +2 -1
- package/src/strand-solicitation.ts +519 -500
- package/src/strand-wake-protocol.ts +401 -355
- package/src/types.ts +150 -61
- package/dist/strand-peer-book-file.d.ts +0 -12
- package/dist/strand-peer-book-file.js +0 -37
- package/dist/strand-peer-book-file.js.map +0 -1
- package/dist/strand-peer-book-protocol.d.ts +0 -159
- package/dist/strand-peer-book-protocol.js +0 -360
- package/dist/strand-peer-book-protocol.js.map +0 -1
- package/dist/strand-peer-book-swap.d.ts +0 -104
- package/dist/strand-peer-book-swap.js +0 -284
- package/dist/strand-peer-book-swap.js.map +0 -1
- package/dist/strand-peer-book.d.ts +0 -159
- package/dist/strand-peer-book.js +0 -354
- package/dist/strand-peer-book.js.map +0 -1
- package/dist/strand-peer-observer.d.ts +0 -88
- package/dist/strand-peer-observer.js +0 -180
- package/dist/strand-peer-observer.js.map +0 -1
- package/src/strand-peer-book-file.ts +0 -39
- package/src/strand-peer-book-protocol.ts +0 -463
- package/src/strand-peer-book-swap.ts +0 -338
- package/src/strand-peer-book.ts +0 -468
- package/src/strand-peer-observer.ts +0 -235
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ControlDatabase } from './control-database.js';
|
|
2
2
|
import { type FormationApprover } from './formation-approval.js';
|
|
3
|
-
import type { FormationUsageRecorder, ResolvedHostStrand } from './strand-solicitation.js';
|
|
3
|
+
import type { AuthorizedFormationUsage, FormationUsageParams, FormationUsageRecorder, ResolvedHostStrand } from './strand-solicitation.js';
|
|
4
4
|
import type { OpenInvitation } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* {@link FormationUsageRecorder} backed by the real `CadreControl` tables.
|
|
@@ -11,7 +11,8 @@ import type { OpenInvitation } from './types.js';
|
|
|
11
11
|
*
|
|
12
12
|
* - **Bound (provision-then-record):** the host strand already exists (owner-signed
|
|
13
13
|
* up front and named by the invite's `StrandId`), so {@link resolveStrand} reports it
|
|
14
|
-
* and {@link
|
|
14
|
+
* and {@link authorizeUsage} then its `record()` (or {@link recordUsage}, both at once)
|
|
15
|
+
* writes the consent row against that pre-existing strand
|
|
15
16
|
* (record-only) rather than inserting a new `Strand`.
|
|
16
17
|
* - **Unbound (responder-provisions):** the invite carries no `StrandId`, so
|
|
17
18
|
* {@link provisionAndRecord} mints a fresh strand and records consent against it
|
|
@@ -29,7 +30,10 @@ import type { OpenInvitation } from './types.js';
|
|
|
29
30
|
* approval ({@link obtainApproval}) — the recorder is the one place where the nonce that is
|
|
30
31
|
* SIGNED and the nonce that is INSERTED are trivially the same value, since it also performs
|
|
31
32
|
* the write. That makes this class do network I/O; failures surface as
|
|
32
|
-
* {@link FormationApprovalError}s the manager maps to protocol rejection reasons.
|
|
33
|
+
* {@link FormationApprovalError}s the manager maps to protocol rejection reasons. The bound
|
|
34
|
+
* path is split in two ({@link authorizeUsage}, then the returned `record()`) so the manager
|
|
35
|
+
* can issue the joiner's membership pass between the approval and the write; the handle
|
|
36
|
+
* carries the approved fields across, which keeps the signed and inserted nonce the same.
|
|
33
37
|
*/
|
|
34
38
|
export declare class ControlFormationUsageRecorder implements FormationUsageRecorder {
|
|
35
39
|
private readonly controlDatabase;
|
|
@@ -101,18 +105,37 @@ export declare class ControlFormationUsageRecorder implements FormationUsageReco
|
|
|
101
105
|
* spent on AND provably the joiner that agreed. Use
|
|
102
106
|
* {@link ControlDatabase.redeemInvitation} for the consent-creates-strand path instead.
|
|
103
107
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
108
|
+
* Authorizes ({@link authorizeUsage}) and writes in one step, for callers with nothing to do
|
|
109
|
+
* in between.
|
|
106
110
|
*/
|
|
107
|
-
recordUsage(params:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
recordUsage(params: FormationUsageParams): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Everything {@link recordUsage} does before its write, so the manager can issue the
|
|
114
|
+
* joiner's membership pass into the host strand only once the redemption is cleared, and
|
|
115
|
+
* spend the token only after that.
|
|
116
|
+
*
|
|
117
|
+
* Reads the invite first (one extra read per redemption) to learn its `ValidationUrl` and
|
|
118
|
+
* seat budget, obtains the approval through {@link obtainApproval} when one is demanded,
|
|
119
|
+
* re-checks the abort (the approval call may have been long), and pre-checks the seat
|
|
120
|
+
* budget ({@link assertSeatFree}). The returned `record()` closes over the captured fields
|
|
121
|
+
* and approval, so the nonce the approver signed is the nonce inserted without the caller
|
|
122
|
+
* ever passing it back.
|
|
123
|
+
*/
|
|
124
|
+
authorizeUsage(params: FormationUsageParams): Promise<AuthorizedFormationUsage>;
|
|
125
|
+
/**
|
|
126
|
+
* Refuse a redemption whose invite has no seat left, BEFORE anything is written — the
|
|
127
|
+
* authoritative check is the one `recordFormationUsage` repeats inside its write lock.
|
|
128
|
+
* Catches a same-node rival that took the last seat while this redemption waited on the
|
|
129
|
+
* approval hook, which `runSession`'s up-front `isTokenUsed` check could not see.
|
|
130
|
+
*
|
|
131
|
+
* NOTE: two redemptions that both pass this check within the time one of them takes to
|
|
132
|
+
* issue its membership pass still both issue one, and the loser's pass is orphaned
|
|
133
|
+
* (expiry-bounded, never handed out). Holding the control write lock across issuance would
|
|
134
|
+
* close it, but `lockedWithRetry` re-runs locked bodies on transient failures (a re-run
|
|
135
|
+
* would double-issue) and every control write would stall behind a strand-database write.
|
|
136
|
+
* If orphaned passes from same-node races ever matter, reserve the seat before issuance.
|
|
137
|
+
*/
|
|
138
|
+
private assertSeatFree;
|
|
116
139
|
/**
|
|
117
140
|
* Classify the host strand this invite binds to (see {@link ResolvedHostStrand}):
|
|
118
141
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
|
-
import { FormationAbortedError } from './control-database.js';
|
|
2
|
+
import { FormationAbortedError, InvitationExhaustedError } from './control-database.js';
|
|
3
3
|
import { mintStrandId } from './strand-id.js';
|
|
4
4
|
import { createHttpFormationApprover, verifyFormationApproval, FormationApprovalError } from './formation-approval.js';
|
|
5
5
|
const log = debug('sereus:cadre:formation-recorder');
|
|
@@ -14,7 +14,8 @@ const NEVER_EXPIRES = new Date(8640000000000000);
|
|
|
14
14
|
*
|
|
15
15
|
* - **Bound (provision-then-record):** the host strand already exists (owner-signed
|
|
16
16
|
* up front and named by the invite's `StrandId`), so {@link resolveStrand} reports it
|
|
17
|
-
* and {@link
|
|
17
|
+
* and {@link authorizeUsage} then its `record()` (or {@link recordUsage}, both at once)
|
|
18
|
+
* writes the consent row against that pre-existing strand
|
|
18
19
|
* (record-only) rather than inserting a new `Strand`.
|
|
19
20
|
* - **Unbound (responder-provisions):** the invite carries no `StrandId`, so
|
|
20
21
|
* {@link provisionAndRecord} mints a fresh strand and records consent against it
|
|
@@ -32,7 +33,10 @@ const NEVER_EXPIRES = new Date(8640000000000000);
|
|
|
32
33
|
* approval ({@link obtainApproval}) — the recorder is the one place where the nonce that is
|
|
33
34
|
* SIGNED and the nonce that is INSERTED are trivially the same value, since it also performs
|
|
34
35
|
* the write. That makes this class do network I/O; failures surface as
|
|
35
|
-
* {@link FormationApprovalError}s the manager maps to protocol rejection reasons.
|
|
36
|
+
* {@link FormationApprovalError}s the manager maps to protocol rejection reasons. The bound
|
|
37
|
+
* path is split in two ({@link authorizeUsage}, then the returned `record()`) so the manager
|
|
38
|
+
* can issue the joiner's membership pass between the approval and the write; the handle
|
|
39
|
+
* carries the approved fields across, which keeps the signed and inserted nonce the same.
|
|
36
40
|
*/
|
|
37
41
|
export class ControlFormationUsageRecorder {
|
|
38
42
|
/**
|
|
@@ -154,10 +158,25 @@ export class ControlFormationUsageRecorder {
|
|
|
154
158
|
* spent on AND provably the joiner that agreed. Use
|
|
155
159
|
* {@link ControlDatabase.redeemInvitation} for the consent-creates-strand path instead.
|
|
156
160
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
161
|
+
* Authorizes ({@link authorizeUsage}) and writes in one step, for callers with nothing to do
|
|
162
|
+
* in between.
|
|
159
163
|
*/
|
|
160
164
|
async recordUsage(params) {
|
|
165
|
+
await (await this.authorizeUsage(params)).record();
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Everything {@link recordUsage} does before its write, so the manager can issue the
|
|
169
|
+
* joiner's membership pass into the host strand only once the redemption is cleared, and
|
|
170
|
+
* spend the token only after that.
|
|
171
|
+
*
|
|
172
|
+
* Reads the invite first (one extra read per redemption) to learn its `ValidationUrl` and
|
|
173
|
+
* seat budget, obtains the approval through {@link obtainApproval} when one is demanded,
|
|
174
|
+
* re-checks the abort (the approval call may have been long), and pre-checks the seat
|
|
175
|
+
* budget ({@link assertSeatFree}). The returned `record()` closes over the captured fields
|
|
176
|
+
* and approval, so the nonce the approver signed is the nonce inserted without the caller
|
|
177
|
+
* ever passing it back.
|
|
178
|
+
*/
|
|
179
|
+
async authorizeUsage(params) {
|
|
161
180
|
const { token, peerKey, peerSignature, usageStampId, strandId, disclosure, signal } = params;
|
|
162
181
|
if (signal?.aborted) {
|
|
163
182
|
throw new FormationAbortedError(token, 'usage recording');
|
|
@@ -172,11 +191,42 @@ export class ControlFormationUsageRecorder {
|
|
|
172
191
|
token, usageStampId, strandId, peerKey, disclosure,
|
|
173
192
|
validationUrl: invite?.validationUrl ?? null,
|
|
174
193
|
}, signal);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
if (signal?.aborted) {
|
|
195
|
+
throw new FormationAbortedError(token, 'usage recording');
|
|
196
|
+
}
|
|
197
|
+
const totalUses = invite?.totalUses ?? null;
|
|
198
|
+
await this.assertSeatFree(token, totalUses);
|
|
199
|
+
return {
|
|
200
|
+
record: async () => {
|
|
201
|
+
await this.controlDatabase.recordFormationUsage({
|
|
202
|
+
token, strandId, peerKey, peerSignature, usageStampId, disclosure, signal,
|
|
203
|
+
totalUses, ...approval,
|
|
204
|
+
});
|
|
205
|
+
log('Recorded formation usage: token=%s strand=%s', token, strandId);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Refuse a redemption whose invite has no seat left, BEFORE anything is written — the
|
|
211
|
+
* authoritative check is the one `recordFormationUsage` repeats inside its write lock.
|
|
212
|
+
* Catches a same-node rival that took the last seat while this redemption waited on the
|
|
213
|
+
* approval hook, which `runSession`'s up-front `isTokenUsed` check could not see.
|
|
214
|
+
*
|
|
215
|
+
* NOTE: two redemptions that both pass this check within the time one of them takes to
|
|
216
|
+
* issue its membership pass still both issue one, and the loser's pass is orphaned
|
|
217
|
+
* (expiry-bounded, never handed out). Holding the control write lock across issuance would
|
|
218
|
+
* close it, but `lockedWithRetry` re-runs locked bodies on transient failures (a re-run
|
|
219
|
+
* would double-issue) and every control write would stall behind a strand-database write.
|
|
220
|
+
* If orphaned passes from same-node races ever matter, reserve the seat before issuance.
|
|
221
|
+
*/
|
|
222
|
+
async assertSeatFree(token, totalUses) {
|
|
223
|
+
if (totalUses === null) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const used = await this.controlDatabase.countFormationUsage(token);
|
|
227
|
+
if (used >= totalUses) {
|
|
228
|
+
throw new InvitationExhaustedError(token, used, totalUses);
|
|
229
|
+
}
|
|
180
230
|
}
|
|
181
231
|
/**
|
|
182
232
|
* Classify the host strand this invite binds to (see {@link ResolvedHostStrand}):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-formation-recorder.js","sourceRoot":"","sources":["../src/control-formation-recorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAwB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"control-formation-recorder.js","sourceRoot":"","sources":["../src/control-formation-recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import type { ControlRetryOptions } from './control-retry.js';
|
|
|
10
10
|
* was absorbed). This module gives reads the same bounded re-presentation with a SHORTER
|
|
11
11
|
* deadline and a NARROWER failure set, because the tightest caller deadline over a control
|
|
12
12
|
* read is the inbound admission gate's 2 s fail-open timeout
|
|
13
|
-
* (`ADMISSION_DECISION_TIMEOUT_MS`, `
|
|
13
|
+
* (`ADMISSION_DECISION_TIMEOUT_MS`, `link-budget.ts`) — a read budget that
|
|
14
14
|
* does not fit inside it with headroom spends its retries after the gate has already
|
|
15
15
|
* admitted.
|
|
16
16
|
*
|
|
@@ -60,17 +60,18 @@ export declare const CONTROL_READ_RETRY_DELAYS_MS: readonly number[];
|
|
|
60
60
|
* 2 s only because each stays well under it. Do not raise these numbers without
|
|
61
61
|
* re-checking against `ADMISSION_DECISION_TIMEOUT_MS`.
|
|
62
62
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* `clusterPolicy.cohortQueryTimeoutMs`,
|
|
66
|
-
* (`
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
63
|
+
* It cuts the loop off by design, so it does not grow with the link or with the number that
|
|
64
|
+
* dominates a slow attempt: an attempt whose cohort consult finds a silent peer costs
|
|
65
|
+
* `clusterPolicy.cohortQueryTimeoutMs`, two link round trips at the declared link
|
|
66
|
+
* (`cohortReadDeadlineMs` in `link-budget.ts`, 7 000 ms at the default declaration; the plugin's
|
|
67
|
+
* `COHORT_READ_DEADLINE_MS` is the same number). That is more than four times this budget, so
|
|
68
|
+
* such an attempt is never retried and the caller gets its error unchanged. That is deliberate:
|
|
69
|
+
* the caller this budget is sized for, the admission gate, has
|
|
70
|
+
* already taken its fail-open answer by then, and a retry would only spend time after it. What
|
|
71
|
+
* the budget still buys is the fast failures it was built for: the ~25 ms transactor read-phase
|
|
72
|
+
* aggregate off a stream still forming, and a `cohort-unreachable` read during bring-up, which
|
|
73
|
+
* fails fast because there is no connection to ask. Both retry twice well inside 1500 ms. See
|
|
74
|
+
* `docs/cadre-consistency.md` → "Deadlines Over Optimystic's Reads and Commits".
|
|
74
75
|
*/
|
|
75
76
|
export declare const CONTROL_READ_RETRY_BUDGET_MS = 1500;
|
|
76
77
|
/**
|
|
@@ -12,7 +12,7 @@ import { isUncommittedTransactorAggregate } from './control-write-retry.js';
|
|
|
12
12
|
* was absorbed). This module gives reads the same bounded re-presentation with a SHORTER
|
|
13
13
|
* deadline and a NARROWER failure set, because the tightest caller deadline over a control
|
|
14
14
|
* read is the inbound admission gate's 2 s fail-open timeout
|
|
15
|
-
* (`ADMISSION_DECISION_TIMEOUT_MS`, `
|
|
15
|
+
* (`ADMISSION_DECISION_TIMEOUT_MS`, `link-budget.ts`) — a read budget that
|
|
16
16
|
* does not fit inside it with headroom spends its retries after the gate has already
|
|
17
17
|
* admitted.
|
|
18
18
|
*
|
|
@@ -62,18 +62,20 @@ export const CONTROL_READ_RETRY_DELAYS_MS = [100, 400];
|
|
|
62
62
|
* 2 s only because each stays well under it. Do not raise these numbers without
|
|
63
63
|
* re-checking against `ADMISSION_DECISION_TIMEOUT_MS`.
|
|
64
64
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* `clusterPolicy.cohortQueryTimeoutMs`,
|
|
68
|
-
* (`
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
65
|
+
* It cuts the loop off by design, so it does not grow with the link or with the number that
|
|
66
|
+
* dominates a slow attempt: an attempt whose cohort consult finds a silent peer costs
|
|
67
|
+
* `clusterPolicy.cohortQueryTimeoutMs`, two link round trips at the declared link
|
|
68
|
+
* (`cohortReadDeadlineMs` in `link-budget.ts`, 7 000 ms at the default declaration; the plugin's
|
|
69
|
+
* `COHORT_READ_DEADLINE_MS` is the same number). That is more than four times this budget, so
|
|
70
|
+
* such an attempt is never retried and the caller gets its error unchanged. That is deliberate:
|
|
71
|
+
* the caller this budget is sized for, the admission gate, has
|
|
72
|
+
* already taken its fail-open answer by then, and a retry would only spend time after it. What
|
|
73
|
+
* the budget still buys is the fast failures it was built for: the ~25 ms transactor read-phase
|
|
74
|
+
* aggregate off a stream still forming, and a `cohort-unreachable` read during bring-up, which
|
|
75
|
+
* fails fast because there is no connection to ask. Both retry twice well inside 1500 ms. See
|
|
76
|
+
* `docs/cadre-consistency.md` → "Deadlines Over Optimystic's Reads and Commits".
|
|
76
77
|
*/
|
|
78
|
+
// eslint-disable-next-line no-restricted-syntax -- cuts off by design: a slow attempt is not retried, because the admission gate this budget is sized for has already failed open by then; see docs/cadre-consistency.md → "Deadlines Over Optimystic's Reads and Commits"
|
|
77
79
|
export const CONTROL_READ_RETRY_BUDGET_MS = 1500;
|
|
78
80
|
/**
|
|
79
81
|
* The block-unavailability reasons a repeat read can actually improve on, matched against
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-read-retry.js","sourceRoot":"","sources":["../src/control-read-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEtF,OAAO,EAAE,gCAAgC,EAAE,MAAM,0BAA0B,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAE1E
|
|
1
|
+
{"version":3,"file":"control-read-retry.js","sourceRoot":"","sources":["../src/control-read-retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEtF,OAAO,EAAE,gCAAgC,EAAE,MAAM,0BAA0B,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,2QAA2Q;AAC3Q,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAK,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,2BAA2B,GAChC,uEAAuE,CAAC;AAEzE,kFAAkF;AAClF,SAAS,2BAA2B,CAAC,OAAe;IACnD,OAAO,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAClD,CAAC;AAWD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAAc;IAC3D,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAC1C,gCAAgC,CAAC,OAAO,CAAC,IAAI,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpC,IAAI,YAAY,qBAAqB,IAAI,IAAI,CAAC,MAAM,KAAK,oBAAoB,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,gBAAgB,CAC/B,OAAyB,EACzB,UAAmC,EAAE;IAErC,OAAO,qBAAqB,CAAC,OAAO,EAAE;QACrC,QAAQ,EAAE,qBAAqB;QAC/B,QAAQ,EAAE,4BAA4B;QACtC,QAAQ,EAAE,4BAA4B;QACtC,WAAW,EAAE,6BAA6B;QAC1C,SAAS,EAAE,cAAc;KACzB,EAAE,OAAO,CAAC,CAAC;AACb,CAAC"}
|