@olane/o-node 0.7.47 → 0.7.49
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/o-node-connection.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.js +0 -2
- package/dist/src/connection/o-node-connection.manager.d.ts +25 -34
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +90 -241
- package/dist/src/interfaces/i-reconnectable-node.d.ts +9 -0
- package/dist/src/interfaces/i-reconnectable-node.d.ts.map +1 -1
- package/dist/src/interfaces/i-registrable-node.d.ts +68 -0
- package/dist/src/interfaces/i-registrable-node.d.ts.map +1 -0
- package/dist/src/interfaces/i-registrable-node.js +1 -0
- package/dist/src/managers/o-reconnection.manager.d.ts.map +1 -1
- package/dist/src/managers/o-reconnection.manager.js +42 -15
- package/dist/src/managers/o-registration.manager.d.ts +79 -0
- package/dist/src/managers/o-registration.manager.d.ts.map +1 -0
- package/dist/src/managers/o-registration.manager.js +216 -0
- package/dist/src/o-node.d.ts +3 -1
- package/dist/src/o-node.d.ts.map +1 -1
- package/dist/src/o-node.js +14 -59
- package/dist/src/o-node.tool.d.ts +1 -0
- package/dist/src/o-node.tool.d.ts.map +1 -1
- package/dist/src/o-node.tool.js +7 -3
- package/dist/src/utils/connection.utils.d.ts +5 -0
- package/dist/src/utils/connection.utils.d.ts.map +1 -1
- package/dist/src/utils/connection.utils.js +31 -10
- package/package.json +7 -7
- package/dist/test/astream-reuse.spec.d.ts +0 -2
- package/dist/test/astream-reuse.spec.d.ts.map +0 -1
- package/dist/test/astream-reuse.spec.js +0 -107
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,qBAAa,eAAgB,SAAQ,WAAW;IAKlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAJrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAEV,MAAM,EAAE,qBAAqB;
|
|
1
|
+
{"version":3,"file":"o-node-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-node-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAU,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAEL,WAAW,EAGX,QAAQ,EACR,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAEV,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,qBAAa,eAAgB,SAAQ,WAAW;IAKlC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB;IAJrD,aAAa,EAAE,UAAU,CAAC;IACjC,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAEV,MAAM,EAAE,qBAAqB;IAO5D,IAAI,YAAY,qCAEf;IAED,IAAI,UAAU,wCAEb;IAED;;OAEG;IACH,IAAI,gBAAgB,IAAI,qBAAqB,CAE5C;IAED,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAMzC,iBAAiB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAiBnD,YAAY,IAAI,OAAO,CAAC,qBAAqB,CAAC;IA6BpD,IAAI,OAAO,IAAI,qBAAqB,EAAE,CAcrC;IAEK,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA6C/C,YAAY,CAAC,MAAM,EAAE,qBAAqB;IAI1C,KAAK,CAAC,KAAK,EAAE,KAAK;IAMlB,KAAK;CAKZ"}
|
|
@@ -8,7 +8,6 @@ export class oNodeConnection extends oConnection {
|
|
|
8
8
|
this.p2pConnection = config.p2pConnection;
|
|
9
9
|
this.streamHandler = new StreamHandler(this.logger);
|
|
10
10
|
this.reusePolicy = config.reusePolicy ?? 'none';
|
|
11
|
-
console.log('oNodeConnection constructor', this.reusePolicy);
|
|
12
11
|
}
|
|
13
12
|
get remotePeerId() {
|
|
14
13
|
return this.p2pConnection.remotePeer;
|
|
@@ -28,7 +27,6 @@ export class oNodeConnection extends oConnection {
|
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
async getOrCreateStream() {
|
|
31
|
-
console.log('getOrCreateStream', this.reusePolicy);
|
|
32
30
|
if (this.reusePolicy === 'reuse') {
|
|
33
31
|
this.logger.debug('Reusing stream...');
|
|
34
32
|
// search for streams that allow re-use
|
|
@@ -5,58 +5,55 @@ import { oNodeConnection } from './o-node-connection.js';
|
|
|
5
5
|
export declare class oNodeConnectionManager extends oConnectionManager {
|
|
6
6
|
readonly config: oNodeConnectionManagerConfig;
|
|
7
7
|
protected p2pNode: Libp2p;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
private nodeConnectionByConnectionId;
|
|
8
|
+
protected defaultReadTimeoutMs?: number;
|
|
9
|
+
protected defaultDrainTimeoutMs?: number;
|
|
10
|
+
/** Single cache of oNodeConnection instances keyed by address */
|
|
11
|
+
protected cachedConnections: Map<string, oNodeConnection[]>;
|
|
12
|
+
protected pendingDialsByAddress: Map<string, Promise<Connection>>;
|
|
14
13
|
constructor(config: oNodeConnectionManagerConfig);
|
|
15
14
|
/**
|
|
16
15
|
* Set up listeners to maintain connection cache state
|
|
17
16
|
*/
|
|
18
|
-
|
|
17
|
+
protected setupConnectionListeners(): void;
|
|
19
18
|
/**
|
|
20
19
|
* Build a stable cache key from an address.
|
|
21
20
|
*
|
|
22
21
|
* We key the cache by address value (e.g., "o://my-tool") to maintain
|
|
23
22
|
* a simple one-to-one mapping between addresses and connections.
|
|
24
23
|
*/
|
|
25
|
-
|
|
24
|
+
protected getAddressKey(address: oAddress): string | null;
|
|
26
25
|
/**
|
|
27
26
|
* Extract peer ID string from an address
|
|
28
27
|
* @param address - The address to extract peer ID from
|
|
29
28
|
* @returns The peer ID string or null if not found
|
|
30
29
|
*/
|
|
31
|
-
|
|
30
|
+
protected getPeerIdFromAddress(address: oAddress): string | null;
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Get the first valid (open) connection for the given address key.
|
|
33
|
+
* Cleans up stale connections from the cache automatically.
|
|
35
34
|
*
|
|
36
|
-
* @param
|
|
37
|
-
* @param reuseContext - If true, prefer inbound connections; otherwise prefer outbound
|
|
38
|
-
* @returns The best connection or null if none are suitable
|
|
39
|
-
*/
|
|
40
|
-
private selectBestConnection;
|
|
41
|
-
/**
|
|
42
|
-
* Get a cached oNodeConnection for the given p2p connection if it exists and is valid.
|
|
43
|
-
* @param p2pConnection - The p2p connection to look up
|
|
35
|
+
* @param addressKey - The address key to look up
|
|
44
36
|
* @returns A valid oNodeConnection or null if none found
|
|
45
37
|
*/
|
|
46
|
-
|
|
38
|
+
protected getValidConnection(addressKey: string): oNodeConnection | null;
|
|
47
39
|
/**
|
|
48
|
-
* Cache an oNodeConnection by its
|
|
40
|
+
* Cache an oNodeConnection by its address key.
|
|
49
41
|
* @param conn - The oNodeConnection to cache
|
|
42
|
+
* @param addressKey - The address key to cache under
|
|
43
|
+
*/
|
|
44
|
+
protected cacheConnection(conn: oNodeConnection, addressKey: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Get or create a raw p2p connection to the given address.
|
|
47
|
+
* Subclasses can override connect() and use this method to get the underlying p2p connection.
|
|
50
48
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
private performDial;
|
|
49
|
+
protected getOrCreateP2pConnection(nextHopAddress: oAddress, addressKey: string): Promise<Connection>;
|
|
50
|
+
protected performDial(nextHopAddress: oAddress, addressKey: string): Promise<Connection>;
|
|
54
51
|
answer(config: oConnectionConfig & {
|
|
55
52
|
p2pConnection: Connection;
|
|
56
53
|
reuse?: boolean;
|
|
57
54
|
}): Promise<oNodeConnection>;
|
|
58
55
|
/**
|
|
59
|
-
* Connect to a given address, reusing oNodeConnection
|
|
56
|
+
* Connect to a given address, reusing oNodeConnection when possible
|
|
60
57
|
* @param config - Connection configuration
|
|
61
58
|
* @returns The connection object
|
|
62
59
|
*/
|
|
@@ -68,11 +65,11 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
68
65
|
*/
|
|
69
66
|
isCached(address: oAddress): boolean;
|
|
70
67
|
/**
|
|
71
|
-
* Get an existing
|
|
68
|
+
* Get an existing cached oNodeConnection for the target address
|
|
72
69
|
* @param address - The address to get a connection for
|
|
73
|
-
* @returns The
|
|
70
|
+
* @returns The oNodeConnection or null if not found
|
|
74
71
|
*/
|
|
75
|
-
|
|
72
|
+
getCachedConnection(address: oAddress): oNodeConnection | null;
|
|
76
73
|
/**
|
|
77
74
|
* Get cache statistics for monitoring and debugging
|
|
78
75
|
* @returns Object containing cache statistics
|
|
@@ -81,7 +78,6 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
81
78
|
cachedAddresses: number;
|
|
82
79
|
totalCachedConnections: number;
|
|
83
80
|
pendingDials: number;
|
|
84
|
-
cachedNodeConnections: number;
|
|
85
81
|
connectionsByPeer: Array<{
|
|
86
82
|
peerId: string;
|
|
87
83
|
status: string;
|
|
@@ -93,10 +89,5 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
93
89
|
* @returns Number of connections removed
|
|
94
90
|
*/
|
|
95
91
|
cleanupStaleConnections(): number;
|
|
96
|
-
/**
|
|
97
|
-
* Clean up all stale oNodeConnections from cache
|
|
98
|
-
* @returns Number of oNodeConnections removed
|
|
99
|
-
*/
|
|
100
|
-
cleanupStaleNodeConnections(): number;
|
|
101
92
|
}
|
|
102
93
|
//# sourceMappingURL=o-node-connection.manager.d.ts.map
|
|
@@ -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,
|
|
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,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,4BAA4B,EAAE,MAAM,kDAAkD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,qBAAa,sBAAuB,SAAQ,kBAAkB;IAQhD,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IAPzD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACzC,iEAAiE;IACjE,SAAS,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAa;IACxE,SAAS,CAAC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAa;gBAEzD,MAAM,EAAE,4BAA4B;IAWzD;;OAEG;IACH,SAAS,CAAC,wBAAwB,IAAI,IAAI;IA+B1C;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI;IASzD;;;;OAIG;IACH,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI;IAahE;;;;;;OAMG;IACH,SAAS,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAkBxE;;;;OAIG;IACH,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAY1E;;;OAGG;cACa,wBAAwB,CACtC,cAAc,EAAE,QAAQ,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC;cAoCN,WAAW,CACzB,cAAc,EAAE,QAAQ,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC;IAqBhB,MAAM,CACV,MAAM,EAAE,iBAAiB,GAAG;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,eAAe,CAAC;IAqD3B;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAyDlE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO;IAcpC;;;;OAIG;IACH,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,eAAe,GAAG,IAAI;IAc9D;;;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"}
|
|
@@ -4,10 +4,9 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
4
4
|
constructor(config) {
|
|
5
5
|
super(config);
|
|
6
6
|
this.config = config;
|
|
7
|
-
|
|
7
|
+
/** Single cache of oNodeConnection instances keyed by address */
|
|
8
|
+
this.cachedConnections = new Map();
|
|
8
9
|
this.pendingDialsByAddress = new Map();
|
|
9
|
-
/** Cache of oNodeConnection instances keyed by p2p connection ID */
|
|
10
|
-
this.nodeConnectionByConnectionId = new Map();
|
|
11
10
|
this.p2pNode = config.p2pNode;
|
|
12
11
|
this.defaultReadTimeoutMs = config.defaultReadTimeoutMs;
|
|
13
12
|
this.defaultDrainTimeoutMs = config.defaultDrainTimeoutMs;
|
|
@@ -24,31 +23,18 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
24
23
|
if (!connection) {
|
|
25
24
|
return;
|
|
26
25
|
}
|
|
27
|
-
// Clean up libp2p connection cache
|
|
28
|
-
// for (const [
|
|
29
|
-
// addressKey,
|
|
30
|
-
// cachedConnections,
|
|
31
|
-
// ] of this.connectionsByAddress.entries()) {
|
|
32
|
-
// const index = cachedConnections.indexOf(connection);
|
|
33
|
-
// if (index !== -1) {
|
|
34
|
-
// this.logger.debug(
|
|
35
|
-
// 'Connection closed, removing from cache for address:',
|
|
36
|
-
// addressKey,
|
|
37
|
-
// );
|
|
38
|
-
// cachedConnections.splice(index, 1);
|
|
39
|
-
// // Remove the address key entirely if no connections remain
|
|
40
|
-
// if (cachedConnections.length === 0) {
|
|
41
|
-
// this.connectionsByAddress.delete(addressKey);
|
|
42
|
-
// } else {
|
|
43
|
-
// this.connectionsByAddress.set(addressKey, cachedConnections);
|
|
44
|
-
// }
|
|
45
|
-
// }
|
|
46
|
-
// }
|
|
47
|
-
// Clean up oNodeConnection cache by connection ID
|
|
48
26
|
const connectionId = connection.id;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
// Clean up cached connections by filtering out the closed connection
|
|
28
|
+
for (const [key, conns] of this.cachedConnections.entries()) {
|
|
29
|
+
const filtered = conns.filter((c) => c.p2pConnection.id !== connectionId);
|
|
30
|
+
if (filtered.length === 0) {
|
|
31
|
+
this.logger.debug('Connection closed, removing all cached connections for address:', key);
|
|
32
|
+
this.cachedConnections.delete(key);
|
|
33
|
+
}
|
|
34
|
+
else if (filtered.length !== conns.length) {
|
|
35
|
+
this.logger.debug('Connection closed, updating cached connections for address:', key);
|
|
36
|
+
this.cachedConnections.set(key, filtered);
|
|
37
|
+
}
|
|
52
38
|
}
|
|
53
39
|
});
|
|
54
40
|
}
|
|
@@ -86,117 +72,53 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
86
72
|
}
|
|
87
73
|
}
|
|
88
74
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
75
|
+
* Get the first valid (open) connection for the given address key.
|
|
76
|
+
* Cleans up stale connections from the cache automatically.
|
|
91
77
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @
|
|
94
|
-
* @returns The best connection or null if none are suitable
|
|
78
|
+
* @param addressKey - The address key to look up
|
|
79
|
+
* @returns A valid oNodeConnection or null if none found
|
|
95
80
|
*/
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.map((conn) => ({
|
|
105
|
-
conn,
|
|
106
|
-
activeStreamCount: conn.streams.filter((s) => s.protocol.includes('/o/') &&
|
|
107
|
-
s.status === 'open' &&
|
|
108
|
-
s.writeStatus === 'writable' &&
|
|
109
|
-
s.remoteReadStatus === 'readable').length,
|
|
110
|
-
}))
|
|
111
|
-
.filter((item) => item.activeStreamCount > 0)
|
|
112
|
-
.sort((a, b) => b.activeStreamCount - a.activeStreamCount);
|
|
113
|
-
if (connectionsWithStreams.length > 0) {
|
|
114
|
-
this.logger.debug('Selected connection with active streams', {
|
|
115
|
-
streamCount: connectionsWithStreams[0].activeStreamCount,
|
|
116
|
-
});
|
|
117
|
-
return connectionsWithStreams[0].conn;
|
|
118
|
-
}
|
|
119
|
-
// Priority 2: Based on reuse context
|
|
120
|
-
if (reuseContext) {
|
|
121
|
-
// Prefer inbound connections
|
|
122
|
-
const inbound = openConnections.find((c) => c.direction === 'inbound' || !c.direction);
|
|
123
|
-
if (inbound) {
|
|
124
|
-
this.logger.debug('Selected inbound connection (reuse context)');
|
|
125
|
-
return inbound;
|
|
81
|
+
getValidConnection(addressKey) {
|
|
82
|
+
const connections = this.cachedConnections.get(addressKey) || [];
|
|
83
|
+
// Filter to open connections
|
|
84
|
+
const valid = connections.filter((c) => c.p2pConnection?.status === 'open');
|
|
85
|
+
// Update cache if we cleaned any stale connections
|
|
86
|
+
if (valid.length !== connections.length) {
|
|
87
|
+
if (valid.length === 0) {
|
|
88
|
+
this.cachedConnections.delete(addressKey);
|
|
126
89
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Prefer outbound connections
|
|
130
|
-
const outbound = openConnections.find((c) => c.direction === 'outbound');
|
|
131
|
-
if (outbound) {
|
|
132
|
-
this.logger.debug('Selected outbound connection (non-reuse context)');
|
|
133
|
-
return outbound;
|
|
90
|
+
else {
|
|
91
|
+
this.cachedConnections.set(addressKey, valid);
|
|
134
92
|
}
|
|
135
93
|
}
|
|
136
|
-
|
|
137
|
-
this.logger.debug('Selected first available open connection', connectionsWithStreams);
|
|
138
|
-
return openConnections[0];
|
|
94
|
+
return valid[0] ?? null;
|
|
139
95
|
}
|
|
140
96
|
/**
|
|
141
|
-
*
|
|
142
|
-
* @param
|
|
143
|
-
* @
|
|
97
|
+
* Cache an oNodeConnection by its address key.
|
|
98
|
+
* @param conn - The oNodeConnection to cache
|
|
99
|
+
* @param addressKey - The address key to cache under
|
|
144
100
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Clean up stale entry if connection is no longer open
|
|
151
|
-
if (cached) {
|
|
152
|
-
this.nodeConnectionByConnectionId.delete(p2pConnection.id);
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
101
|
+
cacheConnection(conn, addressKey) {
|
|
102
|
+
this.logger.debug('Caching connection for address:', addressKey, conn.p2pConnection.id, conn.p2pConnection.direction);
|
|
103
|
+
const existing = this.cachedConnections.get(addressKey) || [];
|
|
104
|
+
existing.push(conn);
|
|
105
|
+
this.cachedConnections.set(addressKey, existing);
|
|
155
106
|
}
|
|
156
107
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
108
|
+
* Get or create a raw p2p connection to the given address.
|
|
109
|
+
* Subclasses can override connect() and use this method to get the underlying p2p connection.
|
|
159
110
|
*/
|
|
160
|
-
|
|
161
|
-
this
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (!addressKey) {
|
|
170
|
-
throw new Error(`Unable to extract address key from address: ${nextHopAddress.toString()}`);
|
|
171
|
-
}
|
|
172
|
-
// Check if we have cached connections by address key
|
|
173
|
-
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
174
|
-
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
175
|
-
if (bestConnection) {
|
|
176
|
-
this.logger.debug('Reusing cached connection for address:', nextHopAddress?.value, bestConnection.id);
|
|
177
|
-
return bestConnection;
|
|
178
|
-
}
|
|
179
|
-
// Clean up stale connections if they exist but are not open
|
|
180
|
-
if (cachedConnections.length > 0) {
|
|
181
|
-
const openConnections = cachedConnections.filter((c) => c.status === 'open');
|
|
182
|
-
if (openConnections.length === 0) {
|
|
183
|
-
this.logger.debug('Removing all stale connections for address:', addressKey);
|
|
184
|
-
this.connectionsByAddress.delete(addressKey);
|
|
185
|
-
}
|
|
186
|
-
else if (openConnections.length < cachedConnections.length) {
|
|
187
|
-
this.logger.debug('Cleaning up some stale connections for address:', addressKey);
|
|
188
|
-
this.connectionsByAddress.set(addressKey, openConnections);
|
|
111
|
+
async getOrCreateP2pConnection(nextHopAddress, addressKey) {
|
|
112
|
+
// Check if libp2p already has an active connection for this peer
|
|
113
|
+
const peerId = this.getPeerIdFromAddress(nextHopAddress);
|
|
114
|
+
if (peerId) {
|
|
115
|
+
const connections = this.p2pNode.getConnections();
|
|
116
|
+
const existingConnection = connections.find((conn) => conn.remotePeer?.toString() === peerId && conn.status === 'open');
|
|
117
|
+
if (existingConnection) {
|
|
118
|
+
this.logger.debug('Found existing libp2p connection for address:', addressKey);
|
|
119
|
+
return existingConnection;
|
|
189
120
|
}
|
|
190
121
|
}
|
|
191
|
-
// Check if libp2p has an active connection for this address
|
|
192
|
-
const libp2pConnection = this.getCachedLibp2pConnection(nextHopAddress);
|
|
193
|
-
if (libp2pConnection && libp2pConnection.status === 'open') {
|
|
194
|
-
this.logger.debug('Caching existing libp2p connection for address:', addressKey);
|
|
195
|
-
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
196
|
-
connections.push(libp2pConnection);
|
|
197
|
-
this.connectionsByAddress.set(addressKey, connections);
|
|
198
|
-
return libp2pConnection;
|
|
199
|
-
}
|
|
200
122
|
// Check if dial is already in progress for this address key
|
|
201
123
|
const pendingDial = this.pendingDialsByAddress.get(addressKey);
|
|
202
124
|
if (pendingDial) {
|
|
@@ -207,12 +129,7 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
207
129
|
const dialPromise = this.performDial(nextHopAddress, addressKey);
|
|
208
130
|
this.pendingDialsByAddress.set(addressKey, dialPromise);
|
|
209
131
|
try {
|
|
210
|
-
|
|
211
|
-
// Add the established connection to the cache array
|
|
212
|
-
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
213
|
-
connections.push(connection);
|
|
214
|
-
this.connectionsByAddress.set(addressKey, connections);
|
|
215
|
-
return connection;
|
|
132
|
+
return await dialPromise;
|
|
216
133
|
}
|
|
217
134
|
finally {
|
|
218
135
|
this.pendingDialsByAddress.delete(addressKey);
|
|
@@ -233,6 +150,17 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
233
150
|
}
|
|
234
151
|
async answer(config) {
|
|
235
152
|
const { address, nextHopAddress, callerAddress, readTimeoutMs, drainTimeoutMs, p2pConnection, reuse, } = config;
|
|
153
|
+
const addressKey = this.getAddressKey(nextHopAddress);
|
|
154
|
+
if (!addressKey) {
|
|
155
|
+
this.logger.error('Failed to generate an address key for address:', nextHopAddress);
|
|
156
|
+
throw new Error(`Unable to extract address key from address: ${nextHopAddress.toString()}`);
|
|
157
|
+
}
|
|
158
|
+
// Check if we already have a cached connection for this address
|
|
159
|
+
const existingConnection = this.getValidConnection(addressKey);
|
|
160
|
+
if (existingConnection) {
|
|
161
|
+
this.logger.debug('Reusing cached connection for answer:', addressKey, existingConnection.p2pConnection.id);
|
|
162
|
+
return existingConnection;
|
|
163
|
+
}
|
|
236
164
|
const connection = new oNodeConnection({
|
|
237
165
|
nextHopAddress: nextHopAddress,
|
|
238
166
|
address: address,
|
|
@@ -246,38 +174,33 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
246
174
|
requestHandler: config.requestHandler ?? undefined,
|
|
247
175
|
reusePolicy: reuse ? 'reuse' : 'none',
|
|
248
176
|
});
|
|
249
|
-
// Cache the new connection
|
|
250
|
-
this.
|
|
251
|
-
const addressKey = this.getAddressKey(nextHopAddress);
|
|
252
|
-
if (addressKey) {
|
|
253
|
-
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
254
|
-
// Only add if not already in the cache
|
|
255
|
-
if (!connections.includes(p2pConnection)) {
|
|
256
|
-
connections.push(p2pConnection);
|
|
257
|
-
this.connectionsByAddress.set(addressKey, connections);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
this.logger.error('Should not happen! Failed to generate an address key for address:', nextHopAddress);
|
|
262
|
-
}
|
|
177
|
+
// Cache the new connection
|
|
178
|
+
this.cacheConnection(connection, addressKey);
|
|
263
179
|
return connection;
|
|
264
180
|
}
|
|
265
181
|
/**
|
|
266
|
-
* Connect to a given address, reusing oNodeConnection
|
|
182
|
+
* Connect to a given address, reusing oNodeConnection when possible
|
|
267
183
|
* @param config - Connection configuration
|
|
268
184
|
* @returns The connection object
|
|
269
185
|
*/
|
|
270
186
|
async connect(config) {
|
|
271
187
|
const { address, nextHopAddress, callerAddress, readTimeoutMs, drainTimeoutMs, } = config;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
188
|
+
if (!nextHopAddress) {
|
|
189
|
+
throw new Error('Invalid address passed');
|
|
190
|
+
}
|
|
191
|
+
const addressKey = this.getAddressKey(nextHopAddress);
|
|
192
|
+
if (!addressKey) {
|
|
193
|
+
throw new Error(`Unable to extract address key from address: ${nextHopAddress.toString()}`);
|
|
194
|
+
}
|
|
195
|
+
// Check for existing valid cached connection
|
|
196
|
+
const existingConnection = this.getValidConnection(addressKey);
|
|
276
197
|
if (existingConnection) {
|
|
277
|
-
this.logger.debug('Reusing cached
|
|
198
|
+
this.logger.debug('Reusing cached connection for address:', addressKey, existingConnection.p2pConnection.id);
|
|
278
199
|
return existingConnection;
|
|
279
200
|
}
|
|
280
|
-
//
|
|
201
|
+
// Get or create the underlying p2p connection
|
|
202
|
+
const p2pConnection = await this.getOrCreateP2pConnection(nextHopAddress, addressKey);
|
|
203
|
+
// Create new oNodeConnection
|
|
281
204
|
const connection = new oNodeConnection({
|
|
282
205
|
nextHopAddress: nextHopAddress,
|
|
283
206
|
address: address,
|
|
@@ -290,8 +213,8 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
290
213
|
runOnLimitedConnection: this.config.runOnLimitedConnection ?? false,
|
|
291
214
|
requestHandler: config.requestHandler ?? undefined,
|
|
292
215
|
});
|
|
293
|
-
// Cache the new connection
|
|
294
|
-
this.
|
|
216
|
+
// Cache the new connection
|
|
217
|
+
this.cacheConnection(connection, addressKey);
|
|
295
218
|
return connection;
|
|
296
219
|
}
|
|
297
220
|
/**
|
|
@@ -305,30 +228,7 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
305
228
|
if (!addressKey) {
|
|
306
229
|
return false;
|
|
307
230
|
}
|
|
308
|
-
|
|
309
|
-
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
310
|
-
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
311
|
-
if (bestConnection) {
|
|
312
|
-
return true;
|
|
313
|
-
}
|
|
314
|
-
// Fall back to checking libp2p's connections
|
|
315
|
-
const peerId = this.getPeerIdFromAddress(address);
|
|
316
|
-
// the following works since the peer id param is not really required: https://github.com/libp2p/js-libp2p/blob/0bbf5021b53938b2bffcffca6c13c479a95c2a60/packages/libp2p/src/connection-manager/index.ts#L508
|
|
317
|
-
const connections = this.p2pNode.getConnections(peerId); // ignore since converting to a proper peer id breaks the browser implementation
|
|
318
|
-
// Check if we have at least one open connection
|
|
319
|
-
const hasOpenConnection = connections.some((conn) => conn.status === 'open');
|
|
320
|
-
// If libp2p has an open connection, update our cache
|
|
321
|
-
if (hasOpenConnection) {
|
|
322
|
-
const openConnection = connections.find((conn) => conn.status === 'open');
|
|
323
|
-
if (openConnection) {
|
|
324
|
-
const existingConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
325
|
-
if (!existingConnections.includes(openConnection)) {
|
|
326
|
-
existingConnections.push(openConnection);
|
|
327
|
-
this.connectionsByAddress.set(addressKey, existingConnections);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return hasOpenConnection;
|
|
231
|
+
return this.getValidConnection(addressKey) !== null;
|
|
332
232
|
}
|
|
333
233
|
catch (error) {
|
|
334
234
|
this.logger.debug('Error checking cached connection:', error);
|
|
@@ -336,48 +236,17 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
336
236
|
}
|
|
337
237
|
}
|
|
338
238
|
/**
|
|
339
|
-
* Get an existing
|
|
239
|
+
* Get an existing cached oNodeConnection for the target address
|
|
340
240
|
* @param address - The address to get a connection for
|
|
341
|
-
* @returns The
|
|
241
|
+
* @returns The oNodeConnection or null if not found
|
|
342
242
|
*/
|
|
343
|
-
|
|
243
|
+
getCachedConnection(address) {
|
|
344
244
|
try {
|
|
345
245
|
const addressKey = this.getAddressKey(address);
|
|
346
246
|
if (!addressKey) {
|
|
347
247
|
return null;
|
|
348
248
|
}
|
|
349
|
-
|
|
350
|
-
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
351
|
-
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
352
|
-
if (bestConnection) {
|
|
353
|
-
return bestConnection;
|
|
354
|
-
}
|
|
355
|
-
const peerId = this.getPeerIdFromAddress(address);
|
|
356
|
-
if (!peerId) {
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
// Query libp2p for connections to this peer
|
|
360
|
-
const connections = this.p2pNode.getConnections();
|
|
361
|
-
const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerId);
|
|
362
|
-
// Find open connections
|
|
363
|
-
const openConnections = filteredConnections.filter((conn) => conn.status === 'open');
|
|
364
|
-
// If we found open connections in libp2p, add them to cache and select best
|
|
365
|
-
if (openConnections.length > 0) {
|
|
366
|
-
const existingConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
367
|
-
// Add any new connections that aren't already cached
|
|
368
|
-
for (const conn of openConnections) {
|
|
369
|
-
if (!existingConnections.includes(conn)) {
|
|
370
|
-
existingConnections.push(conn);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
this.connectionsByAddress.set(addressKey, existingConnections);
|
|
374
|
-
return this.selectBestConnection(existingConnections, false);
|
|
375
|
-
}
|
|
376
|
-
// Clean up stale cache entries if connections are no longer open
|
|
377
|
-
if (cachedConnections.length > 0) {
|
|
378
|
-
this.connectionsByAddress.delete(addressKey);
|
|
379
|
-
}
|
|
380
|
-
return null;
|
|
249
|
+
return this.getValidConnection(addressKey);
|
|
381
250
|
}
|
|
382
251
|
catch (error) {
|
|
383
252
|
this.logger.debug('Error getting cached connection:', error);
|
|
@@ -390,20 +259,19 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
390
259
|
*/
|
|
391
260
|
getCacheStats() {
|
|
392
261
|
const allConnections = [];
|
|
393
|
-
for (const [addressKey, connections
|
|
262
|
+
for (const [addressKey, connections] of this.cachedConnections.entries()) {
|
|
394
263
|
for (const conn of connections) {
|
|
395
264
|
allConnections.push({
|
|
396
|
-
peerId: conn.remotePeer?.toString() ?? 'unknown',
|
|
397
|
-
status: conn.status,
|
|
265
|
+
peerId: conn.p2pConnection?.remotePeer?.toString() ?? 'unknown',
|
|
266
|
+
status: conn.p2pConnection?.status ?? 'unknown',
|
|
398
267
|
addressKey,
|
|
399
268
|
});
|
|
400
269
|
}
|
|
401
270
|
}
|
|
402
271
|
return {
|
|
403
|
-
cachedAddresses: this.
|
|
272
|
+
cachedAddresses: this.cachedConnections.size,
|
|
404
273
|
totalCachedConnections: allConnections.length,
|
|
405
274
|
pendingDials: this.pendingDialsByAddress.size,
|
|
406
|
-
cachedNodeConnections: this.nodeConnectionByConnectionId.size,
|
|
407
275
|
connectionsByPeer: allConnections,
|
|
408
276
|
};
|
|
409
277
|
}
|
|
@@ -413,18 +281,16 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
413
281
|
*/
|
|
414
282
|
cleanupStaleConnections() {
|
|
415
283
|
let removed = 0;
|
|
416
|
-
for (const [addressKey, connections
|
|
417
|
-
const openConnections = connections.filter((conn) => conn.status === 'open');
|
|
284
|
+
for (const [addressKey, connections] of this.cachedConnections.entries()) {
|
|
285
|
+
const openConnections = connections.filter((conn) => conn.p2pConnection?.status === 'open');
|
|
418
286
|
const staleCount = connections.length - openConnections.length;
|
|
419
287
|
if (staleCount > 0) {
|
|
420
288
|
removed += staleCount;
|
|
421
289
|
if (openConnections.length === 0) {
|
|
422
|
-
|
|
423
|
-
this.connectionsByAddress.delete(addressKey);
|
|
290
|
+
this.cachedConnections.delete(addressKey);
|
|
424
291
|
}
|
|
425
292
|
else {
|
|
426
|
-
|
|
427
|
-
this.connectionsByAddress.set(addressKey, openConnections);
|
|
293
|
+
this.cachedConnections.set(addressKey, openConnections);
|
|
428
294
|
}
|
|
429
295
|
}
|
|
430
296
|
}
|
|
@@ -433,21 +299,4 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
433
299
|
}
|
|
434
300
|
return removed;
|
|
435
301
|
}
|
|
436
|
-
/**
|
|
437
|
-
* Clean up all stale oNodeConnections from cache
|
|
438
|
-
* @returns Number of oNodeConnections removed
|
|
439
|
-
*/
|
|
440
|
-
cleanupStaleNodeConnections() {
|
|
441
|
-
let removed = 0;
|
|
442
|
-
for (const [connectionId, conn,] of this.nodeConnectionByConnectionId.entries()) {
|
|
443
|
-
if (conn.p2pConnection?.status !== 'open') {
|
|
444
|
-
this.nodeConnectionByConnectionId.delete(connectionId);
|
|
445
|
-
removed++;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (removed > 0) {
|
|
449
|
-
this.logger.debug(`Cleaned up ${removed} stale oNodeConnections`);
|
|
450
|
-
}
|
|
451
|
-
return removed;
|
|
452
|
-
}
|
|
453
302
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { oAddress, NodeState, oNotificationManager } from '@olane/o-core';
|
|
2
2
|
import { oNodeAddress } from '../router/o-node.address.js';
|
|
3
3
|
import { oNodeConfig } from './o-node.config.js';
|
|
4
|
+
import { oRegistrationManager } from '../managers/o-registration.manager.js';
|
|
4
5
|
/**
|
|
5
6
|
* Interface for nodes that support reconnection management.
|
|
6
7
|
* This interface defines the contract that oReconnectionManager needs
|
|
@@ -23,10 +24,18 @@ export interface IReconnectableNode {
|
|
|
23
24
|
* The notification manager for subscribing to events
|
|
24
25
|
*/
|
|
25
26
|
notificationManager: oNotificationManager;
|
|
27
|
+
/**
|
|
28
|
+
* The registration manager for tracking registration state
|
|
29
|
+
*/
|
|
30
|
+
registrationManager: oRegistrationManager;
|
|
26
31
|
/**
|
|
27
32
|
* Register with the parent node
|
|
28
33
|
*/
|
|
29
34
|
registerParent(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Register with the leader's global registry
|
|
37
|
+
*/
|
|
38
|
+
registerLeader(): Promise<void>;
|
|
30
39
|
/**
|
|
31
40
|
* Register with the leader's global registry
|
|
32
41
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i-reconnectable-node.d.ts","sourceRoot":"","sources":["../../../src/interfaces/i-reconnectable-node.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,SAAS,EACT,oBAAoB,EAErB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"i-reconnectable-node.d.ts","sourceRoot":"","sources":["../../../src/interfaces/i-reconnectable-node.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,SAAS,EACT,oBAAoB,EAErB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAE7E;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,mBAAmB,EAAE,oBAAoB,CAAC;IAE1C;;OAEG;IACH,mBAAmB,EAAE,oBAAoB,CAAC;IAE1C;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1B,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,GAAG,CACD,OAAO,EAAE,QAAQ,EACjB,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAC;QAChC,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GACA,OAAO,CAAC,GAAG,CAAC,CAAC;CACjB"}
|