@streamr/dht 100.2.1 → 100.2.3

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.
Files changed (118) hide show
  1. package/dist/package.json +6 -6
  2. package/dist/src/connection/ConnectionLockStates.js +4 -6
  3. package/dist/src/connection/ConnectionLockStates.js.map +1 -1
  4. package/dist/src/connection/ConnectionManager.d.ts +1 -0
  5. package/dist/src/connection/ConnectionManager.js +8 -2
  6. package/dist/src/connection/ConnectionManager.js.map +1 -1
  7. package/dist/src/connection/ManagedConnection.js +7 -9
  8. package/dist/src/connection/ManagedConnection.js.map +1 -1
  9. package/dist/src/connection/connectivityChecker.js +0 -3
  10. package/dist/src/connection/connectivityChecker.js.map +1 -1
  11. package/dist/src/connection/websocket/WebsocketConnector.js +1 -1
  12. package/dist/src/connection/websocket/WebsocketConnector.js.map +1 -1
  13. package/dist/src/dht/DhtNode.d.ts +10 -7
  14. package/dist/src/dht/DhtNode.js +51 -32
  15. package/dist/src/dht/DhtNode.js.map +1 -1
  16. package/dist/src/dht/DhtNodeRpcLocal.d.ts +2 -2
  17. package/dist/src/dht/DhtNodeRpcLocal.js +5 -4
  18. package/dist/src/dht/DhtNodeRpcLocal.js.map +1 -1
  19. package/dist/src/dht/DhtNodeRpcRemote.js +1 -0
  20. package/dist/src/dht/DhtNodeRpcRemote.js.map +1 -1
  21. package/dist/src/dht/PeerManager.d.ts +14 -14
  22. package/dist/src/dht/PeerManager.js +23 -59
  23. package/dist/src/dht/PeerManager.js.map +1 -1
  24. package/dist/src/dht/contact/ContactList.d.ts +2 -2
  25. package/dist/src/dht/contact/RandomContactList.js +2 -2
  26. package/dist/src/dht/contact/RandomContactList.js.map +1 -1
  27. package/dist/src/dht/contact/RingContactList.d.ts +2 -5
  28. package/dist/src/dht/contact/RingContactList.js +3 -11
  29. package/dist/src/dht/contact/RingContactList.js.map +1 -1
  30. package/dist/src/dht/contact/SortedContactList.d.ts +4 -1
  31. package/dist/src/dht/contact/SortedContactList.js +4 -5
  32. package/dist/src/dht/contact/SortedContactList.js.map +1 -1
  33. package/dist/src/dht/contact/getClosestContacts.d.ts +7 -0
  34. package/dist/src/dht/contact/getClosestContacts.js +18 -0
  35. package/dist/src/dht/contact/getClosestContacts.js.map +1 -0
  36. package/dist/src/dht/discovery/DiscoverySession.d.ts +8 -8
  37. package/dist/src/dht/discovery/DiscoverySession.js +30 -35
  38. package/dist/src/dht/discovery/DiscoverySession.js.map +1 -1
  39. package/dist/src/dht/discovery/PeerDiscovery.d.ts +1 -2
  40. package/dist/src/dht/discovery/PeerDiscovery.js +6 -8
  41. package/dist/src/dht/discovery/PeerDiscovery.js.map +1 -1
  42. package/dist/src/dht/discovery/RingDiscoverySession.d.ts +4 -4
  43. package/dist/src/dht/discovery/RingDiscoverySession.js +13 -13
  44. package/dist/src/dht/discovery/RingDiscoverySession.js.map +1 -1
  45. package/dist/src/dht/recursive-operation/RecursiveOperationManager.d.ts +2 -2
  46. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js +15 -15
  47. package/dist/src/dht/recursive-operation/RecursiveOperationManager.js.map +1 -1
  48. package/dist/src/dht/recursive-operation/RecursiveOperationSession.d.ts +1 -1
  49. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js +11 -13
  50. package/dist/src/dht/recursive-operation/RecursiveOperationSession.js.map +1 -1
  51. package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.js +1 -1
  52. package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.d.ts +1 -1
  53. package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js +2 -2
  54. package/dist/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.js.map +1 -1
  55. package/dist/src/dht/routing/Router.d.ts +1 -2
  56. package/dist/src/dht/routing/Router.js +2 -3
  57. package/dist/src/dht/routing/Router.js.map +1 -1
  58. package/dist/src/dht/routing/RoutingSession.d.ts +1 -2
  59. package/dist/src/dht/routing/RoutingSession.js +2 -2
  60. package/dist/src/dht/routing/RoutingSession.js.map +1 -1
  61. package/dist/src/dht/store/StoreRpcLocal.js +3 -3
  62. package/dist/src/dht/store/StoreRpcLocal.js.map +1 -1
  63. package/dist/src/proto/packages/dht/protos/DhtRpc.client.d.ts +8 -0
  64. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js +4 -0
  65. package/dist/src/proto/packages/dht/protos/DhtRpc.client.js.map +1 -1
  66. package/dist/src/proto/packages/dht/protos/DhtRpc.d.ts +10 -2
  67. package/dist/src/proto/packages/dht/protos/DhtRpc.js +1 -1
  68. package/dist/src/proto/packages/dht/protos/DhtRpc.js.map +1 -1
  69. package/dist/src/proto/packages/dht/protos/DhtRpc.server.d.ts +4 -0
  70. package/dist/src/transport/ITransport.d.ts +3 -0
  71. package/dist/src/transport/ITransport.js.map +1 -1
  72. package/package.json +6 -6
  73. package/protos/DhtRpc.proto +7 -1
  74. package/src/connection/ConnectionLockStates.ts +3 -5
  75. package/src/connection/ConnectionManager.ts +9 -2
  76. package/src/connection/ManagedConnection.ts +11 -14
  77. package/src/connection/connectivityChecker.ts +0 -2
  78. package/src/connection/webrtc/BrowserWebrtcConnection.ts +0 -1
  79. package/src/connection/websocket/WebsocketConnector.ts +1 -1
  80. package/src/dht/DhtNode.ts +63 -44
  81. package/src/dht/DhtNodeRpcLocal.ts +7 -6
  82. package/src/dht/DhtNodeRpcRemote.ts +1 -0
  83. package/src/dht/PeerManager.ts +38 -73
  84. package/src/dht/contact/ContactList.ts +2 -2
  85. package/src/dht/contact/RandomContactList.ts +2 -3
  86. package/src/dht/contact/RingContactList.ts +6 -16
  87. package/src/dht/contact/SortedContactList.ts +9 -10
  88. package/src/dht/contact/getClosestContacts.ts +22 -0
  89. package/src/dht/discovery/DiscoverySession.ts +35 -45
  90. package/src/dht/discovery/PeerDiscovery.ts +6 -9
  91. package/src/dht/discovery/RingDiscoverySession.ts +13 -13
  92. package/src/dht/recursive-operation/RecursiveOperationManager.ts +16 -16
  93. package/src/dht/recursive-operation/RecursiveOperationSession.ts +11 -13
  94. package/src/dht/recursive-operation/RecursiveOperationSessionRpcLocal.ts +1 -1
  95. package/src/dht/recursive-operation/RecursiveOperationSessionRpcRemote.ts +2 -2
  96. package/src/dht/routing/Router.ts +3 -5
  97. package/src/dht/routing/RoutingSession.ts +3 -4
  98. package/src/dht/store/StoreRpcLocal.ts +3 -3
  99. package/src/proto/packages/dht/protos/DhtRpc.client.ts +8 -0
  100. package/src/proto/packages/dht/protos/DhtRpc.server.ts +4 -0
  101. package/src/proto/packages/dht/protos/DhtRpc.ts +11 -3
  102. package/src/transport/ITransport.ts +3 -0
  103. package/test/end-to-end/Layer0MixedConnectionTypes.test.ts +3 -5
  104. package/test/integration/DhtNode.test.ts +81 -0
  105. package/test/integration/Layer1-scale.test.ts +1 -1
  106. package/test/integration/ScaleDownDht.test.ts +7 -4
  107. package/test/unit/DiscoverySession.test.ts +85 -0
  108. package/test/unit/PeerManager.test.ts +3 -17
  109. package/test/unit/RecursiveOperationManager.test.ts +5 -3
  110. package/test/unit/Router.test.ts +2 -2
  111. package/test/unit/RoutingSession.test.ts +2 -2
  112. package/test/unit/SortedContactList.test.ts +4 -4
  113. package/test/unit/getClosestContacts.test.ts +28 -0
  114. package/test/utils/FakeTransport.ts +34 -7
  115. package/test/utils/mock/Router.ts +0 -3
  116. package/test/utils/mock/Transport.ts +10 -0
  117. package/test/utils/topology.ts +80 -0
  118. package/test/RandomGraphSimulation.ts +0 -53
