@olane/o-node 0.7.44 → 0.7.45
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.
|
@@ -27,6 +27,15 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
27
27
|
* @returns The peer ID string or null if not found
|
|
28
28
|
*/
|
|
29
29
|
private getPeerIdFromAddress;
|
|
30
|
+
/**
|
|
31
|
+
* Select the best connection from an array of connections.
|
|
32
|
+
* Prioritizes connections with active streams, then by direction based on context.
|
|
33
|
+
*
|
|
34
|
+
* @param connections - Array of connections to choose from
|
|
35
|
+
* @param reuseContext - If true, prefer inbound connections; otherwise prefer outbound
|
|
36
|
+
* @returns The best connection or null if none are suitable
|
|
37
|
+
*/
|
|
38
|
+
private selectBestConnection;
|
|
30
39
|
getOrCreateConnection(nextHopAddress: oAddress, address: oAddress): Promise<Connection>;
|
|
31
40
|
private performDial;
|
|
32
41
|
answer(config: oConnectionConfig & {
|
|
@@ -56,11 +65,13 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
56
65
|
* @returns Object containing cache statistics
|
|
57
66
|
*/
|
|
58
67
|
getCacheStats(): {
|
|
59
|
-
|
|
68
|
+
cachedAddresses: number;
|
|
69
|
+
totalCachedConnections: number;
|
|
60
70
|
pendingDials: number;
|
|
61
71
|
connectionsByPeer: Array<{
|
|
62
72
|
peerId: string;
|
|
63
73
|
status: string;
|
|
74
|
+
addressKey: string;
|
|
64
75
|
}>;
|
|
65
76
|
};
|
|
66
77
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAQhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IAPzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"o-node-connection.manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAQhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IAPzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,oBAAoB,CAAwC;IACpE,OAAO,CAAC,qBAAqB,CACjB;gBAES,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA6DtB,qBAAqB,CACzB,cAAc,EAAE,QAAQ,EACxB,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,UAAU,CAAC;YA+ER,WAAW;IAwBnB,MAAM,CACV,MAAM,EAAE,iBAAiB,GAAG;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,eAAe,CAAC;IAwC3B;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IA8CpC;;;;OAIG;IACH,yBAAyB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI;IA0D/D;;;OAGG;IACH,aAAa,IAAI;QACf,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,KAAK,CAAC;YACvB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC,CAAC;KACJ;IAyBD;;;OAGG;IACH,uBAAuB,IAAI,MAAM;CAuBlC"}
|
|
@@ -22,10 +22,18 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
22
22
|
if (!connection) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
for (const [addressKey,
|
|
26
|
-
|
|
25
|
+
for (const [addressKey, cachedConnections,] of this.connectionsByAddress.entries()) {
|
|
26
|
+
const index = cachedConnections.indexOf(connection);
|
|
27
|
+
if (index !== -1) {
|
|
27
28
|
this.logger.debug('Connection closed, removing from cache for address:', addressKey);
|
|
28
|
-
|
|
29
|
+
cachedConnections.splice(index, 1);
|
|
30
|
+
// Remove the address key entirely if no connections remain
|
|
31
|
+
if (cachedConnections.length === 0) {
|
|
32
|
+
this.connectionsByAddress.delete(addressKey);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
this.connectionsByAddress.set(addressKey, cachedConnections);
|
|
36
|
+
}
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
});
|
|
@@ -63,6 +71,59 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
63
71
|
return null;
|
|
64
72
|
}
|
|
65
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Select the best connection from an array of connections.
|
|
76
|
+
* Prioritizes connections with active streams, then by direction based on context.
|
|
77
|
+
*
|
|
78
|
+
* @param connections - Array of connections to choose from
|
|
79
|
+
* @param reuseContext - If true, prefer inbound connections; otherwise prefer outbound
|
|
80
|
+
* @returns The best connection or null if none are suitable
|
|
81
|
+
*/
|
|
82
|
+
selectBestConnection(connections, reuseContext = false) {
|
|
83
|
+
// Filter to only open connections
|
|
84
|
+
const openConnections = connections.filter((c) => c.status === 'open');
|
|
85
|
+
if (openConnections.length === 0) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// Priority 1: Connections with active o-protocol streams
|
|
89
|
+
const connectionsWithStreams = openConnections
|
|
90
|
+
.map((conn) => ({
|
|
91
|
+
conn,
|
|
92
|
+
activeStreamCount: conn.streams.filter((s) => s.protocol.includes('/o/') &&
|
|
93
|
+
s.status === 'open' &&
|
|
94
|
+
s.writeStatus === 'writable' &&
|
|
95
|
+
s.remoteReadStatus === 'readable').length,
|
|
96
|
+
}))
|
|
97
|
+
.filter((item) => item.activeStreamCount > 0)
|
|
98
|
+
.sort((a, b) => b.activeStreamCount - a.activeStreamCount);
|
|
99
|
+
if (connectionsWithStreams.length > 0) {
|
|
100
|
+
this.logger.debug('Selected connection with active streams', {
|
|
101
|
+
streamCount: connectionsWithStreams[0].activeStreamCount,
|
|
102
|
+
});
|
|
103
|
+
return connectionsWithStreams[0].conn;
|
|
104
|
+
}
|
|
105
|
+
// Priority 2: Based on reuse context
|
|
106
|
+
if (reuseContext) {
|
|
107
|
+
// Prefer inbound connections
|
|
108
|
+
const inbound = openConnections.find((c) => c.direction === 'inbound' ||
|
|
109
|
+
!c.direction);
|
|
110
|
+
if (inbound) {
|
|
111
|
+
this.logger.debug('Selected inbound connection (reuse context)');
|
|
112
|
+
return inbound;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Prefer outbound connections
|
|
117
|
+
const outbound = openConnections.find((c) => c.direction === 'outbound');
|
|
118
|
+
if (outbound) {
|
|
119
|
+
this.logger.debug('Selected outbound connection (non-reuse context)');
|
|
120
|
+
return outbound;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Priority 3: Return first open connection
|
|
124
|
+
this.logger.debug('Selected first available open connection');
|
|
125
|
+
return openConnections[0];
|
|
126
|
+
}
|
|
66
127
|
async getOrCreateConnection(nextHopAddress, address) {
|
|
67
128
|
if (!nextHopAddress) {
|
|
68
129
|
throw new Error('Invalid address passed');
|
|
@@ -72,22 +133,32 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
72
133
|
if (!addressKey) {
|
|
73
134
|
throw new Error(`Unable to extract address key from address: ${nextHopAddress.toString()}`);
|
|
74
135
|
}
|
|
75
|
-
// Check if we have
|
|
76
|
-
const
|
|
77
|
-
|
|
136
|
+
// Check if we have cached connections by address key
|
|
137
|
+
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
138
|
+
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
139
|
+
if (bestConnection) {
|
|
78
140
|
this.logger.debug('Reusing cached connection for address:', nextHopAddress?.value);
|
|
79
|
-
return
|
|
141
|
+
return bestConnection;
|
|
80
142
|
}
|
|
81
|
-
// Clean up stale
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
143
|
+
// Clean up stale connections if they exist but are not open
|
|
144
|
+
if (cachedConnections.length > 0) {
|
|
145
|
+
const openConnections = cachedConnections.filter((c) => c.status === 'open');
|
|
146
|
+
if (openConnections.length === 0) {
|
|
147
|
+
this.logger.debug('Removing all stale connections for address:', addressKey);
|
|
148
|
+
this.connectionsByAddress.delete(addressKey);
|
|
149
|
+
}
|
|
150
|
+
else if (openConnections.length < cachedConnections.length) {
|
|
151
|
+
this.logger.debug('Cleaning up some stale connections for address:', addressKey);
|
|
152
|
+
this.connectionsByAddress.set(addressKey, openConnections);
|
|
153
|
+
}
|
|
85
154
|
}
|
|
86
155
|
// Check if libp2p has an active connection for this address
|
|
87
156
|
const libp2pConnection = this.getCachedLibp2pConnection(nextHopAddress);
|
|
88
157
|
if (libp2pConnection && libp2pConnection.status === 'open') {
|
|
89
158
|
this.logger.debug('Caching existing libp2p connection for address:', addressKey);
|
|
90
|
-
this.connectionsByAddress.
|
|
159
|
+
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
160
|
+
connections.push(libp2pConnection);
|
|
161
|
+
this.connectionsByAddress.set(addressKey, connections);
|
|
91
162
|
return libp2pConnection;
|
|
92
163
|
}
|
|
93
164
|
// Check if dial is already in progress for this address key
|
|
@@ -101,8 +172,10 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
101
172
|
this.pendingDialsByAddress.set(addressKey, dialPromise);
|
|
102
173
|
try {
|
|
103
174
|
const connection = await dialPromise;
|
|
104
|
-
//
|
|
105
|
-
this.connectionsByAddress.
|
|
175
|
+
// Add the established connection to the cache array
|
|
176
|
+
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
177
|
+
connections.push(connection);
|
|
178
|
+
this.connectionsByAddress.set(addressKey, connections);
|
|
106
179
|
return connection;
|
|
107
180
|
}
|
|
108
181
|
finally {
|
|
@@ -139,7 +212,12 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
139
212
|
});
|
|
140
213
|
const addressKey = this.getAddressKey(nextHopAddress);
|
|
141
214
|
if (addressKey) {
|
|
142
|
-
this.connectionsByAddress.
|
|
215
|
+
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
216
|
+
// Only add if not already in the cache
|
|
217
|
+
if (!connections.includes(p2pConnection)) {
|
|
218
|
+
connections.push(p2pConnection);
|
|
219
|
+
this.connectionsByAddress.set(addressKey, connections);
|
|
220
|
+
}
|
|
143
221
|
}
|
|
144
222
|
else {
|
|
145
223
|
this.logger.error('Should not happen! Failed to generate an address key for address:', nextHopAddress);
|
|
@@ -180,8 +258,9 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
180
258
|
return false;
|
|
181
259
|
}
|
|
182
260
|
// Check our address-based cache first
|
|
183
|
-
const
|
|
184
|
-
|
|
261
|
+
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
262
|
+
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
263
|
+
if (bestConnection) {
|
|
185
264
|
return true;
|
|
186
265
|
}
|
|
187
266
|
// Fall back to checking libp2p's connections
|
|
@@ -194,7 +273,11 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
194
273
|
if (hasOpenConnection) {
|
|
195
274
|
const openConnection = connections.find((conn) => conn.status === 'open');
|
|
196
275
|
if (openConnection) {
|
|
197
|
-
this.connectionsByAddress.
|
|
276
|
+
const existingConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
277
|
+
if (!existingConnections.includes(openConnection)) {
|
|
278
|
+
existingConnections.push(openConnection);
|
|
279
|
+
this.connectionsByAddress.set(addressKey, existingConnections);
|
|
280
|
+
}
|
|
198
281
|
}
|
|
199
282
|
}
|
|
200
283
|
return hasOpenConnection;
|
|
@@ -216,9 +299,10 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
216
299
|
return null;
|
|
217
300
|
}
|
|
218
301
|
// Check address-based cache first
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
302
|
+
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
303
|
+
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
304
|
+
if (bestConnection) {
|
|
305
|
+
return bestConnection;
|
|
222
306
|
}
|
|
223
307
|
const peerId = this.getPeerIdFromAddress(address);
|
|
224
308
|
if (!peerId) {
|
|
@@ -227,15 +311,22 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
227
311
|
// Query libp2p for connections to this peer
|
|
228
312
|
const connections = this.p2pNode.getConnections();
|
|
229
313
|
const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerId);
|
|
230
|
-
// Find
|
|
231
|
-
const
|
|
232
|
-
// If we found
|
|
233
|
-
if (
|
|
234
|
-
this.connectionsByAddress.
|
|
235
|
-
|
|
314
|
+
// Find open connections
|
|
315
|
+
const openConnections = filteredConnections.filter((conn) => conn.status === 'open');
|
|
316
|
+
// If we found open connections in libp2p, add them to cache and select best
|
|
317
|
+
if (openConnections.length > 0) {
|
|
318
|
+
const existingConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
319
|
+
// Add any new connections that aren't already cached
|
|
320
|
+
for (const conn of openConnections) {
|
|
321
|
+
if (!existingConnections.includes(conn)) {
|
|
322
|
+
existingConnections.push(conn);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
this.connectionsByAddress.set(addressKey, existingConnections);
|
|
326
|
+
return this.selectBestConnection(existingConnections, false);
|
|
236
327
|
}
|
|
237
|
-
// Clean up stale cache
|
|
238
|
-
if (
|
|
328
|
+
// Clean up stale cache entries if connections are no longer open
|
|
329
|
+
if (cachedConnections.length > 0) {
|
|
239
330
|
this.connectionsByAddress.delete(addressKey);
|
|
240
331
|
}
|
|
241
332
|
return null;
|
|
@@ -250,13 +341,21 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
250
341
|
* @returns Object containing cache statistics
|
|
251
342
|
*/
|
|
252
343
|
getCacheStats() {
|
|
344
|
+
const allConnections = [];
|
|
345
|
+
for (const [addressKey, connections] of this.connectionsByAddress.entries()) {
|
|
346
|
+
for (const conn of connections) {
|
|
347
|
+
allConnections.push({
|
|
348
|
+
peerId: conn.remotePeer?.toString() ?? 'unknown',
|
|
349
|
+
status: conn.status,
|
|
350
|
+
addressKey,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
253
354
|
return {
|
|
254
|
-
|
|
355
|
+
cachedAddresses: this.connectionsByAddress.size,
|
|
356
|
+
totalCachedConnections: allConnections.length,
|
|
255
357
|
pendingDials: this.pendingDialsByAddress.size,
|
|
256
|
-
connectionsByPeer:
|
|
257
|
-
peerId: conn.remotePeer?.toString() ?? 'unknown',
|
|
258
|
-
status: conn.status,
|
|
259
|
-
})),
|
|
358
|
+
connectionsByPeer: allConnections,
|
|
260
359
|
};
|
|
261
360
|
}
|
|
262
361
|
/**
|
|
@@ -265,10 +364,19 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
265
364
|
*/
|
|
266
365
|
cleanupStaleConnections() {
|
|
267
366
|
let removed = 0;
|
|
268
|
-
for (const [addressKey,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
367
|
+
for (const [addressKey, connections] of this.connectionsByAddress.entries()) {
|
|
368
|
+
const openConnections = connections.filter((conn) => conn.status === 'open');
|
|
369
|
+
const staleCount = connections.length - openConnections.length;
|
|
370
|
+
if (staleCount > 0) {
|
|
371
|
+
removed += staleCount;
|
|
372
|
+
if (openConnections.length === 0) {
|
|
373
|
+
// Remove the entire entry if no connections remain
|
|
374
|
+
this.connectionsByAddress.delete(addressKey);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
// Keep only the open connections
|
|
378
|
+
this.connectionsByAddress.set(addressKey, openConnections);
|
|
379
|
+
}
|
|
272
380
|
}
|
|
273
381
|
}
|
|
274
382
|
if (removed > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olane/o-node",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.45",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/eslintrc": "^3.3.1",
|
|
42
42
|
"@eslint/js": "^9.29.0",
|
|
43
|
-
"@olane/o-test": "0.7.
|
|
43
|
+
"@olane/o-test": "0.7.45",
|
|
44
44
|
"@tsconfig/node20": "^20.1.6",
|
|
45
45
|
"@types/jest": "^30.0.0",
|
|
46
46
|
"@types/json5": "^2.2.0",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"typescript": "5.4.5"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@olane/o-config": "0.7.
|
|
64
|
-
"@olane/o-core": "0.7.
|
|
65
|
-
"@olane/o-protocol": "0.7.
|
|
66
|
-
"@olane/o-tool": "0.7.
|
|
63
|
+
"@olane/o-config": "0.7.45",
|
|
64
|
+
"@olane/o-core": "0.7.45",
|
|
65
|
+
"@olane/o-protocol": "0.7.45",
|
|
66
|
+
"@olane/o-tool": "0.7.45",
|
|
67
67
|
"debug": "^4.4.1",
|
|
68
68
|
"dotenv": "^16.5.0",
|
|
69
69
|
"json5": "^2.2.3"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "d336869dfa3105b453d2249ae388870088b17343"
|
|
72
72
|
}
|