@dignetwork/dig-sdk 0.0.1-alpha.145 → 0.0.1-alpha.148
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/blockchain/FullNodePeer.d.ts +2 -1
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +50 -3
- package/dist/utils/PeerRanker.d.ts +6 -18
- package/dist/utils/PeerRanker.d.ts.map +1 -1
- package/dist/utils/PeerRanker.js +47 -46
- package/dist/utils/promiseUtils.d.ts +3 -1
- package/dist/utils/promiseUtils.d.ts.map +1 -1
- package/dist/utils/promiseUtils.js +8 -3
- package/package.json +1 -1
|
@@ -85,7 +85,8 @@ export declare class FullNodePeer {
|
|
|
85
85
|
* Connects to the best available peer based on weighted selection and reliability.
|
|
86
86
|
* @returns {Promise<Peer>} The connected Peer instance.
|
|
87
87
|
*/
|
|
88
|
-
private
|
|
88
|
+
private getBestPeer;
|
|
89
|
+
private createPeerProxy;
|
|
89
90
|
/**
|
|
90
91
|
* Handles peer disconnection by marking it in cooldown and updating internal states.
|
|
91
92
|
* @param {string} peerIP - The IP address of the disconnected peer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAO,MAAM,8BAA8B,CAAC;AAmCzD;;GAEG;AACH,qBAAa,YAAY;IA0BH,OAAO,CAAC,IAAI;IAxBhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IAGpD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAG3E,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuD;IAGnF,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkC;IAG5D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAgB;IAG/C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAoC;IAG5D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAoD;IAG9E,OAAO,CAAC,MAAM,CAAC,YAAY,CAAsC;IAGjE,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,YAAY;IAOzC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuB9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IASjC;;;;OAIG;mBACkB,eAAe;IAoFpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAgBpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAwBjC;;;OAGG;mBACkB,UAAU;IAK/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;OAGG;
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAO,MAAM,8BAA8B,CAAC;AAmCzD;;GAEG;AACH,qBAAa,YAAY;IA0BH,OAAO,CAAC,IAAI;IAxBhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IAGpD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAG3E,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuD;IAGnF,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkC;IAG5D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAgB;IAG/C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAoC;IAG5D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAoD;IAG9E,OAAO,CAAC,MAAM,CAAC,YAAY,CAAsC;IAGjE,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,YAAY;IAOzC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuB9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IASjC;;;;OAIG;mBACkB,eAAe;IAoFpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAgBpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAwBjC;;;OAGG;mBACkB,UAAU;IAK/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;OAGG;YACW,WAAW;IAgFzB,OAAO,CAAC,eAAe;IA0DvB;;;OAGG;IACI,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAqBpD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAS5B;;;;OAIG;WACiB,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAkDpB"}
|
|
@@ -57,7 +57,7 @@ class FullNodePeer {
|
|
|
57
57
|
if (this.peer)
|
|
58
58
|
return; // Already initialized
|
|
59
59
|
try {
|
|
60
|
-
const bestPeer = await
|
|
60
|
+
const bestPeer = await this.getBestPeer();
|
|
61
61
|
this.peer = bestPeer;
|
|
62
62
|
FullNodePeer.instance = this; // Assign the initialized instance
|
|
63
63
|
}
|
|
@@ -266,7 +266,7 @@ class FullNodePeer {
|
|
|
266
266
|
* Connects to the best available peer based on weighted selection and reliability.
|
|
267
267
|
* @returns {Promise<Peer>} The connected Peer instance.
|
|
268
268
|
*/
|
|
269
|
-
|
|
269
|
+
async getBestPeer() {
|
|
270
270
|
const now = Date.now();
|
|
271
271
|
// Refresh cachedPeer if expired
|
|
272
272
|
if (FullNodePeer.cachedPeer &&
|
|
@@ -324,10 +324,57 @@ class FullNodePeer {
|
|
|
324
324
|
});
|
|
325
325
|
// Initialize rate limiter for this peer
|
|
326
326
|
FullNodePeer.peerLimiters.set(selectedPeerIP, limiter);
|
|
327
|
+
const proxiedPeer = this.createPeerProxy(peer, selectedPeerIP);
|
|
327
328
|
// Cache the peer
|
|
328
329
|
FullNodePeer.cachedPeer = { peer: peer, timestamp: now };
|
|
329
330
|
console.log(`Using Fullnode Peer: ${selectedPeerIP}`);
|
|
330
|
-
return
|
|
331
|
+
return proxiedPeer;
|
|
332
|
+
}
|
|
333
|
+
createPeerProxy(peer, peerIP) {
|
|
334
|
+
return new Proxy(peer, {
|
|
335
|
+
get: (target, prop) => {
|
|
336
|
+
const originalMethod = target[prop];
|
|
337
|
+
if (typeof originalMethod === "function") {
|
|
338
|
+
return async (...args) => {
|
|
339
|
+
let timeoutId;
|
|
340
|
+
// Start the timeout to forget the peer after 1 minute
|
|
341
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
342
|
+
timeoutId = setTimeout(() => {
|
|
343
|
+
FullNodePeer.cachedPeer = null;
|
|
344
|
+
reject(new Error("Operation timed out. Reconnecting to a new peer."));
|
|
345
|
+
}, 60000); // 1 minute
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
// Run the original method and race it against the timeout
|
|
349
|
+
const result = await Promise.race([
|
|
350
|
+
originalMethod.apply(target, args),
|
|
351
|
+
timeoutPromise,
|
|
352
|
+
]);
|
|
353
|
+
// Clear the timeout if the operation succeeded
|
|
354
|
+
if (timeoutId) {
|
|
355
|
+
clearTimeout(timeoutId);
|
|
356
|
+
}
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
// If the error is WebSocket-related or timeout, reset the peer
|
|
361
|
+
if (error.message.includes("WebSocket") ||
|
|
362
|
+
error.message.includes("Operation timed out")) {
|
|
363
|
+
FullNodePeer.cachedPeer = null;
|
|
364
|
+
// @ts-ignore
|
|
365
|
+
FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
|
|
366
|
+
console.info(`Fullnode Peer error, reconnecting to a new peer...`);
|
|
367
|
+
this.handlePeerDisconnection(peerIP);
|
|
368
|
+
const newPeer = await this.getBestPeer();
|
|
369
|
+
return newPeer[prop](...args);
|
|
370
|
+
}
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return originalMethod;
|
|
376
|
+
},
|
|
377
|
+
});
|
|
331
378
|
}
|
|
332
379
|
/**
|
|
333
380
|
* Handles peer disconnection by marking it in cooldown and updating internal states.
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interface representing the metrics of a peer.
|
|
3
|
-
*/
|
|
4
1
|
export interface PeerMetrics {
|
|
5
2
|
ip: string;
|
|
6
3
|
latency: number;
|
|
7
4
|
bandwidth: number;
|
|
8
5
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Configuration options for the PeerRanker.
|
|
11
|
-
*/
|
|
12
|
-
interface PeerRankerOptions {
|
|
13
|
-
pingPath?: string;
|
|
14
|
-
timeout?: number;
|
|
15
|
-
uploadTestSize?: number;
|
|
16
|
-
}
|
|
17
6
|
/**
|
|
18
7
|
* Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
|
|
19
8
|
*/
|
|
@@ -21,7 +10,6 @@ export declare class PeerRanker {
|
|
|
21
10
|
private ipAddresses;
|
|
22
11
|
private static certPath;
|
|
23
12
|
private static keyPath;
|
|
24
|
-
private pingPath;
|
|
25
13
|
private timeout;
|
|
26
14
|
private uploadTestSize;
|
|
27
15
|
private sortedPeers;
|
|
@@ -29,27 +17,28 @@ export declare class PeerRanker {
|
|
|
29
17
|
/**
|
|
30
18
|
* Constructs a PeerRanker instance.
|
|
31
19
|
* @param ipAddresses - Array of IP addresses to rank.
|
|
32
|
-
* @param options - Configuration options including paths to client certificates.
|
|
33
20
|
*/
|
|
34
|
-
constructor(ipAddresses: string[],
|
|
21
|
+
constructor(ipAddresses: string[], timeout?: number, uploadTestSize?: number);
|
|
35
22
|
/**
|
|
36
23
|
* Measures the latency of a given IP address using an HTTPS request.
|
|
37
24
|
* Tries HEAD first, then falls back to GET if HEAD is not supported.
|
|
38
25
|
* @param ip - The IP address of the peer.
|
|
39
|
-
* @returns Promise resolving to the latency in milliseconds.
|
|
26
|
+
* @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
|
|
40
27
|
*/
|
|
41
28
|
private measureLatency;
|
|
42
29
|
/**
|
|
43
30
|
* Measures the upload bandwidth of a given IP address by sending random data.
|
|
44
31
|
* @param ip - The IP address of the peer.
|
|
45
|
-
* @returns Promise resolving to the upload bandwidth in bytes per second.
|
|
32
|
+
* @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
|
|
46
33
|
*/
|
|
47
34
|
private measureBandwidth;
|
|
48
35
|
/**
|
|
49
36
|
* Ranks the peers based on measured latency and upload bandwidth.
|
|
37
|
+
* Unresponsive peers are excluded from the final ranking.
|
|
38
|
+
* @param cooldown - Cooldown time in milliseconds between batches.
|
|
50
39
|
* @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
|
|
51
40
|
*/
|
|
52
|
-
rankPeers(): Promise<PeerMetrics[]>;
|
|
41
|
+
rankPeers(cooldown?: number): Promise<PeerMetrics[]>;
|
|
53
42
|
/**
|
|
54
43
|
* Returns the next best peer based on the ranking.
|
|
55
44
|
* Cycles through the list, resetting to the beginning once the end is reached.
|
|
@@ -61,5 +50,4 @@ export declare class PeerRanker {
|
|
|
61
50
|
*/
|
|
62
51
|
resetIterator(): void;
|
|
63
52
|
}
|
|
64
|
-
export {};
|
|
65
53
|
//# sourceMappingURL=PeerRanker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeerRanker.d.ts","sourceRoot":"","sources":["../../src/utils/PeerRanker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PeerRanker.d.ts","sourceRoot":"","sources":["../../src/utils/PeerRanker.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAa;IAEjC;;;OAGG;gBACS,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAa,EAAE,cAAc,GAAE,MAAoB;IAW/F;;;;;OAKG;YACW,cAAc;IA2C5B;;;;OAIG;YACW,gBAAgB;IAmC9B;;;;;OAKG;IACU,SAAS,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAmCtE;;;;OAIG;IACI,eAAe,IAAI,WAAW,GAAG,IAAI;IAW5C;;OAEG;IACI,aAAa,IAAI,IAAI;CAG7B"}
|
package/dist/utils/PeerRanker.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/PeerRanker.ts
|
|
3
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
4
|
};
|
|
@@ -9,6 +8,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
9
8
|
const fs_1 = __importDefault(require("fs"));
|
|
10
9
|
const https_1 = __importDefault(require("https"));
|
|
11
10
|
const ssl_1 = require("./ssl");
|
|
11
|
+
const promiseUtils_1 = require("./promiseUtils");
|
|
12
12
|
/**
|
|
13
13
|
* Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
|
|
14
14
|
*/
|
|
@@ -16,16 +16,15 @@ class PeerRanker {
|
|
|
16
16
|
/**
|
|
17
17
|
* Constructs a PeerRanker instance.
|
|
18
18
|
* @param ipAddresses - Array of IP addresses to rank.
|
|
19
|
-
* @param options - Configuration options including paths to client certificates.
|
|
20
19
|
*/
|
|
21
|
-
constructor(ipAddresses,
|
|
20
|
+
constructor(ipAddresses, timeout = 5000, uploadTestSize = 1024 * 1024) {
|
|
22
21
|
// Internal properties for iteration
|
|
23
22
|
this.sortedPeers = [];
|
|
24
23
|
this.currentIndex = 0;
|
|
25
24
|
this.ipAddresses = ipAddresses;
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
|
|
25
|
+
this.timeout = timeout; // Allow customizable timeout
|
|
26
|
+
this.uploadTestSize = uploadTestSize; // Default upload size: 1MB
|
|
27
|
+
// Fetch the SSL certificates used for mTLS.
|
|
29
28
|
const { certPath, keyPath } = (0, ssl_1.getOrCreateSSLCerts)();
|
|
30
29
|
PeerRanker.certPath = certPath;
|
|
31
30
|
PeerRanker.keyPath = keyPath;
|
|
@@ -34,39 +33,36 @@ class PeerRanker {
|
|
|
34
33
|
* Measures the latency of a given IP address using an HTTPS request.
|
|
35
34
|
* Tries HEAD first, then falls back to GET if HEAD is not supported.
|
|
36
35
|
* @param ip - The IP address of the peer.
|
|
37
|
-
* @returns Promise resolving to the latency in milliseconds.
|
|
36
|
+
* @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
|
|
38
37
|
*/
|
|
39
38
|
async measureLatency(ip) {
|
|
40
|
-
const
|
|
41
|
-
const url = `https://${ip}${path}`;
|
|
42
|
-
// Configuration for HEAD request
|
|
39
|
+
const url = `https://${ip}:4159/diagnostics/ping`;
|
|
43
40
|
const configHead = {
|
|
44
41
|
url: url,
|
|
45
42
|
method: 'HEAD',
|
|
46
43
|
httpsAgent: new https_1.default.Agent({
|
|
47
44
|
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
48
45
|
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
49
|
-
rejectUnauthorized: false,
|
|
46
|
+
rejectUnauthorized: false,
|
|
50
47
|
}),
|
|
51
48
|
timeout: this.timeout,
|
|
52
|
-
validateStatus: (status) => status < 500,
|
|
49
|
+
validateStatus: (status) => status < 500,
|
|
53
50
|
};
|
|
54
51
|
const startTime = Date.now();
|
|
55
52
|
try {
|
|
56
53
|
const response = await (0, axios_1.default)(configHead);
|
|
57
|
-
if (response.status === 405) {
|
|
58
|
-
// Fallback to GET with Range header to minimize data transfer
|
|
54
|
+
if (response.status === 405) {
|
|
59
55
|
const configGet = {
|
|
60
56
|
url: url,
|
|
61
57
|
method: 'GET',
|
|
62
58
|
httpsAgent: new https_1.default.Agent({
|
|
63
59
|
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
64
60
|
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
65
|
-
rejectUnauthorized: false,
|
|
61
|
+
rejectUnauthorized: false,
|
|
66
62
|
}),
|
|
67
63
|
timeout: this.timeout,
|
|
68
64
|
headers: {
|
|
69
|
-
'Range': 'bytes=0-0',
|
|
65
|
+
'Range': 'bytes=0-0',
|
|
70
66
|
},
|
|
71
67
|
validateStatus: (status) => status < 500,
|
|
72
68
|
};
|
|
@@ -77,18 +73,17 @@ class PeerRanker {
|
|
|
77
73
|
}
|
|
78
74
|
catch (error) {
|
|
79
75
|
console.error(`Latency measurement failed for IP ${ip}:`, error.message);
|
|
80
|
-
|
|
76
|
+
throw new Error(`Latency measurement failed for IP ${ip}`);
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
/**
|
|
84
80
|
* Measures the upload bandwidth of a given IP address by sending random data.
|
|
85
81
|
* @param ip - The IP address of the peer.
|
|
86
|
-
* @returns Promise resolving to the upload bandwidth in bytes per second.
|
|
82
|
+
* @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
|
|
87
83
|
*/
|
|
88
84
|
async measureBandwidth(ip) {
|
|
89
|
-
const url = `https://${ip}/
|
|
90
|
-
|
|
91
|
-
const randomData = Buffer.alloc(this.uploadTestSize, 'a'); // 1MB of 'a's
|
|
85
|
+
const url = `https://${ip}:4159/diagnostics/bandwidth`;
|
|
86
|
+
const randomData = Buffer.alloc(this.uploadTestSize, 'a');
|
|
92
87
|
const config = {
|
|
93
88
|
url: url,
|
|
94
89
|
method: 'POST',
|
|
@@ -100,39 +95,47 @@ class PeerRanker {
|
|
|
100
95
|
httpsAgent: new https_1.default.Agent({
|
|
101
96
|
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
102
97
|
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
103
|
-
rejectUnauthorized: false,
|
|
98
|
+
rejectUnauthorized: false,
|
|
104
99
|
}),
|
|
105
100
|
timeout: this.timeout,
|
|
106
101
|
maxContentLength: Infinity,
|
|
107
102
|
maxBodyLength: Infinity,
|
|
108
103
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(0, axios_1.default)(config)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
});
|
|
104
|
+
const startTime = Date.now();
|
|
105
|
+
try {
|
|
106
|
+
await (0, axios_1.default)(config);
|
|
107
|
+
const timeElapsed = (Date.now() - startTime) / 1000;
|
|
108
|
+
const bandwidth = this.uploadTestSize / timeElapsed;
|
|
109
|
+
return bandwidth;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(`Bandwidth measurement failed for IP ${ip}:`, error.message);
|
|
113
|
+
throw new Error(`Bandwidth measurement failed for IP ${ip}`);
|
|
114
|
+
}
|
|
122
115
|
}
|
|
123
116
|
/**
|
|
124
117
|
* Ranks the peers based on measured latency and upload bandwidth.
|
|
118
|
+
* Unresponsive peers are excluded from the final ranking.
|
|
119
|
+
* @param cooldown - Cooldown time in milliseconds between batches.
|
|
125
120
|
* @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
|
|
126
121
|
*/
|
|
127
|
-
async rankPeers() {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
122
|
+
async rankPeers(cooldown = 500) {
|
|
123
|
+
const limit = 5; // Limit to 5 parallel requests at a time
|
|
124
|
+
const iteratorFn = async (ip) => {
|
|
125
|
+
try {
|
|
126
|
+
const [latency, bandwidth] = await Promise.all([
|
|
127
|
+
this.measureLatency(ip),
|
|
128
|
+
this.measureBandwidth(ip),
|
|
129
|
+
]);
|
|
130
|
+
return { ip, latency, bandwidth };
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// Peer failed, skip it by returning null
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
// Process all peers with a concurrency limit and cooldown between batches
|
|
138
|
+
const peerMetrics = (await (0, promiseUtils_1.asyncPool)(limit, this.ipAddresses, iteratorFn, cooldown)).filter((metrics) => metrics !== null); // Use a type guard
|
|
136
139
|
// Sort by lowest latency first, then by highest bandwidth
|
|
137
140
|
peerMetrics.sort((a, b) => {
|
|
138
141
|
if (a.latency === b.latency) {
|
|
@@ -140,9 +143,7 @@ class PeerRanker {
|
|
|
140
143
|
}
|
|
141
144
|
return a.latency - b.latency; // Lower latency is better
|
|
142
145
|
});
|
|
143
|
-
// Update the internal sorted list
|
|
144
146
|
this.sortedPeers = peerMetrics;
|
|
145
|
-
// Reset the iterator index
|
|
146
147
|
this.currentIndex = 0;
|
|
147
148
|
return peerMetrics;
|
|
148
149
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Processes items in sequential batches with a concurrency limit.
|
|
3
|
+
* Adds a cooldown between batches.
|
|
3
4
|
* @param {number} limit - The maximum number of concurrent executions per batch.
|
|
4
5
|
* @param {Array<T>} items - The array of items to process.
|
|
5
6
|
* @param {(item: T) => Promise<R>} iteratorFn - The async function to apply to each item.
|
|
7
|
+
* @param {number} cooldownMs - The cooldown duration between batches in milliseconds.
|
|
6
8
|
* @returns {Promise<Array<R>>} - A promise that resolves when all items have been processed.
|
|
7
9
|
*/
|
|
8
|
-
export declare function asyncPool<T, R>(limit: number, items: T[], iteratorFn: (item: T) => Promise<R
|
|
10
|
+
export declare function asyncPool<T, R>(limit: number, items: T[], iteratorFn: (item: T) => Promise<R>, cooldownMs?: number): Promise<R[]>;
|
|
9
11
|
/**
|
|
10
12
|
* Helper function to add a timeout to a promise.
|
|
11
13
|
* @param promise The original promise.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promiseUtils.d.ts","sourceRoot":"","sources":["../../src/utils/promiseUtils.ts"],"names":[],"mappings":"AACA
|
|
1
|
+
{"version":3,"file":"promiseUtils.d.ts","sourceRoot":"","sources":["../../src/utils/promiseUtils.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,CAAC,EAClC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,EAAE,EACV,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,UAAU,GAAE,MAAY,GACvB,OAAO,CAAC,CAAC,EAAE,CAAC,CAkBd;AACC;;;;;;EAMC;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,WAClB,OAAO,CAAC,CAAC,CAAC,MACf,MAAM,kBACM,MAAM,KACrB,OAAO,CAAC,CAAC,CAOX,CAAC"}
|
|
@@ -4,12 +4,15 @@ exports.withTimeout = void 0;
|
|
|
4
4
|
exports.asyncPool = asyncPool;
|
|
5
5
|
/**
|
|
6
6
|
* Processes items in sequential batches with a concurrency limit.
|
|
7
|
+
* Adds a cooldown between batches.
|
|
7
8
|
* @param {number} limit - The maximum number of concurrent executions per batch.
|
|
8
9
|
* @param {Array<T>} items - The array of items to process.
|
|
9
10
|
* @param {(item: T) => Promise<R>} iteratorFn - The async function to apply to each item.
|
|
11
|
+
* @param {number} cooldownMs - The cooldown duration between batches in milliseconds.
|
|
10
12
|
* @returns {Promise<Array<R>>} - A promise that resolves when all items have been processed.
|
|
11
13
|
*/
|
|
12
|
-
async function asyncPool(limit, items, iteratorFn
|
|
14
|
+
async function asyncPool(limit, items, iteratorFn, cooldownMs = 500 // Default cooldown of 500ms
|
|
15
|
+
) {
|
|
13
16
|
const ret = [];
|
|
14
17
|
for (let i = 0; i < items.length; i += limit) {
|
|
15
18
|
const batchItems = items.slice(i, i + limit);
|
|
@@ -17,8 +20,10 @@ async function asyncPool(limit, items, iteratorFn) {
|
|
|
17
20
|
// Wait for the current batch to complete before starting the next one
|
|
18
21
|
const batchResults = await Promise.all(batchPromises);
|
|
19
22
|
ret.push(...batchResults);
|
|
20
|
-
//
|
|
21
|
-
|
|
23
|
+
// Add a cooldown between batches, except after the last batch
|
|
24
|
+
if (i + limit < items.length) {
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, cooldownMs));
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
return ret;
|
|
24
29
|
}
|