Skip to content

Commit 57dbdaa

Browse files
authored
fix: empty routing table on stop (#3166)
When the node is stopped, empty the routing table
1 parent ec73d59 commit 57dbdaa

File tree

3 files changed

+83
-27
lines changed

3 files changed

+83
-27
lines changed

packages/kad-dht/src/routing-table/index.ts

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
9797
kadBucketEvents: CounterGroup<'ping_old_contact' | 'ping_old_contact_error' | 'ping_new_contact' | 'ping_new_contact_error' | 'peer_added' | 'peer_removed'>
9898
}
9999

100+
private shutdownController: AbortController
101+
100102
constructor (components: RoutingTableComponents, init: RoutingTableInit) {
101103
super()
102104

@@ -114,6 +116,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
114116
this.peerRemoved = this.peerRemoved.bind(this)
115117
this.populateFromDatastoreOnStart = init.populateFromDatastoreOnStart ?? POPULATE_FROM_DATASTORE_ON_START
116118
this.populateFromDatastoreLimit = init.populateFromDatastoreLimit ?? POPULATE_FROM_DATASTORE_LIMIT
119+
this.shutdownController = new AbortController()
117120

118121
this.pingOldContactQueue = new PeerQueue({
119122
concurrency: init.pingOldContactConcurrency ?? PING_OLD_CONTACT_CONCURRENCY,
@@ -185,49 +188,62 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
185188

186189
this.running = true
187190

191+
this.shutdownController = new AbortController()
188192
await start(this.closestPeerTagger, this.kb)
189-
await this.kb.addSelfPeer(this.components.peerId)
190193
}
191194

192195
async afterStart (): Promise<void> {
196+
let peerStorePeers = 0
197+
193198
// do this async to not block startup but iterate serially to not overwhelm
194199
// the ping queue
195200
Promise.resolve().then(async () => {
196201
if (!this.populateFromDatastoreOnStart) {
197202
return
198203
}
199204

200-
let peerStorePeers = 0
201-
202-
// add existing peers from the peer store to routing table
203-
for (const peer of await this.components.peerStore.all({
204-
filters: [(peer) => {
205-
return peer.protocols.includes(this.protocol) && peer.tags.has(KAD_PEER_TAG_NAME)
206-
}],
207-
limit: this.populateFromDatastoreLimit
208-
})) {
209-
if (!this.running) {
210-
// bail if we've been shut down
211-
return
212-
}
205+
const signal = anySignal([
206+
this.shutdownController.signal,
207+
AbortSignal.timeout(20_000)
208+
])
209+
setMaxListeners(Infinity, signal)
210+
211+
try {
212+
// add existing peers from the peer store to routing table
213+
for (const peer of await this.components.peerStore.all({
214+
filters: [(peer) => {
215+
return peer.protocols.includes(this.protocol) && peer.tags.has(KAD_PEER_TAG_NAME)
216+
}],
217+
limit: this.populateFromDatastoreLimit,
218+
signal
219+
})) {
220+
if (!this.running) {
221+
// bail if we've been shut down
222+
return
223+
}
213224

214-
try {
215-
await this.add(peer.id)
216-
peerStorePeers++
217-
} catch (err) {
218-
this.log('failed to add peer %p to routing table, removing kad-dht peer tags - %e')
219-
await this.components.peerStore.merge(peer.id, {
220-
tags: {
221-
[this.peerTagName]: undefined
222-
}
223-
})
225+
try {
226+
await this.add(peer.id, {
227+
signal
228+
})
229+
peerStorePeers++
230+
} catch (err) {
231+
this.log('failed to add peer %p to routing table, removing kad-dht peer tags - %e')
232+
await this.components.peerStore.merge(peer.id, {
233+
tags: {
234+
[this.peerTagName]: undefined
235+
}
236+
})
237+
}
224238
}
239+
} finally {
240+
signal.clear()
225241
}
226242

227243
this.log('added %d peer store peers to the routing table', peerStorePeers)
228244
})
229245
.catch(err => {
230-
this.log.error('error adding peer store peers to the routing table %e', err)
246+
this.log.error('error adding %d, peer store peers to the routing table - %e', peerStorePeers, err)
231247
})
232248
}
233249

@@ -236,6 +252,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
236252
await stop(this.closestPeerTagger, this.kb)
237253
this.pingOldContactQueue.abort()
238254
this.pingNewContactQueue.abort()
255+
this.shutdownController.abort()
239256
}
240257

241258
private async peerAdded (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void> {
@@ -310,7 +327,11 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
310327

311328
const result = await this.pingOldContactQueue.add(async (options) => {
312329
const signal = this.pingOldContactTimeout.getTimeoutSignal()
313-
const signals = anySignal([signal, options?.signal])
330+
const signals = anySignal([
331+
signal,
332+
this.shutdownController.signal,
333+
options?.signal
334+
])
314335
setMaxListeners(Infinity, signal, signals)
315336

316337
try {
@@ -342,7 +363,11 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
342363

343364
async verifyNewContact (contact: Peer, options?: AbortOptions): Promise<boolean> {
344365
const signal = this.pingNewContactTimeout.getTimeoutSignal()
345-
const signals = anySignal([signal, options?.signal])
366+
const signals = anySignal([
367+
signal,
368+
this.shutdownController.signal,
369+
options?.signal
370+
])
346371
setMaxListeners(Infinity, signal, signals)
347372

348373
try {

packages/kad-dht/src/routing-table/k-bucket.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface OnMoveCallback {
4646
}
4747

4848
export interface KBucketComponents {
49+
peerId: PeerId
4950
metrics?: Metrics
5051
}
5152

@@ -134,6 +135,7 @@ export function isLeafBucket (obj: any): obj is LeafBucket {
134135
* configurable prefix length, bucket split threshold and size.
135136
*/
136137
export class KBucket {
138+
private readonly peerId: PeerId
137139
public root: Bucket
138140
public localPeer?: Peer
139141
private readonly prefixLength: number
@@ -149,6 +151,7 @@ export class KBucket {
149151
private readonly addingPeerMap: PeerMap<Promise<void>>
150152

151153
constructor (components: KBucketComponents, options: KBucketOptions) {
154+
this.peerId = components.peerId
152155
this.prefixLength = options.prefixLength ?? PREFIX_LENGTH
153156
this.kBucketSize = options.kBucketSize ?? KBUCKET_SIZE
154157
this.splitThreshold = options.splitThreshold ?? this.kBucketSize
@@ -170,8 +173,18 @@ export class KBucket {
170173
}
171174
}
172175

176+
async start (): Promise<void> {
177+
await this.addSelfPeer(this.peerId)
178+
}
179+
173180
stop (): void {
174181
this.addingPeerMap.clear()
182+
183+
this.root = {
184+
prefix: '',
185+
depth: 0,
186+
peers: []
187+
}
175188
}
176189

177190
async addSelfPeer (peerId: PeerId, options?: AbortOptions): Promise<void> {

packages/kad-dht/test/routing-table.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,24 @@ describe('Routing Table', () => {
549549
}
550550
})
551551

552+
it('should remove peers on stop', async function () {
553+
this.timeout(20 * 1000)
554+
555+
const ids = await createPeerIdsWithPrivateKey(20)
556+
557+
await Promise.all(
558+
Array.from({ length: 1000 }).map(async () => { await table.add(ids[random(ids.length - 1)].peerId) })
559+
)
560+
561+
expect(table.kb.root).to.have.property('peers').that.has.lengthOf(20)
562+
563+
await table.stop()
564+
565+
expect(table.kb.root).to.have.property('depth', 0)
566+
expect(table.kb.root).to.have.property('prefix', '')
567+
expect(table.kb.root).to.have.property('peers').that.is.empty()
568+
})
569+
552570
describe('max size', () => {
553571
it('should constrain size to 10', async () => {
554572
const prefixLength = 8

0 commit comments

Comments
 (0)