@optimystic/db-p2p 1.0.0-beta.2 → 1.0.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/block-transfer-service.d.ts +73 -1
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +70 -3
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +16 -1
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +73 -72
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/client.d.ts +4 -6
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +8 -13
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-policy.d.ts +2 -1
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +172 -17
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +553 -149
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-proof.js +2 -2
- package/dist/src/cluster/race-resolution.d.ts +51 -7
- package/dist/src/cluster/race-resolution.d.ts.map +1 -1
- package/dist/src/cluster/race-resolution.js +71 -7
- package/dist/src/cluster/race-resolution.js.map +1 -1
- package/dist/src/cluster/rebalance-monitor.d.ts +67 -5
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +120 -26
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +34 -47
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +8 -6
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/cohort-topic/membership-source.js +1 -1
- package/dist/src/cohort-topic/membership-source.js.map +1 -1
- package/dist/src/cohort-topic/peer-codec.d.ts.map +1 -1
- package/dist/src/cohort-topic/peer-codec.js +5 -2
- package/dist/src/cohort-topic/peer-codec.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +24 -3
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +36 -3
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/cohort-topic/topic-router.d.ts +9 -0
- package/dist/src/cohort-topic/topic-router.d.ts.map +1 -1
- package/dist/src/cohort-topic/topic-router.js +12 -2
- package/dist/src/cohort-topic/topic-router.js.map +1 -1
- package/dist/src/dispute/client.d.ts.map +1 -1
- package/dist/src/dispute/client.js +15 -3
- package/dist/src/dispute/client.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +78 -17
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +241 -147
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +35 -1
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +151 -26
- 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/logger.d.ts.map +1 -1
- package/dist/src/logger.js +19 -7
- package/dist/src/logger.js.map +1 -1
- package/dist/src/matchmaking/query-transport.d.ts +3 -4
- package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
- package/dist/src/matchmaking/query-transport.js +9 -9
- package/dist/src/matchmaking/query-transport.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +10 -5
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +11 -2
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/network/relay-reservation.d.ts +170 -0
- package/dist/src/network/relay-reservation.d.ts.map +1 -0
- package/dist/src/network/relay-reservation.js +527 -0
- package/dist/src/network/relay-reservation.js.map +1 -0
- package/dist/src/pend-validation.d.ts +3 -2
- package/dist/src/pend-validation.d.ts.map +1 -1
- package/dist/src/pend-validation.js +3 -2
- package/dist/src/pend-validation.js.map +1 -1
- package/dist/src/protocol-client.d.ts +8 -1
- package/dist/src/protocol-client.d.ts.map +1 -1
- package/dist/src/protocol-client.js +11 -0
- package/dist/src/protocol-client.js.map +1 -1
- package/dist/src/reactivity/forwarder-host.d.ts +2 -1
- package/dist/src/reactivity/forwarder-host.d.ts.map +1 -1
- package/dist/src/reactivity/forwarder-host.js +2 -1
- package/dist/src/reactivity/forwarder-host.js.map +1 -1
- package/dist/src/reactivity/origination-manager.d.ts +3 -3
- package/dist/src/reactivity/origination-manager.js +3 -3
- package/dist/src/reactivity/recover-transport.d.ts +22 -14
- package/dist/src/reactivity/recover-transport.d.ts.map +1 -1
- package/dist/src/reactivity/recover-transport.js +32 -19
- package/dist/src/reactivity/recover-transport.js.map +1 -1
- package/dist/src/reactivity/subscription-manager.d.ts +2 -2
- package/dist/src/reactivity/topic-bytes.d.ts +6 -7
- package/dist/src/reactivity/topic-bytes.d.ts.map +1 -1
- package/dist/src/reactivity/topic-bytes.js +8 -9
- package/dist/src/reactivity/topic-bytes.js.map +1 -1
- package/dist/src/repo/client.d.ts +5 -2
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +49 -40
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +119 -5
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +181 -29
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +259 -76
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +716 -328
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/i-under-replication-ledger.d.ts +87 -0
- package/dist/src/repo/i-under-replication-ledger.d.ts.map +1 -0
- package/dist/src/repo/i-under-replication-ledger.js +2 -0
- package/dist/src/repo/i-under-replication-ledger.js.map +1 -0
- package/dist/src/repo/kv-under-replication-ledger.d.ts +91 -0
- package/dist/src/repo/kv-under-replication-ledger.d.ts.map +1 -0
- package/dist/src/repo/kv-under-replication-ledger.js +287 -0
- package/dist/src/repo/kv-under-replication-ledger.js.map +1 -0
- package/dist/src/repo/responsibility.d.ts +37 -0
- package/dist/src/repo/responsibility.d.ts.map +1 -0
- package/dist/src/repo/responsibility.js +37 -0
- package/dist/src/repo/responsibility.js.map +1 -0
- package/dist/src/repo/service.d.ts +44 -15
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +64 -27
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/repo/stuck-reservation.d.ts +127 -0
- package/dist/src/repo/stuck-reservation.d.ts.map +1 -0
- package/dist/src/repo/stuck-reservation.js +179 -0
- package/dist/src/repo/stuck-reservation.js.map +1 -0
- package/dist/src/repo/under-replication-drain.d.ts +208 -0
- package/dist/src/repo/under-replication-drain.d.ts.map +1 -0
- package/dist/src/repo/under-replication-drain.js +461 -0
- package/dist/src/repo/under-replication-drain.js.map +1 -0
- package/dist/src/rn.d.ts +5 -0
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +5 -0
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-latch.d.ts +3 -2
- package/dist/src/storage/block-latch.d.ts.map +1 -1
- package/dist/src/storage/block-latch.js +7 -4
- package/dist/src/storage/block-latch.js.map +1 -1
- package/dist/src/storage/block-storage.d.ts +45 -3
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +156 -9
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +57 -5
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-block-storage.js.map +1 -1
- package/dist/src/storage/pending-claim.d.ts +167 -0
- package/dist/src/storage/pending-claim.d.ts.map +1 -0
- package/dist/src/storage/pending-claim.js +156 -0
- package/dist/src/storage/pending-claim.js.map +1 -0
- package/dist/src/storage/raw-store-codec.d.ts +13 -0
- package/dist/src/storage/raw-store-codec.d.ts.map +1 -1
- package/dist/src/storage/raw-store-codec.js +24 -3
- package/dist/src/storage/raw-store-codec.js.map +1 -1
- package/dist/src/storage/restoration-coordinator.d.ts.map +1 -1
- package/dist/src/storage/restoration-coordinator.js +2 -1
- package/dist/src/storage/restoration-coordinator.js.map +1 -1
- package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -1
- package/dist/src/storage/ring-shift-coordinator.js +3 -3
- package/dist/src/storage/ring-shift-coordinator.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +137 -5
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +273 -68
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/storage/struct.d.ts +58 -0
- package/dist/src/storage/struct.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts +92 -11
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +246 -103
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +27 -3
- package/src/cluster/block-transfer-service.ts +132 -4
- package/src/cluster/block-transfer.ts +79 -84
- package/src/cluster/client.ts +115 -119
- package/src/cluster/cluster-policy.ts +2 -1
- package/src/cluster/cluster-repo.ts +3115 -2671
- package/src/cluster/commit-proof.ts +2 -2
- package/src/cluster/race-resolution.ts +76 -7
- package/src/cluster/rebalance-monitor.ts +677 -539
- package/src/cluster/spread-on-churn.ts +34 -54
- package/src/cohort-topic/host.ts +8 -6
- package/src/cohort-topic/membership-source.ts +1 -1
- package/src/cohort-topic/peer-codec.ts +34 -31
- package/src/cohort-topic/stream-util.ts +39 -4
- package/src/cohort-topic/topic-router.ts +12 -2
- package/src/dispute/client.ts +17 -7
- package/src/index.ts +64 -59
- package/src/libp2p-key-network.ts +1377 -1253
- package/src/libp2p-node-base.ts +186 -27
- package/src/libp2p-node-rn.ts +31 -30
- package/src/libp2p-node.ts +37 -36
- package/src/logger.ts +20 -7
- package/src/matchmaking/query-transport.ts +9 -9
- package/src/network/network-manager-service.ts +16 -5
- package/src/network/relay-reservation.ts +636 -0
- package/src/pend-validation.ts +3 -2
- package/src/protocol-client.ts +13 -1
- package/src/reactivity/forwarder-host.ts +2 -1
- package/src/reactivity/origination-manager.ts +3 -3
- package/src/reactivity/recover-transport.ts +39 -22
- package/src/reactivity/subscription-manager.ts +2 -2
- package/src/reactivity/topic-bytes.ts +35 -37
- package/src/repo/client.ts +172 -173
- package/src/repo/cluster-coordinator.ts +1296 -1113
- package/src/repo/coordinator-repo.ts +3087 -2687
- package/src/repo/i-under-replication-ledger.ts +89 -0
- package/src/repo/kv-under-replication-ledger.ts +302 -0
- package/src/repo/responsibility.ts +48 -0
- package/src/repo/service.ts +76 -39
- package/src/repo/stuck-reservation.ts +224 -0
- package/src/repo/under-replication-drain.ts +582 -0
- package/src/rn.ts +74 -69
- package/src/storage/block-latch.ts +7 -4
- package/src/storage/block-storage.ts +161 -10
- package/src/storage/i-block-storage.ts +60 -5
- package/src/storage/pending-claim.ts +200 -0
- package/src/storage/raw-store-codec.ts +26 -3
- package/src/storage/restoration-coordinator.ts +212 -211
- package/src/storage/ring-shift-coordinator.ts +271 -271
- package/src/storage/storage-repo.ts +311 -73
- package/src/storage/struct.ts +58 -0
- package/src/testing/mesh-harness.ts +329 -115
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { createLogger } from "../logger.js";
|
|
2
|
+
const log = createLogger('under-replication-ledger');
|
|
3
|
+
/**
|
|
4
|
+
* Key namespace: `under-replicated/<blockId>` → JSON(UnderReplicatedEntry). A fixed literal first
|
|
5
|
+
* segment, which is what keeps this store safe to share a base path with `FileRawStorage` — see
|
|
6
|
+
* the NOTE on `FileKVStore.keyToPath` for the condition sharing would break under.
|
|
7
|
+
*/
|
|
8
|
+
export const UNDER_REPLICATED_KEY_PREFIX = 'under-replicated/';
|
|
9
|
+
/**
|
|
10
|
+
* The backstop cap. Generous on purpose: the drain's give-up counter is what retires entries, and
|
|
11
|
+
* this only stops a node whose partner never returns from growing the ledger without limit.
|
|
12
|
+
* NOTE: every entry is one small file under `FileKVStore` and one block id held in memory by the
|
|
13
|
+
* eviction index; if a deployment ever sits at this cap, measure both before raising it.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_UNDER_REPLICATION_MAX_ENTRIES = 100_000;
|
|
16
|
+
const SHORTFALL_QUORUMS = ['majority', 'local', 'unrouted'];
|
|
17
|
+
/**
|
|
18
|
+
* {@link IUnderReplicationLedger} over the portable {@link IKVStore}, the same shape
|
|
19
|
+
* `PersistentTransactionStateStore` uses for its node-local state.
|
|
20
|
+
*
|
|
21
|
+
* Two pieces of in-memory state sit beside the store, neither authoritative:
|
|
22
|
+
* - a per-block operation queue, so each read-compare-write is atomic against the others for the
|
|
23
|
+
* same block (`IKVStore` has no compare-and-set);
|
|
24
|
+
* - an eviction index of recorded block ids in oldest-first order, loaded lazily from the store on
|
|
25
|
+
* the first mutation, so enforcing `maxEntries` costs neither a directory listing nor a read per
|
|
26
|
+
* record. It is only as good as this instance's ownership of the key prefix — the reason the
|
|
27
|
+
* interface scopes its guarantees to one instance per store.
|
|
28
|
+
*/
|
|
29
|
+
export class KvUnderReplicationLedger {
|
|
30
|
+
kv;
|
|
31
|
+
maxEntries;
|
|
32
|
+
queues = new Map();
|
|
33
|
+
indexLoad;
|
|
34
|
+
constructor(kv, options) {
|
|
35
|
+
this.kv = kv;
|
|
36
|
+
const maxEntries = options?.maxEntries ?? DEFAULT_UNDER_REPLICATION_MAX_ENTRIES;
|
|
37
|
+
if (!Number.isInteger(maxEntries) || maxEntries < 1) {
|
|
38
|
+
throw new Error(`KvUnderReplicationLedger: maxEntries must be a positive integer, got ${maxEntries}`);
|
|
39
|
+
}
|
|
40
|
+
this.maxEntries = maxEntries;
|
|
41
|
+
}
|
|
42
|
+
async record(entry) {
|
|
43
|
+
await this.serialized(entry.blockId, async () => {
|
|
44
|
+
const index = await this.index();
|
|
45
|
+
const existing = await this.read(entry.blockId);
|
|
46
|
+
if (existing !== undefined && existing.rev > entry.rev) {
|
|
47
|
+
log('record:skip-lower-rev %o', { blockId: entry.blockId, rev: entry.rev, recordedRev: existing.rev });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const sameShortfall = existing !== undefined && existing.rev === entry.rev;
|
|
51
|
+
const stored = sameShortfall
|
|
52
|
+
? { ...entry, recordedAt: existing.recordedAt, attempts: existing.attempts }
|
|
53
|
+
: entry;
|
|
54
|
+
await this.kv.set(keyFor(entry.blockId), JSON.stringify(stored));
|
|
55
|
+
if (!sameShortfall) {
|
|
56
|
+
// A new shortfall is the newest one: move it to the young end of the eviction order.
|
|
57
|
+
index.delete(entry.blockId);
|
|
58
|
+
index.add(entry.blockId);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
await this.evictBeyondCap();
|
|
62
|
+
}
|
|
63
|
+
async settle(blockId, rev) {
|
|
64
|
+
await this.serialized(blockId, async () => {
|
|
65
|
+
// Every fully replicated commit settles each of its blocks, and almost none has an entry:
|
|
66
|
+
// answer those from the index instead of a store read. An entry missing from the index is
|
|
67
|
+
// one `read` would report absent anyway (malformed), so nothing is skipped that could be settled.
|
|
68
|
+
const index = await this.index();
|
|
69
|
+
if (!index.has(blockId))
|
|
70
|
+
return;
|
|
71
|
+
const existing = await this.read(blockId);
|
|
72
|
+
if (existing === undefined)
|
|
73
|
+
return;
|
|
74
|
+
if (existing.rev > rev) {
|
|
75
|
+
log('settle:keep-higher-rev %o', { blockId, rev, recordedRev: existing.rev });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await this.remove(blockId);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async get(blockId) {
|
|
82
|
+
return this.read(blockId);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A full scan. The FIRST scan after start also becomes the eviction index's load, so the drain's
|
|
86
|
+
* start pass warms the index for free and the first commit after a restart finds it loaded
|
|
87
|
+
* instead of paying for the scan itself.
|
|
88
|
+
*/
|
|
89
|
+
async list() {
|
|
90
|
+
if (this.indexLoad === undefined) {
|
|
91
|
+
const scan = this.scan();
|
|
92
|
+
this.indexLoad = this.indexFrom(scan);
|
|
93
|
+
return scan;
|
|
94
|
+
}
|
|
95
|
+
return this.scan();
|
|
96
|
+
}
|
|
97
|
+
async size() {
|
|
98
|
+
return (await this.index()).size;
|
|
99
|
+
}
|
|
100
|
+
async scan() {
|
|
101
|
+
const keys = await this.kv.list(UNDER_REPLICATED_KEY_PREFIX);
|
|
102
|
+
const entries = [];
|
|
103
|
+
// Sequential on purpose: a ledger at its cap under `FileKVStore` is that many files, and
|
|
104
|
+
// opening them all at once would trade a slower scan for file-descriptor exhaustion.
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const entry = await this.read(key.slice(UNDER_REPLICATED_KEY_PREFIX.length));
|
|
107
|
+
if (entry !== undefined)
|
|
108
|
+
entries.push(entry);
|
|
109
|
+
}
|
|
110
|
+
return entries.sort((a, b) => a.recordedAt - b.recordedAt);
|
|
111
|
+
}
|
|
112
|
+
async satisfy(blockId, peerIds, heldRev) {
|
|
113
|
+
return this.serialized(blockId, async () => {
|
|
114
|
+
const existing = await this.read(blockId);
|
|
115
|
+
if (existing === undefined)
|
|
116
|
+
return undefined;
|
|
117
|
+
if (heldRev !== undefined && existing.rev > heldRev) {
|
|
118
|
+
log('satisfy:keep-higher-rev %o', { blockId, heldRev, recordedRev: existing.rev });
|
|
119
|
+
return existing;
|
|
120
|
+
}
|
|
121
|
+
// Unknown is not empty: see `IUnderReplicationLedger.satisfy`.
|
|
122
|
+
if (existing.missingPeerIds.length === 0)
|
|
123
|
+
return existing;
|
|
124
|
+
const confirmed = new Set(peerIds);
|
|
125
|
+
const remaining = existing.missingPeerIds.filter(peerId => !confirmed.has(peerId));
|
|
126
|
+
if (remaining.length === 0) {
|
|
127
|
+
await this.remove(blockId);
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
if (remaining.length === existing.missingPeerIds.length)
|
|
131
|
+
return existing;
|
|
132
|
+
const updated = { ...existing, missingPeerIds: remaining };
|
|
133
|
+
await this.kv.set(keyFor(blockId), JSON.stringify(updated));
|
|
134
|
+
return updated;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async name(blockId, rev, missingPeerIds) {
|
|
138
|
+
return this.serialized(blockId, async () => {
|
|
139
|
+
const existing = await this.read(blockId);
|
|
140
|
+
if (existing === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (existing.rev !== rev) {
|
|
143
|
+
log('name:keep-other-rev %o', { blockId, rev, recordedRev: existing.rev });
|
|
144
|
+
return existing;
|
|
145
|
+
}
|
|
146
|
+
if (existing.missingPeerIds.length > 0)
|
|
147
|
+
return existing;
|
|
148
|
+
const updated = { ...existing, missingPeerIds: [...missingPeerIds] };
|
|
149
|
+
await this.kv.set(keyFor(blockId), JSON.stringify(updated));
|
|
150
|
+
return updated;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async noteAttempt(blockId) {
|
|
154
|
+
await this.serialized(blockId, async () => {
|
|
155
|
+
const existing = await this.read(blockId);
|
|
156
|
+
if (existing === undefined)
|
|
157
|
+
return;
|
|
158
|
+
const updated = { ...existing, attempts: existing.attempts + 1 };
|
|
159
|
+
await this.kv.set(keyFor(blockId), JSON.stringify(updated));
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async delete(blockId) {
|
|
163
|
+
await this.serialized(blockId, () => this.remove(blockId));
|
|
164
|
+
}
|
|
165
|
+
/** Delete from the store and the eviction index. Callers hold the block's queue. */
|
|
166
|
+
async remove(blockId) {
|
|
167
|
+
const index = await this.index();
|
|
168
|
+
await this.kv.delete(keyFor(blockId));
|
|
169
|
+
index.delete(blockId);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Evict oldest-recorded entries until the ledger is back within `maxEntries`. Runs AFTER the
|
|
173
|
+
* recording block's queue is released and takes each victim's own queue, so no call ever holds
|
|
174
|
+
* two blocks' queues at once and two recordings evicting each other cannot deadlock. A victim a
|
|
175
|
+
* concurrent operation already removed is skipped, and the loop re-measures, so concurrent
|
|
176
|
+
* recordings at the cap never evict more than the overflow.
|
|
177
|
+
*
|
|
178
|
+
* An eviction failure is logged and ends the pass rather than failing the recording that
|
|
179
|
+
* triggered it: that entry is already written, and the next recording tries again.
|
|
180
|
+
*/
|
|
181
|
+
async evictBeyondCap() {
|
|
182
|
+
const index = await this.index();
|
|
183
|
+
while (index.size > this.maxEntries) {
|
|
184
|
+
const victim = index.values().next().value;
|
|
185
|
+
if (victim === undefined)
|
|
186
|
+
return;
|
|
187
|
+
try {
|
|
188
|
+
await this.serialized(victim, async () => {
|
|
189
|
+
if (!index.has(victim))
|
|
190
|
+
return;
|
|
191
|
+
await this.remove(victim);
|
|
192
|
+
log('evict:over-cap %o', { blockId: victim, maxEntries: this.maxEntries });
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
log.error('evict:failed %o', { blockId: victim, error: err.message });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The eviction index, loaded once from the store: every entry's block id, oldest `recordedAt`
|
|
203
|
+
* first. A failed load is not cached, so the next mutation retries it instead of the ledger
|
|
204
|
+
* failing every write for the life of the process.
|
|
205
|
+
*
|
|
206
|
+
* NOTE: the load is one read per stored entry. It runs on the first `list`, `size` or mutation
|
|
207
|
+
* after start. With the drain wired that is its start pass on every node — `list` when a pushable
|
|
208
|
+
* peer is connected, otherwise the `size` that decides whether to arm its re-check — so the load
|
|
209
|
+
* runs at boot, off the commit path; without the drain it is the first commit. A solo node keeps
|
|
210
|
+
* an entry per block it writes, so a commit that arrives before the load finishes still waits on
|
|
211
|
+
* it and can stall on slow storage — see
|
|
212
|
+
* `backlog/debt-solo-node-ledger-is-reread-whole-on-first-commit-after-restart`.
|
|
213
|
+
*/
|
|
214
|
+
index() {
|
|
215
|
+
if (this.indexLoad === undefined) {
|
|
216
|
+
this.indexLoad = this.indexFrom(this.scan());
|
|
217
|
+
}
|
|
218
|
+
return this.indexLoad;
|
|
219
|
+
}
|
|
220
|
+
indexFrom(scan) {
|
|
221
|
+
return scan
|
|
222
|
+
.then(entries => new Set(entries.map(entry => entry.blockId)))
|
|
223
|
+
.catch((err) => {
|
|
224
|
+
this.indexLoad = undefined;
|
|
225
|
+
throw err;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async read(blockId) {
|
|
229
|
+
const raw = await this.kv.get(keyFor(blockId));
|
|
230
|
+
return raw === undefined ? undefined : parseEntry(blockId, raw);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Run `operation` after every earlier operation queued for `blockId`, whatever their outcome.
|
|
234
|
+
* The queue entry is dropped once nothing is waiting behind it, so the map holds only blocks with
|
|
235
|
+
* work in flight.
|
|
236
|
+
*/
|
|
237
|
+
async serialized(blockId, operation) {
|
|
238
|
+
const previous = this.queues.get(blockId) ?? Promise.resolve();
|
|
239
|
+
const run = previous.then(operation);
|
|
240
|
+
const tail = run.then(() => undefined, () => undefined);
|
|
241
|
+
this.queues.set(blockId, tail);
|
|
242
|
+
try {
|
|
243
|
+
return await run;
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
if (this.queues.get(blockId) === tail)
|
|
247
|
+
this.queues.delete(blockId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function keyFor(blockId) {
|
|
252
|
+
return `${UNDER_REPLICATED_KEY_PREFIX}${blockId}`;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* An entry as stored, or `undefined` for one that is not a well-formed entry for `blockId`. Stored
|
|
256
|
+
* bytes are this class's own output, but they outlive the process that wrote them, so they are
|
|
257
|
+
* checked rather than cast. A malformed entry is logged and read as absent — never deleted by a
|
|
258
|
+
* read — and the next recording for the block overwrites it.
|
|
259
|
+
*/
|
|
260
|
+
function parseEntry(blockId, raw) {
|
|
261
|
+
let value;
|
|
262
|
+
try {
|
|
263
|
+
value = JSON.parse(raw);
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
log.error('read:unparseable %o', { blockId, error: err.message });
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
if (!isEntryFor(blockId, value)) {
|
|
270
|
+
log.error('read:malformed %o', { blockId, raw });
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
function isEntryFor(blockId, value) {
|
|
276
|
+
if (typeof value !== 'object' || value === null)
|
|
277
|
+
return false;
|
|
278
|
+
const entry = value;
|
|
279
|
+
return entry.blockId === blockId
|
|
280
|
+
&& Number.isInteger(entry.rev)
|
|
281
|
+
&& typeof entry.actionId === 'string'
|
|
282
|
+
&& SHORTFALL_QUORUMS.includes(entry.quorum)
|
|
283
|
+
&& Array.isArray(entry.missingPeerIds) && entry.missingPeerIds.every(peerId => typeof peerId === 'string')
|
|
284
|
+
&& typeof entry.recordedAt === 'number'
|
|
285
|
+
&& Number.isInteger(entry.attempts);
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=kv-under-replication-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-under-replication-ledger.js","sourceRoot":"","sources":["../../../src/repo/kv-under-replication-ledger.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,GAAG,GAAG,YAAY,CAAC,0BAA0B,CAAC,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,OAAO,CAAC;AAQ7D,MAAM,iBAAiB,GAA+B,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AAExF;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,wBAAwB;IAKP;IAJZ,UAAU,CAAS;IACnB,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IACpD,SAAS,CAAoC;IAErD,YAA6B,EAAY,EAAE,OAAyC;QAAvD,OAAE,GAAF,EAAE,CAAU;QACxC,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,qCAAqC,CAAC;QAChF,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,wEAAwE,UAAU,EAAE,CAAC,CAAC;QACvG,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAA2B;QACvC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAChD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;gBACxD,GAAG,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;gBACvG,OAAO;YACR,CAAC;YACD,MAAM,aAAa,GAAG,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC;YAC3E,MAAM,MAAM,GAAyB,aAAa;gBACjD,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE;gBAC5E,CAAC,CAAC,KAAK,CAAC;YACT,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,aAAa,EAAE,CAAC;gBACpB,qFAAqF;gBACrF,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC5B,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAgB,EAAE,GAAW;QACzC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACzC,0FAA0F;YAC1F,0FAA0F;YAC1F,kGAAkG;YAClG,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO;YAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO;YACnC,IAAI,QAAQ,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;gBACxB,GAAG,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC9E,OAAO;YACR,CAAC;YACD,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAgB;QACzB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI;QACT,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC;QACb,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,IAAI;QACT,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,IAAI;QACjB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC7D,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,yFAAyF;QACzF,qFAAqF;QACrF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,MAAM,CAAY,CAAC,CAAC;YACxF,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAgB,EAAE,OAA0B,EAAE,OAAgB;QAC3E,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAC7C,IAAI,OAAO,KAAK,SAAS,IAAI,QAAQ,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC;gBACrD,GAAG,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;gBACnF,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,+DAA+D;YAC/D,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC;YAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;YACnC,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;YACnF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC3B,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,IAAI,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,cAAc,CAAC,MAAM;gBAAE,OAAO,QAAQ,CAAC;YACzE,MAAM,OAAO,GAAyB,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;YACjF,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5D,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAgB,EAAE,GAAW,EAAE,cAAiC;QAC1E,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAC7C,IAAI,QAAQ,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBAC1B,GAAG,CAAC,wBAAwB,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC3E,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,QAAQ,CAAC;YACxD,MAAM,OAAO,GAAyB,EAAE,GAAG,QAAQ,EAAE,cAAc,EAAE,CAAC,GAAG,cAAc,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5D,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAgB;QACjC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACzC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO;YACnC,MAAM,OAAO,GAAyB,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAgB;QAC5B,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,oFAAoF;IAC5E,KAAK,CAAC,MAAM,CAAC,OAAgB;QACpC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,cAAc;QAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC3C,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO;YACjC,IAAI,CAAC;gBACJ,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;oBACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;wBAAE,OAAO;oBAC/B,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC1B,GAAG,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;gBAC5E,CAAC,CAAC,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;gBACjF,OAAO;YACR,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,KAAK;QACZ,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACvB,CAAC;IAEO,SAAS,CAAC,IAAqC;QACtD,OAAO,IAAI;aACT,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC7D,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAC3B,MAAM,GAAG,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,OAAgB;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/C,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,UAAU,CAAI,OAAgB,EAAE,SAA2B;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC;YACJ,OAAO,MAAM,GAAG,CAAC;QAClB,CAAC;gBAAS,CAAC;YACV,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpE,CAAC;IACF,CAAC;CACD;AAED,SAAS,MAAM,CAAC,OAAgB;IAC/B,OAAO,GAAG,2BAA2B,GAAG,OAAO,EAAE,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,OAAgB,EAAE,GAAW;IAChD,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7E,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACjD,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB,EAAE,KAAc;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,KAAgC,CAAC;IAC/C,OAAO,KAAK,CAAC,OAAO,KAAK,OAAO;WAC5B,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;WAC3B,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;WAClC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAyB,CAAC;WAC3D,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC;WACvG,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;WACpC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { BlockId } from "@optimystic/db-core";
|
|
2
|
+
/**
|
|
3
|
+
* How long one node's answer to "is this node in the block's cohort?" is reused before it is looked up
|
|
4
|
+
* again. Shared by the two places that ask it — `CoordinatorRepo`'s write-path responsibility check and
|
|
5
|
+
* `RepoService`'s redirect check — as a VALUE, never as a cache: they sit on either side of the served-repo
|
|
6
|
+
* boundary and each keeps its own memo. With one TTL, the two can disagree about a block only inside the
|
|
7
|
+
* same minute after its cohort changed, and in that window the coordinator's refusal is the backstop.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RESPONSIBILITY_TTL_MS = 60000;
|
|
10
|
+
/**
|
|
11
|
+
* Why a node refused a request on responsibility grounds.
|
|
12
|
+
* - `not-responsible`: the cohort lookup answered, and this node is not in the cohort for these blocks.
|
|
13
|
+
* The request was sent to the wrong machine; the writer re-picks inside the cohort.
|
|
14
|
+
* - `undetermined`: the cohort lookup THREW, so this node cannot tell whether it is responsible. A routing
|
|
15
|
+
* fault on this node, not a misrouted request — and not a licence to accept: accepting there is what
|
|
16
|
+
* let a write commit on a phone that consulted nobody (GitHub #19).
|
|
17
|
+
*/
|
|
18
|
+
export type ResponsibilityRefusalKind = 'not-responsible' | 'undetermined';
|
|
19
|
+
/**
|
|
20
|
+
* A write refused because this node is not, or cannot tell whether it is, responsible for the blocks it
|
|
21
|
+
* names. Typed so a caller matches on {@link kind} and {@link blockIds} rather than on message text.
|
|
22
|
+
*
|
|
23
|
+
* Local only: over the repo protocol a thrown error aborts the stream, so a remote writer sees a failed
|
|
24
|
+
* batch, not this class. Either way the writer's transactor excludes the peer and re-picks, which is the
|
|
25
|
+
* whole remedy for both kinds.
|
|
26
|
+
*/
|
|
27
|
+
export declare class ResponsibilityRefusalError extends Error {
|
|
28
|
+
readonly kind: ResponsibilityRefusalKind;
|
|
29
|
+
/** Every block the refusal is about — all of them, so one log line names the whole misroute. */
|
|
30
|
+
readonly blockIds: readonly BlockId[];
|
|
31
|
+
constructor(kind: ResponsibilityRefusalKind,
|
|
32
|
+
/** Every block the refusal is about — all of them, so one log line names the whole misroute. */
|
|
33
|
+
blockIds: readonly BlockId[],
|
|
34
|
+
/** Extra context for the message; never parsed. */
|
|
35
|
+
detail?: string);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=responsibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responsibility.d.ts","sourceRoot":"","sources":["../../../src/repo/responsibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAE3E;;;;;;;GAOG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IAEnD,QAAQ,CAAC,IAAI,EAAE,yBAAyB;IACxC,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE;gBAF5B,IAAI,EAAE,yBAAyB;IACxC,gGAAgG;IACvF,QAAQ,EAAE,SAAS,OAAO,EAAE;IACrC,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM;CAKhB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long one node's answer to "is this node in the block's cohort?" is reused before it is looked up
|
|
3
|
+
* again. Shared by the two places that ask it — `CoordinatorRepo`'s write-path responsibility check and
|
|
4
|
+
* `RepoService`'s redirect check — as a VALUE, never as a cache: they sit on either side of the served-repo
|
|
5
|
+
* boundary and each keeps its own memo. With one TTL, the two can disagree about a block only inside the
|
|
6
|
+
* same minute after its cohort changed, and in that window the coordinator's refusal is the backstop.
|
|
7
|
+
*/
|
|
8
|
+
export const RESPONSIBILITY_TTL_MS = 60_000;
|
|
9
|
+
/**
|
|
10
|
+
* A write refused because this node is not, or cannot tell whether it is, responsible for the blocks it
|
|
11
|
+
* names. Typed so a caller matches on {@link kind} and {@link blockIds} rather than on message text.
|
|
12
|
+
*
|
|
13
|
+
* Local only: over the repo protocol a thrown error aborts the stream, so a remote writer sees a failed
|
|
14
|
+
* batch, not this class. Either way the writer's transactor excludes the peer and re-picks, which is the
|
|
15
|
+
* whole remedy for both kinds.
|
|
16
|
+
*/
|
|
17
|
+
export class ResponsibilityRefusalError extends Error {
|
|
18
|
+
kind;
|
|
19
|
+
blockIds;
|
|
20
|
+
constructor(kind,
|
|
21
|
+
/** Every block the refusal is about — all of them, so one log line names the whole misroute. */
|
|
22
|
+
blockIds,
|
|
23
|
+
/** Extra context for the message; never parsed. */
|
|
24
|
+
detail) {
|
|
25
|
+
super(responsibilityRefusalMessage(kind, blockIds, detail));
|
|
26
|
+
this.kind = kind;
|
|
27
|
+
this.blockIds = blockIds;
|
|
28
|
+
this.name = 'ResponsibilityRefusalError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function responsibilityRefusalMessage(kind, blockIds, detail) {
|
|
32
|
+
const head = kind === 'not-responsible'
|
|
33
|
+
? `Not responsible for block(s): ${blockIds.join(', ')}`
|
|
34
|
+
: `Cannot determine responsibility for block(s): ${blockIds.join(', ')}`;
|
|
35
|
+
return detail === undefined ? head : `${head} (${detail})`;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=responsibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responsibility.js","sourceRoot":"","sources":["../../../src/repo/responsibility.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAY5C;;;;;;;GAOG;AACH,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAE1C;IAEA;IAHV,YACU,IAA+B;IACxC,gGAAgG;IACvF,QAA4B;IACrC,mDAAmD;IACnD,MAAe;QAEf,KAAK,CAAC,4BAA4B,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QANnD,SAAI,GAAJ,IAAI,CAA2B;QAE/B,aAAQ,GAAR,QAAQ,CAAoB;QAKrC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC1C,CAAC;CACD;AAED,SAAS,4BAA4B,CAAC,IAA+B,EAAE,QAA4B,EAAE,MAA0B;IAC9H,MAAM,IAAI,GAAG,IAAI,KAAK,iBAAiB;QACtC,CAAC,CAAC,iCAAiC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACxD,CAAC,CAAC,iDAAiD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1E,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,GAAG,CAAC;AAC5D,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Startable, StreamHandler, PeerId, Libp2p } from '@libp2p/interface';
|
|
2
|
-
import type { IRepo, RepoMessage } from '@optimystic/db-core';
|
|
2
|
+
import type { IKeyNetwork, IRepo, RepoMessage } from '@optimystic/db-core';
|
|
3
3
|
import { type RedirectPayload } from './redirect.js';
|
|
4
4
|
import { type InboundStreamAuthorizationInit } from '../inbound-authorization.js';
|
|
5
5
|
interface BaseComponents {
|
|
@@ -8,12 +8,16 @@ interface BaseComponents {
|
|
|
8
8
|
unhandle: (protocol: string) => Promise<void>;
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
11
|
+
/** The one key-network call the redirect check makes. The node's own `Libp2pKeyPeerNetwork` satisfies it. */
|
|
12
|
+
export type ClusterLookup = Pick<IKeyNetwork, 'findCluster'>;
|
|
14
13
|
export type RepoServiceComponents = BaseComponents & {
|
|
15
14
|
repo: IRepo;
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Where the redirect check asks who is responsible for a block. Absent → the `keyNetwork` attachment of
|
|
17
|
+
* the node injected by {@link RepoService.setLibp2p}, which is the production source: the same key
|
|
18
|
+
* network the writer's transactor and this node's coordinator ask, so all three apply one rule.
|
|
19
|
+
*/
|
|
20
|
+
keyNetwork?: ClusterLookup;
|
|
17
21
|
peerId?: PeerId;
|
|
18
22
|
/**
|
|
19
23
|
* Optional resolver for the addresses this node may publish for a redirect target. Async
|
|
@@ -44,12 +48,10 @@ export type RepoServiceInit = InboundStreamAuthorizationInit & {
|
|
|
44
48
|
logPrefix?: string;
|
|
45
49
|
kBucketSize?: number;
|
|
46
50
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* it will redirect requests to closer peers.
|
|
52
|
-
* Default: 1 (only the closest peer handles requests)
|
|
51
|
+
* Small-mesh bypass for the redirect check: when the block's cohort has fewer than this many peers,
|
|
52
|
+
* the request is handled locally without a membership test (and the coordinator's own responsibility
|
|
53
|
+
* check then decides). It does not size the cohort — the key network's `clusterSize` does.
|
|
54
|
+
* Default: 1 (only an empty cohort bypasses)
|
|
53
55
|
*/
|
|
54
56
|
responsibilityK?: number;
|
|
55
57
|
};
|
|
@@ -71,7 +73,7 @@ export declare class RepoService implements Startable {
|
|
|
71
73
|
* The libp2p node, injected post-construction by the node wiring (see
|
|
72
74
|
* libp2p-node-base.ts, mirroring how `networkManager`/`fret` receive theirs).
|
|
73
75
|
* The libp2p `components.libp2p` proxy does NOT reliably resolve from inside a
|
|
74
|
-
* service at request time, so the redirect path resolves the network
|
|
76
|
+
* service at request time, so the redirect path resolves the key network,
|
|
75
77
|
* self identity, and connection addrs through this explicitly-set reference.
|
|
76
78
|
*/
|
|
77
79
|
private libp2pRef;
|
|
@@ -83,6 +85,16 @@ export declare class RepoService implements Startable {
|
|
|
83
85
|
* ingress point, instead of keeping it filterable as the single `peer-address-book` namespace.
|
|
84
86
|
*/
|
|
85
87
|
private readonly addressLog;
|
|
88
|
+
/**
|
|
89
|
+
* Per block key, the responsible peer ids the key network last named: one cohort lookup per block per
|
|
90
|
+
* minute on the inbound path, instead of one per request on top of the coordinator's own.
|
|
91
|
+
*
|
|
92
|
+
* NOTE: same TTL as `CoordinatorRepo`'s responsibility cache, deliberately not the same cache — the two
|
|
93
|
+
* sit on either side of the served-repo boundary. Their verdicts can therefore disagree about a block
|
|
94
|
+
* only inside the minute after its cohort changes; a request this check wrongly lets through is refused
|
|
95
|
+
* by the coordinator, and one it wrongly redirects is followed by the client to a member that serves it.
|
|
96
|
+
*/
|
|
97
|
+
private readonly responsibleIds;
|
|
86
98
|
constructor(components: RepoServiceComponents, init?: RepoServiceInit);
|
|
87
99
|
readonly [Symbol.toStringTag] = "@libp2p/repo-service";
|
|
88
100
|
/**
|
|
@@ -100,7 +112,12 @@ export declare class RepoService implements Startable {
|
|
|
100
112
|
* Stop the service
|
|
101
113
|
*/
|
|
102
114
|
stop(): Promise<void>;
|
|
103
|
-
|
|
115
|
+
/**
|
|
116
|
+
* The key network the redirect check asks. Absent only before the node attaches it, which happens after
|
|
117
|
+
* `start()`; a request in that window is handled locally, where the served-repo proxy has no
|
|
118
|
+
* coordinator to hand it to yet either.
|
|
119
|
+
*/
|
|
120
|
+
private getKeyNetwork;
|
|
104
121
|
private getSelfId;
|
|
105
122
|
/**
|
|
106
123
|
* The addresses this node may publish for `peerId` in a redirect payload.
|
|
@@ -122,7 +139,7 @@ export declare class RepoService implements Startable {
|
|
|
122
139
|
* - pend → blockIdsForTransforms(transforms)[0]
|
|
123
140
|
* - cancel → actionRef.blockIds[0]
|
|
124
141
|
* - commit → blockIds[0] (CoordinatorRepo.commit anchors consensus on
|
|
125
|
-
*
|
|
142
|
+
* resolveCohort(blockIds[0]) / executeClusterTransaction(blockIds[0]) and guards
|
|
126
143
|
* with verifyResponsibility(blockIds) — NOT tailId; for a per-block commit batch
|
|
127
144
|
* whose blockIds[0] !== tailId, keying on tailId redirected the commit to the
|
|
128
145
|
* collection tail's cluster, which then fails verifyResponsibility for the non-tail block.)
|
|
@@ -138,9 +155,21 @@ export declare class RepoService implements Startable {
|
|
|
138
155
|
/**
|
|
139
156
|
* Check if this node should redirect the request for a given key.
|
|
140
157
|
* Returns a RedirectPayload if not responsible, null if should handle locally.
|
|
141
|
-
* Also attaches
|
|
158
|
+
* Also attaches the responsible peer ids to the message for downstream use.
|
|
159
|
+
*
|
|
160
|
+
* The responsible set comes from the node's own key network — `findCluster` on the block's routing
|
|
161
|
+
* key, the same call and the same bytes the writer's transactor and this node's `CoordinatorRepo` use —
|
|
162
|
+
* so a correctly routed request is never redirected, including on machines shared by several networks,
|
|
163
|
+
* where FRET's raw cohort would include peers that do not serve this one.
|
|
164
|
+
*
|
|
165
|
+
* A lookup that THROWS splits by operation: a `get` is handled locally (a read is best-effort, and the
|
|
166
|
+
* coordinator flags what it could not confirm); every other operation propagates the error, which
|
|
167
|
+
* aborts the stream so the writer excludes this peer and re-picks — the posture `CoordinatorRepo` takes
|
|
168
|
+
* on the same fault.
|
|
142
169
|
*/
|
|
143
170
|
checkRedirect(blockKey: string, opName: string, message: RepoMessage): Promise<RedirectPayload | null>;
|
|
171
|
+
/** The block's responsible peer ids, memoized per block key for {@link RESPONSIBILITY_TTL_MS}. A throw is never memoized. */
|
|
172
|
+
private responsiblePeerIds;
|
|
144
173
|
/**
|
|
145
174
|
* Handle incoming streams on the repo protocol
|
|
146
175
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/repo/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAsB,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AACrG,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/repo/service.ts"],"names":[],"mappings":"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"}
|