@optimystic/db-p2p 0.21.0 → 0.22.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/dist/src/cluster/cluster-policy.d.ts +13 -2
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +51 -4
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +3 -3
- package/dist/src/cluster/cluster-repo.js +3 -3
- package/dist/src/cluster/cluster-size-coupling.d.ts +28 -0
- package/dist/src/cluster/cluster-size-coupling.d.ts.map +1 -0
- package/dist/src/cluster/cluster-size-coupling.js +35 -0
- package/dist/src/cluster/cluster-size-coupling.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +6 -0
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +1 -1
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +15 -3
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +46 -5
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +40 -9
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +3 -2
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +834 -777
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/libp2p-node-rn.d.ts +2 -2
- package/dist/src/libp2p-node-rn.d.ts.map +1 -1
- package/dist/src/libp2p-node-rn.js.map +1 -1
- package/dist/src/libp2p-node.d.ts +2 -2
- package/dist/src/libp2p-node.d.ts.map +1 -1
- package/dist/src/libp2p-node.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +2 -0
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +4 -0
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/optimystic-node.d.ts +35 -0
- package/dist/src/optimystic-node.d.ts.map +1 -0
- package/dist/src/optimystic-node.js +2 -0
- package/dist/src/optimystic-node.js.map +1 -0
- package/dist/src/repo/coordinator-repo.d.ts +16 -5
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +24 -8
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/rn.d.ts +2 -0
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +2 -0
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +57 -5
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +20 -1
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +56 -3
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +124 -18
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +17 -0
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +41 -26
- package/src/cluster/cluster-policy.ts +55 -4
- package/src/cluster/cluster-repo.ts +3 -3
- package/src/cluster/cluster-size-coupling.ts +45 -0
- package/src/cluster/quorum-restore.ts +1 -1
- package/src/cluster/reconcile-block.ts +15 -3
- package/src/index.ts +2 -0
- package/src/libp2p-key-network.ts +41 -9
- package/src/libp2p-node-base.ts +907 -847
- package/src/libp2p-node-rn.ts +2 -2
- package/src/libp2p-node.ts +2 -2
- package/src/network/network-manager-service.ts +5 -0
- package/src/optimystic-node.ts +36 -0
- package/src/repo/coordinator-repo.ts +24 -8
- package/src/rn.ts +2 -0
- package/src/storage/block-storage.ts +59 -6
- package/src/storage/i-block-storage.ts +20 -1
- package/src/storage/storage-repo.ts +129 -18
- package/src/testing/raw-storage-conformance.ts +20 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_SUPER_MAJORITY_THRESHOLD, type ClusterConsensusConfig } from "@optimystic/db-core";
|
|
2
|
+
import { createLogger } from "../logger.js";
|
|
3
|
+
import { CORROBORATION_FLOOR } from "./quorum-restore.js";
|
|
4
|
+
|
|
5
|
+
const log = createLogger('cluster-policy');
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Resolves the operator-facing cluster knobs (`clusterSize`, `clusterPolicy.*`) into the concrete
|
|
@@ -48,6 +52,16 @@ import { DEFAULT_SUPER_MAJORITY_THRESHOLD, type ClusterConsensusConfig } from "@
|
|
|
48
52
|
*/
|
|
49
53
|
export const minAbsoluteClusterSize = 2;
|
|
50
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Default replication factor / target cohort breadth when the operator declares no `clusterSize`.
|
|
57
|
+
*
|
|
58
|
+
* Exported (and re-exported from the package root) rather than left inline because a caller that
|
|
59
|
+
* must construct a `Libp2pKeyPeerNetwork` for a node it did not build has to state a cluster size —
|
|
60
|
+
* the constructor no longer supplies one — and the only defensible answer is "whatever a node built
|
|
61
|
+
* here would have resolved to". Repeating the literal is how the two drifted last time.
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_CLUSTER_SIZE = 10;
|
|
64
|
+
|
|
51
65
|
/**
|
|
52
66
|
* The operator-facing cluster knobs. `NodeOptions` (`libp2p-node-base.ts`) intersects this rather
|
|
53
67
|
* than restating it, so a knob added here is one `resolveClusterPolicy` is guaranteed to see — a
|
|
@@ -110,8 +124,10 @@ export type ResolvedClusterPolicy = ClusterConsensusConfig & {
|
|
|
110
124
|
};
|
|
111
125
|
|
|
112
126
|
/**
|
|
113
|
-
* Apply every cluster-policy default a node needs.
|
|
114
|
-
* composition root's behavior is unit-testable (`test/cluster-policy.spec.ts`).
|
|
127
|
+
* Apply every cluster-policy default a node needs. Same options in, same numbers out, so the
|
|
128
|
+
* composition root's behavior is unit-testable (`test/cluster-policy.spec.ts`). Its one side effect
|
|
129
|
+
* is the `assumed-cluster-size-unset` advisory below, which lives here because this is the only place
|
|
130
|
+
* that knows the resolution produced a self-defeating combination.
|
|
115
131
|
*/
|
|
116
132
|
export function resolveClusterPolicy(options: ClusterPolicyOptions): ResolvedClusterPolicy {
|
|
117
133
|
// undefined here means "the operator said nothing", which is the only case where the two
|
|
@@ -124,7 +140,42 @@ export function resolveClusterPolicy(options: ClusterPolicyOptions): ResolvedClu
|
|
|
124
140
|
// rejects non-positive integers). If another composition root starts accepting unvalidated config,
|
|
125
141
|
// clamp here rather than in each consumer.
|
|
126
142
|
const declaredCohortSize = options.clusterPolicy?.assumedClusterSize;
|
|
127
|
-
const clusterSize = options.clusterSize ??
|
|
143
|
+
const clusterSize = options.clusterSize ?? DEFAULT_CLUSTER_SIZE;
|
|
144
|
+
const repairCorroborationClusterSize = declaredCohortSize ?? clusterSize;
|
|
145
|
+
|
|
146
|
+
// Called once per node (resolveClusterPolicy runs once at construction), so this fires once per
|
|
147
|
+
// node startup, not per repair — a per-attempt warn on a busy node would be noise that gets
|
|
148
|
+
// filtered, defeating the point. Fires purely off configuration (not an observed cohort), so a
|
|
149
|
+
// deployment that genuinely runs `clusterSize` machines sees it too; worded as a conditional
|
|
150
|
+
// ("if you run fewer than N machines") rather than a fault for exactly that reason.
|
|
151
|
+
//
|
|
152
|
+
// The machine count in the message is CORROBORATION_FLOOR + 1, NOT repairCorroborationClusterSize:
|
|
153
|
+
// `corroboratorCapacity` caps only the FLOOR of two (`quorum-restore.ts`), so a cohort with two
|
|
154
|
+
// peers besides the reader meets it whatever the declared size. What an undeclared size costs is
|
|
155
|
+
// the relaxation below two, which is only reachable when repairCorroborationClusterSize <= 2.
|
|
156
|
+
// NOTE: only the UNDECLARED case warns. An operator who declares an assumedClusterSize larger than
|
|
157
|
+
// the cohort they actually run is equally unable to repair and gets no warning — deliberate, since
|
|
158
|
+
// a declaration is an explicit assertion and this function has no observed cohort to contradict it
|
|
159
|
+
// with. If `feat-admission-floor-from-observed-cohort-high-water-mark` ever lands (deriving the
|
|
160
|
+
// yardstick from observation), that warning becomes cheap and worth adding here.
|
|
161
|
+
if (declaredCohortSize === undefined && clusterSize > minAbsoluteClusterSize) {
|
|
162
|
+
const minimumSelfHealingDeployment = CORROBORATION_FLOOR + 1;
|
|
163
|
+
log('assumed-cluster-size-unset', {
|
|
164
|
+
clusterSize,
|
|
165
|
+
repairCorroborationClusterSize,
|
|
166
|
+
corroborationFloor: CORROBORATION_FLOOR,
|
|
167
|
+
minimumSelfHealingDeployment,
|
|
168
|
+
message:
|
|
169
|
+
`No clusterPolicy.assumedClusterSize declared: block repair (read-repair and reconcile) requires ` +
|
|
170
|
+
`${CORROBORATION_FLOOR} distinct corroborating peers other than the reader, and that floor is ` +
|
|
171
|
+
`relaxed only for a cohort that DECLARES it is smaller — with ` +
|
|
172
|
+
`repairCorroborationClusterSize=${repairCorroborationClusterSize} it never relaxes. So a deployment ` +
|
|
173
|
+
`that actually runs fewer than ${minimumSelfHealingDeployment} machines can never supply the floor ` +
|
|
174
|
+
`and every repair declines, permanently. If you run fewer than ${minimumSelfHealingDeployment} ` +
|
|
175
|
+
`machines, set clusterPolicy.assumedClusterSize to your real cohort size; it does not lower ` +
|
|
176
|
+
`clusterSize=${clusterSize} (the replication factor). Larger deployments can ignore this.`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
128
179
|
|
|
129
180
|
return {
|
|
130
181
|
superMajorityThreshold: options.clusterPolicy?.superMajorityThreshold ?? DEFAULT_SUPER_MAJORITY_THRESHOLD,
|
|
@@ -147,6 +198,6 @@ export function resolveClusterPolicy(options: ClusterPolicyOptions): ResolvedClu
|
|
|
147
198
|
// Repair corroboration floor, every repair. Defaults strict — to the replication factor — so an
|
|
148
199
|
// unconfigured node cannot have its floor talked down to a single voter by a shrunken cohort
|
|
149
200
|
// view. A genuinely small mesh declares its size (either field) to regain self-repair.
|
|
150
|
-
repairCorroborationClusterSize
|
|
201
|
+
repairCorroborationClusterSize
|
|
151
202
|
};
|
|
152
203
|
}
|
|
@@ -990,9 +990,9 @@ export class ClusterMember implements ICluster {
|
|
|
990
990
|
*
|
|
991
991
|
* NOTE: partition safety needs `2 · membershipAdmissionFraction · superMajorityThreshold > 1` — each
|
|
992
992
|
* side of a split must recruit `fraction · threshold · K` distinct honest members, and two sides cannot
|
|
993
|
-
* both find them in one K-peer cluster. At the shipped defaults (0.75 · 0.
|
|
994
|
-
*
|
|
995
|
-
* `docs/correctness.md` before shipping it.
|
|
993
|
+
* both find them in one K-peer cluster. At the shipped defaults (0.75 · 0.75 — both default to
|
|
994
|
+
* `DEFAULT_SUPER_MAJORITY_THRESHOLD` / `membershipAdmissionFraction`'s own default) that product is
|
|
995
|
+
* 1.125. If either default is ever lowered, re-check Theorem 2 in `docs/correctness.md` before shipping it.
|
|
996
996
|
*/
|
|
997
997
|
private admissionFloor(k: number): number {
|
|
998
998
|
const scaled = Math.ceil(this.membershipAdmissionFraction * k);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** A component that exposes the cluster size it actually resolved to. */
|
|
2
|
+
export interface HasEffectiveClusterSize {
|
|
3
|
+
readonly effectiveClusterSize: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fail-fast coupling check for a live node's cluster-size wiring, mirroring
|
|
8
|
+
* `assertSuperMajorityCoupling`.
|
|
9
|
+
*
|
|
10
|
+
* `resolveClusterPolicy` (`cluster/cluster-policy.ts`) is the single place that settles the
|
|
11
|
+
* replication factor / target cohort breadth (`clusterSize`). Every consumer that resolves its
|
|
12
|
+
* own cluster size — `Libp2pKeyPeerNetwork` (peer selection) and `NetworkManagerService` (ring
|
|
13
|
+
* sizing) — must be constructed from that SAME resolved value, or the membership admission
|
|
14
|
+
* gate's "is this declared peer set suspiciously small?" yardstick silently diverges from the
|
|
15
|
+
* cohort width peer selection actually assembles (ticket
|
|
16
|
+
* `bug-cluster-size-resolution-single-source`: an unconfigured node ran peer selection at 16
|
|
17
|
+
* while consensus believed full size was 10, so a cohort that had quietly lost six members still
|
|
18
|
+
* measured as full).
|
|
19
|
+
*
|
|
20
|
+
* On a live node both are constructed from `resolveClusterPolicy(options).clusterSize`, so this
|
|
21
|
+
* check normally passes. It exists to catch *future* drift — a call site reverting to its own
|
|
22
|
+
* default, a new consumer added without threading the resolved value — by throwing at
|
|
23
|
+
* construction with every resolved value and its source, rather than letting the node come up
|
|
24
|
+
* mismatched.
|
|
25
|
+
*
|
|
26
|
+
* @throws Error naming the resolved size and every consumer that disagrees with it.
|
|
27
|
+
*/
|
|
28
|
+
export function assertClusterSizeCoupling(
|
|
29
|
+
resolvedClusterSize: number,
|
|
30
|
+
consumers: Record<string, HasEffectiveClusterSize | undefined>
|
|
31
|
+
): void {
|
|
32
|
+
const mismatches = Object.entries(consumers)
|
|
33
|
+
.filter((entry): entry is [string, HasEffectiveClusterSize] => entry[1] !== undefined)
|
|
34
|
+
.filter(([, consumer]) => consumer.effectiveClusterSize !== resolvedClusterSize)
|
|
35
|
+
.map(([name, consumer]) => `${name} resolved ${consumer.effectiveClusterSize}`);
|
|
36
|
+
|
|
37
|
+
if (mismatches.length > 0) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Cluster size mismatch at node startup: resolveClusterPolicy() produced clusterSize=${resolvedClusterSize}, ` +
|
|
40
|
+
`but ${mismatches.join(', ')}. Every cluster-size consumer must be constructed from the SAME resolved ` +
|
|
41
|
+
`value (see cluster/cluster-policy.ts), or the membership admission gate's yardstick can diverge from ` +
|
|
42
|
+
`the cohort width peer selection actually assembles.`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -38,7 +38,7 @@ export interface QuorumRev {
|
|
|
38
38
|
* seconded by a second, independent peer. See {@link quorumSize} for the cap that
|
|
39
39
|
* applies when the cohort is smaller than this.
|
|
40
40
|
*/
|
|
41
|
-
const CORROBORATION_FLOOR = 2;
|
|
41
|
+
export const CORROBORATION_FLOOR = 2;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Number of distinct corroborating votes required to accept a claim:
|
|
@@ -4,7 +4,7 @@ import type { ReconcileBlockCallback } from "./cluster-repo.js";
|
|
|
4
4
|
import type { IPeerReputation } from "../reputation/types.js";
|
|
5
5
|
import { PenaltyReason } from "../reputation/types.js";
|
|
6
6
|
import {
|
|
7
|
-
selectQuorumRev, selectQuorumBlock, canonicalBlockHash, corroboratorCapacity,
|
|
7
|
+
selectQuorumRev, selectQuorumBlock, canonicalBlockHash, corroboratorCapacity, quorumSize,
|
|
8
8
|
type RevClaim, type BlockHashCandidate, type QuorumRev
|
|
9
9
|
} from "./quorum-restore.js";
|
|
10
10
|
import { createLogger } from '../logger.js';
|
|
@@ -172,14 +172,26 @@ export function createReconcileBlock(deps: ReconcileBlockDeps): ReconcileBlockCa
|
|
|
172
172
|
const selected = selectQuorumRev(revClaims, deps.simpleMajorityThreshold, capacity);
|
|
173
173
|
if (!selected) {
|
|
174
174
|
// Leave the block behind; churn/rebalance and the next commit retry.
|
|
175
|
-
log('reconcile:no-rev-quorum', {
|
|
175
|
+
log('reconcile:no-rev-quorum', {
|
|
176
|
+
blockId,
|
|
177
|
+
rev: committed.rev,
|
|
178
|
+
responders: revClaims.length,
|
|
179
|
+
required: quorumSize(revClaims.length, deps.simpleMajorityThreshold, capacity),
|
|
180
|
+
repairCorroborationClusterSize: deps.repairCorroborationClusterSize
|
|
181
|
+
});
|
|
176
182
|
return;
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
const hashCandidates = await hashCarriers(candidates, selected);
|
|
180
186
|
const agreed = selectQuorumBlock(hashCandidates, deps.simpleMajorityThreshold, capacity);
|
|
181
187
|
if (!agreed) {
|
|
182
|
-
log('reconcile:no-content-quorum', {
|
|
188
|
+
log('reconcile:no-content-quorum', {
|
|
189
|
+
blockId,
|
|
190
|
+
rev: selected.rev,
|
|
191
|
+
carriers: hashCandidates.length,
|
|
192
|
+
required: quorumSize(hashCandidates.length, deps.simpleMajorityThreshold, capacity),
|
|
193
|
+
repairCorroborationClusterSize: deps.repairCorroborationClusterSize
|
|
194
|
+
});
|
|
183
195
|
return;
|
|
184
196
|
}
|
|
185
197
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./cluster/client.js";
|
|
2
2
|
export * from "./cluster/cluster-repo.js";
|
|
3
|
+
export * from "./cluster/cluster-policy.js";
|
|
3
4
|
export * from "./cluster/commit-cert.js";
|
|
4
5
|
export * from "./cluster/service.js";
|
|
5
6
|
export * from "./cluster/rebalance-monitor.js";
|
|
@@ -31,6 +32,7 @@ export * from "./sync/service.js";
|
|
|
31
32
|
export * from "./it-utility.js";
|
|
32
33
|
export * from "./libp2p-key-network.js";
|
|
33
34
|
export * from "./libp2p-node.js";
|
|
35
|
+
export * from "./optimystic-node.js";
|
|
34
36
|
export * from "./routing/responsibility.js";
|
|
35
37
|
export * from "./routing/libp2p-known-peers.js";
|
|
36
38
|
export * from "./network/network-manager-service.js";
|
|
@@ -149,9 +149,19 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
149
149
|
private readonly networkMode: NetworkMode;
|
|
150
150
|
private readonly persistence?: NetworkStatePersistence;
|
|
151
151
|
|
|
152
|
+
// NOTE: seven positional parameters, and the list stays that way for now — converting to an
|
|
153
|
+
// options bag would touch ~50 construction sites in `test/libp2p-key-network.spec.ts` alone.
|
|
154
|
+
// Revisit if an eighth parameter is ever needed, or if that spec is being rewritten anyway.
|
|
152
155
|
constructor(
|
|
153
156
|
private readonly libp2p: Libp2p,
|
|
154
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Replication factor / target cohort breadth for peer selection. REQUIRED, deliberately:
|
|
159
|
+
* a silent default here meant a caller that did not know the node's cluster size quietly
|
|
160
|
+
* selected a different-width cohort than the node's own consensus path used for the same
|
|
161
|
+
* key. Reuse the node's own instance (`node.keyNetwork`) where one exists; a caller that
|
|
162
|
+
* genuinely must construct standalone passes `DEFAULT_CLUSTER_SIZE` (`cluster/cluster-policy.ts`).
|
|
163
|
+
*/
|
|
164
|
+
private readonly clusterSize: number,
|
|
155
165
|
selfCoordinationConfig?: SelfCoordinationConfig,
|
|
156
166
|
networkMode?: NetworkMode,
|
|
157
167
|
persistence?: NetworkStatePersistence,
|
|
@@ -161,17 +171,25 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
161
171
|
* provided, coordinator/cohort selection is scoped to peers that serve THIS
|
|
162
172
|
* network's `cluster`/`repo` protocol, so a peer that only belongs to another
|
|
163
173
|
* network sharing the same physical nodes/bootstraps is never chosen. When
|
|
164
|
-
* ABSENT, the membership filter is disabled
|
|
165
|
-
*
|
|
174
|
+
* ABSENT, the membership filter is disabled.
|
|
175
|
+
*
|
|
176
|
+
* NOTE: optional for the same reason `clusterSize` used to be — "most call sites don't
|
|
177
|
+
* know the network name" — and that reason no longer holds: both production sites now
|
|
178
|
+
* pass it (`libp2p-node-base.ts`, and the foreign-node fallback in the Quereus
|
|
179
|
+
* collection-factory), and only the mock-based cases in `test/libp2p-key-network.spec.ts`
|
|
180
|
+
* omit it. So a caller omitting it today gets the filter silently off, exactly the shape
|
|
181
|
+
* that let a second key network be built with a wrong cohort width. Left optional because
|
|
182
|
+
* making it required would touch ~50 construction sites in that one spec and no reachable
|
|
183
|
+
* caller is affected. Make it required (or take the whole list as an options bag) the
|
|
184
|
+
* moment a THIRD production construction site appears, or when that spec is rewritten.
|
|
166
185
|
*/
|
|
167
186
|
private readonly protocolPrefix?: string
|
|
168
187
|
) {
|
|
169
|
-
// NOTE: no construction site in this repo passes a SelfCoordinationConfig —
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
// first. Low urgency: a grace-period denial no longer fails the caller, it only costs
|
|
188
|
+
// NOTE: no production construction site in this repo passes a SelfCoordinationConfig —
|
|
189
|
+
// both leave it `undefined` (libp2p-node-base.ts, and the foreign-node fallback in
|
|
190
|
+
// quereus-plugin-optimystic's collection-factory.ts), so these defaults are always what
|
|
191
|
+
// is in force and no operator can tune them. If tuning `gracePeriodMs` is ever needed,
|
|
192
|
+
// those two sites have to thread the config through first. Low urgency: a grace-period denial no longer fails the caller, it only costs
|
|
175
193
|
// a write the ~1s findCoordinator retry window before self-coordinating.
|
|
176
194
|
this.selfCoordinationConfig = {
|
|
177
195
|
gracePeriodMs: selfCoordinationConfig?.gracePeriodMs ?? 30_000,
|
|
@@ -183,6 +201,20 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
183
201
|
this.setupConnectionTracking();
|
|
184
202
|
}
|
|
185
203
|
|
|
204
|
+
/** The cluster size this instance actually resolved to, for `assertClusterSizeCoupling`. */
|
|
205
|
+
get effectiveClusterSize(): number {
|
|
206
|
+
return this.clusterSize;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The network-namespaced protocol prefix (`/optimystic/<networkName>`) selection is scoped to,
|
|
211
|
+
* or `undefined` when the network-membership filter is off. Readable so a spec can assert the
|
|
212
|
+
* node's attached instance really is network-scoped without reaching into a private field.
|
|
213
|
+
*/
|
|
214
|
+
get effectiveProtocolPrefix(): string | undefined {
|
|
215
|
+
return this.protocolPrefix;
|
|
216
|
+
}
|
|
217
|
+
|
|
186
218
|
// coordinator cache: key (base64url) -> peerId until expiry (bounded LRU-ish via Map insertion order)
|
|
187
219
|
private readonly coordinatorCache = new Map<string, { id: PeerId, expires: number }>()
|
|
188
220
|
private static readonly MAX_CACHE_ENTRIES = 1000
|