@peerbit/stream 2.0.4 → 2.0.5
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/lib/esm/index.d.ts +4 -1
- package/lib/esm/index.js +31 -31
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/routes.d.ts +20 -27
- package/lib/esm/routes.js +103 -86
- package/lib/esm/routes.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +42 -34
- package/src/routes.ts +131 -127
package/lib/esm/routes.js
CHANGED
|
@@ -1,17 +1,46 @@
|
|
|
1
|
+
import { AbortError, delay } from "@peerbit/time";
|
|
1
2
|
export const MAX_ROUTE_DISTANCE = Number.MAX_SAFE_INTEGER - 1;
|
|
2
3
|
export class Routes {
|
|
3
4
|
me;
|
|
4
5
|
// END receiver -> Neighbour
|
|
5
6
|
routes = new Map();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
constructor(me) {
|
|
7
|
+
routeMaxRetentionPeriod;
|
|
8
|
+
signal;
|
|
9
|
+
constructor(me, options) {
|
|
9
10
|
this.me = me;
|
|
10
|
-
this.
|
|
11
|
+
this.routeMaxRetentionPeriod = options.routeMaxRetentionPeriod;
|
|
12
|
+
this.signal = options.signal;
|
|
11
13
|
}
|
|
12
14
|
clear() {
|
|
13
15
|
this.routes.clear();
|
|
14
|
-
|
|
16
|
+
}
|
|
17
|
+
cleanup(from, to) {
|
|
18
|
+
const fromMap = this.routes.get(from);
|
|
19
|
+
if (fromMap) {
|
|
20
|
+
const map = fromMap.get(to);
|
|
21
|
+
if (map) {
|
|
22
|
+
const now = +new Date();
|
|
23
|
+
const keepRoutes = [];
|
|
24
|
+
for (const route of map.list) {
|
|
25
|
+
// delete all routes after a while
|
|
26
|
+
if (route.expireAt != null && route.expireAt < now) {
|
|
27
|
+
// expired
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
keepRoutes.push(route);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (keepRoutes.length > 0) {
|
|
34
|
+
map.list = keepRoutes;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
fromMap.delete(to);
|
|
38
|
+
if (fromMap.size === 1) {
|
|
39
|
+
this.routes.delete(from);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
15
44
|
}
|
|
16
45
|
add(from, neighbour, target, distance, session) {
|
|
17
46
|
let fromMap = this.routes.get(from);
|
|
@@ -19,35 +48,65 @@ export class Routes {
|
|
|
19
48
|
fromMap = new Map();
|
|
20
49
|
this.routes.set(from, fromMap);
|
|
21
50
|
}
|
|
22
|
-
let prev = fromMap.get(target)
|
|
23
|
-
|
|
24
|
-
list: []
|
|
25
|
-
|
|
26
|
-
this.latestSession = Math.max(this.latestSession, session);
|
|
27
|
-
if (session != null) {
|
|
28
|
-
// this condition means that when we add new routes in a session that is newer
|
|
29
|
-
if (prev.session < session) {
|
|
30
|
-
prev = { session, list: [] }; // reset route info how to reach this target
|
|
31
|
-
}
|
|
32
|
-
else if (prev.session > session) {
|
|
33
|
-
return; // new routing information superseedes this
|
|
34
|
-
}
|
|
51
|
+
let prev = fromMap.get(target);
|
|
52
|
+
if (!prev) {
|
|
53
|
+
prev = { latestSession: 0, list: [] };
|
|
54
|
+
fromMap.set(target, prev);
|
|
35
55
|
}
|
|
36
56
|
if (from === this.me && neighbour === target) {
|
|
37
57
|
// force distance to neighbour as targets to always favor directly sending to them
|
|
38
58
|
// i.e. if target is our neighbour, always assume the shortest path to them is the direct path
|
|
39
59
|
distance = -1;
|
|
40
60
|
}
|
|
61
|
+
// Update routes and cleanup all old routes that are older than latest session - some threshold
|
|
62
|
+
const isNewSession = session > prev.latestSession;
|
|
63
|
+
prev.latestSession = Math.max(session, prev.latestSession);
|
|
64
|
+
if (isNewSession) {
|
|
65
|
+
// Mark previous routes as old
|
|
66
|
+
const expireAt = +new Date() + this.routeMaxRetentionPeriod;
|
|
67
|
+
for (const route of prev.list) {
|
|
68
|
+
// delete all routes after a while
|
|
69
|
+
if (!route.expireAt) {
|
|
70
|
+
route.expireAt = expireAt;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Initiate cleanup
|
|
74
|
+
if (distance !== -1) {
|
|
75
|
+
delay(this.routeMaxRetentionPeriod + 100, { signal: this.signal })
|
|
76
|
+
.then(() => {
|
|
77
|
+
this.cleanup(from, target);
|
|
78
|
+
})
|
|
79
|
+
.catch((e) => {
|
|
80
|
+
if (e instanceof AbortError) {
|
|
81
|
+
// skip
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw e;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Modify list for new/update route
|
|
41
89
|
for (const route of prev.list) {
|
|
42
90
|
if (route.hash === neighbour) {
|
|
43
|
-
route
|
|
44
|
-
|
|
45
|
-
|
|
91
|
+
// if route is faster or just as fast, update existing route
|
|
92
|
+
if (route.distance > distance) {
|
|
93
|
+
route.distance = distance;
|
|
94
|
+
route.session = session;
|
|
95
|
+
route.expireAt = undefined; // remove expiry since we updated
|
|
96
|
+
prev.list.sort((a, b) => a.distance - b.distance);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
else if (route.distance === distance) {
|
|
100
|
+
route.session = session;
|
|
101
|
+
route.expireAt = undefined; // remove expiry since we updated
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// else break and push the route as a new route (that ought to be longer)
|
|
105
|
+
break;
|
|
46
106
|
}
|
|
47
107
|
}
|
|
48
|
-
prev.list.push({ distance, hash: neighbour });
|
|
108
|
+
prev.list.push({ distance, session, hash: neighbour });
|
|
49
109
|
prev.list.sort((a, b) => a.distance - b.distance);
|
|
50
|
-
fromMap.set(target, prev);
|
|
51
110
|
}
|
|
52
111
|
removeTarget(target) {
|
|
53
112
|
this.routes.delete(target);
|
|
@@ -139,35 +198,40 @@ export class Routes {
|
|
|
139
198
|
return undefined;
|
|
140
199
|
}
|
|
141
200
|
let fanoutMap = undefined;
|
|
142
|
-
const fromKey = from.hashcode();
|
|
143
201
|
// Message to > 0
|
|
144
202
|
if (tos.length > 0) {
|
|
145
203
|
for (const to of tos) {
|
|
146
|
-
if (to === this.me ||
|
|
204
|
+
if (to === this.me || from === to) {
|
|
147
205
|
continue; // don't send to me or backwards
|
|
148
206
|
}
|
|
149
|
-
const neighbour = this.findNeighbor(
|
|
207
|
+
const neighbour = this.findNeighbor(from, to);
|
|
150
208
|
if (neighbour) {
|
|
151
209
|
let foundClosest = false;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
210
|
+
let redundancyModified = redundancy;
|
|
211
|
+
for (let i = 0; i < neighbour.list.length; i++) {
|
|
212
|
+
const { distance, session } = neighbour.list[i];
|
|
213
|
+
if (distance >= redundancyModified) {
|
|
155
214
|
break; // because neighbour listis sorted
|
|
156
215
|
}
|
|
157
|
-
|
|
158
|
-
foundClosest = true;
|
|
159
|
-
}
|
|
160
|
-
const fanout = (fanoutMap || (fanoutMap = new Map())).get(neighbour.list[i].hash);
|
|
216
|
+
let fanout = (fanoutMap || (fanoutMap = new Map())).get(neighbour.list[i].hash);
|
|
161
217
|
if (!fanout) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
]);
|
|
218
|
+
fanout = new Map();
|
|
219
|
+
fanoutMap.set(neighbour.list[i].hash, fanout);
|
|
165
220
|
}
|
|
166
|
-
|
|
167
|
-
|
|
221
|
+
fanout.set(to, { to, timestamp: session });
|
|
222
|
+
if ((distance == 0 && session === neighbour.latestSession) ||
|
|
223
|
+
distance == -1) {
|
|
224
|
+
foundClosest = true;
|
|
225
|
+
if (distance == -1) {
|
|
226
|
+
// remove 1 from the expected redunancy since we got a route with negative 1 distance
|
|
227
|
+
// if we do not do this, we would get 2 routes if redundancy = 1, {-1, 0}, while it should just be
|
|
228
|
+
// {-1} in this case
|
|
229
|
+
redundancyModified -= 1;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
168
232
|
}
|
|
169
233
|
}
|
|
170
|
-
if (!foundClosest && from
|
|
234
|
+
if (!foundClosest && from === this.me) {
|
|
171
235
|
return undefined; // we dont have the shortest path to our target (yet). Send to all
|
|
172
236
|
}
|
|
173
237
|
continue;
|
|
@@ -198,52 +262,5 @@ export class Routes {
|
|
|
198
262
|
}
|
|
199
263
|
return [];
|
|
200
264
|
}
|
|
201
|
-
addPendingRouteConnection(session, route) {
|
|
202
|
-
let map = this.pendingRoutes.get(session);
|
|
203
|
-
if (!map) {
|
|
204
|
-
map = new Map();
|
|
205
|
-
this.pendingRoutes.set(session, map);
|
|
206
|
-
}
|
|
207
|
-
let arr = map.get(route.target);
|
|
208
|
-
if (!arr) {
|
|
209
|
-
arr = [];
|
|
210
|
-
map.set(route.target, arr);
|
|
211
|
-
}
|
|
212
|
-
arr.push(route);
|
|
213
|
-
const neighbour = this.findNeighbor(route.from, route.target);
|
|
214
|
-
if (!neighbour || neighbour.session === session) {
|
|
215
|
-
// Commit directly since we dont have any data at all (better have something than nothing)
|
|
216
|
-
this.commitPendingRouteConnection(session, route.target);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// always commit if we dont know the peer yet
|
|
220
|
-
// do pending commits per remote (?)
|
|
221
|
-
commitPendingRouteConnection(session, target) {
|
|
222
|
-
const map = this.pendingRoutes.get(session);
|
|
223
|
-
if (!map) {
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
if (target) {
|
|
227
|
-
const routes = map.get(target);
|
|
228
|
-
if (routes) {
|
|
229
|
-
for (const route of routes) {
|
|
230
|
-
this.add(route.from, route.neighbour, target, route.distance, session);
|
|
231
|
-
}
|
|
232
|
-
map.delete(target);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
for (const [target, routes] of map) {
|
|
241
|
-
for (const route of routes) {
|
|
242
|
-
this.add(route.from, route.neighbour, target, route.distance, session);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
this.pendingRoutes.delete(session);
|
|
247
|
-
}
|
|
248
265
|
}
|
|
249
266
|
//# sourceMappingURL=routes.js.map
|
package/lib/esm/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peerbit/stream",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "A building block for direct streaming protocols",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
"dao.xyz"
|
|
51
51
|
],
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@peerbit/libp2p-test-utils": "2.0.
|
|
53
|
+
"@peerbit/libp2p-test-utils": "2.0.1",
|
|
54
54
|
"@types/yallist": "^4.0.1"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@dao-xyz/borsh": "^5.1.8",
|
|
58
|
-
"@peerbit/cache": "2.0.
|
|
59
|
-
"@peerbit/crypto": "2.1.
|
|
60
|
-
"@peerbit/stream-interface": "^2.0.
|
|
58
|
+
"@peerbit/cache": "2.0.1",
|
|
59
|
+
"@peerbit/crypto": "2.1.1",
|
|
60
|
+
"@peerbit/stream-interface": "^2.0.2",
|
|
61
61
|
"abortable-iterator": "^5.0.1",
|
|
62
62
|
"libp2p": "^1.1.0",
|
|
63
63
|
"yallist": "^4.0.0"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "f4cb6a526d26e98360c32fd28dcdf9eb7ea10551"
|
|
66
66
|
}
|
package/src/index.ts
CHANGED
|
@@ -85,6 +85,7 @@ export interface PeerStreamEvents {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const SEEK_DELIVERY_TIMEOUT = 15e3;
|
|
88
|
+
const ROUTE_MAX_RETANTION_PERIOD = 10 * 1000;
|
|
88
89
|
const MAX_DATA_LENGTH = 1e7 + 1000; // 10 mb and some metadata
|
|
89
90
|
const MAX_QUEUED_BYTES = MAX_DATA_LENGTH * 50;
|
|
90
91
|
|
|
@@ -360,6 +361,7 @@ export type DirectStreamOptions = {
|
|
|
360
361
|
connectionManager?: ConnectionManagerArguments;
|
|
361
362
|
routeSeekInterval?: number;
|
|
362
363
|
seekTimeout?: number;
|
|
364
|
+
routeMaxRetentionPeriod?: number;
|
|
363
365
|
};
|
|
364
366
|
|
|
365
367
|
export interface DirectStreamComponents extends Components {
|
|
@@ -417,6 +419,7 @@ export abstract class DirectStream<
|
|
|
417
419
|
private healthChecks: Map<string, ReturnType<typeof setTimeout>>;
|
|
418
420
|
private pruneConnectionsTimeout: ReturnType<typeof setInterval>;
|
|
419
421
|
private prunedConnectionsCache?: Cache<string>;
|
|
422
|
+
private routeMaxRetentionPeriod: number;
|
|
420
423
|
routeSeekInterval: number;
|
|
421
424
|
seekTimeout: number;
|
|
422
425
|
closeController: AbortController;
|
|
@@ -447,7 +450,8 @@ export abstract class DirectStream<
|
|
|
447
450
|
signaturePolicy = "StictSign",
|
|
448
451
|
connectionManager,
|
|
449
452
|
routeSeekInterval = ROUTE_UPDATE_DELAY_FACTOR,
|
|
450
|
-
seekTimeout = SEEK_DELIVERY_TIMEOUT
|
|
453
|
+
seekTimeout = SEEK_DELIVERY_TIMEOUT,
|
|
454
|
+
routeMaxRetentionPeriod = ROUTE_MAX_RETANTION_PERIOD
|
|
451
455
|
} = options || {};
|
|
452
456
|
|
|
453
457
|
const signKey = getKeypairFromPeerId(components.peerId);
|
|
@@ -459,7 +463,6 @@ export abstract class DirectStream<
|
|
|
459
463
|
this.multicodecs = multicodecs;
|
|
460
464
|
this.started = false;
|
|
461
465
|
this.peers = new Map<string, PeerStreams>();
|
|
462
|
-
this.routes = new Routes(this.publicKeyHash);
|
|
463
466
|
this.canRelayMessage = canRelayMessage;
|
|
464
467
|
this.healthChecks = new Map();
|
|
465
468
|
this.routeSeekInterval = routeSeekInterval;
|
|
@@ -467,7 +470,7 @@ export abstract class DirectStream<
|
|
|
467
470
|
this.maxInboundStreams = maxInboundStreams;
|
|
468
471
|
this.maxOutboundStreams = maxOutboundStreams;
|
|
469
472
|
this.seenCache = new Cache({ max: 1e6, ttl: 10 * 60 * 1e3 });
|
|
470
|
-
|
|
473
|
+
this.routeMaxRetentionPeriod = routeMaxRetentionPeriod;
|
|
471
474
|
this.peerKeyHashToPublicKey = new Map();
|
|
472
475
|
this._onIncomingStream = this._onIncomingStream.bind(this);
|
|
473
476
|
this.onPeerConnected = this.onPeerConnected.bind(this);
|
|
@@ -527,6 +530,12 @@ export abstract class DirectStream<
|
|
|
527
530
|
await ready;
|
|
528
531
|
|
|
529
532
|
this.closeController = new AbortController();
|
|
533
|
+
|
|
534
|
+
this.routes = new Routes(this.publicKeyHash, {
|
|
535
|
+
routeMaxRetentionPeriod: this.routeMaxRetentionPeriod,
|
|
536
|
+
signal: this.closeController.signal
|
|
537
|
+
});
|
|
538
|
+
|
|
530
539
|
this.started = true;
|
|
531
540
|
|
|
532
541
|
logger.debug("starting");
|
|
@@ -757,7 +766,7 @@ export abstract class DirectStream<
|
|
|
757
766
|
this.publicKeyHash,
|
|
758
767
|
peerKey.hashcode(),
|
|
759
768
|
peerKey,
|
|
760
|
-
|
|
769
|
+
-1,
|
|
761
770
|
+new Date()
|
|
762
771
|
);
|
|
763
772
|
} catch (err: any) {
|
|
@@ -828,24 +837,15 @@ export abstract class DirectStream<
|
|
|
828
837
|
neighbour: string,
|
|
829
838
|
target: PublicSignKey,
|
|
830
839
|
distance: number,
|
|
831
|
-
session: number
|
|
832
|
-
pending = false
|
|
840
|
+
session: number
|
|
833
841
|
) {
|
|
834
842
|
const targetHash = typeof target === "string" ? target : target.hashcode();
|
|
835
843
|
const wasReachable =
|
|
836
844
|
from === this.publicKeyHash
|
|
837
845
|
? this.routes.isReachable(from, targetHash)
|
|
838
846
|
: true;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
distance,
|
|
842
|
-
from,
|
|
843
|
-
neighbour,
|
|
844
|
-
target: targetHash
|
|
845
|
-
});
|
|
846
|
-
} else {
|
|
847
|
-
this.routes.add(from, neighbour, targetHash, distance, session);
|
|
848
|
-
}
|
|
847
|
+
|
|
848
|
+
this.routes.add(from, neighbour, targetHash, distance, session);
|
|
849
849
|
|
|
850
850
|
const newPeer =
|
|
851
851
|
wasReachable === false && this.routes.isReachable(from, targetHash);
|
|
@@ -892,11 +892,7 @@ export abstract class DirectStream<
|
|
|
892
892
|
): PeerStreams {
|
|
893
893
|
const publicKeyHash = publicKey.hashcode();
|
|
894
894
|
|
|
895
|
-
|
|
896
|
-
if (hc) {
|
|
897
|
-
clearTimeout(hc);
|
|
898
|
-
this.healthChecks.delete(publicKeyHash);
|
|
899
|
-
}
|
|
895
|
+
this.clearHealthcheckTimer(publicKeyHash);
|
|
900
896
|
|
|
901
897
|
const existing = this.peers.get(publicKeyHash);
|
|
902
898
|
|
|
@@ -932,6 +928,7 @@ export abstract class DirectStream<
|
|
|
932
928
|
protected async _removePeer(publicKey: PublicSignKey) {
|
|
933
929
|
const hash = publicKey.hashcode();
|
|
934
930
|
const peerStreams = this.peers.get(hash);
|
|
931
|
+
this.clearHealthcheckTimer(hash);
|
|
935
932
|
|
|
936
933
|
if (peerStreams == null) {
|
|
937
934
|
return;
|
|
@@ -1212,7 +1209,7 @@ export abstract class DirectStream<
|
|
|
1212
1209
|
(bytes) => sha256Base64(bytes)
|
|
1213
1210
|
);
|
|
1214
1211
|
|
|
1215
|
-
if (seenBefore >
|
|
1212
|
+
if (seenBefore > 0) {
|
|
1216
1213
|
logger.debug(
|
|
1217
1214
|
"Received message already seen of type: " + message.constructor.name
|
|
1218
1215
|
);
|
|
@@ -1240,6 +1237,9 @@ export abstract class DirectStream<
|
|
|
1240
1237
|
if (nextStream) {
|
|
1241
1238
|
await this.publishMessage(this.publicKey, message, [nextStream], true);
|
|
1242
1239
|
} else {
|
|
1240
|
+
if (myIndex !== 0) {
|
|
1241
|
+
throw new Error("Unexpected");
|
|
1242
|
+
}
|
|
1243
1243
|
// if origin exist (we can connect to remote peer) && we have autodialer turned on
|
|
1244
1244
|
if (message.header.origin && this.connectionManagerOptions.dialer) {
|
|
1245
1245
|
this.maybeConnectDirectly(
|
|
@@ -1340,7 +1340,7 @@ export abstract class DirectStream<
|
|
|
1340
1340
|
?.get(hash);
|
|
1341
1341
|
if (
|
|
1342
1342
|
!neighbourRoutes ||
|
|
1343
|
-
now - neighbourRoutes.
|
|
1343
|
+
now - neighbourRoutes.latestSession >
|
|
1344
1344
|
neighbourRoutes.list.length * this.routeSeekInterval
|
|
1345
1345
|
) {
|
|
1346
1346
|
mode = new SeekDelivery({
|
|
@@ -1381,8 +1381,13 @@ export abstract class DirectStream<
|
|
|
1381
1381
|
throw new Error("Not started");
|
|
1382
1382
|
}
|
|
1383
1383
|
|
|
1384
|
-
|
|
1384
|
+
if ((options as WithMode).mode && (options as WithTo).to) {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
"Expecting either 'to' or 'mode' to be provided not both"
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1385
1389
|
|
|
1390
|
+
const message = await this.createMessage(data, options);
|
|
1386
1391
|
await this.publishMessage(this.publicKey, message, undefined);
|
|
1387
1392
|
return message.id;
|
|
1388
1393
|
}
|
|
@@ -1408,6 +1413,12 @@ export abstract class DirectStream<
|
|
|
1408
1413
|
}
|
|
1409
1414
|
}
|
|
1410
1415
|
|
|
1416
|
+
private clearHealthcheckTimer(to: string) {
|
|
1417
|
+
const timer = this.healthChecks.get(to);
|
|
1418
|
+
clearTimeout(timer);
|
|
1419
|
+
this.healthChecks.delete(to);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1411
1422
|
private async createDeliveryPromise(
|
|
1412
1423
|
from: PublicSignKey,
|
|
1413
1424
|
message: DataMessage,
|
|
@@ -1459,13 +1470,11 @@ export abstract class DirectStream<
|
|
|
1459
1470
|
|
|
1460
1471
|
const finalize = () => {
|
|
1461
1472
|
this._ackCallbacks.delete(idString);
|
|
1462
|
-
if (message.header.mode instanceof SeekDelivery) {
|
|
1463
|
-
this.routes.commitPendingRouteConnection(session);
|
|
1464
|
-
}
|
|
1465
1473
|
};
|
|
1466
1474
|
|
|
1467
1475
|
const uniqueAcks = new Set();
|
|
1468
1476
|
const session = +new Date();
|
|
1477
|
+
|
|
1469
1478
|
const timeout = setTimeout(async () => {
|
|
1470
1479
|
let hasAll = true;
|
|
1471
1480
|
finalize();
|
|
@@ -1495,7 +1504,7 @@ export abstract class DirectStream<
|
|
|
1495
1504
|
fastestNodesReached.size
|
|
1496
1505
|
}/${messageToSet.size}). Mode: ${
|
|
1497
1506
|
message.header.mode.constructor.name
|
|
1498
|
-
}`
|
|
1507
|
+
}. Redundancy: ${message.header.mode["redundancy"]}`
|
|
1499
1508
|
)
|
|
1500
1509
|
);
|
|
1501
1510
|
} else {
|
|
@@ -1511,9 +1520,7 @@ export abstract class DirectStream<
|
|
|
1511
1520
|
const seenCounter = ack.seenCounter;
|
|
1512
1521
|
|
|
1513
1522
|
// remove the automatic removal of route timeout since we have observed lifesigns of a peer
|
|
1514
|
-
|
|
1515
|
-
clearTimeout(timer);
|
|
1516
|
-
this.healthChecks.delete(messageTargetHash);
|
|
1523
|
+
this.clearHealthcheckTimer(messageTargetHash);
|
|
1517
1524
|
|
|
1518
1525
|
// if the target is not inside the original message to, we still ad the target to our routes
|
|
1519
1526
|
// this because a relay might modify the 'to' list and we might receive more answers than initially set
|
|
@@ -1523,8 +1530,7 @@ export abstract class DirectStream<
|
|
|
1523
1530
|
messageThrough.publicKey.hashcode(),
|
|
1524
1531
|
messageTarget,
|
|
1525
1532
|
seenCounter,
|
|
1526
|
-
session
|
|
1527
|
-
true
|
|
1533
|
+
session
|
|
1528
1534
|
); // we assume the seenCounter = distance. The more the message has been seen by the target the longer the path is to the target
|
|
1529
1535
|
}
|
|
1530
1536
|
|
|
@@ -1630,7 +1636,7 @@ export abstract class DirectStream<
|
|
|
1630
1636
|
message.header.mode.to
|
|
1631
1637
|
) {
|
|
1632
1638
|
const fanout = this.routes.getFanout(
|
|
1633
|
-
from,
|
|
1639
|
+
from.hashcode(),
|
|
1634
1640
|
message.header.mode.to,
|
|
1635
1641
|
message.header.mode.redundancy
|
|
1636
1642
|
);
|
|
@@ -1817,6 +1823,8 @@ export abstract class DirectStream<
|
|
|
1817
1823
|
if (this.peers.size <= this.connectionManagerOptions.minConnections) {
|
|
1818
1824
|
return;
|
|
1819
1825
|
}
|
|
1826
|
+
console.log("PRUNE CONNECTIONS");
|
|
1827
|
+
|
|
1820
1828
|
const sorted = [...this.peers.values()]
|
|
1821
1829
|
.sort((x, y) => x.usedBandwidth - y.usedBandwidth)
|
|
1822
1830
|
.map((x) => x.publicKey.hashcode());
|