@olane/o-node 0.7.43 → 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.
- package/dist/src/connection/o-node-connection.manager.d.ts +18 -8
- package/dist/src/connection/o-node-connection.manager.d.ts.map +1 -1
- package/dist/src/connection/o-node-connection.manager.js +182 -82
- package/dist/src/utils/connection.utils.d.ts.map +1 -1
- package/dist/src/utils/connection.utils.js +4 -4
- package/package.json +7 -7
|
@@ -7,27 +7,35 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
7
7
|
protected p2pNode: Libp2p;
|
|
8
8
|
private defaultReadTimeoutMs?;
|
|
9
9
|
private defaultDrainTimeoutMs?;
|
|
10
|
-
private
|
|
11
|
-
private
|
|
10
|
+
private connectionsByAddress;
|
|
11
|
+
private pendingDialsByAddress;
|
|
12
12
|
constructor(config: oNodeConnectionManagerConfig);
|
|
13
13
|
/**
|
|
14
14
|
* Set up listeners to maintain connection cache state
|
|
15
15
|
*/
|
|
16
16
|
private setupConnectionListeners;
|
|
17
17
|
/**
|
|
18
|
-
* Build a stable cache key from
|
|
18
|
+
* Build a stable cache key from an address.
|
|
19
19
|
*
|
|
20
|
-
* We
|
|
21
|
-
*
|
|
22
|
-
* change but the libp2p transports are the true dial targets.
|
|
20
|
+
* We key the cache by address value (e.g., "o://my-tool") to maintain
|
|
21
|
+
* a simple one-to-one mapping between addresses and connections.
|
|
23
22
|
*/
|
|
24
|
-
private
|
|
23
|
+
private getAddressKey;
|
|
25
24
|
/**
|
|
26
25
|
* Extract peer ID string from an address
|
|
27
26
|
* @param address - The address to extract peer ID from
|
|
28
27
|
* @returns The peer ID string or null if not found
|
|
29
28
|
*/
|
|
30
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;
|
|
31
39
|
getOrCreateConnection(nextHopAddress: oAddress, address: oAddress): Promise<Connection>;
|
|
32
40
|
private performDial;
|
|
33
41
|
answer(config: oConnectionConfig & {
|
|
@@ -57,11 +65,13 @@ export declare class oNodeConnectionManager extends oConnectionManager {
|
|
|
57
65
|
* @returns Object containing cache statistics
|
|
58
66
|
*/
|
|
59
67
|
getCacheStats(): {
|
|
60
|
-
|
|
68
|
+
cachedAddresses: number;
|
|
69
|
+
totalCachedConnections: number;
|
|
61
70
|
pendingDials: number;
|
|
62
71
|
connectionsByPeer: Array<{
|
|
63
72
|
peerId: string;
|
|
64
73
|
status: string;
|
|
74
|
+
addressKey: string;
|
|
65
75
|
}>;
|
|
66
76
|
};
|
|
67
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,
|
|
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"}
|
|
@@ -4,8 +4,8 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
4
4
|
constructor(config) {
|
|
5
5
|
super(config);
|
|
6
6
|
this.config = config;
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
7
|
+
this.connectionsByAddress = new Map();
|
|
8
|
+
this.pendingDialsByAddress = new Map();
|
|
9
9
|
this.p2pNode = config.p2pNode;
|
|
10
10
|
this.defaultReadTimeoutMs = config.defaultReadTimeoutMs;
|
|
11
11
|
this.defaultDrainTimeoutMs = config.defaultDrainTimeoutMs;
|
|
@@ -22,34 +22,34 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
22
22
|
if (!connection) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
for (const [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
25
|
+
for (const [addressKey, cachedConnections,] of this.connectionsByAddress.entries()) {
|
|
26
|
+
const index = cachedConnections.indexOf(connection);
|
|
27
|
+
if (index !== -1) {
|
|
28
|
+
this.logger.debug('Connection closed, removing from cache for address:', addressKey);
|
|
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
|
});
|
|
32
40
|
}
|
|
33
41
|
/**
|
|
34
|
-
* Build a stable cache key from
|
|
42
|
+
* Build a stable cache key from an address.
|
|
35
43
|
*
|
|
36
|
-
* We
|
|
37
|
-
*
|
|
38
|
-
* change but the libp2p transports are the true dial targets.
|
|
44
|
+
* We key the cache by address value (e.g., "o://my-tool") to maintain
|
|
45
|
+
* a simple one-to-one mapping between addresses and connections.
|
|
39
46
|
*/
|
|
40
|
-
|
|
47
|
+
getAddressKey(address) {
|
|
41
48
|
try {
|
|
42
|
-
|
|
43
|
-
const transports = nodeAddress.libp2pTransports;
|
|
44
|
-
if (!transports?.length) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
// Sort to ensure deterministic keys regardless of transport ordering.
|
|
48
|
-
const parts = transports.map((t) => t.toString()).sort();
|
|
49
|
-
return parts.join('|');
|
|
49
|
+
return address.value || null;
|
|
50
50
|
}
|
|
51
51
|
catch (error) {
|
|
52
|
-
this.logger.debug('Error extracting
|
|
52
|
+
this.logger.debug('Error extracting address key from address:', error);
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -71,60 +71,125 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
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
|
+
}
|
|
74
127
|
async getOrCreateConnection(nextHopAddress, address) {
|
|
75
128
|
if (!nextHopAddress) {
|
|
76
129
|
throw new Error('Invalid address passed');
|
|
77
130
|
}
|
|
78
|
-
// Build
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
81
|
-
throw new Error(`Unable to extract
|
|
131
|
+
// Build an address-based cache key from the next hop address
|
|
132
|
+
const addressKey = this.getAddressKey(nextHopAddress);
|
|
133
|
+
if (!addressKey) {
|
|
134
|
+
throw new Error(`Unable to extract address key from address: ${nextHopAddress.toString()}`);
|
|
82
135
|
}
|
|
83
|
-
// Check if we have
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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) {
|
|
140
|
+
this.logger.debug('Reusing cached connection for address:', nextHopAddress?.value);
|
|
141
|
+
return bestConnection;
|
|
88
142
|
}
|
|
89
|
-
// Clean up stale
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
}
|
|
93
154
|
}
|
|
94
155
|
// Check if libp2p has an active connection for this address
|
|
95
156
|
const libp2pConnection = this.getCachedLibp2pConnection(nextHopAddress);
|
|
96
157
|
if (libp2pConnection && libp2pConnection.status === 'open') {
|
|
97
|
-
this.logger.debug('Caching existing libp2p connection for
|
|
98
|
-
this.
|
|
158
|
+
this.logger.debug('Caching existing libp2p connection for address:', addressKey);
|
|
159
|
+
const connections = this.connectionsByAddress.get(addressKey) || [];
|
|
160
|
+
connections.push(libp2pConnection);
|
|
161
|
+
this.connectionsByAddress.set(addressKey, connections);
|
|
99
162
|
return libp2pConnection;
|
|
100
163
|
}
|
|
101
|
-
// Check if dial is already in progress for this
|
|
102
|
-
const pendingDial = this.
|
|
164
|
+
// Check if dial is already in progress for this address key
|
|
165
|
+
const pendingDial = this.pendingDialsByAddress.get(addressKey);
|
|
103
166
|
if (pendingDial) {
|
|
104
|
-
this.logger.debug('Awaiting existing dial for
|
|
167
|
+
this.logger.debug('Awaiting existing dial for address:', addressKey);
|
|
105
168
|
return pendingDial;
|
|
106
169
|
}
|
|
107
|
-
// Start new dial and cache the promise by
|
|
108
|
-
const dialPromise = this.performDial(nextHopAddress,
|
|
109
|
-
this.
|
|
170
|
+
// Start new dial and cache the promise by address key
|
|
171
|
+
const dialPromise = this.performDial(nextHopAddress, addressKey);
|
|
172
|
+
this.pendingDialsByAddress.set(addressKey, dialPromise);
|
|
110
173
|
try {
|
|
111
174
|
const connection = await dialPromise;
|
|
112
|
-
//
|
|
113
|
-
this.
|
|
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);
|
|
114
179
|
return connection;
|
|
115
180
|
}
|
|
116
181
|
finally {
|
|
117
|
-
this.
|
|
182
|
+
this.pendingDialsByAddress.delete(addressKey);
|
|
118
183
|
}
|
|
119
184
|
}
|
|
120
|
-
async performDial(nextHopAddress,
|
|
185
|
+
async performDial(nextHopAddress, addressKey) {
|
|
121
186
|
this.logger.debug('Dialing new connection', {
|
|
122
187
|
address: nextHopAddress.value,
|
|
123
|
-
|
|
188
|
+
addressKey,
|
|
124
189
|
});
|
|
125
190
|
const connection = await this.p2pNode.dial(nextHopAddress.libp2pTransports.map((ma) => ma.toMultiaddr()));
|
|
126
191
|
this.logger.debug('Successfully dialed connection', {
|
|
127
|
-
|
|
192
|
+
addressKey,
|
|
128
193
|
status: connection.status,
|
|
129
194
|
remotePeer: connection.remotePeer?.toString(),
|
|
130
195
|
});
|
|
@@ -145,12 +210,17 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
145
210
|
requestHandler: config.requestHandler ?? undefined,
|
|
146
211
|
reusePolicy: reuse ? 'reuse' : 'none',
|
|
147
212
|
});
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
this.
|
|
213
|
+
const addressKey = this.getAddressKey(nextHopAddress);
|
|
214
|
+
if (addressKey) {
|
|
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
|
+
}
|
|
151
221
|
}
|
|
152
222
|
else {
|
|
153
|
-
this.logger.error('Should not happen! Failed to generate
|
|
223
|
+
this.logger.error('Should not happen! Failed to generate an address key for address:', nextHopAddress);
|
|
154
224
|
}
|
|
155
225
|
return connection;
|
|
156
226
|
}
|
|
@@ -183,13 +253,14 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
183
253
|
*/
|
|
184
254
|
isCached(address) {
|
|
185
255
|
try {
|
|
186
|
-
const
|
|
187
|
-
if (!
|
|
256
|
+
const addressKey = this.getAddressKey(address);
|
|
257
|
+
if (!addressKey) {
|
|
188
258
|
return false;
|
|
189
259
|
}
|
|
190
|
-
// Check our
|
|
191
|
-
const
|
|
192
|
-
|
|
260
|
+
// Check our address-based cache first
|
|
261
|
+
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
262
|
+
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
263
|
+
if (bestConnection) {
|
|
193
264
|
return true;
|
|
194
265
|
}
|
|
195
266
|
// Fall back to checking libp2p's connections
|
|
@@ -202,7 +273,11 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
202
273
|
if (hasOpenConnection) {
|
|
203
274
|
const openConnection = connections.find((conn) => conn.status === 'open');
|
|
204
275
|
if (openConnection) {
|
|
205
|
-
this.
|
|
276
|
+
const existingConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
277
|
+
if (!existingConnections.includes(openConnection)) {
|
|
278
|
+
existingConnections.push(openConnection);
|
|
279
|
+
this.connectionsByAddress.set(addressKey, existingConnections);
|
|
280
|
+
}
|
|
206
281
|
}
|
|
207
282
|
}
|
|
208
283
|
return hasOpenConnection;
|
|
@@ -219,14 +294,15 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
219
294
|
*/
|
|
220
295
|
getCachedLibp2pConnection(address) {
|
|
221
296
|
try {
|
|
222
|
-
const
|
|
223
|
-
if (!
|
|
297
|
+
const addressKey = this.getAddressKey(address);
|
|
298
|
+
if (!addressKey) {
|
|
224
299
|
return null;
|
|
225
300
|
}
|
|
226
|
-
// Check
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
301
|
+
// Check address-based cache first
|
|
302
|
+
const cachedConnections = this.connectionsByAddress.get(addressKey) || [];
|
|
303
|
+
const bestConnection = this.selectBestConnection(cachedConnections, false);
|
|
304
|
+
if (bestConnection) {
|
|
305
|
+
return bestConnection;
|
|
230
306
|
}
|
|
231
307
|
const peerId = this.getPeerIdFromAddress(address);
|
|
232
308
|
if (!peerId) {
|
|
@@ -235,16 +311,23 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
235
311
|
// Query libp2p for connections to this peer
|
|
236
312
|
const connections = this.p2pNode.getConnections();
|
|
237
313
|
const filteredConnections = connections.filter((conn) => conn.remotePeer?.toString() === peerId);
|
|
238
|
-
// Find
|
|
239
|
-
const
|
|
240
|
-
// If we found
|
|
241
|
-
if (
|
|
242
|
-
this.
|
|
243
|
-
|
|
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);
|
|
244
327
|
}
|
|
245
|
-
// Clean up stale cache
|
|
246
|
-
if (
|
|
247
|
-
this.
|
|
328
|
+
// Clean up stale cache entries if connections are no longer open
|
|
329
|
+
if (cachedConnections.length > 0) {
|
|
330
|
+
this.connectionsByAddress.delete(addressKey);
|
|
248
331
|
}
|
|
249
332
|
return null;
|
|
250
333
|
}
|
|
@@ -258,13 +341,21 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
258
341
|
* @returns Object containing cache statistics
|
|
259
342
|
*/
|
|
260
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
|
+
}
|
|
261
354
|
return {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
status: conn.status,
|
|
267
|
-
})),
|
|
355
|
+
cachedAddresses: this.connectionsByAddress.size,
|
|
356
|
+
totalCachedConnections: allConnections.length,
|
|
357
|
+
pendingDials: this.pendingDialsByAddress.size,
|
|
358
|
+
connectionsByPeer: allConnections,
|
|
268
359
|
};
|
|
269
360
|
}
|
|
270
361
|
/**
|
|
@@ -273,10 +364,19 @@ export class oNodeConnectionManager extends oConnectionManager {
|
|
|
273
364
|
*/
|
|
274
365
|
cleanupStaleConnections() {
|
|
275
366
|
let removed = 0;
|
|
276
|
-
for (const [
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
+
}
|
|
280
380
|
}
|
|
281
381
|
}
|
|
282
382
|
if (removed > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/connection.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAuB,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7D,qBAAa,eAAgB,SAAQ,OAAO;WACtB,qBAAqB,CAAC,OAAO,EAAE;QACjD,WAAW,EAAE,GAAG,CAAC;QACjB,UAAU,EAAE,UAAU,CAAC;KACxB;
|
|
1
|
+
{"version":3,"file":"connection.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/connection.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA2B,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAuB,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7D,qBAAa,eAAgB,SAAQ,OAAO;WACtB,qBAAqB,CAAC,OAAO,EAAE;QACjD,WAAW,EAAE,GAAG,CAAC;QACjB,UAAU,EAAE,UAAU,CAAC;KACxB;CA+DF"}
|
|
@@ -4,12 +4,13 @@ import { oNodeTransport } from '../router/o-node.transport.js';
|
|
|
4
4
|
export class ConnectionUtils extends oObject {
|
|
5
5
|
static async addressFromConnection(options) {
|
|
6
6
|
try {
|
|
7
|
-
console.log('[ConnectionUtils] addressFromConnection');
|
|
8
7
|
const { currentNode, connection } = options;
|
|
9
8
|
const p2pNode = currentNode.p2pNode;
|
|
10
9
|
// Extract the actual olane address from the peer store
|
|
11
10
|
const peers = await p2pNode.peerStore.all();
|
|
12
|
-
const remotePeer = peers.find((peer) =>
|
|
11
|
+
const remotePeer = peers.find((peer) => {
|
|
12
|
+
return peer.id.toString() === connection.remotePeer.toString();
|
|
13
|
+
});
|
|
13
14
|
if (!remotePeer) {
|
|
14
15
|
console.log('Failed to find peer:', remotePeer);
|
|
15
16
|
throw new Error(`Failed to extract remote address, peer ${connection.remotePeer.toString()} not found in peer store.`);
|
|
@@ -19,8 +20,7 @@ export class ConnectionUtils extends oObject {
|
|
|
19
20
|
if (!originAddress) {
|
|
20
21
|
throw new Error('Origin address is not configured');
|
|
21
22
|
}
|
|
22
|
-
const
|
|
23
|
-
const oProtocol = remotePeer.protocols.find((p) => p.startsWith('/o/') && p.startsWith(originProtocol) === false);
|
|
23
|
+
const oProtocol = remotePeer.protocols.find((p) => p.startsWith('/o/'));
|
|
24
24
|
if (!oProtocol) {
|
|
25
25
|
throw new Error('Failed to extract remote address, could not find o-protocol in peer protocols.');
|
|
26
26
|
}
|
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
|
}
|