@serfab/cadre-core 0.9.0 → 0.11.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 +255 -253
- package/dist/bootstrap-peer-store-file.d.ts +13 -0
- package/dist/bootstrap-peer-store-file.js +42 -0
- package/dist/bootstrap-peer-store-file.js.map +1 -0
- package/dist/bootstrap-peer-store.d.ts +73 -0
- package/dist/bootstrap-peer-store.js +128 -0
- package/dist/bootstrap-peer-store.js.map +1 -0
- package/dist/cadre-node.d.ts +1121 -81
- package/dist/cadre-node.js +2276 -262
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +93 -0
- package/dist/control-authorization.js +57 -0
- package/dist/control-authorization.js.map +1 -0
- package/dist/control-cohort.d.ts +26 -0
- package/dist/control-cohort.js +26 -0
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +959 -54
- package/dist/control-database.js +1421 -148
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +90 -10
- package/dist/control-formation-recorder.js +130 -14
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +651 -72
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +38 -4
- package/dist/control-stream.js +70 -4
- package/dist/control-stream.js.map +1 -1
- package/dist/control-write-retry.d.ts +161 -0
- package/dist/control-write-retry.js +412 -0
- package/dist/control-write-retry.js.map +1 -0
- package/dist/delegate-admission.d.ts +134 -0
- package/dist/delegate-admission.js +244 -0
- package/dist/delegate-admission.js.map +1 -0
- package/dist/device-token.d.ts +11 -11
- package/dist/device-token.js +14 -13
- package/dist/device-token.js.map +1 -1
- package/dist/ed25519-key.d.ts +26 -0
- package/dist/ed25519-key.js +56 -1
- package/dist/ed25519-key.js.map +1 -1
- package/dist/file-durable-slot.d.ts +20 -0
- package/dist/file-durable-slot.js +53 -0
- package/dist/file-durable-slot.js.map +1 -0
- package/dist/formation-approval.d.ts +157 -0
- package/dist/formation-approval.js +375 -0
- package/dist/formation-approval.js.map +1 -0
- package/dist/fs-atomic.d.ts +38 -0
- package/dist/fs-atomic.js +97 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/identity-key.d.ts +49 -0
- package/dist/identity-key.js +81 -0
- package/dist/identity-key.js.map +1 -0
- package/dist/index.d.ts +17 -7
- package/dist/index.js +40 -6
- package/dist/index.js.map +1 -1
- package/dist/key-store-file.d.ts +2 -24
- package/dist/key-store-file.js +10 -77
- package/dist/key-store-file.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +146 -0
- package/dist/membership-connection-gater.js +180 -0
- package/dist/membership-connection-gater.js.map +1 -0
- package/dist/node-local-snapshot.d.ts +109 -0
- package/dist/node-local-snapshot.js +182 -0
- package/dist/node-local-snapshot.js.map +1 -0
- package/dist/peer-addr-book.d.ts +92 -0
- package/dist/peer-addr-book.js +220 -0
- package/dist/peer-addr-book.js.map +1 -0
- package/dist/peer-authorization.d.ts +135 -18
- package/dist/peer-authorization.js +152 -21
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-record.d.ts +21 -8
- package/dist/peer-record.js +26 -10
- package/dist/peer-record.js.map +1 -1
- package/dist/relay-addrs.d.ts +45 -0
- package/dist/relay-addrs.js +103 -0
- package/dist/relay-addrs.js.map +1 -0
- package/dist/relay-reservation.d.ts +266 -0
- package/dist/relay-reservation.js +613 -0
- package/dist/relay-reservation.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +228 -56
- package/dist/seed-bootstrap.js +340 -198
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +47 -21
- package/dist/seed-trust-policy.js +24 -15
- package/dist/seed-trust-policy.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +42 -13
- package/dist/strand-addr-protocol.js +58 -52
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-backfill.d.ts +109 -0
- package/dist/strand-backfill.js +279 -0
- package/dist/strand-backfill.js.map +1 -0
- package/dist/strand-cohort.d.ts +4 -37
- package/dist/strand-cohort.js +4 -15
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +11 -16
- package/dist/strand-database.js +19 -7
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +36 -7
- package/dist/strand-formation-manager.js +115 -15
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +103 -4
- package/dist/strand-formation-protocol.js +232 -4
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +88 -19
- package/dist/strand-instance-manager.js +178 -34
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-registry.d.ts +5 -5
- package/dist/strand-member-registry.js +18 -10
- package/dist/strand-member-registry.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +484 -88
- package/dist/strand-membership-writer.js +751 -128
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +99 -33
- package/dist/strand-solicitation.js +100 -52
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-transport-key.d.ts +40 -0
- package/dist/strand-transport-key.js +56 -0
- package/dist/strand-transport-key.js.map +1 -0
- package/dist/strand-wake-protocol.d.ts +9 -7
- package/dist/strand-wake-protocol.js +22 -46
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/strand-watcher.d.ts +17 -2
- package/dist/strand-watcher.js +47 -3
- package/dist/strand-watcher.js.map +1 -1
- package/dist/trusted-owner-store-file.d.ts +12 -0
- package/dist/trusted-owner-store-file.js +39 -0
- package/dist/trusted-owner-store-file.js.map +1 -0
- package/dist/trusted-owner-store.d.ts +64 -0
- package/dist/trusted-owner-store.js +126 -0
- package/dist/trusted-owner-store.js.map +1 -0
- package/dist/types.d.ts +307 -44
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/package.json +20 -8
- package/src/arachnode-stub.ts +159 -159
- package/src/bootstrap-peer-store-file.ts +44 -0
- package/src/bootstrap-peer-store.ts +172 -0
- package/src/cadre-node.ts +5158 -2981
- package/src/canonical-json.ts +30 -30
- package/src/control-authorization.ts +114 -0
- package/src/control-cohort.ts +130 -103
- package/src/control-database.ts +2406 -948
- package/src/control-formation-recorder.ts +305 -151
- package/src/control-schema.ts +651 -72
- package/src/control-stream.ts +205 -128
- package/src/control-write-retry.ts +483 -0
- package/src/delegate-admission.ts +302 -0
- package/src/device-token.ts +94 -89
- package/src/diagnostics/connection-path.ts +277 -277
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -222
- package/src/ed25519-key.ts +66 -1
- package/src/enrollment.ts +199 -199
- package/src/file-durable-slot.ts +61 -0
- package/src/formation-approval.ts +604 -0
- package/src/fs-atomic.ts +107 -0
- package/src/hibernation-manager.ts +437 -437
- package/src/identity-key.ts +109 -0
- package/src/index.ts +137 -5
- package/src/key-store-file.ts +132 -203
- package/src/key-store.ts +99 -99
- package/src/membership-connection-gater.ts +204 -0
- package/src/node-local-snapshot.ts +260 -0
- package/src/peer-addr-book.ts +250 -0
- package/src/peer-authorization.ts +259 -81
- package/src/peer-record.ts +164 -146
- package/src/push-credentials.ts +84 -84
- package/src/push-node.ts +64 -64
- package/src/push-notifier-apns.ts +247 -247
- package/src/push-notifier-fcm.ts +220 -220
- package/src/push-notifier-shared.ts +28 -28
- package/src/push-notifier.ts +52 -52
- package/src/relay-addrs.ts +111 -0
- package/src/relay-reservation.ts +823 -0
- package/src/seed-bootstrap.ts +409 -209
- package/src/seed-trust-policy.ts +140 -107
- package/src/strand-addr-protocol.ts +441 -410
- package/src/strand-backfill.ts +366 -0
- package/src/strand-cohort.ts +38 -77
- package/src/strand-database.ts +202 -204
- package/src/strand-formation-manager.ts +491 -368
- package/src/strand-formation-protocol.ts +692 -391
- package/src/strand-instance-manager.ts +201 -46
- package/src/strand-member-key.ts +39 -39
- package/src/strand-member-registry.ts +182 -177
- package/src/strand-membership-writer.ts +938 -146
- package/src/strand-solicitation.ts +472 -407
- package/src/strand-transport-key.ts +58 -0
- package/src/strand-wake-payload.ts +32 -32
- package/src/strand-wake-protocol.ts +314 -331
- package/src/strand-watcher.ts +314 -251
- package/src/trusted-owner-store-file.ts +41 -0
- package/src/trusted-owner-store.ts +171 -0
- package/src/types.ts +1309 -1032
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG
|
|
1
|
+
{"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAqxBH,CAAC"}
|
package/dist/control-stream.d.ts
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* abort-on-timeout read-to-EOF helper so a misbehaving peer that opens a stream
|
|
10
10
|
* and never half-closes its write end cannot pin a receiver forever.
|
|
11
11
|
*
|
|
12
|
+
* The sender side of all three protocols is the same exchange — dial under a
|
|
13
|
+
* deadline, write one frame, half-close, read one response, reset the stream on
|
|
14
|
+
* any failure — so that shape lives here too, as {@link withDeadline} +
|
|
15
|
+
* {@link exchangeFrame}; each protocol supplies only its dial options, its
|
|
16
|
+
* request object, and its response decoder.
|
|
17
|
+
*
|
|
12
18
|
* Dependency-free by design: it imports nothing from the protocol modules, so
|
|
13
19
|
* the import graph stays acyclic (`control-stream` ← `seed-bootstrap` ←
|
|
14
20
|
* `strand-wake-protocol`). In particular it returns RAW bytes from
|
|
@@ -28,12 +34,40 @@ export interface ControlStream extends AsyncIterable<Uint8Array> {
|
|
|
28
34
|
export declare function writeFrame(stream: ControlStream, obj: unknown): void;
|
|
29
35
|
/**
|
|
30
36
|
* Reject if `op` does not settle within `ms`. On timeout, invoke `onTimeout`
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
37
|
+
* before rejecting — a hook for releasing whatever the op is parked on. A
|
|
38
|
+
* throwing `onTimeout` is swallowed so it cannot mask the timeout rejection. The
|
|
39
|
+
* rejection message is exactly `<label> timed out after <ms>ms`, so callers fold
|
|
40
|
+
* any domain prefix into `label`.
|
|
41
|
+
*
|
|
42
|
+
* Senders want {@link withDeadline}, which wires `onTimeout` to an
|
|
43
|
+
* `AbortController` so the in-flight dial/stream is cancelled rather than leaked.
|
|
35
44
|
*/
|
|
36
45
|
export declare function withTimeout<T>(ms: number, label: string, op: () => Promise<T>, onTimeout?: () => void): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Run `op` under a `withTimeout` deadline that also *cancels* it: the signal
|
|
48
|
+
* handed to `op` is aborted immediately before the timeout rejection, so a
|
|
49
|
+
* sender can pass it to `dialProtocol` (cancelling an in-flight connect) and to
|
|
50
|
+
* {@link exchangeFrame} (resetting a live stream) instead of leaking either.
|
|
51
|
+
*/
|
|
52
|
+
export declare function withDeadline<T>(ms: number, label: string, op: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Send one request frame on an open stream, half-close the write end, and read
|
|
55
|
+
* the response through `readResponse`.
|
|
56
|
+
*
|
|
57
|
+
* `signal` is the caller's deadline (see {@link withDeadline}): an abort resets
|
|
58
|
+
* the stream, releasing the otherwise unbounded response read. If the deadline
|
|
59
|
+
* already fired while the stream was being dialed, the freshly-opened stream is
|
|
60
|
+
* reset here rather than left dangling. Every reset — deadline, response-decode
|
|
61
|
+
* failure, or a `readResponse` that rejects without aborting (the size-cap path
|
|
62
|
+
* of {@link readStreamToEnd}) — goes through one idempotent `abort`, so the
|
|
63
|
+
* deadline listener and the error path cannot double-reset a stream.
|
|
64
|
+
*
|
|
65
|
+
* `readResponse` owns its own read bound, which is a backstop rather than the
|
|
66
|
+
* primary one: the caller's deadline started first and therefore fires first.
|
|
67
|
+
* It matters only when an abort does not release the read (e.g. a stream double
|
|
68
|
+
* whose `abort()` is a no-op).
|
|
69
|
+
*/
|
|
70
|
+
export declare function exchangeFrame<T>(stream: ControlStream, signal: AbortSignal, request: unknown, readResponse: (stream: ControlStream) => Promise<T>, abortMessage: string): Promise<T>;
|
|
37
71
|
/**
|
|
38
72
|
* Read a stream to EOF, capped at `maxBytes`, bounded by `timeoutMs`, returning
|
|
39
73
|
* the assembled bytes (the caller decodes via `decodeLengthPrefixedFrame`).
|
package/dist/control-stream.js
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* abort-on-timeout read-to-EOF helper so a misbehaving peer that opens a stream
|
|
10
10
|
* and never half-closes its write end cannot pin a receiver forever.
|
|
11
11
|
*
|
|
12
|
+
* The sender side of all three protocols is the same exchange — dial under a
|
|
13
|
+
* deadline, write one frame, half-close, read one response, reset the stream on
|
|
14
|
+
* any failure — so that shape lives here too, as {@link withDeadline} +
|
|
15
|
+
* {@link exchangeFrame}; each protocol supplies only its dial options, its
|
|
16
|
+
* request object, and its response decoder.
|
|
17
|
+
*
|
|
12
18
|
* Dependency-free by design: it imports nothing from the protocol modules, so
|
|
13
19
|
* the import graph stays acyclic (`control-stream` ← `seed-bootstrap` ←
|
|
14
20
|
* `strand-wake-protocol`). In particular it returns RAW bytes from
|
|
@@ -25,10 +31,13 @@ export function writeFrame(stream, obj) {
|
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Reject if `op` does not settle within `ms`. On timeout, invoke `onTimeout`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
34
|
+
* before rejecting — a hook for releasing whatever the op is parked on. A
|
|
35
|
+
* throwing `onTimeout` is swallowed so it cannot mask the timeout rejection. The
|
|
36
|
+
* rejection message is exactly `<label> timed out after <ms>ms`, so callers fold
|
|
37
|
+
* any domain prefix into `label`.
|
|
38
|
+
*
|
|
39
|
+
* Senders want {@link withDeadline}, which wires `onTimeout` to an
|
|
40
|
+
* `AbortController` so the in-flight dial/stream is cancelled rather than leaked.
|
|
32
41
|
*/
|
|
33
42
|
export function withTimeout(ms, label, op, onTimeout) {
|
|
34
43
|
return new Promise((resolve, reject) => {
|
|
@@ -44,6 +53,63 @@ export function withTimeout(ms, label, op, onTimeout) {
|
|
|
44
53
|
op().then(resolve, reject).finally(() => clearTimeout(timer));
|
|
45
54
|
});
|
|
46
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Run `op` under a `withTimeout` deadline that also *cancels* it: the signal
|
|
58
|
+
* handed to `op` is aborted immediately before the timeout rejection, so a
|
|
59
|
+
* sender can pass it to `dialProtocol` (cancelling an in-flight connect) and to
|
|
60
|
+
* {@link exchangeFrame} (resetting a live stream) instead of leaking either.
|
|
61
|
+
*/
|
|
62
|
+
export function withDeadline(ms, label, op) {
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
return withTimeout(ms, label, () => op(controller.signal), () => controller.abort());
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Send one request frame on an open stream, half-close the write end, and read
|
|
68
|
+
* the response through `readResponse`.
|
|
69
|
+
*
|
|
70
|
+
* `signal` is the caller's deadline (see {@link withDeadline}): an abort resets
|
|
71
|
+
* the stream, releasing the otherwise unbounded response read. If the deadline
|
|
72
|
+
* already fired while the stream was being dialed, the freshly-opened stream is
|
|
73
|
+
* reset here rather than left dangling. Every reset — deadline, response-decode
|
|
74
|
+
* failure, or a `readResponse` that rejects without aborting (the size-cap path
|
|
75
|
+
* of {@link readStreamToEnd}) — goes through one idempotent `abort`, so the
|
|
76
|
+
* deadline listener and the error path cannot double-reset a stream.
|
|
77
|
+
*
|
|
78
|
+
* `readResponse` owns its own read bound, which is a backstop rather than the
|
|
79
|
+
* primary one: the caller's deadline started first and therefore fires first.
|
|
80
|
+
* It matters only when an abort does not release the read (e.g. a stream double
|
|
81
|
+
* whose `abort()` is a no-op).
|
|
82
|
+
*/
|
|
83
|
+
export async function exchangeFrame(stream, signal, request, readResponse, abortMessage) {
|
|
84
|
+
let reset = false;
|
|
85
|
+
const resetStream = (err) => {
|
|
86
|
+
if (reset)
|
|
87
|
+
return;
|
|
88
|
+
reset = true;
|
|
89
|
+
stream.abort(err);
|
|
90
|
+
};
|
|
91
|
+
const abortErr = new Error(abortMessage);
|
|
92
|
+
const onAbort = () => resetStream(abortErr);
|
|
93
|
+
if (signal.aborted) {
|
|
94
|
+
onAbort();
|
|
95
|
+
throw abortErr;
|
|
96
|
+
}
|
|
97
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
98
|
+
try {
|
|
99
|
+
writeFrame(stream, request);
|
|
100
|
+
// close() half-closes the write end (EOF) while the read end stays open for
|
|
101
|
+
// the response — the libp2p 3.x request/response pattern.
|
|
102
|
+
await stream.close();
|
|
103
|
+
return await readResponse(stream);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
resetStream(err instanceof Error ? err : new Error(String(err)));
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
signal.removeEventListener('abort', onAbort);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
47
113
|
/** Normalize a libp2p chunk (Uint8Array or Uint8ArrayList) to a flat Uint8Array. */
|
|
48
114
|
function toBytes(chunk) {
|
|
49
115
|
return chunk instanceof Uint8Array ? chunk : chunk.subarray();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-stream.js","sourceRoot":"","sources":["../src/control-stream.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"control-stream.js","sourceRoot":"","sources":["../src/control-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded retry for TRANSIENT control-write failures — the classifier and loop behind
|
|
3
|
+
* `ControlDatabase.lockedWithRetry`, the single funnel every local control write passes
|
|
4
|
+
* through.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: the control database replicates every block to the whole party, so a
|
|
7
|
+
* three-machine party commits control writes unanimously (`ceil(3 × 0.75) = 3` approvals) —
|
|
8
|
+
* and no `superMajorityThreshold` can lower that bar without violating Optimystic's
|
|
9
|
+
* partition-safety condition at the shipped admission fraction (the arithmetic lives in
|
|
10
|
+
* `docs/architecture.md` → "Replication cluster size"). Cadre therefore accepts unanimity
|
|
11
|
+
* and absorbs the transient half of its cost here: a write that failed because the cohort
|
|
12
|
+
* did not ANSWER (a stream reset mid-connection-formation, zero approvals) is re-presented
|
|
13
|
+
* a moment later, which the degraded-cohort-member scenario measured as safe (a failed
|
|
14
|
+
* write rolls back, nothing half-commits) and effective (the next write commits in ~1 s).
|
|
15
|
+
*
|
|
16
|
+
* A write somebody actually REJECTED is never retried — re-presenting it would re-present
|
|
17
|
+
* a spent signature against a cohort that already said no.
|
|
18
|
+
*
|
|
19
|
+
* TWO policies ship from here, and the split is deliberate: the default
|
|
20
|
+
* ({@link isRetriableControlWriteFailure}, {@link CONTROL_WRITE_ATTEMPTS}) sits under every
|
|
21
|
+
* control write, and {@link SCHEMA_INIT_RETRY_POLICY} is opted into by
|
|
22
|
+
* `ControlDatabase.loadSchema` alone. The widened one absorbs one extra failure class whose
|
|
23
|
+
* re-run safety holds for schema DDL and NOT for writes in general; anything added to the default
|
|
24
|
+
* list must be safe for all ~19 callers.
|
|
25
|
+
*
|
|
26
|
+
* Lives outside `control-database.ts` so a spec can drive the classifier against real engine
|
|
27
|
+
* errors without importing the whole database class.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Attempts allowed per control write: the first, plus two retries of a transiently-failed
|
|
31
|
+
* cluster commit. Bounded so a genuinely unreachable cohort terminates in the transactor's
|
|
32
|
+
* own error rather than spinning; two retries covers the observed failure (a stream still
|
|
33
|
+
* forming when the write fired) with margin.
|
|
34
|
+
*/
|
|
35
|
+
export declare const CONTROL_WRITE_ATTEMPTS = 3;
|
|
36
|
+
/**
|
|
37
|
+
* Attempts allowed for {@link SCHEMA_INIT_RETRY_POLICY}, the one call site that also absorbs a
|
|
38
|
+
* self-coordination refusal.
|
|
39
|
+
*
|
|
40
|
+
* Two more than {@link CONTROL_WRITE_ATTEMPTS} because what this retry buys is wall-clock for a
|
|
41
|
+
* cold-starting node's bootstrap dials to land, and because the refusal is cheap to re-provoke
|
|
42
|
+
* relative to a cluster round-trip: `findCoordinator` already spends its own bounded wait before
|
|
43
|
+
* raising (3 attempts, 500 ms apart, `db-p2p/src/libp2p-key-network.ts` ~422) and then decides
|
|
44
|
+
* LOCALLY that it may not elect itself, so no peer is contacted and no cohort deadline is
|
|
45
|
+
* consumed.
|
|
46
|
+
*
|
|
47
|
+
* Note that the ~1 s `findCoordinator` spends internally is real, on top of this policy's own
|
|
48
|
+
* backoff — which is exactly why {@link CONTROL_WRITE_RETRY_BUDGET_MS} and not this count is what
|
|
49
|
+
* actually terminates the loop in the worst case.
|
|
50
|
+
*/
|
|
51
|
+
export declare const SCHEMA_INIT_ATTEMPTS = 5;
|
|
52
|
+
/**
|
|
53
|
+
* Elapsed-time ceiling on the whole retry loop, measured from the START of the first
|
|
54
|
+
* attempt and checked after a failed attempt BEFORE sleeping.
|
|
55
|
+
*
|
|
56
|
+
* This budget — not the attempt count — is what makes the policy safe to sit under every
|
|
57
|
+
* control write. A transient failure (stream reset while a connection is still forming)
|
|
58
|
+
* surfaces in well under a second, so it is retried and the loop adds at most ~2.2 s. A
|
|
59
|
+
* genuinely silent cohort member fails at ~20 s (two 10 s `ClusterClient` response-deadline
|
|
60
|
+
* attempts, measured in `control-write-degraded-cohort-member.integration.ts`), which
|
|
61
|
+
* already exceeds this budget when attempt 1 returns — so that case is surfaced immediately
|
|
62
|
+
* and retry adds ZERO latency to the case where it cannot help.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CONTROL_WRITE_RETRY_BUDGET_MS = 10000;
|
|
65
|
+
/**
|
|
66
|
+
* Did this control write fail because the cluster cohort did not ANSWER — i.e. is
|
|
67
|
+
* re-presenting the SAME signed write a moment later the right response?
|
|
68
|
+
*
|
|
69
|
+
* Classifies by MESSAGE, walking the `cause` chain with Quereus' own {@link unwrapError} —
|
|
70
|
+
* the failure surface is not recognisable by type. The transactor and the coordinator both throw bare `Error`s, and by
|
|
71
|
+
* the time one reaches `ControlDatabase` it is wrapped in a `QuereusError` (the real chain
|
|
72
|
+
* is `QuereusError` → `Error` → `Error`), so the match must work at any depth. Anything
|
|
73
|
+
* that is not an `Error` is never retried.
|
|
74
|
+
*
|
|
75
|
+
* {@link reportsIndeterminateCommit} vetoes the whole chain before any matcher runs, so a
|
|
76
|
+
* failure that reports a commit-phase batch anywhere is never retried on the strength of some
|
|
77
|
+
* other level looking transient.
|
|
78
|
+
*
|
|
79
|
+
* NOTE: this depends on engine/transactor error TEXT. The messages producible without a
|
|
80
|
+
* network are driven from the REAL engine in `control-formation-seat-budget.spec.ts`, so
|
|
81
|
+
* a rewording reddens a spec rather than silently disabling the retry. The two RETRIABLE
|
|
82
|
+
* messages need a real multi-node cluster to produce; the degraded-cohort scenario
|
|
83
|
+
* (`control-write-degraded-cohort-member.integration.ts`) asserts this classifier against
|
|
84
|
+
* both LIVE — the super-majority shortfall from a real silent member, and the transactor
|
|
85
|
+
* aggregate from a real stream reset, which the retry there absorbs end to end.
|
|
86
|
+
*/
|
|
87
|
+
export declare function isRetriableControlWriteFailure(error: unknown): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* {@link isRetriableControlWriteFailure} widened by exactly one class — the self-coordination
|
|
90
|
+
* grace refusal ({@link RETRIABLE_SCHEMA_INIT_MATCHERS}) — for `ControlDatabase.loadSchema`.
|
|
91
|
+
*
|
|
92
|
+
* Opt-in per call site, via {@link SCHEMA_INIT_RETRY_POLICY}. Every other control write keeps the
|
|
93
|
+
* default classifier untouched.
|
|
94
|
+
*/
|
|
95
|
+
export declare function isRetriableSchemaInitFailure(error: unknown): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Policy and pacing for {@link retryControlWrite}. Production callers pass nothing (or a named
|
|
98
|
+
* policy such as {@link SCHEMA_INIT_RETRY_POLICY}); specs inject a recorded `sleep`, a shorter
|
|
99
|
+
* delay list, or a fake clock so no test ever waits out a real backoff.
|
|
100
|
+
*
|
|
101
|
+
* Every field defaults to the shipped control-write policy, so an omitted field is byte-identical
|
|
102
|
+
* to the behaviour before per-call-site policies existed.
|
|
103
|
+
*/
|
|
104
|
+
export interface ControlWriteRetryOptions {
|
|
105
|
+
/**
|
|
106
|
+
* Total attempts, first included. Default {@link CONTROL_WRITE_ATTEMPTS}, floored at 1 —
|
|
107
|
+
* below that the loop would run the body zero times and rethrow a `lastError` nobody set.
|
|
108
|
+
*/
|
|
109
|
+
attempts?: number;
|
|
110
|
+
/** Backoff base before retry N (last entry repeats). Default {@link CONTROL_WRITE_RETRY_DELAYS_MS}. */
|
|
111
|
+
delaysMs?: readonly number[];
|
|
112
|
+
/**
|
|
113
|
+
* Which failures are transient enough to re-present. Default
|
|
114
|
+
* {@link isRetriableControlWriteFailure}; the only other shipped value is
|
|
115
|
+
* {@link isRetriableSchemaInitFailure}, via {@link SCHEMA_INIT_RETRY_POLICY}.
|
|
116
|
+
*/
|
|
117
|
+
isRetriable?: (error: unknown) => boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Operation label stamped into every log line this loop emits (rendered as
|
|
120
|
+
* `Control write [<label>] …`), and NOTHING else — no behavioural effect. The debug
|
|
121
|
+
* log is the only surface where the loop's decisions are observable (the rethrown
|
|
122
|
+
* error is unchanged and no attempt counter is exposed), and several writes retry
|
|
123
|
+
* CONCURRENTLY in a real party, so an unlabelled line cannot be attributed to a
|
|
124
|
+
* write. The degraded-cohort scenario asserts on these lines per-operation.
|
|
125
|
+
*/
|
|
126
|
+
label?: string;
|
|
127
|
+
/** The sleep primitive. Default: `setTimeout`. */
|
|
128
|
+
sleep?: (ms: number) => Promise<void>;
|
|
129
|
+
/** Clock for the elapsed-budget check. Default: `Date.now`. */
|
|
130
|
+
now?: () => number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The one non-default retry policy: `ControlDatabase.loadSchema`'s distributed DDL, which
|
|
134
|
+
* additionally absorbs optimystic's self-coordination grace refusal and gets more attempts over a
|
|
135
|
+
* longer backoff to do it (see {@link SCHEMA_INIT_ATTEMPTS} and
|
|
136
|
+
* {@link SCHEMA_INIT_RETRY_DELAYS_MS}). Still bounded by the shared
|
|
137
|
+
* {@link CONTROL_WRITE_RETRY_BUDGET_MS}.
|
|
138
|
+
*
|
|
139
|
+
* NOTE: exactly one call site opts in today, and the re-run safety this policy assumes is that
|
|
140
|
+
* site's, not a general property — `apply schema` is a diff and a failed `create table` leaves the
|
|
141
|
+
* catalog clean. A second opt-in must re-derive that argument for its own write body first; if this
|
|
142
|
+
* policy ever grows a third consumer, rename it for what the callers share rather than widening it
|
|
143
|
+
* by default.
|
|
144
|
+
*/
|
|
145
|
+
export declare const SCHEMA_INIT_RETRY_POLICY: Readonly<ControlWriteRetryOptions>;
|
|
146
|
+
/**
|
|
147
|
+
* Run `attempt` up to {@link CONTROL_WRITE_ATTEMPTS} times (or `options.attempts`), retrying only
|
|
148
|
+
* failures the policy's classifier — {@link isRetriableControlWriteFailure} by default —
|
|
149
|
+
* calls transient, and only while {@link CONTROL_WRITE_RETRY_BUDGET_MS} has not elapsed.
|
|
150
|
+
*
|
|
151
|
+
* `attempt` must be atomic and re-runnable: a failed cluster write rolls back (nothing is
|
|
152
|
+
* half-applied), and re-running the body re-runs its reads too — `ControlDatabase`'s
|
|
153
|
+
* locked write bodies all satisfy this (see the contract on
|
|
154
|
+
* `ControlDatabase.withWriteLock`). The backoff sleeps happen with NO lock held; the caller
|
|
155
|
+
* takes and releases its lock inside `attempt`.
|
|
156
|
+
*
|
|
157
|
+
* On exhaustion (attempts or budget) the LAST error is rethrown unchanged — never wrapped,
|
|
158
|
+
* so the exact messages downstream code and the degraded-cohort-member scenario assert on
|
|
159
|
+
* survive. A non-retriable failure propagates from the attempt that raised it.
|
|
160
|
+
*/
|
|
161
|
+
export declare function retryControlWrite<T>(attempt: () => Promise<T>, options?: ControlWriteRetryOptions): Promise<T>;
|