@@ -2,8 +2,8 @@ import EventEmitter from 'eventemitter3'
2
2
  import { DhtAddress } from '../../identifiers'
3
3
 
4
4
  export interface Events<C> {
5
- contactRemoved: (removedContact: C, closestContacts: C[]) => void
6
- contactAdded: (contactAdded: C, closestContacts: C[]) => void
5
+ contactRemoved: (removedContact: C) => void
6
+ contactAdded: (contactAdded: C) => void
7
7
  }
8
8
 
9
9
  export class ContactList<C extends { getNodeId: () => DhtAddress }> extends EventEmitter<Events<C>> {
@@ -30,8 +30,7 @@ export class RandomContactList<C extends { getNodeId: () => DhtAddress }> extend
30
30
  this.contactsById.set(contact.getNodeId(), contact)
31
31
  this.emit(
32
32
  'contactAdded',
33
- contact,
34
- this.getContacts()
33
+ contact
35
34
  )
36
35
  }
37
36
  }
@@ -43,7 +42,7 @@ export class RandomContactList<C extends { getNodeId: () => DhtAddress }> extend
43
42
  const index = this.contactIds.findIndex((nodeId) => (nodeId === id))
44
43
  this.contactIds.splice(index, 1)
45
44
  this.contactsById.delete(id)
