@optimystic/db-p2p 0.0.1
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/index.min.js +52 -0
- package/dist/index.min.js.map +7 -0
- package/dist/src/cluster/client.d.ts +12 -0
- package/dist/src/cluster/client.d.ts.map +1 -0
- package/dist/src/cluster/client.js +65 -0
- package/dist/src/cluster/client.js.map +1 -0
- package/dist/src/cluster/cluster-repo.d.ts +79 -0
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -0
- package/dist/src/cluster/cluster-repo.js +613 -0
- package/dist/src/cluster/cluster-repo.js.map +1 -0
- package/dist/src/cluster/partition-detector.d.ts +59 -0
- package/dist/src/cluster/partition-detector.d.ts.map +1 -0
- package/dist/src/cluster/partition-detector.js +129 -0
- package/dist/src/cluster/partition-detector.js.map +1 -0
- package/dist/src/cluster/service.d.ts +49 -0
- package/dist/src/cluster/service.d.ts.map +1 -0
- package/dist/src/cluster/service.js +107 -0
- package/dist/src/cluster/service.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +29 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/it-utility.d.ts +4 -0
- package/dist/src/it-utility.d.ts.map +1 -0
- package/dist/src/it-utility.js +32 -0
- package/dist/src/it-utility.js.map +1 -0
- package/dist/src/libp2p-key-network.d.ts +59 -0
- package/dist/src/libp2p-key-network.d.ts.map +1 -0
- package/dist/src/libp2p-key-network.js +278 -0
- package/dist/src/libp2p-key-network.js.map +1 -0
- package/dist/src/libp2p-node.d.ts +28 -0
- package/dist/src/libp2p-node.d.ts.map +1 -0
- package/dist/src/libp2p-node.js +270 -0
- package/dist/src/libp2p-node.js.map +1 -0
- package/dist/src/logger.d.ts +3 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/network/get-network-manager.d.ts +4 -0
- package/dist/src/network/get-network-manager.d.ts.map +1 -0
- package/dist/src/network/get-network-manager.js +17 -0
- package/dist/src/network/get-network-manager.js.map +1 -0
- package/dist/src/network/network-manager-service.d.ts +82 -0
- package/dist/src/network/network-manager-service.d.ts.map +1 -0
- package/dist/src/network/network-manager-service.js +283 -0
- package/dist/src/network/network-manager-service.js.map +1 -0
- package/dist/src/peer-utils.d.ts +2 -0
- package/dist/src/peer-utils.d.ts.map +1 -0
- package/dist/src/peer-utils.js +28 -0
- package/dist/src/peer-utils.js.map +1 -0
- package/dist/src/protocol-client.d.ts +12 -0
- package/dist/src/protocol-client.d.ts.map +1 -0
- package/dist/src/protocol-client.js +34 -0
- package/dist/src/protocol-client.js.map +1 -0
- package/dist/src/repo/client.d.ts +17 -0
- package/dist/src/repo/client.d.ts.map +1 -0
- package/dist/src/repo/client.js +82 -0
- package/dist/src/repo/client.js.map +1 -0
- package/dist/src/repo/cluster-coordinator.d.ts +59 -0
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -0
- package/dist/src/repo/cluster-coordinator.js +539 -0
- package/dist/src/repo/cluster-coordinator.js.map +1 -0
- package/dist/src/repo/coordinator-repo.d.ts +29 -0
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -0
- package/dist/src/repo/coordinator-repo.js +102 -0
- package/dist/src/repo/coordinator-repo.js.map +1 -0
- package/dist/src/repo/redirect.d.ts +14 -0
- package/dist/src/repo/redirect.d.ts.map +1 -0
- package/dist/src/repo/redirect.js +9 -0
- package/dist/src/repo/redirect.js.map +1 -0
- package/dist/src/repo/service.d.ts +52 -0
- package/dist/src/repo/service.d.ts.map +1 -0
- package/dist/src/repo/service.js +181 -0
- package/dist/src/repo/service.js.map +1 -0
- package/dist/src/repo/types.d.ts +7 -0
- package/dist/src/repo/types.d.ts.map +1 -0
- package/dist/src/repo/types.js +2 -0
- package/dist/src/repo/types.js.map +1 -0
- package/dist/src/routing/libp2p-known-peers.d.ts +4 -0
- package/dist/src/routing/libp2p-known-peers.d.ts.map +1 -0
- package/dist/src/routing/libp2p-known-peers.js +19 -0
- package/dist/src/routing/libp2p-known-peers.js.map +1 -0
- package/dist/src/routing/responsibility.d.ts +14 -0
- package/dist/src/routing/responsibility.d.ts.map +1 -0
- package/dist/src/routing/responsibility.js +45 -0
- package/dist/src/routing/responsibility.js.map +1 -0
- package/dist/src/routing/simple-cluster-coordinator.d.ts +23 -0
- package/dist/src/routing/simple-cluster-coordinator.d.ts.map +1 -0
- package/dist/src/routing/simple-cluster-coordinator.js +59 -0
- package/dist/src/routing/simple-cluster-coordinator.js.map +1 -0
- package/dist/src/storage/arachnode-fret-adapter.d.ts +65 -0
- package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -0
- package/dist/src/storage/arachnode-fret-adapter.js +93 -0
- package/dist/src/storage/arachnode-fret-adapter.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +31 -0
- package/dist/src/storage/block-storage.d.ts.map +1 -0
- package/dist/src/storage/block-storage.js +154 -0
- package/dist/src/storage/block-storage.js.map +1 -0
- package/dist/src/storage/file-storage.d.ts +30 -0
- package/dist/src/storage/file-storage.d.ts.map +1 -0
- package/dist/src/storage/file-storage.js +127 -0
- package/dist/src/storage/file-storage.js.map +1 -0
- package/dist/src/storage/helpers.d.ts +3 -0
- package/dist/src/storage/helpers.d.ts.map +1 -0
- package/dist/src/storage/helpers.js +28 -0
- package/dist/src/storage/helpers.js.map +1 -0
- package/dist/src/storage/i-block-storage.d.ts +32 -0
- package/dist/src/storage/i-block-storage.d.ts.map +1 -0
- package/dist/src/storage/i-block-storage.js +2 -0
- package/dist/src/storage/i-block-storage.js.map +1 -0
- package/dist/src/storage/i-raw-storage.d.ts +20 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -0
- package/dist/src/storage/i-raw-storage.js +2 -0
- package/dist/src/storage/i-raw-storage.js.map +1 -0
- package/dist/src/storage/memory-storage.d.ts +27 -0
- package/dist/src/storage/memory-storage.d.ts.map +1 -0
- package/dist/src/storage/memory-storage.js +87 -0
- package/dist/src/storage/memory-storage.js.map +1 -0
- package/dist/src/storage/restoration-coordinator-v2.d.ts +63 -0
- package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -0
- package/dist/src/storage/restoration-coordinator-v2.js +157 -0
- package/dist/src/storage/restoration-coordinator-v2.js.map +1 -0
- package/dist/src/storage/ring-selector.d.ts +56 -0
- package/dist/src/storage/ring-selector.d.ts.map +1 -0
- package/dist/src/storage/ring-selector.js +118 -0
- package/dist/src/storage/ring-selector.js.map +1 -0
- package/dist/src/storage/storage-monitor.d.ts +23 -0
- package/dist/src/storage/storage-monitor.d.ts.map +1 -0
- package/dist/src/storage/storage-monitor.js +40 -0
- package/dist/src/storage/storage-monitor.js.map +1 -0
- package/dist/src/storage/storage-repo.d.ts +17 -0
- package/dist/src/storage/storage-repo.d.ts.map +1 -0
- package/dist/src/storage/storage-repo.js +267 -0
- package/dist/src/storage/storage-repo.js.map +1 -0
- package/dist/src/storage/struct.d.ts +29 -0
- package/dist/src/storage/struct.d.ts.map +1 -0
- package/dist/src/storage/struct.js +2 -0
- package/dist/src/storage/struct.js.map +1 -0
- package/dist/src/sync/client.d.ts +27 -0
- package/dist/src/sync/client.d.ts.map +1 -0
- package/dist/src/sync/client.js +32 -0
- package/dist/src/sync/client.js.map +1 -0
- package/dist/src/sync/protocol.d.ts +58 -0
- package/dist/src/sync/protocol.d.ts.map +1 -0
- package/dist/src/sync/protocol.js +12 -0
- package/dist/src/sync/protocol.js.map +1 -0
- package/dist/src/sync/service.d.ts +62 -0
- package/dist/src/sync/service.d.ts.map +1 -0
- package/dist/src/sync/service.js +168 -0
- package/dist/src/sync/service.js.map +1 -0
- package/package.json +73 -0
- package/readme.md +497 -0
- package/src/cluster/client.ts +63 -0
- package/src/cluster/cluster-repo.ts +711 -0
- package/src/cluster/partition-detector.ts +158 -0
- package/src/cluster/service.ts +156 -0
- package/src/index.ts +30 -0
- package/src/it-utility.ts +36 -0
- package/src/libp2p-key-network.ts +334 -0
- package/src/libp2p-node.ts +335 -0
- package/src/logger.ts +9 -0
- package/src/network/get-network-manager.ts +17 -0
- package/src/network/network-manager-service.ts +334 -0
- package/src/peer-utils.ts +24 -0
- package/src/protocol-client.ts +54 -0
- package/src/repo/client.ts +112 -0
- package/src/repo/cluster-coordinator.ts +592 -0
- package/src/repo/coordinator-repo.ts +137 -0
- package/src/repo/redirect.ts +17 -0
- package/src/repo/service.ts +219 -0
- package/src/repo/types.ts +7 -0
- package/src/routing/libp2p-known-peers.ts +26 -0
- package/src/routing/responsibility.ts +63 -0
- package/src/routing/simple-cluster-coordinator.ts +70 -0
- package/src/storage/arachnode-fret-adapter.ts +128 -0
- package/src/storage/block-storage.ts +182 -0
- package/src/storage/file-storage.ts +163 -0
- package/src/storage/helpers.ts +29 -0
- package/src/storage/i-block-storage.ts +40 -0
- package/src/storage/i-raw-storage.ts +30 -0
- package/src/storage/memory-storage.ts +108 -0
- package/src/storage/restoration-coordinator-v2.ts +191 -0
- package/src/storage/ring-selector.ts +155 -0
- package/src/storage/storage-monitor.ts +59 -0
- package/src/storage/storage-repo.ts +320 -0
- package/src/storage/struct.ts +34 -0
- package/src/sync/client.ts +42 -0
- package/src/sync/protocol.ts +71 -0
- package/src/sync/service.ts +229 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export interface PeerStatus {
|
|
2
|
+
peerId: string;
|
|
3
|
+
lastSeen: number;
|
|
4
|
+
lastGoodbye?: number;
|
|
5
|
+
consecutiveFailures: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detects potential network partitions by tracking peer health,
|
|
10
|
+
* goodbye messages, and sudden mass unreachability events.
|
|
11
|
+
*/
|
|
12
|
+
export class PartitionDetector {
|
|
13
|
+
private peerStatuses: Map<string, PeerStatus> = new Map();
|
|
14
|
+
private readonly unreachableThreshold = 3; // consecutive failures
|
|
15
|
+
private readonly rapidChurnThreshold = 5; // peers
|
|
16
|
+
private readonly rapidChurnWindow = 10000; // 10 seconds
|
|
17
|
+
private readonly peerTimeoutMs = 60000; // 1 minute
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Record successful communication with a peer
|
|
21
|
+
*/
|
|
22
|
+
recordSuccess(peerId: string): void {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const status = this.peerStatuses.get(peerId);
|
|
25
|
+
|
|
26
|
+
if (status) {
|
|
27
|
+
status.lastSeen = now;
|
|
28
|
+
status.consecutiveFailures = 0;
|
|
29
|
+
} else {
|
|
30
|
+
this.peerStatuses.set(peerId, {
|
|
31
|
+
peerId,
|
|
32
|
+
lastSeen: now,
|
|
33
|
+
consecutiveFailures: 0
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Clean up old peer records
|
|
38
|
+
this.cleanupOldPeers();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Record failed communication attempt with a peer
|
|
43
|
+
*/
|
|
44
|
+
recordFailure(peerId: string): void {
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const status = this.peerStatuses.get(peerId);
|
|
47
|
+
|
|
48
|
+
if (status) {
|
|
49
|
+
status.consecutiveFailures++;
|
|
50
|
+
status.lastSeen = now;
|
|
51
|
+
} else {
|
|
52
|
+
this.peerStatuses.set(peerId, {
|
|
53
|
+
peerId,
|
|
54
|
+
lastSeen: now,
|
|
55
|
+
consecutiveFailures: 1
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Record explicit goodbye message from a peer
|
|
62
|
+
*/
|
|
63
|
+
recordGoodbye(peerId: string): void {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const status = this.peerStatuses.get(peerId);
|
|
66
|
+
|
|
67
|
+
if (status) {
|
|
68
|
+
status.lastGoodbye = now;
|
|
69
|
+
status.lastSeen = now;
|
|
70
|
+
} else {
|
|
71
|
+
this.peerStatuses.set(peerId, {
|
|
72
|
+
peerId,
|
|
73
|
+
lastSeen: now,
|
|
74
|
+
lastGoodbye: now,
|
|
75
|
+
consecutiveFailures: 0
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Detect if we're likely in a network partition
|
|
82
|
+
* Returns true if sudden mass unreachability or rapid goodbye rate
|
|
83
|
+
*/
|
|
84
|
+
detectPartition(): boolean {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
|
|
87
|
+
// Count recent goodbyes
|
|
88
|
+
const recentGoodbyes = this.getRecentGoodbyes(this.rapidChurnWindow);
|
|
89
|
+
|
|
90
|
+
// Count unreachable peers
|
|
91
|
+
const unreachable = Array.from(this.peerStatuses.values()).filter(
|
|
92
|
+
s => s.consecutiveFailures >= this.unreachableThreshold
|
|
93
|
+
&& !s.lastGoodbye // Exclude peers that said goodbye
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Sudden mass unreachability suggests partition
|
|
97
|
+
const totalChurn = recentGoodbyes.length + unreachable.length;
|
|
98
|
+
|
|
99
|
+
return totalChurn >= this.rapidChurnThreshold;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get list of currently unreachable peers
|
|
104
|
+
*/
|
|
105
|
+
getUnreachablePeers(): string[] {
|
|
106
|
+
return Array.from(this.peerStatuses.values())
|
|
107
|
+
.filter(s => s.consecutiveFailures >= this.unreachableThreshold && !s.lastGoodbye)
|
|
108
|
+
.map(s => s.peerId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get recent goodbye messages within the specified window
|
|
113
|
+
*/
|
|
114
|
+
private getRecentGoodbyes(windowMs: number): PeerStatus[] {
|
|
115
|
+
const cutoff = Date.now() - windowMs;
|
|
116
|
+
return Array.from(this.peerStatuses.values()).filter(
|
|
117
|
+
s => s.lastGoodbye && s.lastGoodbye > cutoff
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Clean up peer records that haven't been seen recently
|
|
123
|
+
*/
|
|
124
|
+
private cleanupOldPeers(): void {
|
|
125
|
+
const cutoff = Date.now() - this.peerTimeoutMs;
|
|
126
|
+
for (const [peerId, status] of this.peerStatuses.entries()) {
|
|
127
|
+
if (status.lastSeen < cutoff) {
|
|
128
|
+
this.peerStatuses.delete(peerId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get statistics for monitoring
|
|
135
|
+
*/
|
|
136
|
+
getStatistics(): {
|
|
137
|
+
totalPeers: number;
|
|
138
|
+
unreachable: number;
|
|
139
|
+
recentGoodbyes: number;
|
|
140
|
+
} {
|
|
141
|
+
const unreachable = this.getUnreachablePeers().length;
|
|
142
|
+
const recentGoodbyes = this.getRecentGoodbyes(this.rapidChurnWindow).length;
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
totalPeers: this.peerStatuses.size,
|
|
146
|
+
unreachable,
|
|
147
|
+
recentGoodbyes
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Reset all tracked peer states (useful for testing)
|
|
153
|
+
*/
|
|
154
|
+
reset(): void {
|
|
155
|
+
this.peerStatuses.clear();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { pipe } from 'it-pipe';
|
|
2
|
+
import { decode as lpDecode, encode as lpEncode } from 'it-length-prefixed';
|
|
3
|
+
import type { Startable, Logger, IncomingStreamData } from '@libp2p/interface';
|
|
4
|
+
import type { ICluster, ClusterRecord } from '@optimystic/db-core';
|
|
5
|
+
import { computeResponsibility } from '../routing/responsibility.js'
|
|
6
|
+
import { peersEqual } from '../peer-utils.js'
|
|
7
|
+
import { buildKnownPeers } from '../routing/libp2p-known-peers.js'
|
|
8
|
+
import { encodePeers } from '../repo/redirect.js'
|
|
9
|
+
import type { Uint8ArrayList } from 'uint8arraylist';
|
|
10
|
+
|
|
11
|
+
interface BaseComponents {
|
|
12
|
+
logger: { forComponent: (name: string) => Logger },
|
|
13
|
+
registrar: {
|
|
14
|
+
handle: (protocol: string, handler: (data: IncomingStreamData) => void, options: any) => Promise<void>,
|
|
15
|
+
unhandle: (protocol: string) => Promise<void>
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ClusterServiceComponents extends BaseComponents {
|
|
20
|
+
cluster: ICluster
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ClusterServiceInit {
|
|
24
|
+
protocol?: string,
|
|
25
|
+
protocolPrefix?: string,
|
|
26
|
+
maxInboundStreams?: number,
|
|
27
|
+
maxOutboundStreams?: number,
|
|
28
|
+
logPrefix?: string,
|
|
29
|
+
kBucketSize?: number,
|
|
30
|
+
configuredClusterSize?: number,
|
|
31
|
+
allowClusterDownsize?: boolean,
|
|
32
|
+
clusterSizeTolerance?: number,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function clusterService(init: ClusterServiceInit = {}): (components: ClusterServiceComponents) => ClusterService {
|
|
36
|
+
return (components: ClusterServiceComponents) => new ClusterService(components, init);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A libp2p service that handles cluster protocol messages
|
|
41
|
+
*/
|
|
42
|
+
export class ClusterService implements Startable {
|
|
43
|
+
private readonly protocol: string;
|
|
44
|
+
private readonly maxInboundStreams: number;
|
|
45
|
+
private readonly maxOutboundStreams: number;
|
|
46
|
+
private readonly log: Logger;
|
|
47
|
+
private readonly cluster: ICluster;
|
|
48
|
+
private readonly components: ClusterServiceComponents;
|
|
49
|
+
private running: boolean;
|
|
50
|
+
private readonly k: number;
|
|
51
|
+
private readonly configuredClusterSize: number;
|
|
52
|
+
private readonly allowDownsize: boolean;
|
|
53
|
+
private readonly sizeTolerance: number;
|
|
54
|
+
|
|
55
|
+
constructor(components: ClusterServiceComponents, init: ClusterServiceInit = {}) {
|
|
56
|
+
this.components = components;
|
|
57
|
+
this.protocol = init.protocol ?? (init.protocolPrefix ?? '/db-p2p') + '/cluster/1.0.0';
|
|
58
|
+
this.maxInboundStreams = init.maxInboundStreams ?? 32;
|
|
59
|
+
this.maxOutboundStreams = init.maxOutboundStreams ?? 64;
|
|
60
|
+
this.log = components.logger.forComponent(init.logPrefix ?? 'db-p2p:cluster');
|
|
61
|
+
this.cluster = components.cluster;
|
|
62
|
+
this.running = false;
|
|
63
|
+
this.k = init.kBucketSize ?? 10;
|
|
64
|
+
this.configuredClusterSize = init.configuredClusterSize ?? 10;
|
|
65
|
+
this.allowDownsize = init.allowClusterDownsize ?? true;
|
|
66
|
+
this.sizeTolerance = init.clusterSizeTolerance ?? 0.5;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
readonly [Symbol.toStringTag] = '@libp2p/cluster';
|
|
70
|
+
|
|
71
|
+
async start(): Promise<void> {
|
|
72
|
+
if (this.running) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await this.components.registrar.handle(this.protocol, this.handleIncomingStream.bind(this), {
|
|
77
|
+
maxInboundStreams: this.maxInboundStreams,
|
|
78
|
+
maxOutboundStreams: this.maxOutboundStreams
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.running = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async stop(): Promise<void> {
|
|
85
|
+
if (!this.running) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await this.components.registrar.unhandle(this.protocol);
|
|
90
|
+
this.running = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private handleIncomingStream(data: IncomingStreamData): void {
|
|
94
|
+
const { stream, connection } = data;
|
|
95
|
+
const peerId = connection.remotePeer;
|
|
96
|
+
|
|
97
|
+
const processStream = async function* (this: ClusterService, source: AsyncIterable<Uint8ArrayList>) {
|
|
98
|
+
for await (const msg of source) {
|
|
99
|
+
// Decode the message
|
|
100
|
+
const decoded = new TextDecoder().decode(msg.subarray());
|
|
101
|
+
const message = JSON.parse(decoded) as { operation: string; record: ClusterRecord };
|
|
102
|
+
|
|
103
|
+
// Process the operation
|
|
104
|
+
let response: any;
|
|
105
|
+
if (message.operation === 'update') {
|
|
106
|
+
// Use message.record.message as key source; this is RepoMessage carrying block IDs
|
|
107
|
+
const tailId = (message.record?.message as any)?.commit?.tailId ?? (message.record?.message as any)?.pend ? Object.keys((message.record as any).message.pend.transforms)[0] : undefined
|
|
108
|
+
// TEMPORARY: Disable cluster membership check to fix empty promises issue
|
|
109
|
+
// The membership check was causing peers to return redirect responses
|
|
110
|
+
// instead of processing cluster updates, leading to empty promise arrays.
|
|
111
|
+
// TODO: Re-enable and fix cluster membership logic for proper DHT routing
|
|
112
|
+
response = await this.cluster.update(message.record)
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error(`Unknown operation: ${message.operation}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Encode and yield the response
|
|
118
|
+
if (message.operation === 'update') {
|
|
119
|
+
const rec = response as any;
|
|
120
|
+
this.log('cluster-service:pre-serialize', {
|
|
121
|
+
messageHash: rec?.messageHash,
|
|
122
|
+
responseType: typeof response,
|
|
123
|
+
hasPromises: 'promises' in (rec ?? {}),
|
|
124
|
+
hasCommits: 'commits' in (rec ?? {}),
|
|
125
|
+
promiseKeys: Object.keys(rec?.promises ?? {}),
|
|
126
|
+
commitKeys: Object.keys(rec?.commits ?? {}),
|
|
127
|
+
promiseValues: rec?.promises,
|
|
128
|
+
commitValues: rec?.commits
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const serialized = JSON.stringify(response);
|
|
132
|
+
if (message.operation === 'update') {
|
|
133
|
+
const deserialized = JSON.parse(serialized);
|
|
134
|
+
this.log('cluster-service:post-serialize', {
|
|
135
|
+
messageHash: (deserialized as any)?.messageHash,
|
|
136
|
+
promiseKeys: Object.keys((deserialized as any)?.promises ?? {}),
|
|
137
|
+
commitKeys: Object.keys((deserialized as any)?.commits ?? {})
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
yield new TextEncoder().encode(serialized);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
Promise.resolve().then(async () => {
|
|
145
|
+
await pipe(
|
|
146
|
+
stream,
|
|
147
|
+
(source) => lpDecode(source),
|
|
148
|
+
processStream.bind(this),
|
|
149
|
+
(source) => lpEncode(source),
|
|
150
|
+
stream
|
|
151
|
+
);
|
|
152
|
+
}).catch((err: Error) => {
|
|
153
|
+
this.log.error('error handling cluster protocol message from %p - %e', peerId, err);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from "./cluster/client.js";
|
|
2
|
+
export * from "./cluster/cluster-repo.js";
|
|
3
|
+
export * from "./cluster/service.js";
|
|
4
|
+
export * from "./protocol-client.js";
|
|
5
|
+
export * from "./repo/client.js";
|
|
6
|
+
export * from "./repo/cluster-coordinator.js";
|
|
7
|
+
export * from "./repo/coordinator-repo.js";
|
|
8
|
+
export * from "./repo/service.js";
|
|
9
|
+
export * from "./storage/block-storage.js";
|
|
10
|
+
export * from "./storage/file-storage.js";
|
|
11
|
+
export * from "./storage/memory-storage.js";
|
|
12
|
+
export * from "./storage/i-block-storage.js";
|
|
13
|
+
export * from "./storage/i-raw-storage.js";
|
|
14
|
+
export * from "./storage/storage-repo.js";
|
|
15
|
+
export * from "./storage/restoration-coordinator-v2.js";
|
|
16
|
+
export * from "./storage/ring-selector.js";
|
|
17
|
+
export * from "./storage/storage-monitor.js";
|
|
18
|
+
export * from "./storage/arachnode-fret-adapter.js";
|
|
19
|
+
export * from "./sync/protocol.js";
|
|
20
|
+
export * from "./sync/client.js";
|
|
21
|
+
export * from "./sync/service.js";
|
|
22
|
+
export * from "./it-utility.js";
|
|
23
|
+
export * from "./libp2p-key-network.js";
|
|
24
|
+
export * from "./libp2p-node.js";
|
|
25
|
+
export * from "./routing/responsibility.js";
|
|
26
|
+
export * from "./routing/libp2p-known-peers.js";
|
|
27
|
+
export * from "./network/network-manager-service.js";
|
|
28
|
+
export * from "./network/get-network-manager.js";
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export async function first<T>(
|
|
2
|
+
createIterable: (signal: AbortSignal) => AsyncIterable<T>,
|
|
3
|
+
onEmpty: () => T = () => { throw new Error('No items found') },
|
|
4
|
+
timeoutMs?: number
|
|
5
|
+
): Promise<T> {
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
const timer = typeof timeoutMs === 'number' ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
|
|
8
|
+
try {
|
|
9
|
+
for await (const item of createIterable(controller.signal)) {
|
|
10
|
+
return item;
|
|
11
|
+
}
|
|
12
|
+
return onEmpty();
|
|
13
|
+
} finally {
|
|
14
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
15
|
+
controller.abort();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function asyncIteratorToArray<T>(iterator: AsyncIterable<T>): Promise<T[]> {
|
|
20
|
+
const result: T[] = [];
|
|
21
|
+
for await (const item of iterator) {
|
|
22
|
+
result.push(item);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function reduce<TP, TC>(iter: IterableIterator<TC>, each: (prior: TP, current: TC, index: number) => TP, start: TP) {
|
|
28
|
+
let prior = start;
|
|
29
|
+
let i = 0;
|
|
30
|
+
for (let current of iter) {
|
|
31
|
+
prior = each(prior, current, i);
|
|
32
|
+
++i;
|
|
33
|
+
}
|
|
34
|
+
return prior;
|
|
35
|
+
}
|
|
36
|
+
|