@streamr/dht 100.2.2 → 100.2.4-beta.0
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/README.md +1 -1
- package/dist/package.json +6 -6
- package/dist/src/connection/ConnectionLockStates.js +4 -6
- package/dist/src/connection/ConnectionLockStates.js.map +1 -1
- package/dist/src/connection/ConnectionManager.d.ts +1 -0
- package/dist/src/connection/ConnectionManager.js +8 -1
- package/dist/src/connection/ConnectionManager.js.map +1 -1
- package/dist/src/connection/ManagedConnection.js +7 -9
- package/dist/src/connection/ManagedConnection.js.map +1 -1
- package/dist/src/connection/websocket/WebsocketConnector.js +1 -0
- package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
- package/dist/src/dht/DhtNode.d.ts +10 -7
- package/dist/src/dht/DhtNode.js +51 -32
- package/dist/src/dht/DhtNode.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcLocal.d.ts +2 -2
- package/dist/src/dht/DhtNodeRpcLocal.js +5 -4
- package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
- package/dist/src/dht/DhtNodeRpcRemote.js +1 -0
- package/dist/src/dht/DhtNodeRpcRemote.js.map +1 -1
- package/dist/src/dht/PeerManager.d.ts +14 -14
- package/dist/src/dht/PeerManager.js +23 -58
- package/dist/src/dht/PeerManager.js.map +1 -1
- package/dist/src/dht/contact/ContactList.d.ts +2 -2
- package/dist/src/dht/contact/RandomContactList.js +2 -2
- package/dist/src/dht/contact/RandomContactList.js.map +1 -1
- package/dist/src/dht/contact/RingContactList.d.ts +2 -5
- package/dist/src/dht/contact/RingContactList.js +3 -11
- package/dist/src/dht/contact/RingContactList.js.map +1 -1
- package/dist/src/dht/contact/SortedContactList.d.ts +4 -1
- package/dist/src/dht/contact/SortedContactList.js +4 -5
- package/dist/src/dht/contact/SortedContactList.js.map +1 -1
- package/dist/src/dht/contact/getClosestContacts.d.ts +7 -0
- package/dist/src/dht/contact/getClosestContacts.js +18 -0
- package/dist/src/dht/contact/getClosestContacts.js.map +1 -0
- package/dist/src/dht/discovery/DiscoverySession.d.ts +8 -8
- package/dist/src/dht/discovery/DiscoverySession.js +30 -35
- package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
- package/dist/src/dht/discovery/PeerDiscovery.d.ts +1 -2
- package/dist/src/dht/discovery/PeerDiscovery.js +6 -8
- package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
- package/dist/src/dht/discovery/RingDiscoverySession.d.ts +4 -4
- package/dist/src/dht/discovery/RingDiscoverySession.js +13 -13
- package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.d.ts +2 -2
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.js +15 -15
- package/dist/src/dht/recursive-operation/RecursiveOperationManager.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.d.ts +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.js +11 -13
- package/dist/src/dht/recursive-operation/RecursiveOperationSession.js.map +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.js +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.d.ts +1 -1
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js +2 -2
- package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js.map +1 -1
- package/dist/src/dht/routing/Router.d.ts +1 -2
- package/dist/src/dht/routing/Router.js +2 -3
- package/dist/src/dht/routing/Router.js.map +1 -1
- package/dist/src/dht/routing/RoutingSession.d.ts +1 -2
- package/dist/src/dht/routing/RoutingSession.js +2 -2
- package/dist/src/dht/routing/RoutingSession.js.map +1 -1
- package/dist/src/dht/store/StoreRpcLocal.js +3 -3
- package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +8 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +4 -0
- package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +10 -2
- package/dist/src/proto/packages/dht/protos/DhtRpc.js +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
- package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +4 -0
- package/dist/src/transport/ITransport.d.ts +3 -0
- package/dist/src/transport/ITransport.js.map +1 -1
- package/package.json +6 -6
- package/protos/DhtRpc.proto +7 -1
- package/src/connection/ConnectionLockStates.ts +3 -5
- package/src/connection/ConnectionManager.ts +9 -1
- package/src/connection/ManagedConnection.ts +11 -14
- package/src/connection/websocket/WebsocketConnector.ts +1 -0
- package/src/dht/DhtNode.ts +63 -44
- package/src/dht/DhtNodeRpcLocal.ts +7 -6
- package/src/dht/DhtNodeRpcRemote.ts +1 -0
- package/src/dht/PeerManager.ts +38 -72
- package/src/dht/contact/ContactList.ts +2 -2
- package/src/dht/contact/RandomContactList.ts +2 -3
- package/src/dht/contact/RingContactList.ts +6 -16
- package/src/dht/contact/SortedContactList.ts +9 -10
- package/src/dht/contact/getClosestContacts.ts +22 -0
- package/src/dht/discovery/DiscoverySession.ts +35 -45
- package/src/dht/discovery/PeerDiscovery.ts +6 -9
- package/src/dht/discovery/RingDiscoverySession.ts +13 -13
- package/src/dht/recursive-operation/RecursiveOperationManager.ts +16 -16
- package/src/dht/recursive-operation/RecursiveOperationSession.ts +11 -13
- package/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.ts +1 -1
- package/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.ts +2 -2
- package/src/dht/routing/Router.ts +3 -5
- package/src/dht/routing/RoutingSession.ts +3 -4
- package/src/dht/store/StoreRpcLocal.ts +3 -3
- package/src/proto/packages/dht/protos/DhtRpc.client.ts +8 -0
- package/src/proto/packages/dht/protos/DhtRpc.server.ts +4 -0
- package/src/proto/packages/dht/protos/DhtRpc.ts +11 -3
- package/src/transport/ITransport.ts +3 -0
- package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +3 -5
- package/test/integration/DhtNode.test.ts +81 -0
- package/test/integration/Layer1-scale.test.ts +1 -1
- package/test/integration/ScaleDownDht.test.ts +7 -4
- package/test/unit/DiscoverySession.test.ts +85 -0
- package/test/unit/PeerManager.test.ts +3 -17
- package/test/unit/RecursiveOperationManager.test.ts +5 -3
- package/test/unit/Router.test.ts +2 -2
- package/test/unit/RoutingSession.test.ts +2 -2
- package/test/unit/SortedContactList.test.ts +4 -4
- package/test/unit/getClosestContacts.test.ts +28 -0
- package/test/utils/FakeTransport.ts +34 -7
- package/test/utils/mock/Transport.ts +10 -0
- package/test/utils/topology.ts +80 -0
- package/test/RandomGraphSimulation.ts +0 -53
|
@@ -3,16 +3,14 @@ import { OrderedMap } from '@js-sdsl/ordered-map'
|
|
|
3
3
|
import { RingDistance, RingId, RingIdRaw, getLeftDistance, getRightDistance, getRingIdFromPeerDescriptor, getRingIdFromRaw } from './ringIdentifiers'
|
|
4
4
|
import { DhtAddress, getNodeIdFromPeerDescriptor } from '../../identifiers'
|
|
5
5
|
import EventEmitter from 'eventemitter3'
|
|
6
|
+
import { Events } from './ContactList'
|
|
6
7
|
|
|
7
8
|
export interface RingContacts {
|
|
8
9
|
left: PeerDescriptor[]
|
|
9
10
|
right: PeerDescriptor[]
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ringContactRemoved: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
|
|
14
|
-
}
|
|
15
|
-
export class RingContactList<C extends { getPeerDescriptor(): PeerDescriptor }> extends EventEmitter<RingContactListEvents> {
|
|
12
|
+
|
|
13
|
+
export class RingContactList<C extends { getPeerDescriptor(): PeerDescriptor }> extends EventEmitter<Events<C>> {
|
|
16
14
|
|
|
17
15
|
private readonly numNeighborsPerSide = 5
|
|
18
16
|
private readonly referenceId: RingId
|
|
@@ -59,16 +57,11 @@ export class RingContactList<C extends { getPeerDescriptor(): PeerDescriptor }>
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
if (this.hasEventListeners() && (elementAdded || elementRemoved)) {
|
|
62
|
-
const closestContacts = this.getClosestContacts()
|
|
63
|
-
const closestDescriptors = {
|
|
64
|
-
left: closestContacts.left.map((c) => c.getPeerDescriptor()),
|
|
65
|
-
right: closestContacts.right.map((c) => c.getPeerDescriptor())
|
|
66
|
-
}
|
|
67
60
|
if (elementAdded) {
|
|
68
|
-
this.emit('
|
|
61
|
+
this.emit('contactAdded', contact)
|
|
69
62
|
}
|
|
70
63
|
if (elementRemoved) {
|
|
71
|
-
this.emit('
|
|
64
|
+
this.emit('contactRemoved', contact)
|
|
72
65
|
}
|
|
73
66
|
}
|
|
74
67
|
}
|
|
@@ -91,10 +84,7 @@ export class RingContactList<C extends { getPeerDescriptor(): PeerDescriptor }>
|
|
|
91
84
|
}
|
|
92
85
|
|
|
93
86
|
if (this.hasEventListeners() && elementRemoved) {
|
|
94
|
-
|
|
95
|
-
const closestDescriptors = { left: closestContacts.left.map((c) => c.getPeerDescriptor()),
|
|
96
|
-
right: closestContacts.right.map((c) => c.getPeerDescriptor()) }
|
|
97
|
-
this.emit('ringContactRemoved', contact.getPeerDescriptor(), closestDescriptors)
|
|
87
|
+
this.emit('contactRemoved', contact)
|
|
98
88
|
}
|
|
99
89
|
}
|
|
100
90
|
|
|
@@ -4,6 +4,10 @@ import EventEmitter from 'eventemitter3'
|
|
|
4
4
|
import { getDistance } from '../PeerManager'
|
|
5
5
|
import { DhtAddress, getRawFromDhtAddress } from '../../identifiers'
|
|
6
6
|
|
|
7
|
+
// add other getters in the future if needed
|
|
8
|
+
export type ReadonlySortedContactList<C extends { getNodeId: () => DhtAddress }> =
|
|
9
|
+
Pick<SortedContactList<C>, 'getClosestContacts' | 'getAllContactsInUndefinedOrder'>
|
|
10
|
+
|
|
7
11
|
export interface SortedContactListConfig {
|
|
8
12
|
referenceId: DhtAddress // all contacts in this list are in sorted by the distance to this ID
|
|
9
13
|
allowToContainReferenceId: boolean
|
|
@@ -53,8 +57,7 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
53
57
|
if (this.hasEventListeners()) {
|
|
54
58
|
this.emit(
|
|
55
59
|
'contactAdded',
|
|
56
|
-
contact
|
|
57
|
-
this.getClosestContacts()
|
|
60
|
+
contact
|
|
58
61
|
)
|
|
59
62
|
}
|
|
60
63
|
} else if (this.compareIds(this.contactIds[this.config.maxSize - 1], contactId) > 0) {
|
|
@@ -65,16 +68,13 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
65
68
|
const index = sortedIndexBy(this.contactIds, contactId, (id: DhtAddress) => { return this.distanceToReferenceId(id) })
|
|
66
69
|
this.contactIds.splice(index, 0, contactId)
|
|
67
70
|
if (this.hasEventListeners()) {
|
|
68
|
-
const closestContacts = this.getClosestContacts()
|
|
69
71
|
this.emit(
|
|
70
72
|
'contactRemoved',
|
|
71
|
-
removedContact
|
|
72
|
-
closestContacts
|
|
73
|
+
removedContact
|
|
73
74
|
)
|
|
74
75
|
this.emit(
|
|
75
76
|
'contactAdded',
|
|
76
|
-
contact
|
|
77
|
-
closestContacts
|
|
77
|
+
contact
|
|
78
78
|
)
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -133,8 +133,7 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
133
133
|
if (this.hasEventListeners()) {
|
|
134
134
|
this.emit(
|
|
135
135
|
'contactRemoved',
|
|
136
|
-
removed
|
|
137
|
-
this.getClosestContacts()
|
|
136
|
+
removed
|
|
138
137
|
)
|
|
139
138
|
}
|
|
140
139
|
return true
|
|
@@ -142,7 +141,7 @@ export class SortedContactList<C extends { getNodeId: () => DhtAddress }> extend
|
|
|
142
141
|
return false
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
public getAllContactsInUndefinedOrder():
|
|
144
|
+
public getAllContactsInUndefinedOrder(): Iterable<C> {
|
|
146
145
|
return this.contactsById.values()
|
|
147
146
|
}
|
|
148
147
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DhtAddress } from '../../identifiers'
|
|
2
|
+
import { SortedContactList } from './SortedContactList'
|
|
3
|
+
|
|
4
|
+
export const getClosestContacts = <C extends { getNodeId: () => DhtAddress }>(
|
|
5
|
+
referenceId: DhtAddress,
|
|
6
|
+
contacts: Iterable<C>,
|
|
7
|
+
opts?: {
|
|
8
|
+
maxCount?: number
|
|
9
|
+
excludedNodeIds?: Set<DhtAddress>
|
|
10
|
+
}
|
|
11
|
+
): C[] => {
|
|
12
|
+
const list = new SortedContactList<C>({
|
|
13
|
+
referenceId,
|
|
14
|
+
allowToContainReferenceId: true,
|
|
15
|
+
excludedNodeIds: opts?.excludedNodeIds,
|
|
16
|
+
maxSize: opts?.maxCount
|
|
17
|
+
})
|
|
18
|
+
for (const contact of contacts) {
|
|
19
|
+
list.addContact(contact)
|
|
20
|
+
}
|
|
21
|
+
return list.getClosestContacts()
|
|
22
|
+
}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import { Logger,
|
|
2
|
-
import EventEmitter from 'eventemitter3'
|
|
1
|
+
import { Gate, Logger, withTimeout } from '@streamr/utils'
|
|
3
2
|
import { v4 } from 'uuid'
|
|
3
|
+
import { DhtAddress, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
|
|
4
4
|
import { PeerDescriptor } from '../../proto/packages/dht/protos/DhtRpc'
|
|
5
|
-
import { PeerManager, getDistance } from '../PeerManager'
|
|
6
5
|
import { DhtNodeRpcRemote } from '../DhtNodeRpcRemote'
|
|
7
|
-
import {
|
|
6
|
+
import { PeerManager, getDistance } from '../PeerManager'
|
|
7
|
+
import { getClosestContacts } from '../contact/getClosestContacts'
|
|
8
8
|
|
|
9
9
|
const logger = new Logger(module)
|
|
10
10
|
|
|
11
|
-
interface DiscoverySessionEvents {
|
|
12
|
-
discoveryCompleted: () => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
interface DiscoverySessionConfig {
|
|
16
12
|
targetId: DhtAddress
|
|
17
13
|
parallelism: number
|
|
@@ -19,15 +15,15 @@ interface DiscoverySessionConfig {
|
|
|
19
15
|
peerManager: PeerManager
|
|
20
16
|
// Note that contacted peers will be mutated by the DiscoverySession or other parallel sessions
|
|
21
17
|
contactedPeers: Set<DhtAddress>
|
|
18
|
+
abortSignal: AbortSignal
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
export class DiscoverySession {
|
|
25
22
|
|
|
26
23
|
public readonly id = v4()
|
|
27
|
-
private stopped = false
|
|
28
|
-
private emitter = new EventEmitter<DiscoverySessionEvents>()
|
|
29
24
|
private noProgressCounter = 0
|
|
30
|
-
private
|
|
25
|
+
private ongoingRequests: Set<DhtAddress> = new Set()
|
|
26
|
+
private doneGate = new Gate(false)
|
|
31
27
|
private readonly config: DiscoverySessionConfig
|
|
32
28
|
|
|
33
29
|
constructor(config: DiscoverySessionConfig) {
|
|
@@ -35,7 +31,7 @@ export class DiscoverySession {
|
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
private addContacts(contacts: PeerDescriptor[]): void {
|
|
38
|
-
if (this.
|
|
34
|
+
if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
|
|
39
35
|
return
|
|
40
36
|
}
|
|
41
37
|
for (const contact of contacts) {
|
|
@@ -43,22 +39,22 @@ export class DiscoverySession {
|
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
private async
|
|
47
|
-
if (this.
|
|
42
|
+
private async fetchClosestNeighborsFromRemote(contact: DhtNodeRpcRemote): Promise<PeerDescriptor[]> {
|
|
43
|
+
if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
|
|
48
44
|
return []
|
|
49
45
|
}
|
|
50
|
-
logger.trace(`Getting closest
|
|
46
|
+
logger.trace(`Getting closest neighbors from remote: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
|
|
51
47
|
this.config.contactedPeers.add(contact.getNodeId())
|
|
52
48
|
const returnedContacts = await contact.getClosestPeers(this.config.targetId)
|
|
53
49
|
this.config.peerManager.setContactActive(contact.getNodeId())
|
|
54
50
|
return returnedContacts
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
private
|
|
58
|
-
if (!this.
|
|
53
|
+
private onRequestSucceeded(nodeId: DhtAddress, contacts: PeerDescriptor[]) {
|
|
54
|
+
if (!this.ongoingRequests.has(nodeId)) {
|
|
59
55
|
return
|
|
60
56
|
}
|
|
61
|
-
this.
|
|
57
|
+
this.ongoingRequests.delete(nodeId)
|
|
62
58
|
const targetId = getRawFromDhtAddress(this.config.targetId)
|
|
63
59
|
const oldClosestNeighbor = this.config.peerManager.getClosestNeighborsTo(this.config.targetId, 1)[0]
|
|
64
60
|
const oldClosestDistance = getDistance(targetId, getRawFromDhtAddress(oldClosestNeighbor.getNodeId()))
|
|
@@ -70,37 +66,39 @@ export class DiscoverySession {
|
|
|
70
66
|
}
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
private
|
|
74
|
-
if (!this.
|
|
69
|
+
private onRequestFailed(peer: DhtNodeRpcRemote) {
|
|
70
|
+
if (!this.ongoingRequests.has(peer.getNodeId())) {
|
|
75
71
|
return
|
|
76
72
|
}
|
|
77
|
-
this.
|
|
73
|
+
this.ongoingRequests.delete(peer.getNodeId())
|
|
78
74
|
this.config.peerManager.removeContact(peer.getNodeId())
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
private findMoreContacts(): void {
|
|
82
|
-
if (this.
|
|
78
|
+
if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
|
|
83
79
|
return
|
|
84
80
|
}
|
|
85
|
-
const uncontacted =
|
|
81
|
+
const uncontacted = getClosestContacts(
|
|
86
82
|
this.config.targetId,
|
|
87
|
-
this.config.
|
|
88
|
-
|
|
83
|
+
this.config.peerManager.getClosestContacts().getAllContactsInUndefinedOrder(),
|
|
84
|
+
{
|
|
85
|
+
maxCount: this.config.parallelism,
|
|
86
|
+
excludedNodeIds: this.config.contactedPeers
|
|
87
|
+
}
|
|
89
88
|
)
|
|
90
|
-
if (uncontacted.length === 0 || this.noProgressCounter >= this.config.noProgressLimit) {
|
|
91
|
-
this.
|
|
92
|
-
this.stopped = true
|
|
89
|
+
if ((uncontacted.length === 0 && this.ongoingRequests.size === 0) || (this.noProgressCounter >= this.config.noProgressLimit)) {
|
|
90
|
+
this.doneGate.open()
|
|
93
91
|
return
|
|
94
92
|
}
|
|
95
93
|
for (const nextPeer of uncontacted) {
|
|
96
|
-
if (this.
|
|
94
|
+
if (this.ongoingRequests.size >= this.config.parallelism) {
|
|
97
95
|
break
|
|
98
96
|
}
|
|
99
|
-
this.
|
|
97
|
+
this.ongoingRequests.add(nextPeer.getNodeId())
|
|
100
98
|
// eslint-disable-next-line promise/catch-or-return
|
|
101
|
-
this.
|
|
102
|
-
.then((contacts) => this.
|
|
103
|
-
.catch(() => this.
|
|
99
|
+
this.fetchClosestNeighborsFromRemote(nextPeer)
|
|
100
|
+
.then((contacts) => this.onRequestSucceeded(nextPeer.getNodeId(), contacts))
|
|
101
|
+
.catch(() => this.onRequestFailed(nextPeer))
|
|
104
102
|
.finally(() => {
|
|
105
103
|
this.findMoreContacts()
|
|
106
104
|
})
|
|
@@ -111,17 +109,9 @@ export class DiscoverySession {
|
|
|
111
109
|
if (this.config.peerManager.getContactCount(this.config.contactedPeers) === 0) {
|
|
112
110
|
return
|
|
113
111
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
timeout
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
public stop(): void {
|
|
123
|
-
this.stopped = true
|
|
124
|
-
this.emitter.emit('discoveryCompleted')
|
|
125
|
-
this.emitter.removeAllListeners()
|
|
112
|
+
setImmediate(() => {
|
|
113
|
+
this.findMoreContacts()
|
|
114
|
+
})
|
|
115
|
+
await withTimeout(this.doneGate.waitUntilOpen(), timeout, 'discovery session timed out', this.config.abortSignal)
|
|
126
116
|
}
|
|
127
117
|
}
|
|
@@ -12,7 +12,6 @@ import { RingIdRaw, getRingIdRawFromPeerDescriptor } from '../contact/ringIdenti
|
|
|
12
12
|
interface PeerDiscoveryConfig {
|
|
13
13
|
localPeerDescriptor: PeerDescriptor
|
|
14
14
|
joinNoProgressLimit: number
|
|
15
|
-
peerDiscoveryQueryBatchSize: number
|
|
16
15
|
serviceId: ServiceID
|
|
17
16
|
parallelism: number
|
|
18
17
|
joinTimeout: number
|
|
@@ -102,7 +101,8 @@ export class PeerDiscovery {
|
|
|
102
101
|
parallelism: this.config.parallelism,
|
|
103
102
|
noProgressLimit: this.config.joinNoProgressLimit,
|
|
104
103
|
peerManager: this.config.peerManager,
|
|
105
|
-
contactedPeers
|
|
104
|
+
contactedPeers,
|
|
105
|
+
abortSignal: this.abortController.signal
|
|
106
106
|
}
|
|
107
107
|
return new DiscoverySession(sessionOptions)
|
|
108
108
|
}
|
|
@@ -184,11 +184,11 @@ export class PeerDiscovery {
|
|
|
184
184
|
if (!this.recoveryIntervalStarted) {
|
|
185
185
|
this.recoveryIntervalStarted = true
|
|
186
186
|
// TODO use config option or named constant?
|
|
187
|
-
await scheduleAtInterval(() => this.
|
|
187
|
+
await scheduleAtInterval(() => this.fetchClosestNeighbors(), 60000, true, this.abortController.signal)
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
private async
|
|
191
|
+
private async fetchClosestNeighbors(): Promise<void> {
|
|
192
192
|
if (this.isStopped()) {
|
|
193
193
|
return
|
|
194
194
|
}
|
|
@@ -198,8 +198,8 @@ export class PeerDiscovery {
|
|
|
198
198
|
this.config.parallelism
|
|
199
199
|
)
|
|
200
200
|
await Promise.allSettled(
|
|
201
|
-
nodes.map(async (
|
|
202
|
-
const contacts = await
|
|
201
|
+
nodes.map(async (node: DhtNodeRpcRemote) => {
|
|
202
|
+
const contacts = await node.getClosestPeers(localNodeId)
|
|
203
203
|
for (const contact of contacts) {
|
|
204
204
|
this.config.peerManager.addContact(contact)
|
|
205
205
|
}
|
|
@@ -221,9 +221,6 @@ export class PeerDiscovery {
|
|
|
221
221
|
|
|
222
222
|
public stop(): void {
|
|
223
223
|
this.abortController.abort()
|
|
224
|
-
this.ongoingDiscoverySessions.forEach((session) => {
|
|
225
|
-
session.stop()
|
|
226
|
-
})
|
|
227
224
|
this.ongoingRingDiscoverySessions.forEach((session) => {
|
|
228
225
|
session.stop()
|
|
229
226
|
})
|
|
@@ -29,7 +29,7 @@ export class RingDiscoverySession {
|
|
|
29
29
|
private stopped = false
|
|
30
30
|
private emitter = new EventEmitter<RingDiscoverySessionEvents>()
|
|
31
31
|
private noProgressCounter = 0
|
|
32
|
-
private
|
|
32
|
+
private ongoingRequests: Set<DhtAddress> = new Set()
|
|
33
33
|
private readonly config: RingDiscoverySessionConfig
|
|
34
34
|
private numContactedPeers = 0
|
|
35
35
|
private targetIdAsRingId: RingId
|
|
@@ -48,7 +48,7 @@ export class RingDiscoverySession {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
private async
|
|
51
|
+
private async fetchClosestContactsFromRemote(contact: DhtNodeRpcRemote): Promise<RingContacts> {
|
|
52
52
|
if (this.stopped) {
|
|
53
53
|
return { left: [], right: [] }
|
|
54
54
|
}
|
|
@@ -60,11 +60,11 @@ export class RingDiscoverySession {
|
|
|
60
60
|
return returnedContacts
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
private
|
|
64
|
-
if (!this.
|
|
63
|
+
private onRequestSucceeded(nodeId: DhtAddress, contacts: RingContacts) {
|
|
64
|
+
if (!this.ongoingRequests.has(nodeId)) {
|
|
65
65
|
return
|
|
66
66
|
}
|
|
67
|
-
this.
|
|
67
|
+
this.ongoingRequests.delete(nodeId)
|
|
68
68
|
const oldClosestContacts = this.config.peerManager.getClosestRingContactsTo(this.config.targetId, 1)
|
|
69
69
|
const oldClosestLeftDistance = getLeftDistance(
|
|
70
70
|
this.targetIdAsRingId,
|
|
@@ -85,11 +85,11 @@ export class RingDiscoverySession {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
private
|
|
89
|
-
if (!this.
|
|
88
|
+
private onRequestFailed(peer: DhtNodeRpcRemote) {
|
|
89
|
+
if (!this.ongoingRequests.has(peer.getNodeId())) {
|
|
90
90
|
return
|
|
91
91
|
}
|
|
92
|
-
this.
|
|
92
|
+
this.ongoingRequests.delete(peer.getNodeId())
|
|
93
93
|
this.config.peerManager.removeContact(peer.getNodeId())
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -128,14 +128,14 @@ export class RingDiscoverySession {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
for (const nextPeer of merged) {
|
|
131
|
-
if (this.
|
|
131
|
+
if (this.ongoingRequests.size >= this.config.parallelism) {
|
|
132
132
|
break
|
|
133
133
|
}
|
|
134
|
-
this.
|
|
134
|
+
this.ongoingRequests.add(nextPeer.getNodeId())
|
|
135
135
|
// eslint-disable-next-line promise/catch-or-return
|
|
136
|
-
this.
|
|
137
|
-
.then((contacts) => this.
|
|
138
|
-
.catch(() => this.
|
|
136
|
+
this.fetchClosestContactsFromRemote(nextPeer)
|
|
137
|
+
.then((contacts) => this.onRequestSucceeded(nextPeer.getNodeId(), contacts))
|
|
138
|
+
.catch(() => this.onRequestFailed(nextPeer))
|
|
139
139
|
.finally(() => {
|
|
140
140
|
this.findMoreContacts()
|
|
141
141
|
})
|
|
@@ -29,12 +29,12 @@ import { getDistance } from '../PeerManager'
|
|
|
29
29
|
interface RecursiveOperationManagerConfig {
|
|
30
30
|
rpcCommunicator: RoutingRpcCommunicator
|
|
31
31
|
sessionTransport: ITransport
|
|
32
|
-
connections: Map<DhtAddress, DhtNodeRpcRemote>
|
|
33
32
|
router: Router
|
|
34
33
|
localPeerDescriptor: PeerDescriptor
|
|
35
34
|
serviceId: ServiceID
|
|
36
35
|
localDataStore: LocalDataStore
|
|
37
36
|
addContact: (contact: PeerDescriptor) => void
|
|
37
|
+
createDhtNodeRpcRemote: (peerDescriptor: PeerDescriptor) => DhtNodeRpcRemote
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export interface RecursiveOperationResult { closestNodes: Array<PeerDescriptor>, dataEntries?: Array<DataEntry> }
|
|
@@ -87,14 +87,14 @@ export class RecursiveOperationManager {
|
|
|
87
87
|
targetId,
|
|
88
88
|
localPeerDescriptor: this.config.localPeerDescriptor,
|
|
89
89
|
// TODO use config option or named constant?
|
|
90
|
-
waitedRoutingPathCompletions: this.config.
|
|
90
|
+
waitedRoutingPathCompletions: this.config.sessionTransport.getConnectionCount() > 1 ? 2 : 1,
|
|
91
91
|
operation,
|
|
92
92
|
// TODO would it make sense to give excludedPeer as one of the fields RecursiveOperationSession?
|
|
93
93
|
doRouteRequest: (routedMessage: RouteMessageWrapper) => {
|
|
94
94
|
return this.doRouteRequest(routedMessage, excludedPeer)
|
|
95
95
|
}
|
|
96
96
|
})
|
|
97
|
-
if (this.config.
|
|
97
|
+
if (this.config.sessionTransport.getConnectionCount() === 0) {
|
|
98
98
|
const dataEntries = Array.from(this.config.localDataStore.values(targetId))
|
|
99
99
|
session.onResponseReceived(
|
|
100
100
|
getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor),
|
|
@@ -140,7 +140,7 @@ export class RecursiveOperationManager {
|
|
|
140
140
|
routingPath: PeerDescriptor[],
|
|
141
141
|
targetPeerDescriptor: PeerDescriptor,
|
|
142
142
|
serviceId: ServiceID,
|
|
143
|
-
|
|
143
|
+
closestConnectedNodes: PeerDescriptor[],
|
|
144
144
|
dataEntries: DataEntry[],
|
|
145
145
|
noCloserNodesFound: boolean = false
|
|
146
146
|
): void {
|
|
@@ -150,7 +150,7 @@ export class RecursiveOperationManager {
|
|
|
150
150
|
.onResponseReceived(
|
|
151
151
|
getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor),
|
|
152
152
|
routingPath,
|
|
153
|
-
|
|
153
|
+
closestConnectedNodes,
|
|
154
154
|
dataEntries,
|
|
155
155
|
noCloserNodesFound
|
|
156
156
|
)
|
|
@@ -165,7 +165,7 @@ export class RecursiveOperationManager {
|
|
|
165
165
|
// TODO use config option or named constant?
|
|
166
166
|
10000
|
|
167
167
|
)
|
|
168
|
-
rpcRemote.sendResponse(routingPath,
|
|
168
|
+
rpcRemote.sendResponse(routingPath, closestConnectedNodes, dataEntries, noCloserNodesFound)
|
|
169
169
|
remoteCommunicator.destroy()
|
|
170
170
|
}
|
|
171
171
|
}
|
|
@@ -177,7 +177,7 @@ export class RecursiveOperationManager {
|
|
|
177
177
|
const targetId = getDhtAddressFromRaw(routedMessage.target)
|
|
178
178
|
const request = (routedMessage.message!.body as { recursiveOperationRequest: RecursiveOperationRequest }).recursiveOperationRequest
|
|
179
179
|
// TODO use config option or named constant?
|
|
180
|
-
const
|
|
180
|
+
const closestConnectedNodes = this.getClosestConnectedNodes(targetId, 5)
|
|
181
181
|
const dataEntries = (request.operation === RecursiveOperation.FETCH_DATA)
|
|
182
182
|
? Array.from(this.config.localDataStore.values(targetId))
|
|
183
183
|
: []
|
|
@@ -190,7 +190,7 @@ export class RecursiveOperationManager {
|
|
|
190
190
|
routedMessage.routingPath,
|
|
191
191
|
routedMessage.sourcePeer!,
|
|
192
192
|
request.sessionId,
|
|
193
|
-
|
|
193
|
+
closestConnectedNodes,
|
|
194
194
|
dataEntries,
|
|
195
195
|
true
|
|
196
196
|
)
|
|
@@ -200,15 +200,15 @@ export class RecursiveOperationManager {
|
|
|
200
200
|
if ((ack.error === undefined) || (ack.error === RouteMessageError.NO_TARGETS)) {
|
|
201
201
|
const noCloserContactsFound = (ack.error === RouteMessageError.NO_TARGETS) ||
|
|
202
202
|
(
|
|
203
|
-
|
|
203
|
+
closestConnectedNodes.length > 0
|
|
204
204
|
&& getPreviousPeer(routedMessage)
|
|
205
|
-
&& !this.isPeerCloserToIdThanSelf(
|
|
205
|
+
&& !this.isPeerCloserToIdThanSelf(closestConnectedNodes[0], targetId)
|
|
206
206
|
)
|
|
207
207
|
this.sendResponse(
|
|
208
208
|
routedMessage.routingPath,
|
|
209
209
|
routedMessage.sourcePeer!,
|
|
210
210
|
request.sessionId,
|
|
211
|
-
|
|
211
|
+
closestConnectedNodes,
|
|
212
212
|
dataEntries,
|
|
213
213
|
noCloserContactsFound
|
|
214
214
|
)
|
|
@@ -217,15 +217,15 @@ export class RecursiveOperationManager {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
private
|
|
221
|
-
const
|
|
222
|
-
const
|
|
220
|
+
private getClosestConnectedNodes(referenceId: DhtAddress, limit: number): PeerDescriptor[] {
|
|
221
|
+
const connectedNodes = this.config.sessionTransport.getConnections().map((c) => this.config.createDhtNodeRpcRemote(c))
|
|
222
|
+
const sorted = new SortedContactList<DhtNodeRpcRemote>({
|
|
223
223
|
referenceId,
|
|
224
224
|
maxSize: limit,
|
|
225
225
|
allowToContainReferenceId: true
|
|
226
226
|
})
|
|
227
|
-
|
|
228
|
-
return
|
|
227
|
+
sorted.addContacts(connectedNodes)
|
|
228
|
+
return sorted.getClosestContacts(limit).map((peer) => peer.getPeerDescriptor())
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
private isPeerCloserToIdThanSelf(peer: PeerDescriptor, nodeIdOrDataKey: DhtAddress): boolean {
|
|
@@ -66,11 +66,11 @@ export class RecursiveOperationSession extends EventEmitter<RecursiveOperationSe
|
|
|
66
66
|
onResponseReceived: (
|
|
67
67
|
sourceId: DhtAddress,
|
|
68
68
|
routingPath: PeerDescriptor[],
|
|
69
|
-
|
|
69
|
+
closestConnectedNodes: PeerDescriptor[],
|
|
70
70
|
dataEntries: DataEntry[],
|
|
71
71
|
noCloserNodesFound: boolean
|
|
72
72
|
) => {
|
|
73
|
-
this.onResponseReceived(sourceId, routingPath,
|
|
73
|
+
this.onResponseReceived(sourceId, routingPath, closestConnectedNodes, dataEntries, noCloserNodesFound)
|
|
74
74
|
}
|
|
75
75
|
})
|
|
76
76
|
this.rpcCommunicator.registerRpcNotification(RecursiveOperationResponse, 'sendResponse',
|
|
@@ -131,7 +131,7 @@ export class RecursiveOperationSession extends EventEmitter<RecursiveOperationSe
|
|
|
131
131
|
public onResponseReceived(
|
|
132
132
|
sourceId: DhtAddress,
|
|
133
133
|
routingPath: PeerDescriptor[],
|
|
134
|
-
|
|
134
|
+
closestConnectedNodes: PeerDescriptor[],
|
|
135
135
|
dataEntries: DataEntry[],
|
|
136
136
|
noCloserNodesFound: boolean
|
|
137
137
|
): void {
|
|
@@ -139,7 +139,7 @@ export class RecursiveOperationSession extends EventEmitter<RecursiveOperationSe
|
|
|
139
139
|
if (routingPath.length >= 1) {
|
|
140
140
|
this.setHopAsReported(routingPath[routingPath.length - 1])
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
closestConnectedNodes.forEach((descriptor: PeerDescriptor) => {
|
|
143
143
|
this.results.addContact(new Contact(descriptor))
|
|
144
144
|
})
|
|
145
145
|
this.processFoundData(dataEntries)
|
|
@@ -197,15 +197,13 @@ export class RecursiveOperationSession extends EventEmitter<RecursiveOperationSe
|
|
|
197
197
|
clearTimeout(this.timeoutTask)
|
|
198
198
|
this.timeoutTask = undefined
|
|
199
199
|
}
|
|
200
|
-
} else {
|
|
201
|
-
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}, 4000) // TODO use config option or named constant?
|
|
208
|
-
}
|
|
200
|
+
} else if (!this.timeoutTask && !this.completionEventEmitted) {
|
|
201
|
+
this.timeoutTask = setTimeout(() => {
|
|
202
|
+
if (!this.completionEventEmitted) {
|
|
203
|
+
this.emit('completed')
|
|
204
|
+
this.completionEventEmitted = true
|
|
205
|
+
}
|
|
206
|
+
}, 4000) // TODO use config option or named constant?
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
209
|
|
|
@@ -29,7 +29,7 @@ export class RecursiveOperationSessionRpcLocal implements IRecursiveOperationSes
|
|
|
29
29
|
async sendResponse(report: RecursiveOperationResponse, context: ServerCallContext): Promise<Empty> {
|
|
30
30
|
const sourceId = getNodeIdFromPeerDescriptor((context as DhtCallContext).incomingSourceDescriptor!)
|
|
31
31
|
logger.trace('RecursiveOperationResponse arrived: ' + JSON.stringify(report))
|
|
32
|
-
this.config.onResponseReceived(sourceId, report.routingPath, report.
|
|
32
|
+
this.config.onResponseReceived(sourceId, report.routingPath, report.closestConnectedNodes, report.dataEntries, report.noCloserNodesFound)
|
|
33
33
|
return {}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -13,13 +13,13 @@ export class RecursiveOperationSessionRpcRemote extends RpcRemote<RecursiveOpera
|
|
|
13
13
|
|
|
14
14
|
sendResponse(
|
|
15
15
|
routingPath: PeerDescriptor[],
|
|
16
|
-
|
|
16
|
+
closestConnectedNodes: PeerDescriptor[],
|
|
17
17
|
dataEntries: DataEntry[],
|
|
18
18
|
noCloserNodesFound: boolean
|
|
19
19
|
): void {
|
|
20
20
|
const report: RecursiveOperationResponse = {
|
|
21
21
|
routingPath,
|
|
22
|
-
|
|
22
|
+
closestConnectedNodes,
|
|
23
23
|
dataEntries,
|
|
24
24
|
noCloserNodesFound
|
|
25
25
|
}
|
|
@@ -3,7 +3,6 @@ import { RoutingMode, RoutingRemoteContact, RoutingSession, RoutingSessionEvents
|
|
|
3
3
|
import { Logger, executeSafePromise, raceEvents3, withTimeout } from '@streamr/utils'
|
|
4
4
|
import { RoutingRpcCommunicator } from '../../transport/RoutingRpcCommunicator'
|
|
5
5
|
import { DuplicateDetector } from './DuplicateDetector'
|
|
6
|
-
import { DhtNodeRpcRemote } from '../DhtNodeRpcRemote'
|
|
7
6
|
import { v4 } from 'uuid'
|
|
8
7
|
import { RouterRpcLocal, createRouteMessageAck } from './RouterRpcLocal'
|
|
9
8
|
import { DhtAddress, areEqualPeerDescriptors, getDhtAddressFromRaw, getNodeIdFromPeerDescriptor } from '../../identifiers'
|
|
@@ -12,8 +11,8 @@ import { RoutingTablesCache } from './RoutingTablesCache'
|
|
|
12
11
|
export interface RouterConfig {
|
|
13
12
|
rpcCommunicator: RoutingRpcCommunicator
|
|
14
13
|
localPeerDescriptor: PeerDescriptor
|
|
15
|
-
connections: Map<DhtAddress, DhtNodeRpcRemote>
|
|
16
14
|
handleMessage: (message: Message) => void
|
|
15
|
+
getConnections: () => PeerDescriptor[]
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
interface ForwardingTableEntry {
|
|
@@ -158,17 +157,16 @@ export class Router {
|
|
|
158
157
|
routedMessage.parallelRootNodeIds.forEach((nodeId) => {
|
|
159
158
|
excludedNodeIds.add(nodeId as DhtAddress)
|
|
160
159
|
})
|
|
161
|
-
logger.trace('routing session created with connections: ' + this.config.connections.size)
|
|
162
160
|
return new RoutingSession({
|
|
163
161
|
rpcCommunicator: this.config.rpcCommunicator,
|
|
164
162
|
localPeerDescriptor: this.config.localPeerDescriptor,
|
|
165
163
|
routedMessage,
|
|
166
|
-
connections: this.config.connections,
|
|
167
164
|
// TODO use config option or named constant?
|
|
168
165
|
parallelism: areEqualPeerDescriptors(this.config.localPeerDescriptor, routedMessage.sourcePeer!) ? 2 : 1,
|
|
169
166
|
mode,
|
|
170
167
|
excludedNodeIds,
|
|
171
|
-
routingTablesCache: this.routingTablesCache
|
|
168
|
+
routingTablesCache: this.routingTablesCache,
|
|
169
|
+
getConnections: this.config.getConnections
|
|
172
170
|
})
|
|
173
171
|
}
|
|
174
172
|
|