46
- this.emit('contactRemoved', removed, this.getContacts())
45
+ this.emit('contactRemoved', removed)
47
46
  return true
48
47
  }
49
48
  return false
@@ -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
- export interface RingContactListEvents {
12
- ringContactAdded: (peerDescriptor: PeerDescriptor, closestPeers: RingContacts) => void
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('ringContactAdded', contact.getPeerDescriptor(), closestDescriptors)
61
+ this.emit('contactAdded', contact)
69
62
  }
70
63
  if (elementRemoved) {
71
- this.emit('ringContactRemoved', contact.getPeerDescriptor(), closestDescriptors)
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
- const closestContacts = this.getClosestContacts()
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(): IterableIterator<C> {
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, runAndWaitForEvents3 } from '@streamr/utils'
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 { DhtAddress, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '../../identifiers'
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 ongoingClosestPeersRequests: Set<DhtAddress> = new Set()
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.stopped) {
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 getClosestPeersFromContact(contact: DhtNodeRpcRemote): Promise<PeerDescriptor[]> {
47
- if (this.stopped) {
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 peers from contact: ${getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())}`)
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 onClosestPeersRequestSucceeded(nodeId: DhtAddress, contacts: PeerDescriptor[]) {
58
- if (!this.ongoingClosestPeersRequests.has(nodeId)) {
53
+ private onRequestSucceeded(nodeId: DhtAddress, contacts: PeerDescriptor[]) {
54
+ if (!this.ongoingRequests.has(nodeId)) {
59
55
  return
60
56
  }
61
- this.ongoingClosestPeersRequests.delete(nodeId)
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 onClosestPeersRequestFailed(peer: DhtNodeRpcRemote) {
74
- if (!this.ongoingClosestPeersRequests.has(peer.getNodeId())) {
69
+ private onRequestFailed(peer: DhtNodeRpcRemote) {
70
+ if (!this.ongoingRequests.has(peer.getNodeId())) {
75
71
  return
76
72
  }
77
- this.ongoingClosestPeersRequests.delete(peer.getNodeId())
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.stopped) {
78
+ if (this.config.abortSignal.aborted || this.doneGate.isOpen()) {
83
79
  return
84
80
  }
85
- const uncontacted = this.config.peerManager.getClosestContactsTo(
81
+ const uncontacted = getClosestContacts(
86
82
  this.config.targetId,
87
- this.config.parallelism,
88
- this.config.contactedPeers
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.emitter.emit('discoveryCompleted')
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.ongoingClosestPeersRequests.size >= this.config.parallelism) {
94
+ if (this.ongoingRequests.size >= this.config.parallelism) {
97
95
  break
98
96
  }
99
- this.ongoingClosestPeersRequests.add(nextPeer.getNodeId())
97
+ this.ongoingRequests.add(nextPeer.getNodeId())
100
98
  // eslint-disable-next-line promise/catch-or-return
101
- this.getClosestPeersFromContact(nextPeer)
102
- .then((contacts) => this.onClosestPeersRequestSucceeded(nextPeer.getNodeId(), contacts))
103
- .catch(() => this.onClosestPeersRequestFailed(nextPeer))
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
- // TODO add abortController and signal it in stop()
115
- await runAndWaitForEvents3<DiscoverySessionEvents>(
116
- [this.findMoreContacts.bind(this)],
117
- [[this.emitter, 'discoveryCompleted']],
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.fetchClosestPeersFromBucket(), 60000, true, this.abortController.signal)
187
+ await scheduleAtInterval(() => this.fetchClosestNeighbors(), 60000, true, this.abortController.signal)
188
188
  }
189
189
  }
190
190
 
191
- private async fetchClosestPeersFromBucket(): Promise<void> {
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 (peer: DhtNodeRpcRemote) => {
202
- const contacts = await peer.getClosestPeers(localNodeId)
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 ongoingClosestPeersRequests: Set<DhtAddress> = new Set()
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 getClosestPeersFromContact(contact: DhtNodeRpcRemote): Promise<RingContacts> {
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 onClosestPeersRequestSucceeded(nodeId: DhtAddress, contacts: RingContacts) {
64
- if (!this.ongoingClosestPeersRequests.has(nodeId)) {
63
+ private onRequestSucceeded(nodeId: DhtAddress, contacts: RingContacts) {
64
+ if (!this.ongoingRequests.has(nodeId)) {
65
65
  return
66
66
  }
67
- this.ongoingClosestPeersRequests.delete(nodeId)
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 onClosestPeersRequestFailed(peer: DhtNodeRpcRemote) {
89
- if (!this.ongoingClosestPeersRequests.has(peer.getNodeId())) {
88
+ private onRequestFailed(peer: DhtNodeRpcRemote) {
89
+ if (!this.ongoingRequests.has(peer.getNodeId())) {
90
90
  return
91
91
  }
92
- this.ongoingClosestPeersRequests.delete(peer.getNodeId())
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.ongoingClosestPeersRequests.size >= this.config.parallelism) {
131
+ if (this.ongoingRequests.size >= this.config.parallelism) {
132
132
  break
133
133
  }
134
- this.ongoingClosestPeersRequests.add(nextPeer.getNodeId())
134
+ this.ongoingRequests.add(nextPeer.getNodeId())
135
135
  // eslint-disable-next-line promise/catch-or-return
136
- this.getClosestPeersFromContact(nextPeer)
137
- .then((contacts) => this.onClosestPeersRequestSucceeded(nextPeer.getNodeId(), contacts))
138
- .catch(() => this.onClosestPeersRequestFailed(nextPeer))
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.connections.size > 1 ? 2 : 1,
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.connections.size === 0) {
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
- closestNodes: PeerDescriptor[],
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
- closestNodes,
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, closestNodes, dataEntries, noCloserNodesFound)
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 closestPeersToDestination = this.getClosestConnections(targetId, 5)
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
- closestPeersToDestination,
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
- closestPeersToDestination.length > 0
203
+ closestConnectedNodes.length > 0
204
204
  && getPreviousPeer(routedMessage)
205
- && !this.isPeerCloserToIdThanSelf(closestPeersToDestination[0], targetId)
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
- closestPeersToDestination,
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 getClosestConnections(referenceId: DhtAddress, limit: number): PeerDescriptor[] {
221
- const connectedPeers = Array.from(this.config.connections.values())
222
- const closestPeers = new SortedContactList<DhtNodeRpcRemote>({
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
- closestPeers.addContacts(connectedPeers)
228
- return closestPeers.getClosestContacts(limit).map((peer) => peer.getPeerDescriptor())
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
- nodes: PeerDescriptor[],
69
+ closestConnectedNodes: PeerDescriptor[],
70
70
  dataEntries: DataEntry[],
71
71
  noCloserNodesFound: boolean
72
72
  ) => {
73
- this.onResponseReceived(sourceId, routingPath, nodes, dataEntries, noCloserNodesFound)
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
- nodes: PeerDescriptor[],
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
- nodes.forEach((descriptor: PeerDescriptor) => {
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
- if (!this.timeoutTask && !this.completionEventEmitted) {
202
- this.timeoutTask = setTimeout(() => {
203
- if (!this.completionEventEmitted) {
204
- this.emit('completed')
205
- this.completionEventEmitted = true
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.closestConnectedPeers, report.dataEntries, report.noCloserNodesFound)
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
- closestNodes: PeerDescriptor[],
16
+ closestConnectedNodes: PeerDescriptor[],
17
17
  dataEntries: DataEntry[],
18
18
  noCloserNodesFound: boolean
19
19
  ): void {
20
20
  const report: RecursiveOperationResponse = {
21
21
  routingPath,
22
- closestConnectedPeers: closestNodes,
22
+ closestConnectedNodes,
23
23
  dataEntries,
24
24
  noCloserNodesFound
25
25
  }