@optimystic/db-p2p 0.14.1 → 0.16.3
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 +616 -609
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +4 -3
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +52 -3
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +129 -6
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +3 -1
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +182 -11
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +429 -69
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/peer-key-binding.d.ts +25 -0
- package/dist/src/cluster/peer-key-binding.d.ts.map +1 -0
- package/dist/src/cluster/peer-key-binding.js +39 -0
- package/dist/src/cluster/peer-key-binding.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +72 -0
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -0
- package/dist/src/cluster/quorum-restore.js +0 -0
- package/dist/src/cluster/quorum-restore.js.map +1 -0
- package/dist/src/cluster/rebalance-monitor.d.ts +23 -1
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +17 -2
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +7 -1
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts +7 -0
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +4 -1
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cluster/supermajority-coupling.d.ts +22 -0
- package/dist/src/cluster/supermajority-coupling.d.ts.map +1 -0
- package/dist/src/cluster/supermajority-coupling.js +27 -0
- package/dist/src/cluster/supermajority-coupling.js.map +1 -0
- package/dist/src/cohort-topic/cohort-gossip-driver.d.ts +46 -4
- package/dist/src/cohort-topic/cohort-gossip-driver.d.ts.map +1 -1
- package/dist/src/cohort-topic/cohort-gossip-driver.js +62 -7
- package/dist/src/cohort-topic/cohort-gossip-driver.js.map +1 -1
- package/dist/src/cohort-topic/cohort-gossip-transport.d.ts +7 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.d.ts.map +1 -1
- package/dist/src/cohort-topic/cohort-gossip-transport.js +9 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts +197 -27
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +643 -87
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/dispute/arbitrator-selection.d.ts +68 -7
- package/dist/src/dispute/arbitrator-selection.d.ts.map +1 -1
- package/dist/src/dispute/arbitrator-selection.js +105 -16
- package/dist/src/dispute/arbitrator-selection.js.map +1 -1
- package/dist/src/dispute/cascade.d.ts +7 -0
- package/dist/src/dispute/cascade.d.ts.map +1 -1
- package/dist/src/dispute/cascade.js +1 -1
- package/dist/src/dispute/cascade.js.map +1 -1
- package/dist/src/dispute/client.d.ts.map +1 -1
- package/dist/src/dispute/client.js +3 -2
- package/dist/src/dispute/client.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts +32 -3
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +82 -9
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/dispute/index.d.ts +1 -1
- package/dist/src/dispute/index.d.ts.map +1 -1
- package/dist/src/dispute/index.js +1 -1
- package/dist/src/dispute/index.js.map +1 -1
- package/dist/src/dispute/invalidation.d.ts +25 -0
- package/dist/src/dispute/invalidation.d.ts.map +1 -1
- package/dist/src/dispute/invalidation.js +14 -2
- package/dist/src/dispute/invalidation.js.map +1 -1
- package/dist/src/dispute/service.d.ts.map +1 -1
- package/dist/src/dispute/service.js +7 -1
- package/dist/src/dispute/service.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/it-utility.d.ts +1 -1
- package/dist/src/it-utility.d.ts.map +1 -1
- package/dist/src/it-utility.js +4 -13
- package/dist/src/it-utility.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +98 -1
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +272 -17
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +12 -0
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +441 -89
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/matchmaking/query-transport.d.ts +2 -0
- package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
- package/dist/src/matchmaking/query-transport.js +6 -0
- package/dist/src/matchmaking/query-transport.js.map +1 -1
- package/dist/src/matchmaking/traffic-validation.js +1 -0
- package/dist/src/matchmaking/traffic-validation.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +2 -2
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +8 -4
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/owned-block-seed.d.ts +27 -0
- package/dist/src/owned-block-seed.d.ts.map +1 -0
- package/dist/src/owned-block-seed.js +37 -0
- package/dist/src/owned-block-seed.js.map +1 -0
- package/dist/src/protocol-client.d.ts +1 -0
- package/dist/src/protocol-client.d.ts.map +1 -1
- package/dist/src/protocol-client.js +9 -2
- package/dist/src/protocol-client.js.map +1 -1
- package/dist/src/protocol-limits.d.ts +32 -0
- package/dist/src/protocol-limits.d.ts.map +1 -0
- package/dist/src/protocol-limits.js +32 -0
- package/dist/src/protocol-limits.js.map +1 -0
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +3 -0
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +41 -4
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +128 -80
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +38 -1
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +92 -13
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +7 -1
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/reputation/peer-reputation.js +1 -1
- package/dist/src/reputation/peer-reputation.js.map +1 -1
- package/dist/src/reputation/types.d.ts +3 -1
- package/dist/src/reputation/types.d.ts.map +1 -1
- package/dist/src/reputation/types.js +3 -0
- package/dist/src/reputation/types.js.map +1 -1
- package/dist/src/rn.d.ts +5 -1
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +7 -1
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/arachnode-fret-adapter.d.ts +30 -1
- package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -1
- package/dist/src/storage/arachnode-fret-adapter.js +15 -2
- package/dist/src/storage/arachnode-fret-adapter.js.map +1 -1
- package/dist/src/storage/arachnode-partition.d.ts +49 -0
- package/dist/src/storage/arachnode-partition.d.ts.map +1 -0
- package/dist/src/storage/arachnode-partition.js +61 -0
- package/dist/src/storage/arachnode-partition.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +40 -1
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +150 -57
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +8 -0
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +13 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.d.ts +45 -0
- package/dist/src/storage/kv-raw-storage.d.ts.map +1 -0
- package/dist/src/storage/kv-raw-storage.js +113 -0
- package/dist/src/storage/kv-raw-storage.js.map +1 -0
- package/dist/src/storage/memory-storage.d.ts +20 -58
- package/dist/src/storage/memory-storage.d.ts.map +1 -1
- package/dist/src/storage/memory-storage.js +22 -148
- package/dist/src/storage/memory-storage.js.map +1 -1
- package/dist/src/storage/memory-store-driver.d.ts +39 -0
- package/dist/src/storage/memory-store-driver.d.ts.map +1 -0
- package/dist/src/storage/memory-store-driver.js +129 -0
- package/dist/src/storage/memory-store-driver.js.map +1 -0
- package/dist/src/storage/raw-store-codec.d.ts +10 -0
- package/dist/src/storage/raw-store-codec.d.ts.map +1 -0
- package/dist/src/storage/raw-store-codec.js +36 -0
- package/dist/src/storage/raw-store-codec.js.map +1 -0
- package/dist/src/storage/raw-store-driver.d.ts +69 -0
- package/dist/src/storage/raw-store-driver.d.ts.map +1 -0
- package/dist/src/storage/raw-store-driver.js +2 -0
- package/dist/src/storage/raw-store-driver.js.map +1 -0
- package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -1
- package/dist/src/storage/restoration-coordinator-v2.js +1 -1
- package/dist/src/storage/restoration-coordinator-v2.js.map +1 -1
- package/dist/src/storage/restoration-coordinator.d.ts +73 -0
- package/dist/src/storage/restoration-coordinator.d.ts.map +1 -0
- package/dist/src/storage/restoration-coordinator.js +181 -0
- package/dist/src/storage/restoration-coordinator.js.map +1 -0
- package/dist/src/storage/ring-selector.d.ts +92 -5
- package/dist/src/storage/ring-selector.d.ts.map +1 -1
- package/dist/src/storage/ring-selector.js +153 -51
- package/dist/src/storage/ring-selector.js.map +1 -1
- package/dist/src/storage/ring-shift-coordinator.d.ts +128 -0
- package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -0
- package/dist/src/storage/ring-shift-coordinator.js +196 -0
- package/dist/src/storage/ring-shift-coordinator.js.map +1 -0
- package/dist/src/storage/storage-monitor.d.ts +28 -0
- package/dist/src/storage/storage-monitor.d.ts.map +1 -1
- package/dist/src/storage/storage-monitor.js +42 -2
- package/dist/src/storage/storage-monitor.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +19 -0
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +172 -22
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/sync/client.d.ts.map +1 -1
- package/dist/src/sync/client.js +5 -1
- package/dist/src/sync/client.js.map +1 -1
- package/dist/src/sync/service.d.ts.map +1 -1
- package/dist/src/sync/service.js +2 -1
- package/dist/src/sync/service.js.map +1 -1
- package/dist/src/testing/cohort-topic-mesh-harness.d.ts +28 -4
- package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/cohort-topic-mesh-harness.js +51 -10
- package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -1
- package/dist/src/testing/index.d.ts +1 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +15 -7
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +24 -0
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -0
- package/dist/src/testing/raw-storage-conformance.js +313 -0
- package/dist/src/testing/raw-storage-conformance.js.map +1 -0
- package/package.json +4 -4
- package/src/cluster/block-transfer-service.ts +4 -3
- package/src/cluster/block-transfer.ts +424 -265
- package/src/cluster/client.ts +3 -1
- package/src/cluster/cluster-repo.ts +481 -71
- package/src/cluster/peer-key-binding.ts +37 -0
- package/src/cluster/quorum-restore.ts +0 -0
- package/src/cluster/rebalance-monitor.ts +34 -3
- package/src/cluster/service.ts +7 -1
- package/src/cluster/spread-on-churn.ts +11 -1
- package/src/cluster/supermajority-coupling.ts +37 -0
- package/src/cohort-topic/cohort-gossip-driver.ts +96 -9
- package/src/cohort-topic/cohort-gossip-transport.ts +10 -0
- package/src/cohort-topic/host.ts +831 -105
- package/src/dispute/arbitrator-selection.ts +138 -28
- package/src/dispute/cascade.ts +8 -1
- package/src/dispute/client.ts +3 -2
- package/src/dispute/dispute-service.ts +94 -9
- package/src/dispute/index.ts +7 -1
- package/src/dispute/invalidation.ts +40 -2
- package/src/dispute/service.ts +7 -1
- package/src/index.ts +46 -43
- package/src/it-utility.ts +5 -13
- package/src/libp2p-key-network.ts +281 -18
- package/src/libp2p-node-base.ts +511 -81
- package/src/matchmaking/query-transport.ts +9 -0
- package/src/network/network-manager-service.ts +8 -2
- package/src/owned-block-seed.ts +40 -0
- package/src/protocol-client.ts +10 -3
- package/src/protocol-limits.ts +33 -0
- package/src/repo/client.ts +3 -0
- package/src/repo/cluster-coordinator.ts +145 -82
- package/src/repo/coordinator-repo.ts +96 -16
- package/src/repo/service.ts +7 -1
- package/src/reputation/types.ts +3 -0
- package/src/rn.ts +7 -1
- package/src/storage/arachnode-fret-adapter.ts +33 -2
- package/src/storage/arachnode-partition.ts +74 -0
- package/src/storage/block-storage.ts +169 -61
- package/src/storage/i-block-storage.ts +9 -0
- package/src/storage/i-raw-storage.ts +14 -0
- package/src/storage/kv-raw-storage.ts +135 -0
- package/src/storage/memory-storage.ts +25 -173
- package/src/storage/memory-store-driver.ts +157 -0
- package/src/storage/raw-store-codec.ts +42 -0
- package/src/storage/raw-store-driver.ts +80 -0
- package/src/storage/{restoration-coordinator-v2.ts → restoration-coordinator.ts} +43 -37
- package/src/storage/ring-selector.ts +317 -176
- package/src/storage/ring-shift-coordinator.ts +271 -0
- package/src/storage/storage-monitor.ts +70 -3
- package/src/storage/storage-repo.ts +710 -558
- package/src/sync/client.ts +5 -1
- package/src/sync/service.ts +2 -1
- package/src/testing/cohort-topic-mesh-harness.ts +61 -11
- package/src/testing/index.ts +1 -0
- package/src/testing/mesh-harness.ts +14 -6
- package/src/testing/raw-storage-conformance.ts +397 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-limits.d.ts","sourceRoot":"","sources":["../../src/protocol-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAkB,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,QAAkB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound-message size caps for the db-p2p consensus protocols.
|
|
3
|
+
*
|
|
4
|
+
* Every inbound stream handler frames its wire data with length-prefixed
|
|
5
|
+
* encoding (`it-length-prefixed`) and `JSON.parse`s each frame. Passing one of
|
|
6
|
+
* these constants as the decoder's `maxDataLength` makes an oversized frame
|
|
7
|
+
* reject at the length-prefix (before any allocation against the declared size)
|
|
8
|
+
* instead of buffering up to the library default (4 MiB) of attacker-controlled
|
|
9
|
+
* JSON per frame. Mirrors the existing precedent
|
|
10
|
+
* `DEFAULT_MAX_MESSAGE_BYTES = 1024 * 1024` in
|
|
11
|
+
* `packages/db-core/src/cohort-topic/wire/codec.ts`.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Max size (bytes) of a single inbound framed message on a *control-plane*
|
|
15
|
+
* protocol — cluster records, dispute votes, sync requests. These carry a peer
|
|
16
|
+
* set + signatures + small metadata, never bulk block data.
|
|
17
|
+
*/
|
|
18
|
+
export const MAX_CONTROL_MESSAGE_BYTES = 1 * 1024 * 1024; // 1 MiB
|
|
19
|
+
/**
|
|
20
|
+
* Max size (bytes) of a single inbound framed message on a *block-carrying*
|
|
21
|
+
* protocol — repo operations (pend/commit transforms + block bodies),
|
|
22
|
+
* block-transfer push payloads (base64-inflated block data, multiple per
|
|
23
|
+
* request), and the block-bearing responses those protocols return.
|
|
24
|
+
*
|
|
25
|
+
* NOTE: the repo enforces no hard per-block byte ceiling today, so this is a
|
|
26
|
+
* heuristic. If a legitimate block/transform can exceed this, the transfer will
|
|
27
|
+
* be rejected — raise this constant (or thread a config override) rather than
|
|
28
|
+
* removing the cap. If the repo later grows a real max-block constant, derive
|
|
29
|
+
* this cap from it.
|
|
30
|
+
*/
|
|
31
|
+
export const MAX_BLOCK_MESSAGE_BYTES = 8 * 1024 * 1024; // 8 MiB
|
|
32
|
+
//# sourceMappingURL=protocol-limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-limits.js","sourceRoot":"","sources":["../../src/protocol-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ;AAElE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/repo/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAC7F,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAC3D,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/repo/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAC7F,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAC3D,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,qBAAa,UAAW,SAAQ,cAAe,YAAW,KAAK;IACC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM;IAA/F,OAAO;IAIP,mCAAmC;WACrB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU;IAI9F,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAO5E,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,GAAG,YAAY,CAAC;IAOxF,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvE,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IAOpF,OAAO,CAAC,oBAAoB;YASd,kBAAkB;YAoElB,wBAAwB;YA6BxB,kCAAkC;CAQhD"}
|
package/dist/src/repo/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { blockIdsForTransforms, blockIdToBytes } from "@optimystic/db-core";
|
|
2
2
|
import { ProtocolClient } from "../protocol-client.js";
|
|
3
|
+
import { MAX_BLOCK_MESSAGE_BYTES } from "../protocol-limits.js";
|
|
3
4
|
import { peerIdFromString } from "@libp2p/peer-id";
|
|
4
5
|
export class RepoClient extends ProtocolClient {
|
|
5
6
|
protocolPrefix;
|
|
@@ -67,6 +68,8 @@ export class RepoClient extends ProtocolClient {
|
|
|
67
68
|
signal: combinedSignal,
|
|
68
69
|
correlationId,
|
|
69
70
|
dialTimeoutMs: options?.dialTimeoutMs,
|
|
71
|
+
// A get response carries block data → block cap (not control).
|
|
72
|
+
maxDataLength: MAX_BLOCK_MESSAGE_BYTES,
|
|
70
73
|
});
|
|
71
74
|
}
|
|
72
75
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/repo/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/repo/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,19 @@ import type { PeerId } from "@libp2p/interface";
|
|
|
4
4
|
import type { FretService } from "p2p-fret";
|
|
5
5
|
import type { IPeerReputation } from "../reputation/types.js";
|
|
6
6
|
import type { ITransactionStateStore } from "../cluster/i-transaction-state-store.js";
|
|
7
|
+
/** Cancel handle for an injected timer; cancels a not-yet-fired timer (safe no-op after fire/cancel). */
|
|
8
|
+
export type TimerCancel = () => void;
|
|
9
|
+
/**
|
|
10
|
+
* Optional injection seam for deterministic time. Production leaves both undefined and gets
|
|
11
|
+
* `Date.now` + an unref'd `setTimeout`; tests inject a fake clock + timer queue so scheduled
|
|
12
|
+
* commit-retries fire in virtual (not wall-clock) time.
|
|
13
|
+
*/
|
|
14
|
+
export interface ClusterCoordinatorClock {
|
|
15
|
+
/** Clock (Unix ms). Defaults to `Date.now`. */
|
|
16
|
+
now?: () => number;
|
|
17
|
+
/** Schedule a one-shot timer, returning a cancel handle. Defaults to an unref'd `setTimeout`. */
|
|
18
|
+
setTimer?: (fn: () => void, delayMs: number) => TimerCancel;
|
|
19
|
+
}
|
|
7
20
|
/** Manages distributed transactions across clusters */
|
|
8
21
|
export declare class ClusterCoordinator {
|
|
9
22
|
private readonly keyNetwork;
|
|
@@ -19,18 +32,42 @@ export declare class ClusterCoordinator {
|
|
|
19
32
|
private readonly retryMaxIntervalMs;
|
|
20
33
|
private readonly retryMaxAttempts;
|
|
21
34
|
private readonly commitBroadcastImmediateRetries;
|
|
35
|
+
private readonly promiseImmediateRetries;
|
|
36
|
+
/** Injected clock/timer seam; production defaults to `Date.now` + unref'd `setTimeout`. */
|
|
37
|
+
private readonly now;
|
|
38
|
+
private readonly setTimer;
|
|
22
39
|
constructor(keyNetwork: IKeyNetwork, createClusterClient: (peerId: PeerId) => ClusterClient, cfg: ClusterConsensusConfig & {
|
|
23
40
|
clusterSize: number;
|
|
24
41
|
}, localCluster?: {
|
|
25
42
|
update: (record: ClusterRecord) => Promise<ClusterRecord>;
|
|
26
43
|
peerId: PeerId;
|
|
27
44
|
wasTransactionExecuted?: (messageHash: string) => boolean;
|
|
28
|
-
} | undefined, fretService?: FretService | undefined, reputation?: IPeerReputation | undefined, stateStore?: ITransactionStateStore | undefined);
|
|
45
|
+
} | undefined, fretService?: FretService | undefined, reputation?: IPeerReputation | undefined, stateStore?: ITransactionStateStore | undefined, clock?: ClusterCoordinatorClock);
|
|
46
|
+
/**
|
|
47
|
+
* Invoke one cluster member's `update`, retrying transient REMOTE failures up to
|
|
48
|
+
* `immediateRetries` times before surfacing the error. The local cluster is invoked
|
|
49
|
+
* exactly once — a local throw is a real fault (validation / merge / consensus), not a
|
|
50
|
+
* transient transport blip. A remote call rides a libp2p stream that a circuit-relay
|
|
51
|
+
* ("limited") connection can reset once a per-circuit cap or reservation lapses, which
|
|
52
|
+
* surfaces as a StreamResetError; an immediate retry on the (usually still-warm)
|
|
53
|
+
* connection recovers most of those without escalating the peer to a failure. Shared by
|
|
54
|
+
* the promise-collection, commit-collection, and commit-broadcast phases so all three
|
|
55
|
+
* react to a relayed reset the same way.
|
|
56
|
+
*/
|
|
57
|
+
private updateMember;
|
|
29
58
|
/**
|
|
30
|
-
* Creates a
|
|
59
|
+
* Creates a base58btc string hash uniquely identifying a transaction. For a v2 record the caller
|
|
60
|
+
* threads in the {@link membershipDigest} of the peer set so the responsible membership is bound into
|
|
61
|
+
* the identity (two different peer sets ⇒ two different hashes). Omitting `membershipDigestValue`
|
|
62
|
+
* reproduces the legacy v1 hash byte-for-byte.
|
|
63
|
+
*
|
|
64
|
+
* NOTE: the whole `message` is hashed (canonicalJson), so a transaction's advisory aged priority —
|
|
65
|
+
* which rides inside the pend operation as `pend.transaction.priority` (multi-collection) or
|
|
66
|
+
* `pend.priority` (single-collection) — is automatically covered here and by the derived
|
|
67
|
+
* promise/commit hashes. That is what makes priority integrity-protected in transit: a relaying peer
|
|
68
|
+
* cannot strip or inflate it without invalidating the message hash the members verify. No separate
|
|
69
|
+
* priority-hashing step is needed.
|
|
31
70
|
*/
|
|
32
|
-
/** Deterministic JSON: sorts object keys so hash is order-independent */
|
|
33
|
-
private static canonicalJson;
|
|
34
71
|
private createMessageHash;
|
|
35
72
|
/**
|
|
36
73
|
* Gets all peers in the cluster for a specific block ID
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster-coordinator.d.ts","sourceRoot":"","sources":["../../../src/repo/cluster-coordinator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAgB,cAAc,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cluster-coordinator.d.ts","sourceRoot":"","sources":["../../../src/repo/cluster-coordinator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { peerIdFromString } from "@libp2p/peer-id";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { CURRENT_MEMBERSHIP_VERSION, computeClusterMessageHash, membershipDigest } from "@optimystic/db-core";
|
|
3
|
+
import { ClusterClient } from "../cluster/client.js";
|
|
4
4
|
import { Pending } from "@optimystic/db-core";
|
|
5
5
|
import { createLogger, verbose } from '../logger.js';
|
|
6
6
|
import { PenaltyReason } from "../reputation/types.js";
|
|
7
7
|
const log = createLogger('cluster');
|
|
8
|
+
/**
|
|
9
|
+
* Production timer binding: a one-shot `setTimeout` whose handle is **unref'd** so a pending
|
|
10
|
+
* commit-retry (or the deferred transaction cleanup) never keeps an otherwise-idle process alive.
|
|
11
|
+
* The returned handle clears the timeout (idempotent). Mirrors the reactivity rotation
|
|
12
|
+
* re-registration scheduler's `defaultSetTimer` (see reactivity/rotation-rereg-scheduler.ts).
|
|
13
|
+
*/
|
|
14
|
+
function defaultSetTimer(fn, delayMs) {
|
|
15
|
+
const handle = setTimeout(fn, delayMs);
|
|
16
|
+
// An idle retry/cleanup timer must not pin a process (mirror rotation re-registration + push-state gossip).
|
|
17
|
+
handle.unref?.();
|
|
18
|
+
return () => clearTimeout(handle);
|
|
19
|
+
}
|
|
8
20
|
/** Manages distributed transactions across clusters */
|
|
9
21
|
export class ClusterCoordinator {
|
|
10
22
|
keyNetwork;
|
|
@@ -20,7 +32,11 @@ export class ClusterCoordinator {
|
|
|
20
32
|
retryMaxIntervalMs;
|
|
21
33
|
retryMaxAttempts;
|
|
22
34
|
commitBroadcastImmediateRetries;
|
|
23
|
-
|
|
35
|
+
promiseImmediateRetries;
|
|
36
|
+
/** Injected clock/timer seam; production defaults to `Date.now` + unref'd `setTimeout`. */
|
|
37
|
+
now;
|
|
38
|
+
setTimer;
|
|
39
|
+
constructor(keyNetwork, createClusterClient, cfg, localCluster, fretService, reputation, stateStore, clock) {
|
|
24
40
|
this.keyNetwork = keyNetwork;
|
|
25
41
|
this.createClusterClient = createClusterClient;
|
|
26
42
|
this.cfg = cfg;
|
|
@@ -33,20 +49,62 @@ export class ClusterCoordinator {
|
|
|
33
49
|
this.retryMaxIntervalMs = cfg.commitBroadcastRetryMaxIntervalMs ?? 8000;
|
|
34
50
|
this.retryMaxAttempts = cfg.commitBroadcastRetryMaxAttempts ?? 5;
|
|
35
51
|
this.commitBroadcastImmediateRetries = cfg.commitBroadcastImmediateRetries ?? 1;
|
|
52
|
+
this.promiseImmediateRetries = cfg.promiseImmediateRetries ?? 1;
|
|
53
|
+
this.now = clock?.now ?? (() => Date.now());
|
|
54
|
+
this.setTimer = clock?.setTimer ?? defaultSetTimer;
|
|
36
55
|
}
|
|
37
56
|
/**
|
|
38
|
-
*
|
|
57
|
+
* Invoke one cluster member's `update`, retrying transient REMOTE failures up to
|
|
58
|
+
* `immediateRetries` times before surfacing the error. The local cluster is invoked
|
|
59
|
+
* exactly once — a local throw is a real fault (validation / merge / consensus), not a
|
|
60
|
+
* transient transport blip. A remote call rides a libp2p stream that a circuit-relay
|
|
61
|
+
* ("limited") connection can reset once a per-circuit cap or reservation lapses, which
|
|
62
|
+
* surfaces as a StreamResetError; an immediate retry on the (usually still-warm)
|
|
63
|
+
* connection recovers most of those without escalating the peer to a failure. Shared by
|
|
64
|
+
* the promise-collection, commit-collection, and commit-broadcast phases so all three
|
|
65
|
+
* react to a relayed reset the same way.
|
|
39
66
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
async updateMember(peerIdStr, record, immediateRetries, phase) {
|
|
68
|
+
const isLocal = this.localCluster && peerIdStr === this.localCluster.peerId.toString();
|
|
69
|
+
if (isLocal) {
|
|
70
|
+
return await this.localCluster.update(record);
|
|
71
|
+
}
|
|
72
|
+
const maxAttempts = 1 + Math.max(0, immediateRetries);
|
|
73
|
+
let lastError;
|
|
74
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
75
|
+
try {
|
|
76
|
+
return await this.createClusterClient(peerIdFromString(peerIdStr)).update(record);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
lastError = err;
|
|
80
|
+
if (attempt < maxAttempts) {
|
|
81
|
+
log('cluster-tx:member-update-retry', {
|
|
82
|
+
messageHash: record.messageHash,
|
|
83
|
+
peerId: peerIdStr,
|
|
84
|
+
phase,
|
|
85
|
+
attempt,
|
|
86
|
+
error: err instanceof Error ? err.message : String(err)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw lastError;
|
|
45
92
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Creates a base58btc string hash uniquely identifying a transaction. For a v2 record the caller
|
|
95
|
+
* threads in the {@link membershipDigest} of the peer set so the responsible membership is bound into
|
|
96
|
+
* the identity (two different peer sets ⇒ two different hashes). Omitting `membershipDigestValue`
|
|
97
|
+
* reproduces the legacy v1 hash byte-for-byte.
|
|
98
|
+
*
|
|
99
|
+
* NOTE: the whole `message` is hashed (canonicalJson), so a transaction's advisory aged priority —
|
|
100
|
+
* which rides inside the pend operation as `pend.transaction.priority` (multi-collection) or
|
|
101
|
+
* `pend.priority` (single-collection) — is automatically covered here and by the derived
|
|
102
|
+
* promise/commit hashes. That is what makes priority integrity-protected in transit: a relaying peer
|
|
103
|
+
* cannot strip or inflate it without invalidating the message hash the members verify. No separate
|
|
104
|
+
* priority-hashing step is needed.
|
|
105
|
+
*/
|
|
106
|
+
async createMessageHash(message, membershipDigestValue) {
|
|
107
|
+
return computeClusterMessageHash(message, membershipDigestValue);
|
|
50
108
|
}
|
|
51
109
|
/**
|
|
52
110
|
* Gets all peers in the cluster for a specific block ID
|
|
@@ -64,11 +122,15 @@ export class ClusterCoordinator {
|
|
|
64
122
|
return {};
|
|
65
123
|
}
|
|
66
124
|
}
|
|
67
|
-
makeRecord(peers, messageHash, message) {
|
|
125
|
+
makeRecord(peers, messageHash, message, membershipDigestValue) {
|
|
68
126
|
const peerCount = Object.keys(peers ?? {}).length;
|
|
69
127
|
const record = {
|
|
70
128
|
messageHash,
|
|
71
129
|
peers,
|
|
130
|
+
// v2: bind the responsible membership into the signed identity. messageHash was computed over
|
|
131
|
+
// this same digest, so a different peer set would have produced a different messageHash.
|
|
132
|
+
membershipVersion: CURRENT_MEMBERSHIP_VERSION,
|
|
133
|
+
membershipDigest: membershipDigestValue,
|
|
72
134
|
message,
|
|
73
135
|
coordinatingBlockIds: message.coordinatingBlockIds,
|
|
74
136
|
promises: {},
|
|
@@ -98,10 +160,14 @@ export class ClusterCoordinator {
|
|
|
98
160
|
async executeClusterTransaction(blockId, message, _options) {
|
|
99
161
|
// Get the cluster peers for this block
|
|
100
162
|
const peers = await this.getClusterForBlock(blockId);
|
|
101
|
-
//
|
|
102
|
-
|
|
163
|
+
// Bind the responsible membership into the transaction identity (v2): the digest is folded into
|
|
164
|
+
// the messageHash below, so two different peer sets produce two different messageHashes rather
|
|
165
|
+
// than one hash with a silent internal disagreement about who is responsible.
|
|
166
|
+
const membershipDigestValue = await membershipDigest(peers);
|
|
167
|
+
// Create a unique hash for this transaction (over message + membership digest)
|
|
168
|
+
const messageHash = await this.createMessageHash(message, membershipDigestValue);
|
|
103
169
|
// Create a cluster record for this transaction
|
|
104
|
-
const record = this.makeRecord(peers, messageHash, message);
|
|
170
|
+
const record = this.makeRecord(peers, messageHash, message, membershipDigestValue);
|
|
105
171
|
log('cluster-tx:start', {
|
|
106
172
|
messageHash,
|
|
107
173
|
blockId,
|
|
@@ -119,7 +185,7 @@ export class ClusterCoordinator {
|
|
|
119
185
|
messageHash,
|
|
120
186
|
record,
|
|
121
187
|
pending,
|
|
122
|
-
lastUpdate:
|
|
188
|
+
lastUpdate: this.now()
|
|
123
189
|
};
|
|
124
190
|
this.transactions.set(messageHash, state);
|
|
125
191
|
this.persistCoordinatorState(messageHash, record, 'promising');
|
|
@@ -150,7 +216,7 @@ export class ClusterCoordinator {
|
|
|
150
216
|
// Let the retry completion or abort handle cleanup
|
|
151
217
|
if (!stored?.retry) {
|
|
152
218
|
// Wait a bit before cleanup to allow any in-flight responses to arrive
|
|
153
|
-
|
|
219
|
+
this.setTimer(() => {
|
|
154
220
|
this.transactions.delete(messageHash);
|
|
155
221
|
this.deleteCoordinatorState(messageHash);
|
|
156
222
|
log('cluster-tx:transaction-remove', {
|
|
@@ -240,6 +306,11 @@ export class ClusterCoordinator {
|
|
|
240
306
|
rejectionCount,
|
|
241
307
|
peerCount
|
|
242
308
|
});
|
|
309
|
+
// [dispute-subsystem-dormant] Evidence is computed and persisted but initiateDispute() is
|
|
310
|
+
// intentionally NOT called here. Dispute origination stays dormant pending arbitrator-set
|
|
311
|
+
// anchoring — without it a forged synthetic cohort passes resolution.
|
|
312
|
+
// Gate: tickets/backlog/hardening/invalidation-live-wiring-requires-arbitrator-set-anchoring
|
|
313
|
+
// Wiring plan: tickets/backlog/feat-dispute-subsystem-live-activation
|
|
243
314
|
}
|
|
244
315
|
this.persistCoordinatorState(promised.record.messageHash, promised.record, 'committing');
|
|
245
316
|
return await this.commitTransaction(promised.record);
|
|
@@ -277,13 +348,18 @@ export class ClusterCoordinator {
|
|
|
277
348
|
// Ignore errors
|
|
278
349
|
}
|
|
279
350
|
}
|
|
280
|
-
// Fallback:
|
|
281
|
-
//
|
|
282
|
-
|
|
351
|
+
// Fallback: with no confident network-size estimate, fail CLOSED by default.
|
|
352
|
+
// An undersized cluster with no way to justify its size is unsafe (a lone/
|
|
353
|
+
// near-lone node could rubber-stamp its own writes), so reject unless the
|
|
354
|
+
// operator has explicitly opted in via allowUnvalidatedSmallCluster (e.g.
|
|
355
|
+
// single-node / local dev knowingly running below the floor).
|
|
356
|
+
const admit = this.cfg.allowUnvalidatedSmallCluster ?? false;
|
|
357
|
+
log('cluster-tx:small-cluster-no-confident-estimate', {
|
|
283
358
|
localSize,
|
|
284
|
-
reason: 'no-confident-network-size-estimate'
|
|
359
|
+
reason: 'no-confident-network-size-estimate',
|
|
360
|
+
admit
|
|
285
361
|
});
|
|
286
|
-
return
|
|
362
|
+
return admit;
|
|
287
363
|
}
|
|
288
364
|
/**
|
|
289
365
|
* Collects promises from all peers in the cluster
|
|
@@ -298,14 +374,15 @@ export class ClusterCoordinator {
|
|
|
298
374
|
}));
|
|
299
375
|
log('cluster-tx:promise-peers', { messageHash: record.messageHash, peers: peerDetail });
|
|
300
376
|
}
|
|
301
|
-
// For each peer, create a client and request a promise
|
|
377
|
+
// For each peer, create a client and request a promise. A remote promise rides
|
|
378
|
+
// a libp2p stream that a relayed (limited) connection can reset transiently, so
|
|
379
|
+
// each remote request gets `promiseImmediateRetries` in-line re-attempts before
|
|
380
|
+
// it counts as a failure — without this a single relayed reset drops the peer and
|
|
381
|
+
// sinks super-majority (the commit broadcast already has the same guard).
|
|
302
382
|
const promiseRequests = peerIds.map(peerIdStr => {
|
|
303
383
|
const isLocal = this.localCluster && peerIdStr === this.localCluster.peerId.toString();
|
|
304
384
|
log('cluster-tx:promise-request', { messageHash: record.messageHash, peerId: peerIdStr, isLocal });
|
|
305
|
-
|
|
306
|
-
? this.localCluster.update(record)
|
|
307
|
-
: this.createClusterClient(peerIdFromString(peerIdStr)).update(record);
|
|
308
|
-
return new Pending(promise);
|
|
385
|
+
return new Pending(this.updateMember(peerIdStr, record, this.promiseImmediateRetries, 'promise'));
|
|
309
386
|
});
|
|
310
387
|
// Wait for all promises to complete
|
|
311
388
|
const results = await Promise.all(promiseRequests.map((p, idx) => p.result().then(res => {
|
|
@@ -396,6 +473,10 @@ export class ClusterCoordinator {
|
|
|
396
473
|
const commitPayload = {
|
|
397
474
|
...record
|
|
398
475
|
};
|
|
476
|
+
// No per-peer immediate retry here: a commit-collection failure is recovered
|
|
477
|
+
// downstream by broadcastMergedRecord's in-line retry and the scheduled
|
|
478
|
+
// commit-retry timer. (The promise phase has no such backstop, which is why
|
|
479
|
+
// collectPromises gets the immediate retry instead.)
|
|
399
480
|
const commitRequests = peerIds.map(peerIdStr => {
|
|
400
481
|
const isLocal = this.localCluster && peerIdStr === this.localCluster.peerId.toString();
|
|
401
482
|
log('cluster-tx:commit-request', { messageHash: record.messageHash, peerId: peerIdStr, isLocal });
|
|
@@ -499,48 +580,19 @@ export class ClusterCoordinator {
|
|
|
499
580
|
* Local cluster is invoked exactly once — local failures are fatal, not transient.
|
|
500
581
|
*/
|
|
501
582
|
async broadcastMergedRecord(record, peerIds) {
|
|
502
|
-
const maxAttempts = 1 + Math.max(0, this.commitBroadcastImmediateRetries);
|
|
503
583
|
const results = await Promise.all(peerIds.map(async (peerIdStr) => {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
await this.localCluster.update(record);
|
|
508
|
-
return { peerId: peerIdStr, success: true };
|
|
509
|
-
}
|
|
510
|
-
catch (err) {
|
|
511
|
-
log('cluster-tx:consensus-broadcast-error', {
|
|
512
|
-
messageHash: record.messageHash,
|
|
513
|
-
peerId: peerIdStr,
|
|
514
|
-
error: err.message
|
|
515
|
-
});
|
|
516
|
-
return { peerId: peerIdStr, success: false };
|
|
517
|
-
}
|
|
584
|
+
try {
|
|
585
|
+
await this.updateMember(peerIdStr, record, this.commitBroadcastImmediateRetries, 'commit-broadcast');
|
|
586
|
+
return { peerId: peerIdStr, success: true };
|
|
518
587
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
lastError = err;
|
|
527
|
-
if (attempt < maxAttempts) {
|
|
528
|
-
log('cluster-tx:consensus-broadcast-retry', {
|
|
529
|
-
messageHash: record.messageHash,
|
|
530
|
-
peerId: peerIdStr,
|
|
531
|
-
attempt,
|
|
532
|
-
error: err.message
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
log('cluster-tx:consensus-broadcast-error', {
|
|
590
|
+
messageHash: record.messageHash,
|
|
591
|
+
peerId: peerIdStr,
|
|
592
|
+
error: err instanceof Error ? err.message : String(err)
|
|
593
|
+
});
|
|
594
|
+
return { peerId: peerIdStr, success: false };
|
|
536
595
|
}
|
|
537
|
-
log('cluster-tx:consensus-broadcast-error', {
|
|
538
|
-
messageHash: record.messageHash,
|
|
539
|
-
peerId: peerIdStr,
|
|
540
|
-
attempts: maxAttempts,
|
|
541
|
-
error: lastError instanceof Error ? lastError.message : String(lastError)
|
|
542
|
-
});
|
|
543
|
-
return { peerId: peerIdStr, success: false };
|
|
544
596
|
}));
|
|
545
597
|
const failures = results.filter(r => !r.success).map(r => r.peerId);
|
|
546
598
|
return { failures };
|
|
@@ -552,7 +604,7 @@ export class ClusterCoordinator {
|
|
|
552
604
|
return;
|
|
553
605
|
}
|
|
554
606
|
state.record = { ...record };
|
|
555
|
-
state.lastUpdate =
|
|
607
|
+
state.lastUpdate = this.now();
|
|
556
608
|
log('cluster-tx:transaction-update', {
|
|
557
609
|
messageHash: record.messageHash,
|
|
558
610
|
stage,
|
|
@@ -577,17 +629,15 @@ export class ClusterCoordinator {
|
|
|
577
629
|
}
|
|
578
630
|
const pendingPeers = new Set(missingPeers);
|
|
579
631
|
const baseInterval = existing ? Math.min(existing.intervalMs * this.retryBackoffFactor, this.retryMaxIntervalMs) : this.retryInitialIntervalMs;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
const timer = setTimeout(() => {
|
|
632
|
+
existing?.cancel?.();
|
|
633
|
+
const cancel = this.setTimer(() => {
|
|
584
634
|
void this.retryCommits(messageHash);
|
|
585
635
|
}, baseInterval);
|
|
586
636
|
state.retry = {
|
|
587
637
|
pendingPeers,
|
|
588
638
|
attempt: nextAttempt,
|
|
589
639
|
intervalMs: baseInterval,
|
|
590
|
-
|
|
640
|
+
cancel
|
|
591
641
|
};
|
|
592
642
|
this.persistCoordinatorState(messageHash, state.record, 'broadcasting', {
|
|
593
643
|
pendingPeers: Array.from(pendingPeers),
|
|
@@ -651,12 +701,10 @@ export class ClusterCoordinator {
|
|
|
651
701
|
if (!state?.retry) {
|
|
652
702
|
return;
|
|
653
703
|
}
|
|
654
|
-
|
|
655
|
-
clearTimeout(state.retry.timer);
|
|
656
|
-
}
|
|
704
|
+
state.retry.cancel?.();
|
|
657
705
|
state.retry = undefined;
|
|
658
706
|
// Clean up the transaction after retry is complete
|
|
659
|
-
|
|
707
|
+
this.setTimer(() => {
|
|
660
708
|
this.transactions.delete(messageHash);
|
|
661
709
|
this.deleteCoordinatorState(messageHash);
|
|
662
710
|
log('cluster-tx:transaction-remove', {
|
|
@@ -672,7 +720,7 @@ export class ClusterCoordinator {
|
|
|
672
720
|
this.stateStore.saveCoordinatorState(messageHash, {
|
|
673
721
|
messageHash,
|
|
674
722
|
record,
|
|
675
|
-
lastUpdate:
|
|
723
|
+
lastUpdate: this.now(),
|
|
676
724
|
phase,
|
|
677
725
|
retryState
|
|
678
726
|
}).catch(err => log('cluster-tx:persist-error', { messageHash, error: err.message }));
|
|
@@ -695,7 +743,7 @@ export class ClusterCoordinator {
|
|
|
695
743
|
for (const state of states) {
|
|
696
744
|
const { messageHash } = state;
|
|
697
745
|
// Expired — clean up
|
|
698
|
-
if (state.record.message.expiration && state.record.message.expiration <
|
|
746
|
+
if (state.record.message.expiration && state.record.message.expiration < this.now()) {
|
|
699
747
|
log('cluster-tx:recovery-expired', { messageHash });
|
|
700
748
|
await this.stateStore.deleteCoordinatorState(messageHash);
|
|
701
749
|
continue;
|