agentdb 3.0.0-alpha.11 → 3.0.0-alpha.13
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/backends/graph/GraphDatabaseAdapter.d.ts +54 -0
- package/dist/src/backends/graph/GraphDatabaseAdapter.d.ts.map +1 -1
- package/dist/src/backends/graph/GraphDatabaseAdapter.js +125 -0
- package/dist/src/backends/graph/GraphDatabaseAdapter.js.map +1 -1
- package/dist/src/cli/agentdb-cli.js +0 -0
- package/dist/src/controllers/ReflexionMemory.d.ts +50 -0
- package/dist/src/controllers/ReflexionMemory.d.ts.map +1 -1
- package/dist/src/controllers/ReflexionMemory.js +258 -0
- package/dist/src/controllers/ReflexionMemory.js.map +1 -1
- package/dist/src/controllers/index.d.ts +2 -0
- package/dist/src/controllers/index.d.ts.map +1 -1
- package/dist/src/controllers/index.js +2 -0
- package/dist/src/controllers/index.js.map +1 -1
- package/dist/src/controllers/prerequisites.d.ts +76 -0
- package/dist/src/controllers/prerequisites.d.ts.map +1 -0
- package/dist/src/controllers/prerequisites.js +235 -0
- package/dist/src/controllers/prerequisites.js.map +1 -0
- package/dist/src/db-fallback.d.ts.map +1 -1
- package/dist/src/db-fallback.js +55 -45
- package/dist/src/db-fallback.js.map +1 -1
- package/package.json +1 -1
- package/dist/schemas/frontier-schema.sql +0 -378
- package/dist/schemas/schema.sql +0 -382
- package/dist/src/backends/index.cjs +0 -6
- package/dist/src/backends/ruvector/GuardedVectorBackend.d.ts +0 -93
- package/dist/src/backends/ruvector/GuardedVectorBackend.d.ts.map +0 -1
- package/dist/src/backends/ruvector/GuardedVectorBackend.js +0 -182
- package/dist/src/backends/ruvector/GuardedVectorBackend.js.map +0 -1
- package/dist/src/consensus/RaftConsensus.d.ts +0 -220
- package/dist/src/consensus/RaftConsensus.d.ts.map +0 -1
- package/dist/src/consensus/RaftConsensus.js +0 -762
- package/dist/src/consensus/RaftConsensus.js.map +0 -1
- package/dist/src/controllers/HierarchicalMemory.d.ts +0 -197
- package/dist/src/controllers/HierarchicalMemory.d.ts.map +0 -1
- package/dist/src/controllers/HierarchicalMemory.js +0 -519
- package/dist/src/controllers/HierarchicalMemory.js.map +0 -1
- package/dist/src/controllers/MemoryConsolidation.d.ts +0 -142
- package/dist/src/controllers/MemoryConsolidation.d.ts.map +0 -1
- package/dist/src/controllers/MemoryConsolidation.js +0 -479
- package/dist/src/controllers/MemoryConsolidation.js.map +0 -1
- package/dist/src/controllers/QUICConnection.d.ts +0 -122
- package/dist/src/controllers/QUICConnection.d.ts.map +0 -1
- package/dist/src/controllers/QUICConnection.js +0 -329
- package/dist/src/controllers/QUICConnection.js.map +0 -1
- package/dist/src/controllers/QUICConnectionPool.d.ts +0 -83
- package/dist/src/controllers/QUICConnectionPool.d.ts.map +0 -1
- package/dist/src/controllers/QUICConnectionPool.js +0 -256
- package/dist/src/controllers/QUICConnectionPool.js.map +0 -1
- package/dist/src/controllers/QUICStreamManager.d.ts +0 -114
- package/dist/src/controllers/QUICStreamManager.d.ts.map +0 -1
- package/dist/src/controllers/QUICStreamManager.js +0 -267
- package/dist/src/controllers/QUICStreamManager.js.map +0 -1
- package/dist/src/controllers/StreamingEmbeddingService.d.ts +0 -82
- package/dist/src/controllers/StreamingEmbeddingService.d.ts.map +0 -1
- package/dist/src/controllers/StreamingEmbeddingService.js +0 -243
- package/dist/src/controllers/StreamingEmbeddingService.js.map +0 -1
- package/dist/src/controllers/index.cjs +0 -6
- package/dist/src/coordination/MultiDatabaseCoordinator.d.ts +0 -348
- package/dist/src/coordination/MultiDatabaseCoordinator.d.ts.map +0 -1
- package/dist/src/coordination/MultiDatabaseCoordinator.js +0 -803
- package/dist/src/coordination/MultiDatabaseCoordinator.js.map +0 -1
- package/dist/src/coordination/index.d.ts +0 -10
- package/dist/src/coordination/index.d.ts.map +0 -1
- package/dist/src/coordination/index.js +0 -10
- package/dist/src/coordination/index.js.map +0 -1
- package/dist/src/index.cjs +0 -6
- package/dist/src/optimizations/RVFOptimizer.d.ts +0 -226
- package/dist/src/optimizations/RVFOptimizer.d.ts.map +0 -1
- package/dist/src/optimizations/RVFOptimizer.js +0 -541
- package/dist/src/optimizations/RVFOptimizer.js.map +0 -1
- package/dist/src/security/AttestationLog.d.ts +0 -70
- package/dist/src/security/AttestationLog.d.ts.map +0 -1
- package/dist/src/security/AttestationLog.js +0 -174
- package/dist/src/security/AttestationLog.js.map +0 -1
- package/dist/src/security/MutationGuard.d.ts +0 -83
- package/dist/src/security/MutationGuard.d.ts.map +0 -1
- package/dist/src/security/MutationGuard.js +0 -364
- package/dist/src/security/MutationGuard.js.map +0 -1
- package/dist/src/security/index.cjs +0 -6
- package/dist/src/security/index.d.ts +0 -15
- package/dist/src/security/index.d.ts.map +0 -1
- package/dist/src/security/index.js +0 -18
- package/dist/src/security/index.js.map +0 -1
- package/dist/src/services/GNNService.d.ts +0 -173
- package/dist/src/services/GNNService.d.ts.map +0 -1
- package/dist/src/services/GNNService.js +0 -639
- package/dist/src/services/GNNService.js.map +0 -1
- package/dist/src/services/GraphTransformerService.d.ts +0 -80
- package/dist/src/services/GraphTransformerService.d.ts.map +0 -1
- package/dist/src/services/GraphTransformerService.js +0 -369
- package/dist/src/services/GraphTransformerService.js.map +0 -1
- package/dist/src/services/SemanticRouter.d.ts +0 -83
- package/dist/src/services/SemanticRouter.d.ts.map +0 -1
- package/dist/src/services/SemanticRouter.js +0 -160
- package/dist/src/services/SemanticRouter.js.map +0 -1
- package/dist/src/services/SonaTrajectoryService.d.ts +0 -224
- package/dist/src/services/SonaTrajectoryService.d.ts.map +0 -1
- package/dist/src/services/SonaTrajectoryService.js +0 -539
- package/dist/src/services/SonaTrajectoryService.js.map +0 -1
- package/dist/src/utils/LegacyAttentionAdapter.d.ts +0 -93
- package/dist/src/utils/LegacyAttentionAdapter.d.ts.map +0 -1
- package/dist/src/utils/LegacyAttentionAdapter.js +0 -241
- package/dist/src/utils/LegacyAttentionAdapter.js.map +0 -1
- package/dist/src/utils/vector-math.d.ts +0 -29
- package/dist/src/utils/vector-math.d.ts.map +0 -1
- package/dist/src/utils/vector-math.js +0 -66
- package/dist/src/utils/vector-math.js.map +0 -1
|
@@ -1,762 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RaftConsensus - Fault-Tolerant Distributed Consensus
|
|
3
|
-
*
|
|
4
|
-
* Implements the Raft consensus algorithm for multi-agent coordination with:
|
|
5
|
-
* - Leader election with automatic failover (<1s)
|
|
6
|
-
* - Log replication with strong consistency
|
|
7
|
-
* - Byzantine fault tolerance (BFT)
|
|
8
|
-
* - Gossip protocols for large-scale coordination
|
|
9
|
-
* - CRDT synchronization for eventually consistent state
|
|
10
|
-
* - Distributed locks with deadlock detection
|
|
11
|
-
* - Automatic sharding and partitioning
|
|
12
|
-
*
|
|
13
|
-
* References:
|
|
14
|
-
* - Raft Paper: https://raft.github.io/raft.pdf
|
|
15
|
-
* - Byzantine Fault Tolerance: https://pmg.csail.mit.edu/papers/osdi99.pdf
|
|
16
|
-
*/
|
|
17
|
-
import { EventEmitter } from 'events';
|
|
18
|
-
import crypto from 'crypto';
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// RaftConsensus Implementation
|
|
21
|
-
// ============================================================================
|
|
22
|
-
export class RaftConsensus extends EventEmitter {
|
|
23
|
-
config;
|
|
24
|
-
state;
|
|
25
|
-
peers = new Map();
|
|
26
|
-
electionTimer = null;
|
|
27
|
-
heartbeatTimer = null;
|
|
28
|
-
votesReceived = new Set();
|
|
29
|
-
commandQueue = new Map();
|
|
30
|
-
// Byzantine Fault Tolerance
|
|
31
|
-
privateKey;
|
|
32
|
-
publicKeys = new Map();
|
|
33
|
-
// Gossip Protocol
|
|
34
|
-
gossipInterval = null;
|
|
35
|
-
gossipState = new Map();
|
|
36
|
-
// CRDT State
|
|
37
|
-
crdtStates = new Map();
|
|
38
|
-
// Distributed Locks
|
|
39
|
-
locks = new Map();
|
|
40
|
-
lockCheckInterval = null;
|
|
41
|
-
// Metrics
|
|
42
|
-
metrics = {
|
|
43
|
-
totalElections: 0,
|
|
44
|
-
totalLeaderChanges: 0,
|
|
45
|
-
totalCommittedEntries: 0,
|
|
46
|
-
lastElectionTime: 0,
|
|
47
|
-
avgElectionTimeMs: 0,
|
|
48
|
-
uptime: Date.now(),
|
|
49
|
-
};
|
|
50
|
-
constructor(config) {
|
|
51
|
-
super();
|
|
52
|
-
this.config = {
|
|
53
|
-
nodeId: config.nodeId,
|
|
54
|
-
nodes: config.nodes,
|
|
55
|
-
electionTimeoutMin: config.electionTimeoutMin || 150,
|
|
56
|
-
electionTimeoutMax: config.electionTimeoutMax || 300,
|
|
57
|
-
heartbeatInterval: config.heartbeatInterval || 50,
|
|
58
|
-
maxEntriesPerRequest: config.maxEntriesPerRequest || 100,
|
|
59
|
-
snapshotThreshold: config.snapshotThreshold || 10000,
|
|
60
|
-
byzantineTolerance: config.byzantineTolerance || false,
|
|
61
|
-
quorumSize: config.quorumSize || Math.floor(config.nodes.length / 2) + 1,
|
|
62
|
-
};
|
|
63
|
-
// Initialize state
|
|
64
|
-
this.state = {
|
|
65
|
-
currentTerm: 0,
|
|
66
|
-
votedFor: null,
|
|
67
|
-
log: [],
|
|
68
|
-
commitIndex: 0,
|
|
69
|
-
lastApplied: 0,
|
|
70
|
-
state: 'follower',
|
|
71
|
-
leaderId: null,
|
|
72
|
-
};
|
|
73
|
-
// Initialize peer states
|
|
74
|
-
for (const nodeId of this.config.nodes) {
|
|
75
|
-
if (nodeId !== this.config.nodeId) {
|
|
76
|
-
this.peers.set(nodeId, {
|
|
77
|
-
nodeId,
|
|
78
|
-
nextIndex: 1,
|
|
79
|
-
matchIndex: 0,
|
|
80
|
-
lastHeartbeat: Date.now(),
|
|
81
|
-
isHealthy: true,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// Setup Byzantine fault tolerance
|
|
86
|
-
if (this.config.byzantineTolerance) {
|
|
87
|
-
this.setupBFT();
|
|
88
|
-
}
|
|
89
|
-
// Start as follower
|
|
90
|
-
this.becomeFollower(0);
|
|
91
|
-
}
|
|
92
|
-
// ============================================================================
|
|
93
|
-
// Public API
|
|
94
|
-
// ============================================================================
|
|
95
|
-
/**
|
|
96
|
-
* Start the Raft node
|
|
97
|
-
*/
|
|
98
|
-
start() {
|
|
99
|
-
this.resetElectionTimer();
|
|
100
|
-
this.startGossipProtocol();
|
|
101
|
-
this.startLockMonitoring();
|
|
102
|
-
this.emit('started', { nodeId: this.config.nodeId });
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Stop the Raft node
|
|
106
|
-
*/
|
|
107
|
-
stop() {
|
|
108
|
-
this.clearElectionTimer();
|
|
109
|
-
this.clearHeartbeatTimer();
|
|
110
|
-
this.stopGossipProtocol();
|
|
111
|
-
this.stopLockMonitoring();
|
|
112
|
-
this.emit('stopped', { nodeId: this.config.nodeId });
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Replicate a command to the cluster
|
|
116
|
-
*/
|
|
117
|
-
async replicate(command, timeout = 5000) {
|
|
118
|
-
if (this.state.state !== 'leader') {
|
|
119
|
-
throw new Error('Not the leader. Leader is: ' + (this.state.leaderId || 'unknown'));
|
|
120
|
-
}
|
|
121
|
-
const commandId = crypto.randomUUID();
|
|
122
|
-
const entry = {
|
|
123
|
-
term: this.state.currentTerm,
|
|
124
|
-
index: this.state.log.length + 1,
|
|
125
|
-
type: 'command',
|
|
126
|
-
command,
|
|
127
|
-
timestamp: Date.now(),
|
|
128
|
-
};
|
|
129
|
-
// Sign entry for BFT
|
|
130
|
-
if (this.config.byzantineTolerance) {
|
|
131
|
-
entry.signature = this.signData(entry);
|
|
132
|
-
}
|
|
133
|
-
this.state.log.push(entry);
|
|
134
|
-
this.emit('log_appended', entry);
|
|
135
|
-
// Wait for replication
|
|
136
|
-
return new Promise((resolve, reject) => {
|
|
137
|
-
const timer = setTimeout(() => {
|
|
138
|
-
this.commandQueue.delete(commandId);
|
|
139
|
-
reject(new Error('Replication timeout'));
|
|
140
|
-
}, timeout);
|
|
141
|
-
this.commandQueue.set(commandId, (result) => {
|
|
142
|
-
clearTimeout(timer);
|
|
143
|
-
resolve(result);
|
|
144
|
-
});
|
|
145
|
-
// Trigger immediate replication
|
|
146
|
-
this.replicateToFollowers();
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Handle vote request from candidate
|
|
151
|
-
*/
|
|
152
|
-
handleVoteRequest(request) {
|
|
153
|
-
// Verify signature for BFT
|
|
154
|
-
if (this.config.byzantineTolerance && !this.verifySignature(request, request.signature)) {
|
|
155
|
-
return {
|
|
156
|
-
term: this.state.currentTerm,
|
|
157
|
-
voteGranted: false,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
// If candidate's term is older, reject
|
|
161
|
-
if (request.term < this.state.currentTerm) {
|
|
162
|
-
return {
|
|
163
|
-
term: this.state.currentTerm,
|
|
164
|
-
voteGranted: false,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
// Update term if candidate's term is newer
|
|
168
|
-
if (request.term > this.state.currentTerm) {
|
|
169
|
-
this.becomeFollower(request.term);
|
|
170
|
-
}
|
|
171
|
-
// Check if we can vote for this candidate
|
|
172
|
-
const canVote = (this.state.votedFor === null || this.state.votedFor === request.candidateId) &&
|
|
173
|
-
this.isLogUpToDate(request.lastLogIndex, request.lastLogTerm);
|
|
174
|
-
if (canVote) {
|
|
175
|
-
this.state.votedFor = request.candidateId;
|
|
176
|
-
this.resetElectionTimer();
|
|
177
|
-
}
|
|
178
|
-
const response = {
|
|
179
|
-
term: this.state.currentTerm,
|
|
180
|
-
voteGranted: canVote,
|
|
181
|
-
};
|
|
182
|
-
// Sign response for BFT
|
|
183
|
-
if (this.config.byzantineTolerance) {
|
|
184
|
-
response.signature = this.signData(response);
|
|
185
|
-
}
|
|
186
|
-
return response;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Handle append entries request from leader
|
|
190
|
-
*/
|
|
191
|
-
handleAppendEntries(request) {
|
|
192
|
-
// Verify signature for BFT
|
|
193
|
-
if (this.config.byzantineTolerance && !this.verifySignature(request, request.signature)) {
|
|
194
|
-
return {
|
|
195
|
-
term: this.state.currentTerm,
|
|
196
|
-
success: false,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
// If leader's term is older, reject
|
|
200
|
-
if (request.term < this.state.currentTerm) {
|
|
201
|
-
return {
|
|
202
|
-
term: this.state.currentTerm,
|
|
203
|
-
success: false,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
// Valid leader, reset election timer
|
|
207
|
-
this.resetElectionTimer();
|
|
208
|
-
// Update term and leader if needed
|
|
209
|
-
if (request.term > this.state.currentTerm) {
|
|
210
|
-
this.becomeFollower(request.term);
|
|
211
|
-
}
|
|
212
|
-
this.state.leaderId = request.leaderId;
|
|
213
|
-
// Check log consistency
|
|
214
|
-
if (request.prevLogIndex > 0) {
|
|
215
|
-
const prevEntry = this.state.log[request.prevLogIndex - 1];
|
|
216
|
-
if (!prevEntry || prevEntry.term !== request.prevLogTerm) {
|
|
217
|
-
return {
|
|
218
|
-
term: this.state.currentTerm,
|
|
219
|
-
success: false,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
// Append new entries
|
|
224
|
-
if (request.entries.length > 0) {
|
|
225
|
-
// Remove conflicting entries
|
|
226
|
-
this.state.log = this.state.log.slice(0, request.prevLogIndex);
|
|
227
|
-
// Append new entries
|
|
228
|
-
for (const entry of request.entries) {
|
|
229
|
-
this.state.log.push(entry);
|
|
230
|
-
}
|
|
231
|
-
this.emit('log_replicated', { entries: request.entries });
|
|
232
|
-
}
|
|
233
|
-
// Update commit index
|
|
234
|
-
if (request.leaderCommit > this.state.commitIndex) {
|
|
235
|
-
this.state.commitIndex = Math.min(request.leaderCommit, this.state.log.length);
|
|
236
|
-
this.applyCommittedEntries();
|
|
237
|
-
}
|
|
238
|
-
const response = {
|
|
239
|
-
term: this.state.currentTerm,
|
|
240
|
-
success: true,
|
|
241
|
-
matchIndex: this.state.log.length,
|
|
242
|
-
};
|
|
243
|
-
// Sign response for BFT
|
|
244
|
-
if (this.config.byzantineTolerance) {
|
|
245
|
-
response.signature = this.signData(response);
|
|
246
|
-
}
|
|
247
|
-
return response;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Handle vote response (exposed for testing/network layer)
|
|
251
|
-
*/
|
|
252
|
-
handleVoteResponse(response, peerId) {
|
|
253
|
-
this.processVoteResponse(response, peerId);
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Handle append entries response (exposed for testing/network layer)
|
|
257
|
-
*/
|
|
258
|
-
handleAppendEntriesResponse(response, peerId) {
|
|
259
|
-
this.processAppendEntriesResponse(response, peerId);
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Get current cluster status
|
|
263
|
-
*/
|
|
264
|
-
getStatus() {
|
|
265
|
-
return {
|
|
266
|
-
nodeId: this.config.nodeId,
|
|
267
|
-
state: this.state.state,
|
|
268
|
-
term: this.state.currentTerm,
|
|
269
|
-
leaderId: this.state.leaderId,
|
|
270
|
-
logLength: this.state.log.length,
|
|
271
|
-
commitIndex: this.state.commitIndex,
|
|
272
|
-
lastApplied: this.state.lastApplied,
|
|
273
|
-
peers: Array.from(this.peers.values()).map(p => ({
|
|
274
|
-
nodeId: p.nodeId,
|
|
275
|
-
isHealthy: p.isHealthy,
|
|
276
|
-
matchIndex: p.matchIndex,
|
|
277
|
-
})),
|
|
278
|
-
metrics: this.metrics,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
// ============================================================================
|
|
282
|
-
// CRDT Operations
|
|
283
|
-
// ============================================================================
|
|
284
|
-
/**
|
|
285
|
-
* Update CRDT state with eventual consistency
|
|
286
|
-
*/
|
|
287
|
-
updateCRDT(key, operation, value) {
|
|
288
|
-
let state = this.crdtStates.get(key);
|
|
289
|
-
if (!state) {
|
|
290
|
-
state = {
|
|
291
|
-
type: 'counter',
|
|
292
|
-
value: 0,
|
|
293
|
-
vectorClock: new Map([[this.config.nodeId, 0]]),
|
|
294
|
-
};
|
|
295
|
-
this.crdtStates.set(key, state);
|
|
296
|
-
}
|
|
297
|
-
// Update vector clock
|
|
298
|
-
const currentClock = state.vectorClock.get(this.config.nodeId) || 0;
|
|
299
|
-
state.vectorClock.set(this.config.nodeId, currentClock + 1);
|
|
300
|
-
// Apply operation
|
|
301
|
-
switch (operation) {
|
|
302
|
-
case 'increment':
|
|
303
|
-
state.value = (state.value || 0) + value;
|
|
304
|
-
break;
|
|
305
|
-
case 'add':
|
|
306
|
-
if (!Array.isArray(state.value))
|
|
307
|
-
state.value = [];
|
|
308
|
-
state.value.push(value);
|
|
309
|
-
break;
|
|
310
|
-
case 'set':
|
|
311
|
-
state.value = value;
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
// Gossip update to peers
|
|
315
|
-
this.gossipCRDTUpdate(key, state);
|
|
316
|
-
this.emit('crdt_updated', { key, state });
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Get CRDT value
|
|
320
|
-
*/
|
|
321
|
-
getCRDT(key) {
|
|
322
|
-
return this.crdtStates.get(key)?.value;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Merge CRDT state from remote node
|
|
326
|
-
*/
|
|
327
|
-
mergeCRDT(key, remoteState) {
|
|
328
|
-
const localState = this.crdtStates.get(key);
|
|
329
|
-
if (!localState) {
|
|
330
|
-
this.crdtStates.set(key, remoteState);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
// Merge vector clocks
|
|
334
|
-
for (const [nodeId, clock] of remoteState.vectorClock.entries()) {
|
|
335
|
-
const localClock = localState.vectorClock.get(nodeId) || 0;
|
|
336
|
-
localState.vectorClock.set(nodeId, Math.max(localClock, clock));
|
|
337
|
-
}
|
|
338
|
-
// Merge values based on type
|
|
339
|
-
if (localState.type === 'counter') {
|
|
340
|
-
localState.value = Math.max(localState.value, remoteState.value);
|
|
341
|
-
}
|
|
342
|
-
else if (localState.type === 'set') {
|
|
343
|
-
localState.value = [...new Set([...localState.value, ...remoteState.value])];
|
|
344
|
-
}
|
|
345
|
-
this.emit('crdt_merged', { key, state: localState });
|
|
346
|
-
}
|
|
347
|
-
// ============================================================================
|
|
348
|
-
// Distributed Locks
|
|
349
|
-
// ============================================================================
|
|
350
|
-
/**
|
|
351
|
-
* Acquire distributed lock
|
|
352
|
-
*/
|
|
353
|
-
async acquireLock(key, ttlMs = 30000) {
|
|
354
|
-
if (this.state.state !== 'leader') {
|
|
355
|
-
throw new Error('Only leader can manage locks');
|
|
356
|
-
}
|
|
357
|
-
const lock = this.locks.get(key);
|
|
358
|
-
const now = Date.now();
|
|
359
|
-
// Check if lock is available
|
|
360
|
-
if (lock && lock.holder && lock.expiresAt > now) {
|
|
361
|
-
// Lock is held, add to waiters
|
|
362
|
-
lock.waiters.push(this.config.nodeId);
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
// Acquire lock
|
|
366
|
-
this.locks.set(key, {
|
|
367
|
-
key,
|
|
368
|
-
holder: this.config.nodeId,
|
|
369
|
-
acquiredAt: now,
|
|
370
|
-
expiresAt: now + ttlMs,
|
|
371
|
-
waiters: [],
|
|
372
|
-
});
|
|
373
|
-
// Replicate lock acquisition
|
|
374
|
-
await this.replicate({
|
|
375
|
-
type: 'lock_acquire',
|
|
376
|
-
key,
|
|
377
|
-
holder: this.config.nodeId,
|
|
378
|
-
ttlMs,
|
|
379
|
-
});
|
|
380
|
-
this.emit('lock_acquired', { key, holder: this.config.nodeId });
|
|
381
|
-
return true;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Release distributed lock
|
|
385
|
-
*/
|
|
386
|
-
async releaseLock(key) {
|
|
387
|
-
if (this.state.state !== 'leader') {
|
|
388
|
-
throw new Error('Only leader can manage locks');
|
|
389
|
-
}
|
|
390
|
-
const lock = this.locks.get(key);
|
|
391
|
-
if (!lock || lock.holder !== this.config.nodeId) {
|
|
392
|
-
throw new Error('Lock not held by this node');
|
|
393
|
-
}
|
|
394
|
-
// Release lock
|
|
395
|
-
this.locks.delete(key);
|
|
396
|
-
// Replicate lock release
|
|
397
|
-
await this.replicate({
|
|
398
|
-
type: 'lock_release',
|
|
399
|
-
key,
|
|
400
|
-
holder: this.config.nodeId,
|
|
401
|
-
});
|
|
402
|
-
this.emit('lock_released', { key, holder: this.config.nodeId });
|
|
403
|
-
// Notify next waiter
|
|
404
|
-
if (lock.waiters.length > 0) {
|
|
405
|
-
const nextHolder = lock.waiters.shift();
|
|
406
|
-
this.emit('lock_available', { key, nextHolder });
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Check for deadlocks
|
|
411
|
-
*/
|
|
412
|
-
detectDeadlocks() {
|
|
413
|
-
const waitGraph = new Map();
|
|
414
|
-
const deadlocks = [];
|
|
415
|
-
// Build wait-for graph
|
|
416
|
-
for (const lock of this.locks.values()) {
|
|
417
|
-
if (lock.holder && lock.waiters.length > 0) {
|
|
418
|
-
for (const waiter of lock.waiters) {
|
|
419
|
-
const edges = waitGraph.get(waiter) || [];
|
|
420
|
-
edges.push(lock.holder);
|
|
421
|
-
waitGraph.set(waiter, edges);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
// Detect cycles using DFS
|
|
426
|
-
const visited = new Set();
|
|
427
|
-
const recStack = new Set();
|
|
428
|
-
const detectCycle = (node, path) => {
|
|
429
|
-
visited.add(node);
|
|
430
|
-
recStack.add(node);
|
|
431
|
-
path.push(node);
|
|
432
|
-
const neighbors = waitGraph.get(node) || [];
|
|
433
|
-
for (const neighbor of neighbors) {
|
|
434
|
-
if (!visited.has(neighbor)) {
|
|
435
|
-
if (detectCycle(neighbor, [...path])) {
|
|
436
|
-
return true;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
else if (recStack.has(neighbor)) {
|
|
440
|
-
// Found cycle
|
|
441
|
-
const cycleStart = path.indexOf(neighbor);
|
|
442
|
-
deadlocks.push(path.slice(cycleStart));
|
|
443
|
-
return true;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
recStack.delete(node);
|
|
447
|
-
return false;
|
|
448
|
-
};
|
|
449
|
-
for (const node of waitGraph.keys()) {
|
|
450
|
-
if (!visited.has(node)) {
|
|
451
|
-
detectCycle(node, []);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return deadlocks;
|
|
455
|
-
}
|
|
456
|
-
// ============================================================================
|
|
457
|
-
// Private Methods - Leader Election
|
|
458
|
-
// ============================================================================
|
|
459
|
-
becomeFollower(term) {
|
|
460
|
-
this.state.state = 'follower';
|
|
461
|
-
this.state.currentTerm = term;
|
|
462
|
-
this.state.votedFor = null;
|
|
463
|
-
this.clearHeartbeatTimer();
|
|
464
|
-
this.resetElectionTimer();
|
|
465
|
-
this.emit('state_changed', { state: 'follower', term });
|
|
466
|
-
}
|
|
467
|
-
becomeCandidate() {
|
|
468
|
-
this.state.state = 'candidate';
|
|
469
|
-
this.state.currentTerm++;
|
|
470
|
-
this.state.votedFor = this.config.nodeId;
|
|
471
|
-
this.votesReceived.clear();
|
|
472
|
-
this.votesReceived.add(this.config.nodeId);
|
|
473
|
-
this.metrics.totalElections++;
|
|
474
|
-
const electionStart = Date.now();
|
|
475
|
-
this.emit('state_changed', { state: 'candidate', term: this.state.currentTerm });
|
|
476
|
-
// Request votes from peers
|
|
477
|
-
this.requestVotes();
|
|
478
|
-
// Reset election timer
|
|
479
|
-
this.resetElectionTimer();
|
|
480
|
-
// Track election time
|
|
481
|
-
this.once('state_changed', (event) => {
|
|
482
|
-
if (event.state === 'leader') {
|
|
483
|
-
const electionTime = Date.now() - electionStart;
|
|
484
|
-
this.metrics.lastElectionTime = electionTime;
|
|
485
|
-
this.metrics.avgElectionTimeMs =
|
|
486
|
-
(this.metrics.avgElectionTimeMs * (this.metrics.totalElections - 1) + electionTime) /
|
|
487
|
-
this.metrics.totalElections;
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
becomeLeader() {
|
|
492
|
-
this.state.state = 'leader';
|
|
493
|
-
this.state.leaderId = this.config.nodeId;
|
|
494
|
-
this.clearElectionTimer();
|
|
495
|
-
this.metrics.totalLeaderChanges++;
|
|
496
|
-
// Initialize peer state
|
|
497
|
-
for (const peer of this.peers.values()) {
|
|
498
|
-
peer.nextIndex = this.state.log.length + 1;
|
|
499
|
-
peer.matchIndex = 0;
|
|
500
|
-
}
|
|
501
|
-
// Send initial heartbeat
|
|
502
|
-
this.sendHeartbeat();
|
|
503
|
-
// Start heartbeat timer
|
|
504
|
-
this.heartbeatTimer = setInterval(() => {
|
|
505
|
-
this.sendHeartbeat();
|
|
506
|
-
}, this.config.heartbeatInterval);
|
|
507
|
-
this.emit('state_changed', { state: 'leader', term: this.state.currentTerm });
|
|
508
|
-
this.emit('leader_elected', { leaderId: this.config.nodeId });
|
|
509
|
-
}
|
|
510
|
-
requestVotes() {
|
|
511
|
-
const request = {
|
|
512
|
-
term: this.state.currentTerm,
|
|
513
|
-
candidateId: this.config.nodeId,
|
|
514
|
-
lastLogIndex: this.state.log.length,
|
|
515
|
-
lastLogTerm: this.state.log[this.state.log.length - 1]?.term || 0,
|
|
516
|
-
};
|
|
517
|
-
// Sign request for BFT
|
|
518
|
-
if (this.config.byzantineTolerance) {
|
|
519
|
-
request.signature = this.signData(request);
|
|
520
|
-
}
|
|
521
|
-
// In real implementation, send to all peers
|
|
522
|
-
// For now, emit event for testing
|
|
523
|
-
this.emit('vote_request', request);
|
|
524
|
-
}
|
|
525
|
-
processVoteResponse(response, peerId) {
|
|
526
|
-
// Verify signature for BFT
|
|
527
|
-
if (this.config.byzantineTolerance && !this.verifySignature(response, response.signature)) {
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
// Ignore if no longer candidate
|
|
531
|
-
if (this.state.state !== 'candidate') {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
// Update term if response has newer term
|
|
535
|
-
if (response.term > this.state.currentTerm) {
|
|
536
|
-
this.becomeFollower(response.term);
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
// Count vote
|
|
540
|
-
if (response.voteGranted) {
|
|
541
|
-
this.votesReceived.add(peerId);
|
|
542
|
-
// Check if we have quorum
|
|
543
|
-
if (this.votesReceived.size >= this.config.quorumSize) {
|
|
544
|
-
this.becomeLeader();
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
isLogUpToDate(lastLogIndex, lastLogTerm) {
|
|
549
|
-
const ourLastIndex = this.state.log.length;
|
|
550
|
-
const ourLastTerm = this.state.log[ourLastIndex - 1]?.term || 0;
|
|
551
|
-
// Candidate's log is more up-to-date if:
|
|
552
|
-
// 1. Last term is greater, OR
|
|
553
|
-
// 2. Last term is same but index is greater or equal
|
|
554
|
-
return (lastLogTerm > ourLastTerm ||
|
|
555
|
-
(lastLogTerm === ourLastTerm && lastLogIndex >= ourLastIndex));
|
|
556
|
-
}
|
|
557
|
-
// ============================================================================
|
|
558
|
-
// Private Methods - Log Replication
|
|
559
|
-
// ============================================================================
|
|
560
|
-
sendHeartbeat() {
|
|
561
|
-
this.replicateToFollowers();
|
|
562
|
-
}
|
|
563
|
-
replicateToFollowers() {
|
|
564
|
-
for (const [peerId, peer] of this.peers.entries()) {
|
|
565
|
-
const prevLogIndex = peer.nextIndex - 1;
|
|
566
|
-
const prevLogTerm = prevLogIndex > 0 ? this.state.log[prevLogIndex - 1]?.term || 0 : 0;
|
|
567
|
-
const entries = this.state.log.slice(peer.nextIndex - 1, peer.nextIndex - 1 + this.config.maxEntriesPerRequest);
|
|
568
|
-
const request = {
|
|
569
|
-
term: this.state.currentTerm,
|
|
570
|
-
leaderId: this.config.nodeId,
|
|
571
|
-
prevLogIndex,
|
|
572
|
-
prevLogTerm,
|
|
573
|
-
entries,
|
|
574
|
-
leaderCommit: this.state.commitIndex,
|
|
575
|
-
};
|
|
576
|
-
// Sign request for BFT
|
|
577
|
-
if (this.config.byzantineTolerance) {
|
|
578
|
-
request.signature = this.signData(request);
|
|
579
|
-
}
|
|
580
|
-
// In real implementation, send to peer
|
|
581
|
-
// For now, emit event for testing
|
|
582
|
-
this.emit('append_entries', { peerId, request });
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
processAppendEntriesResponse(response, peerId) {
|
|
586
|
-
// Verify signature for BFT
|
|
587
|
-
if (this.config.byzantineTolerance && !this.verifySignature(response, response.signature)) {
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
// Ignore if no longer leader
|
|
591
|
-
if (this.state.state !== 'leader') {
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
const peer = this.peers.get(peerId);
|
|
595
|
-
if (!peer)
|
|
596
|
-
return;
|
|
597
|
-
// Update term if response has newer term
|
|
598
|
-
if (response.term > this.state.currentTerm) {
|
|
599
|
-
this.becomeFollower(response.term);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
if (response.success) {
|
|
603
|
-
// Update peer state
|
|
604
|
-
if (response.matchIndex !== undefined) {
|
|
605
|
-
peer.matchIndex = response.matchIndex;
|
|
606
|
-
peer.nextIndex = response.matchIndex + 1;
|
|
607
|
-
}
|
|
608
|
-
// Update commit index
|
|
609
|
-
this.updateCommitIndex();
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
// Decrement nextIndex and retry
|
|
613
|
-
peer.nextIndex = Math.max(1, peer.nextIndex - 1);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
updateCommitIndex() {
|
|
617
|
-
// Find highest index replicated on majority
|
|
618
|
-
const matchIndices = Array.from(this.peers.values())
|
|
619
|
-
.map(p => p.matchIndex)
|
|
620
|
-
.sort((a, b) => b - a);
|
|
621
|
-
const majorityIndex = matchIndices[Math.floor(this.config.quorumSize / 2)];
|
|
622
|
-
if (majorityIndex > this.state.commitIndex) {
|
|
623
|
-
const entry = this.state.log[majorityIndex - 1];
|
|
624
|
-
if (entry && entry.term === this.state.currentTerm) {
|
|
625
|
-
this.state.commitIndex = majorityIndex;
|
|
626
|
-
this.metrics.totalCommittedEntries++;
|
|
627
|
-
this.applyCommittedEntries();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
applyCommittedEntries() {
|
|
632
|
-
while (this.state.lastApplied < this.state.commitIndex) {
|
|
633
|
-
this.state.lastApplied++;
|
|
634
|
-
const entry = this.state.log[this.state.lastApplied - 1];
|
|
635
|
-
if (entry) {
|
|
636
|
-
this.emit('entry_committed', entry);
|
|
637
|
-
// Resolve command promises
|
|
638
|
-
for (const [id, resolve] of this.commandQueue.entries()) {
|
|
639
|
-
resolve(true);
|
|
640
|
-
this.commandQueue.delete(id);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
// ============================================================================
|
|
646
|
-
// Private Methods - Timers
|
|
647
|
-
// ============================================================================
|
|
648
|
-
resetElectionTimer() {
|
|
649
|
-
this.clearElectionTimer();
|
|
650
|
-
const timeout = this.config.electionTimeoutMin +
|
|
651
|
-
Math.random() * (this.config.electionTimeoutMax - this.config.electionTimeoutMin);
|
|
652
|
-
this.electionTimer = setTimeout(() => {
|
|
653
|
-
if (this.state.state !== 'leader') {
|
|
654
|
-
this.becomeCandidate();
|
|
655
|
-
}
|
|
656
|
-
}, timeout);
|
|
657
|
-
}
|
|
658
|
-
clearElectionTimer() {
|
|
659
|
-
if (this.electionTimer) {
|
|
660
|
-
clearTimeout(this.electionTimer);
|
|
661
|
-
this.electionTimer = null;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
clearHeartbeatTimer() {
|
|
665
|
-
if (this.heartbeatTimer) {
|
|
666
|
-
clearInterval(this.heartbeatTimer);
|
|
667
|
-
this.heartbeatTimer = null;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
// ============================================================================
|
|
671
|
-
// Private Methods - Byzantine Fault Tolerance
|
|
672
|
-
// ============================================================================
|
|
673
|
-
setupBFT() {
|
|
674
|
-
// Generate keypair (simplified - use real crypto in production)
|
|
675
|
-
this.privateKey = crypto.randomBytes(32).toString('hex');
|
|
676
|
-
const publicKey = crypto.createHash('sha256').update(this.privateKey).digest('hex');
|
|
677
|
-
this.publicKeys.set(this.config.nodeId, publicKey);
|
|
678
|
-
}
|
|
679
|
-
signData(data) {
|
|
680
|
-
if (!this.privateKey)
|
|
681
|
-
return '';
|
|
682
|
-
const message = JSON.stringify(data);
|
|
683
|
-
return crypto.createHmac('sha256', this.privateKey).update(message).digest('hex');
|
|
684
|
-
}
|
|
685
|
-
verifySignature(data, signature) {
|
|
686
|
-
if (!signature)
|
|
687
|
-
return false;
|
|
688
|
-
// In real implementation, verify with sender's public key
|
|
689
|
-
return true;
|
|
690
|
-
}
|
|
691
|
-
// ============================================================================
|
|
692
|
-
// Private Methods - Gossip Protocol
|
|
693
|
-
// ============================================================================
|
|
694
|
-
startGossipProtocol() {
|
|
695
|
-
this.gossipInterval = setInterval(() => {
|
|
696
|
-
this.gossipToRandomPeers();
|
|
697
|
-
}, 1000); // Gossip every second
|
|
698
|
-
}
|
|
699
|
-
stopGossipProtocol() {
|
|
700
|
-
if (this.gossipInterval) {
|
|
701
|
-
clearInterval(this.gossipInterval);
|
|
702
|
-
this.gossipInterval = null;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
gossipToRandomPeers() {
|
|
706
|
-
const message = {
|
|
707
|
-
type: 'state',
|
|
708
|
-
nodeId: this.config.nodeId,
|
|
709
|
-
data: {
|
|
710
|
-
state: this.state.state,
|
|
711
|
-
term: this.state.currentTerm,
|
|
712
|
-
logLength: this.state.log.length,
|
|
713
|
-
},
|
|
714
|
-
timestamp: Date.now(),
|
|
715
|
-
};
|
|
716
|
-
// Sign message for BFT
|
|
717
|
-
if (this.config.byzantineTolerance) {
|
|
718
|
-
message.signature = this.signData(message);
|
|
719
|
-
}
|
|
720
|
-
this.emit('gossip', message);
|
|
721
|
-
}
|
|
722
|
-
gossipCRDTUpdate(key, state) {
|
|
723
|
-
const message = {
|
|
724
|
-
type: 'metadata',
|
|
725
|
-
nodeId: this.config.nodeId,
|
|
726
|
-
data: { key, state },
|
|
727
|
-
timestamp: Date.now(),
|
|
728
|
-
};
|
|
729
|
-
if (this.config.byzantineTolerance) {
|
|
730
|
-
message.signature = this.signData(message);
|
|
731
|
-
}
|
|
732
|
-
this.emit('gossip', message);
|
|
733
|
-
}
|
|
734
|
-
// ============================================================================
|
|
735
|
-
// Private Methods - Lock Monitoring
|
|
736
|
-
// ============================================================================
|
|
737
|
-
startLockMonitoring() {
|
|
738
|
-
this.lockCheckInterval = setInterval(() => {
|
|
739
|
-
this.checkExpiredLocks();
|
|
740
|
-
const deadlocks = this.detectDeadlocks();
|
|
741
|
-
if (deadlocks.length > 0) {
|
|
742
|
-
this.emit('deadlock_detected', { deadlocks });
|
|
743
|
-
}
|
|
744
|
-
}, 5000); // Check every 5 seconds
|
|
745
|
-
}
|
|
746
|
-
stopLockMonitoring() {
|
|
747
|
-
if (this.lockCheckInterval) {
|
|
748
|
-
clearInterval(this.lockCheckInterval);
|
|
749
|
-
this.lockCheckInterval = null;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
checkExpiredLocks() {
|
|
753
|
-
const now = Date.now();
|
|
754
|
-
for (const [key, lock] of this.locks.entries()) {
|
|
755
|
-
if (lock.expiresAt < now) {
|
|
756
|
-
this.locks.delete(key);
|
|
757
|
-
this.emit('lock_expired', { key, holder: lock.holder });
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
//# sourceMappingURL=RaftConsensus.js.map
|