@olane/o-node 0.7.12-alpha.5 → 0.7.12-alpha.50
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/connection/interfaces/o-node-connection-manager.config.d.ts +1 -0
- package/dist/src/connection/interfaces/o-node-connection-manager.config.d.ts.map +1 -1
- package/dist/src/connection/interfaces/o-node-connection.config.d.ts +1 -0
- package/dist/src/connection/interfaces/o-node-connection.config.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.d.ts +3 -2
- package/dist/src/connection/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +58 -18
- package/dist/src/connection/o-node-connection.manager.d.ts +17 -4
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +75 -65
- package/dist/src/connection/o-stream.request.d.ts +11 -0
- package/dist/src/connection/o-stream.request.d.ts.map +1 -0
- package/dist/src/connection/o-stream.request.js +7 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/interfaces/i-heartbeatable-node.d.ts +49 -0
- package/dist/src/interfaces/i-heartbeatable-node.d.ts.map +1 -0
- package/dist/src/interfaces/i-heartbeatable-node.js +1 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts +46 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -0
- package/dist/src/interfaces/i-reconnectable-node.js +1 -0
- package/dist/src/interfaces/o-node.config.d.ts +43 -0
- package/dist/src/interfaces/o-node.config.d.ts.map +1 -1
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts +63 -0
- package/dist/src/managers/o-connection-heartbeat.manager.d.ts.map +1 -0
- package/dist/src/managers/o-connection-heartbeat.manager.js +227 -0
- package/dist/src/managers/o-reconnection.manager.d.ts +51 -0
- package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -0
- package/dist/src/managers/o-reconnection.manager.js +266 -0
- package/dist/src/o-node.d.ts +30 -2
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +245 -33
- package/dist/src/o-node.notification-manager.d.ts +52 -0
- package/dist/src/o-node.notification-manager.d.ts.map +1 -0
- package/dist/src/o-node.notification-manager.js +188 -0
- package/dist/src/o-node.tool.d.ts.map +1 -1
- package/dist/src/o-node.tool.js +27 -22
- package/dist/src/router/o-node.router.d.ts +1 -0
- package/dist/src/router/o-node.router.d.ts.map +1 -1
- package/dist/src/router/o-node.router.js +62 -9
- package/dist/src/router/o-node.routing-policy.d.ts.map +1 -1
- package/dist/src/router/o-node.routing-policy.js +7 -2
- package/dist/src/router/resolvers/o-node.resolver.d.ts.map +1 -1
- package/dist/src/router/resolvers/o-node.resolver.js +5 -1
- package/dist/src/router/resolvers/o-node.search-resolver.d.ts.map +1 -1
- package/dist/src/router/resolvers/o-node.search-resolver.js +34 -9
- package/dist/src/utils/index.d.ts +3 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/stream.utils.d.ts +6 -0
- package/dist/src/utils/stream.utils.d.ts.map +1 -0
- package/dist/src/utils/stream.utils.js +31 -0
- package/dist/test/helpers/test-node.tool.d.ts +15 -0
- package/dist/test/helpers/test-node.tool.d.ts.map +1 -0
- package/dist/test/helpers/test-node.tool.js +27 -0
- package/package.json +6 -6
- package/dist/src/router/resolvers/o-node.child-resolver.d.ts +0 -11
- package/dist/src/router/resolvers/o-node.child-resolver.d.ts.map +0 -1
- package/dist/src/router/resolvers/o-node.child-resolver.js +0 -58
package/dist/src/o-node.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ import { PeerId } from '@olane/o-config';
|
|
|
3
3
|
import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
|
|
4
4
|
import { oNodeConfig } from './interfaces/o-node.config.js';
|
|
5
5
|
import { oNodeTransport } from './router/o-node.transport.js';
|
|
6
|
-
import { oRequest } from '@olane/o-core';
|
|
6
|
+
import { oRequest, oNotificationManager } from '@olane/o-core';
|
|
7
7
|
import { oNodeAddress } from './router/o-node.address.js';
|
|
8
8
|
import { oNodeConnection } from './connection/o-node-connection.js';
|
|
9
9
|
import { oNodeConnectionManager } from './connection/o-node-connection.manager.js';
|
|
10
10
|
import { oToolBase } from '@olane/o-tool';
|
|
11
|
+
import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
|
|
12
|
+
import { oReconnectionManager } from './managers/o-reconnection.manager.js';
|
|
13
|
+
import { oNodeConnectionConfig } from './connection/index.js';
|
|
11
14
|
export declare class oNode extends oToolBase {
|
|
12
15
|
peerId: PeerId;
|
|
13
16
|
p2pNode: Libp2p;
|
|
@@ -15,6 +18,8 @@ export declare class oNode extends oToolBase {
|
|
|
15
18
|
config: oNodeConfig;
|
|
16
19
|
connectionManager: oNodeConnectionManager;
|
|
17
20
|
hierarchyManager: oNodeHierarchyManager;
|
|
21
|
+
connectionHeartbeatManager?: oConnectionHeartbeatManager;
|
|
22
|
+
reconnectionManager?: oReconnectionManager;
|
|
18
23
|
protected didRegister: boolean;
|
|
19
24
|
constructor(config: oNodeConfig);
|
|
20
25
|
get leader(): oNodeAddress | null;
|
|
@@ -22,11 +27,13 @@ export declare class oNode extends oToolBase {
|
|
|
22
27
|
get parentPeerId(): string | null;
|
|
23
28
|
configureTransports(): any[];
|
|
24
29
|
initializeRouter(): Promise<void>;
|
|
30
|
+
protected createNotificationManager(): oNotificationManager;
|
|
25
31
|
get staticAddress(): oNodeAddress;
|
|
26
32
|
get parentTransports(): oNodeTransport[];
|
|
27
33
|
get transports(): oNodeTransport[];
|
|
28
34
|
unregister(): Promise<void>;
|
|
29
35
|
registerParent(): Promise<void>;
|
|
36
|
+
registerLeader(): Promise<void>;
|
|
30
37
|
register(): Promise<void>;
|
|
31
38
|
extractMethod(address: oNodeAddress): string;
|
|
32
39
|
start(): Promise<void>;
|
|
@@ -37,8 +44,29 @@ export declare class oNode extends oToolBase {
|
|
|
37
44
|
*/
|
|
38
45
|
configure(): Promise<Libp2pConfig>;
|
|
39
46
|
protected createNode(): Promise<Libp2p>;
|
|
40
|
-
connect(
|
|
47
|
+
connect(config: oNodeConnectionConfig): Promise<oNodeConnection>;
|
|
48
|
+
initConnectionManager(): Promise<void>;
|
|
49
|
+
initReconnectionManager(): Promise<void>;
|
|
50
|
+
hookInitializeFinished(): Promise<void>;
|
|
51
|
+
hookStartFinished(): Promise<void>;
|
|
41
52
|
initialize(): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Override use() to wrap leader/registry requests with retry logic
|
|
55
|
+
*/
|
|
42
56
|
teardown(): Promise<void>;
|
|
57
|
+
getLeaders(): oNodeAddress[];
|
|
58
|
+
getParents(): oNodeAddress[];
|
|
59
|
+
getChildren(): oNodeAddress[];
|
|
60
|
+
removeChild(childAddress: oNodeAddress): void;
|
|
61
|
+
/**
|
|
62
|
+
* Get the total number of active streams across all connections
|
|
63
|
+
* @returns Total count of active streams
|
|
64
|
+
*/
|
|
65
|
+
getStreamCount(): number;
|
|
66
|
+
/**
|
|
67
|
+
* Get libp2p metrics for this node
|
|
68
|
+
* Tool method that can be called remotely by monitoring systems
|
|
69
|
+
*/
|
|
70
|
+
_tool_get_libp2p_metrics(request: oRequest): Promise<any>;
|
|
43
71
|
}
|
|
44
72
|
//# sourceMappingURL=o-node.d.ts.map
|
package/dist/src/o-node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"o-node.d.ts","sourceRoot":"","sources":["../../src/o-node.ts"],"names":[],"mappings":"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"}
|
package/dist/src/o-node.js
CHANGED
|
@@ -9,6 +9,9 @@ import { oNodeConnectionManager } from './connection/o-node-connection.manager.j
|
|
|
9
9
|
import { oNodeResolver } from './router/resolvers/o-node.resolver.js';
|
|
10
10
|
import { oMethodResolver, oToolBase } from '@olane/o-tool';
|
|
11
11
|
import { oLeaderResolverFallback } from './router/index.js';
|
|
12
|
+
import { oNodeNotificationManager } from './o-node.notification-manager.js';
|
|
13
|
+
import { oConnectionHeartbeatManager } from './managers/o-connection-heartbeat.manager.js';
|
|
14
|
+
import { oReconnectionManager } from './managers/o-reconnection.manager.js';
|
|
12
15
|
export class oNode extends oToolBase {
|
|
13
16
|
constructor(config) {
|
|
14
17
|
super(config);
|
|
@@ -43,6 +46,9 @@ export class oNode extends oToolBase {
|
|
|
43
46
|
});
|
|
44
47
|
this.router = new oNodeRouter();
|
|
45
48
|
}
|
|
49
|
+
createNotificationManager() {
|
|
50
|
+
return new oNodeNotificationManager(this.p2pNode, this.hierarchyManager, this.address);
|
|
51
|
+
}
|
|
46
52
|
get staticAddress() {
|
|
47
53
|
return this.config.address;
|
|
48
54
|
}
|
|
@@ -60,6 +66,30 @@ export class oNode extends oToolBase {
|
|
|
60
66
|
this.logger.debug('Skipping unregistration, node is leader');
|
|
61
67
|
return;
|
|
62
68
|
}
|
|
69
|
+
// Notify parent we're stopping (best-effort, 2s timeout)
|
|
70
|
+
if (this.config.parent) {
|
|
71
|
+
try {
|
|
72
|
+
await Promise.race([
|
|
73
|
+
this.use(this.config.parent, {
|
|
74
|
+
method: 'notify',
|
|
75
|
+
params: {
|
|
76
|
+
eventType: 'node:stopping',
|
|
77
|
+
eventData: {
|
|
78
|
+
address: this.address.toString(),
|
|
79
|
+
reason: 'graceful_shutdown',
|
|
80
|
+
expectedDowntime: null,
|
|
81
|
+
},
|
|
82
|
+
source: this.address.toString(),
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 2000)),
|
|
86
|
+
]);
|
|
87
|
+
this.logger.debug('Notified parent of shutdown');
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
this.logger.warn('Failed to notify parent (will be detected by heartbeat):', error instanceof Error ? error.message : error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
63
93
|
if (!this.config.leader) {
|
|
64
94
|
this.logger.debug('No leader found, skipping unregistration');
|
|
65
95
|
return;
|
|
@@ -72,18 +102,30 @@ export class oNode extends oToolBase {
|
|
|
72
102
|
peerId: this.peerId.toString(),
|
|
73
103
|
},
|
|
74
104
|
};
|
|
75
|
-
|
|
105
|
+
this.use(address, params).catch((error) => {
|
|
106
|
+
this.logger.error('Failed to unregister from network:', error);
|
|
107
|
+
});
|
|
76
108
|
}
|
|
77
109
|
async registerParent() {
|
|
78
110
|
if (this.type === NodeType.LEADER) {
|
|
79
111
|
this.logger.debug('Skipping parent registration, node is leader');
|
|
80
112
|
return;
|
|
81
113
|
}
|
|
114
|
+
if (!this.parent?.libp2pTransports?.length) {
|
|
115
|
+
this.logger.debug('Parent has no transports, waiting for reconnection & leader ack');
|
|
116
|
+
if (this.parent?.toString() === oAddress.leader().toString()) {
|
|
117
|
+
this.parent.setTransports(this.leader?.libp2pTransports || []);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.logger.debug('Waiting for parent and reconnecting...');
|
|
121
|
+
await this.reconnectionManager?.waitForParentAndReconnect();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
82
124
|
// if no parent transports, register with the parent to get them
|
|
83
125
|
// TODO: should we remove the transports check to make this more consistent?
|
|
84
|
-
if (this.config.parent
|
|
85
|
-
this.logger.debug('Registering node with parent...', this.config.parent);
|
|
86
|
-
|
|
126
|
+
if (this.config.parent) {
|
|
127
|
+
this.logger.debug('Registering node with parent...', this.config.parent?.toString());
|
|
128
|
+
await this.use(this.config.parent, {
|
|
87
129
|
method: 'child_register',
|
|
88
130
|
params: {
|
|
89
131
|
address: this.address.toString(),
|
|
@@ -92,11 +134,22 @@ export class oNode extends oToolBase {
|
|
|
92
134
|
_token: this.config.joinToken,
|
|
93
135
|
},
|
|
94
136
|
});
|
|
95
|
-
const { parentTransports } = parentRegistration.result.data;
|
|
96
|
-
// update the parent transports
|
|
97
|
-
this.config.parent.setTransports(parentTransports.map((t) => new oNodeTransport(t)));
|
|
98
137
|
}
|
|
99
138
|
}
|
|
139
|
+
async registerLeader() {
|
|
140
|
+
const address = oAddress.registry();
|
|
141
|
+
const params = {
|
|
142
|
+
method: 'commit',
|
|
143
|
+
params: {
|
|
144
|
+
peerId: this.peerId.toString(),
|
|
145
|
+
address: this.address.toString(),
|
|
146
|
+
protocols: this.p2pNode.getProtocols(),
|
|
147
|
+
transports: this.transports,
|
|
148
|
+
staticAddress: this.staticAddress.toString(),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
await this.use(address, params);
|
|
152
|
+
}
|
|
100
153
|
async register() {
|
|
101
154
|
if (this.type === NodeType.LEADER) {
|
|
102
155
|
this.logger.debug('Skipping registration, node is leader');
|
|
@@ -117,18 +170,7 @@ export class oNode extends oToolBase {
|
|
|
117
170
|
this.logger.debug('Registering node with leader...');
|
|
118
171
|
}
|
|
119
172
|
await this.registerParent();
|
|
120
|
-
|
|
121
|
-
const params = {
|
|
122
|
-
method: 'commit',
|
|
123
|
-
params: {
|
|
124
|
-
peerId: this.peerId.toString(),
|
|
125
|
-
address: this.address.toString(),
|
|
126
|
-
protocols: this.p2pNode.getProtocols(),
|
|
127
|
-
transports: this.transports,
|
|
128
|
-
staticAddress: this.staticAddress.toString(),
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
await this.use(address, params);
|
|
173
|
+
await this.registerLeader();
|
|
132
174
|
this.logger.debug('Registration successful');
|
|
133
175
|
}
|
|
134
176
|
extractMethod(address) {
|
|
@@ -136,11 +178,10 @@ export class oNode extends oToolBase {
|
|
|
136
178
|
}
|
|
137
179
|
async start() {
|
|
138
180
|
await super.start();
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// );
|
|
181
|
+
// Start heartbeat after node is running
|
|
182
|
+
if (this.connectionHeartbeatManager) {
|
|
183
|
+
await this.connectionHeartbeatManager.start();
|
|
184
|
+
}
|
|
144
185
|
}
|
|
145
186
|
async validateJoinRequest(request) {
|
|
146
187
|
return true;
|
|
@@ -190,12 +231,32 @@ export class oNode extends oToolBase {
|
|
|
190
231
|
// ];
|
|
191
232
|
// // let's make sure we only allow communication through the parent transports
|
|
192
233
|
params.connectionGater = {
|
|
234
|
+
denyDialPeer: (peerId) => {
|
|
235
|
+
// we can call the leader
|
|
236
|
+
if (this.config.leader?.libp2pTransports.some((t) => t.toPeerId() === peerId.toString())) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
// we can call our parent
|
|
240
|
+
if (this.parentPeerId === peerId.toString()) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
// we can call our children
|
|
244
|
+
if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
// check for standalone node
|
|
248
|
+
if (!this.config.parent && !this.config.leader) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
},
|
|
193
253
|
// who can call us?
|
|
194
254
|
denyInboundEncryptedConnection: (peerId, maConn) => {
|
|
195
255
|
// deny all inbound connections unless they are from a parent transport
|
|
196
256
|
if (this.parentPeerId === peerId.toString()) {
|
|
197
257
|
return false;
|
|
198
258
|
}
|
|
259
|
+
// allow connections from children (for ping)
|
|
199
260
|
if (this.hierarchyManager.children.some((c) => c.libp2pTransports.some((t) => t.toPeerId() === peerId.toString()))) {
|
|
200
261
|
return false;
|
|
201
262
|
}
|
|
@@ -223,17 +284,13 @@ export class oNode extends oToolBase {
|
|
|
223
284
|
this.p2pNode = await createNode(params);
|
|
224
285
|
return this.p2pNode;
|
|
225
286
|
}
|
|
226
|
-
async connect(
|
|
287
|
+
async connect(config) {
|
|
227
288
|
if (!this.connectionManager) {
|
|
228
289
|
this.logger.error('Connection manager not initialized');
|
|
229
290
|
throw new Error('Node is not ready to connect to other nodes');
|
|
230
291
|
}
|
|
231
292
|
const connection = await this.connectionManager
|
|
232
|
-
.connect(
|
|
233
|
-
address: targetAddress,
|
|
234
|
-
nextHopAddress,
|
|
235
|
-
callerAddress: this.address,
|
|
236
|
-
})
|
|
293
|
+
.connect(config)
|
|
237
294
|
.catch((error) => {
|
|
238
295
|
// TODO: we need to handle this better and document
|
|
239
296
|
if (error.message === 'Can not dial self') {
|
|
@@ -246,6 +303,43 @@ export class oNode extends oToolBase {
|
|
|
246
303
|
}
|
|
247
304
|
return connection;
|
|
248
305
|
}
|
|
306
|
+
async initConnectionManager() {
|
|
307
|
+
this.connectionManager = new oNodeConnectionManager({
|
|
308
|
+
p2pNode: this.p2pNode,
|
|
309
|
+
defaultReadTimeoutMs: this.config.connectionTimeouts?.readTimeoutMs,
|
|
310
|
+
defaultDrainTimeoutMs: this.config.connectionTimeouts?.drainTimeoutMs,
|
|
311
|
+
runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async initReconnectionManager() {
|
|
315
|
+
// Initialize reconnection manager
|
|
316
|
+
if (this.config.reconnection?.enabled !== false) {
|
|
317
|
+
this.reconnectionManager = new oReconnectionManager(this, {
|
|
318
|
+
enabled: true,
|
|
319
|
+
maxAttempts: this.config.reconnection?.maxAttempts ?? 10,
|
|
320
|
+
baseDelayMs: this.config.reconnection?.baseDelayMs ?? 5000,
|
|
321
|
+
maxDelayMs: this.config.reconnection?.maxDelayMs ?? 60000,
|
|
322
|
+
useLeaderFallback: this.config.reconnection?.useLeaderFallback ?? true,
|
|
323
|
+
parentDiscoveryIntervalMs: this.config.reconnection?.parentDiscoveryIntervalMs ?? 10000,
|
|
324
|
+
parentDiscoveryMaxDelayMs: this.config.reconnection?.parentDiscoveryMaxDelayMs ?? 60000,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async hookInitializeFinished() { }
|
|
329
|
+
async hookStartFinished() {
|
|
330
|
+
// Initialize connection heartbeat manager
|
|
331
|
+
this.connectionHeartbeatManager = new oConnectionHeartbeatManager(this, {
|
|
332
|
+
enabled: this.config.connectionHeartbeat?.enabled ?? true,
|
|
333
|
+
intervalMs: this.config.connectionHeartbeat?.intervalMs ?? 15000,
|
|
334
|
+
timeoutMs: this.config.connectionHeartbeat?.timeoutMs ?? 15000,
|
|
335
|
+
failureThreshold: this.config.connectionHeartbeat?.failureThreshold ?? 3,
|
|
336
|
+
checkChildren: this.config.connectionHeartbeat?.checkChildren ?? false,
|
|
337
|
+
checkParent: this.config.connectionHeartbeat?.checkParent ?? true,
|
|
338
|
+
checkLeader: true,
|
|
339
|
+
});
|
|
340
|
+
this.logger.info(`Connection heartbeat config: leader=${this.connectionHeartbeatManager.getConfig().checkLeader}, ` +
|
|
341
|
+
`parent=${this.connectionHeartbeatManager.getConfig().checkParent}`);
|
|
342
|
+
}
|
|
249
343
|
async initialize() {
|
|
250
344
|
this.logger.debug('Initializing node...');
|
|
251
345
|
if (this.p2pNode && this.state !== NodeState.STOPPED) {
|
|
@@ -256,13 +350,13 @@ export class oNode extends oToolBase {
|
|
|
256
350
|
}
|
|
257
351
|
await this.createNode();
|
|
258
352
|
await this.initializeRouter();
|
|
353
|
+
// need to wait until our libpp2 node is initialized before calling super.initialize
|
|
354
|
+
await super.initialize();
|
|
259
355
|
this.logger.debug('Node initialized!', this.transports.map((t) => t.toString()));
|
|
260
356
|
this.address.setTransports(this.transports);
|
|
261
357
|
this.peerId = this.p2pNode.peerId;
|
|
262
358
|
// initialize connection manager
|
|
263
|
-
this.
|
|
264
|
-
p2pNode: this.p2pNode,
|
|
265
|
-
});
|
|
359
|
+
await this.initConnectionManager();
|
|
266
360
|
// initialize address resolution
|
|
267
361
|
this.router.addResolver(new oMethodResolver(this.address));
|
|
268
362
|
this.router.addResolver(new oNodeResolver(this.address));
|
|
@@ -271,12 +365,130 @@ export class oNode extends oToolBase {
|
|
|
271
365
|
this.logger.debug('Adding leader resolver fallback...');
|
|
272
366
|
this.router.addResolver(new oLeaderResolverFallback(this.address));
|
|
273
367
|
}
|
|
368
|
+
// initialize reconnection manager
|
|
369
|
+
await this.initReconnectionManager();
|
|
370
|
+
await this.hookInitializeFinished();
|
|
274
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Override use() to wrap leader/registry requests with retry logic
|
|
374
|
+
*/
|
|
375
|
+
// async use(
|
|
376
|
+
// address: oAddress,
|
|
377
|
+
// data?: {
|
|
378
|
+
// method?: string;
|
|
379
|
+
// params?: { [key: string]: any };
|
|
380
|
+
// id?: string;
|
|
381
|
+
// },
|
|
382
|
+
// options?: UseOptions,
|
|
383
|
+
// ): Promise<any> {
|
|
384
|
+
// // Wrap leader/registry requests with retry logic
|
|
385
|
+
// return super.use(address, data, options),
|
|
386
|
+
// address,
|
|
387
|
+
// data?.method,
|
|
388
|
+
// }
|
|
275
389
|
async teardown() {
|
|
390
|
+
// Stop heartbeat before parent teardown
|
|
391
|
+
if (this.connectionHeartbeatManager) {
|
|
392
|
+
await this.connectionHeartbeatManager.stop();
|
|
393
|
+
}
|
|
276
394
|
await this.unregister();
|
|
277
395
|
await super.teardown();
|
|
278
396
|
if (this.p2pNode) {
|
|
279
397
|
await this.p2pNode.stop();
|
|
280
398
|
}
|
|
281
399
|
}
|
|
400
|
+
// IHeartbeatableNode interface methods
|
|
401
|
+
getLeaders() {
|
|
402
|
+
return [this.leader];
|
|
403
|
+
}
|
|
404
|
+
getParents() {
|
|
405
|
+
return this.hierarchyManager.getParents();
|
|
406
|
+
}
|
|
407
|
+
getChildren() {
|
|
408
|
+
return this.hierarchyManager.getChildren();
|
|
409
|
+
}
|
|
410
|
+
removeChild(childAddress) {
|
|
411
|
+
this.hierarchyManager.removeChild(childAddress);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get the total number of active streams across all connections
|
|
415
|
+
* @returns Total count of active streams
|
|
416
|
+
*/
|
|
417
|
+
getStreamCount() {
|
|
418
|
+
if (!this.p2pNode) {
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
const connections = this.p2pNode.getConnections();
|
|
422
|
+
return connections.reduce((count, conn) => {
|
|
423
|
+
return count + (conn.streams?.length || 0);
|
|
424
|
+
}, 0);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get libp2p metrics for this node
|
|
428
|
+
* Tool method that can be called remotely by monitoring systems
|
|
429
|
+
*/
|
|
430
|
+
async _tool_get_libp2p_metrics(request) {
|
|
431
|
+
if (!this.p2pNode) {
|
|
432
|
+
return {
|
|
433
|
+
error: 'libp2p node not available',
|
|
434
|
+
available: false,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
// Get basic connection stats
|
|
439
|
+
const connections = this.p2pNode.getConnections();
|
|
440
|
+
const peers = await this.p2pNode.peerStore.all();
|
|
441
|
+
const inbound = connections.filter((c) => c.direction === 'inbound').length;
|
|
442
|
+
const outbound = connections.filter((c) => c.direction === 'outbound').length;
|
|
443
|
+
// Get DHT info if available
|
|
444
|
+
const services = this.p2pNode.services;
|
|
445
|
+
const dht = services?.dht;
|
|
446
|
+
const routingTable = dht?.routingTable;
|
|
447
|
+
const kBuckets = routingTable?.kb || null;
|
|
448
|
+
let routingTableSize = 0;
|
|
449
|
+
if (kBuckets) {
|
|
450
|
+
// Handle both array and object-like structures
|
|
451
|
+
if (Array.isArray(kBuckets)) {
|
|
452
|
+
for (const bucket of kBuckets) {
|
|
453
|
+
routingTableSize += bucket.peers?.length || 0;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else if (typeof kBuckets === 'object') {
|
|
457
|
+
// If it's an object, iterate over its values
|
|
458
|
+
for (const bucket of Object.values(kBuckets)) {
|
|
459
|
+
routingTableSize += bucket?.peers?.length || 0;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// Calculate total stream count across all connections
|
|
464
|
+
const streamCount = connections.reduce((count, conn) => {
|
|
465
|
+
return count + (conn.streams?.length || 0);
|
|
466
|
+
}, 0);
|
|
467
|
+
return {
|
|
468
|
+
available: true,
|
|
469
|
+
timestamp: Date.now(),
|
|
470
|
+
nodeAddress: this.address.toString(),
|
|
471
|
+
peerCount: peers.length,
|
|
472
|
+
connectionCount: connections.length,
|
|
473
|
+
inboundConnections: inbound,
|
|
474
|
+
outboundConnections: outbound,
|
|
475
|
+
streamCount,
|
|
476
|
+
dhtEnabled: !!dht,
|
|
477
|
+
dhtMode: dht?.clientMode ? 'client' : 'server',
|
|
478
|
+
dhtRoutingTableSize: routingTableSize,
|
|
479
|
+
protocols: Array.from(this.p2pNode.getProtocols()),
|
|
480
|
+
selfPeerId: this.p2pNode.peerId.toString(),
|
|
481
|
+
multiaddrs: this.p2pNode
|
|
482
|
+
.getMultiaddrs()
|
|
483
|
+
.map((ma) => ma.toString()),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
return {
|
|
488
|
+
error: `Failed to collect libp2p metrics: ${error.message}`,
|
|
489
|
+
available: false,
|
|
490
|
+
nodeAddress: this.address.toString(),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
282
494
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Libp2p } from '@olane/o-config';
|
|
2
|
+
import { oNotificationManager } from '@olane/o-core';
|
|
3
|
+
import { oNodeAddress } from './router/o-node.address.js';
|
|
4
|
+
import { oNodeHierarchyManager } from './o-node.hierarchy-manager.js';
|
|
5
|
+
/**
|
|
6
|
+
* libp2p-specific implementation of oNotificationManager
|
|
7
|
+
* Wraps libp2p events and enriches them with Olane context
|
|
8
|
+
*/
|
|
9
|
+
export declare class oNodeNotificationManager extends oNotificationManager {
|
|
10
|
+
private p2pNode;
|
|
11
|
+
private hierarchyManager;
|
|
12
|
+
private address;
|
|
13
|
+
constructor(p2pNode: Libp2p, hierarchyManager: oNodeHierarchyManager, address: oNodeAddress);
|
|
14
|
+
/**
|
|
15
|
+
* Wire up libp2p event listeners
|
|
16
|
+
*/
|
|
17
|
+
protected setupListeners(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Handle peer connect event from libp2p
|
|
20
|
+
*/
|
|
21
|
+
private handlePeerConnect;
|
|
22
|
+
/**
|
|
23
|
+
* Handle peer disconnect event from libp2p
|
|
24
|
+
*/
|
|
25
|
+
private handlePeerDisconnect;
|
|
26
|
+
/**
|
|
27
|
+
* Handle peer discovery event from libp2p
|
|
28
|
+
*/
|
|
29
|
+
private handlePeerDiscovery;
|
|
30
|
+
/**
|
|
31
|
+
* Handle connection open event from libp2p
|
|
32
|
+
*/
|
|
33
|
+
private handleConnectionOpen;
|
|
34
|
+
/**
|
|
35
|
+
* Handle connection close event from libp2p
|
|
36
|
+
*/
|
|
37
|
+
private handleConnectionClose;
|
|
38
|
+
/**
|
|
39
|
+
* Try to resolve a libp2p peer ID to an Olane address
|
|
40
|
+
* Checks hierarchy manager for known peers
|
|
41
|
+
*/
|
|
42
|
+
private peerIdToAddress;
|
|
43
|
+
/**
|
|
44
|
+
* Check if an address is a direct child
|
|
45
|
+
*/
|
|
46
|
+
private isChild;
|
|
47
|
+
/**
|
|
48
|
+
* Check if an address is a parent
|
|
49
|
+
*/
|
|
50
|
+
private isParent;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=o-node.notification-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"o-node.notification-manager.d.ts","sourceRoot":"","sources":["../../src/o-node.notification-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EACL,oBAAoB,EAQrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,oBAAoB;IAE9D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,OAAO;gBAFP,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,qBAAqB,EACvC,OAAO,EAAE,YAAY;IAK/B;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAgChC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkCvB;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,QAAQ;CAKjB"}
|