Skip to content

Commit 66ee265

Browse files
committed
feat: ensure re-dials are sorted by success rate (libp2p#2150)
1 parent dce8861 commit 66ee265

File tree

2 files changed

+137
-3
lines changed

2 files changed

+137
-3
lines changed

packages/libp2p/src/connection-manager/dial-queue.ts

+41-2
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,7 @@ export class DialQueue {
279279
return signal
280280
}
281281

282-
// eslint-disable-next-line complexity
283-
private async calculateMultiaddrs (peerId?: PeerId, addrs: Address[] = [], options: DialOptions = {}): Promise<Address[]> {
282+
async calculateMultiaddrs (peerId?: PeerId, addrs: Address[] = [], options: DialOptions = {}): Promise<Address[]> {
284283
// if a peer id or multiaddr(s) with a peer id, make sure it isn't our peer id and that we are allowed to dial it
285284
if (peerId != null) {
286285
if (this.peerId.equals(peerId)) {
@@ -380,6 +379,8 @@ export class DialQueue {
380379
// append peer id to multiaddr if it is not already present
381380
if (addressPeerId !== peerId.toString()) {
382381
return {
382+
lastFailure: addr.lastFailure,
383+
lastSuccess: addr.lastSuccess,
383384
multiaddr: addr.multiaddr.encapsulate(peerIdMultiaddr),
384385
isCertified: addr.isCertified
385386
}
@@ -406,9 +407,47 @@ export class DialQueue {
406407
throw new CodeError('The connection gater denied all addresses in the dial request', codes.ERR_NO_VALID_ADDRESSES)
407408
}
408409

410+
sortedGatedAddrs.sort((a, b) => this.sortMultiaddrsByDialability(a, b))
411+
409412
return sortedGatedAddrs
410413
}
411414

415+
private sortMultiaddrsByDialability (a: Address, b: Address): number {
416+
if (a.lastSuccess !== undefined && b.lastSuccess !== undefined) {
417+
if (a.lastSuccess > b.lastSuccess) {
418+
return -1
419+
} else {
420+
return 1
421+
}
422+
}
423+
424+
if (a.lastFailure !== undefined && b.lastFailure !== undefined) {
425+
if (a.lastFailure > b.lastFailure) {
426+
return 1
427+
} else {
428+
return -1
429+
}
430+
}
431+
432+
if (a.lastSuccess !== undefined) {
433+
return -1
434+
}
435+
436+
if (b.lastSuccess !== undefined) {
437+
return 1
438+
}
439+
440+
if (a.lastFailure !== undefined) {
441+
return 1
442+
}
443+
444+
if (b.lastFailure !== undefined) {
445+
return -1
446+
}
447+
448+
return 0
449+
}
450+
412451
private async performDial (pendingDial: PendingDialInternal, options: DialOptions = {}): Promise<Connection> {
413452
const dialAbortControllers: Array<(AbortController | undefined)> = pendingDial.multiaddrs.map(() => new AbortController())
414453

packages/libp2p/test/connection-manager/dial-queue.spec.ts

+96-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { DialQueue } from '../../src/connection-manager/dial-queue.js'
1212
import type { Connection } from '@libp2p/interface/connection'
1313
import type { ConnectionGater } from '@libp2p/interface/connection-gater'
1414
import type { PeerId } from '@libp2p/interface/peer-id'
15-
import type { PeerStore } from '@libp2p/interface/peer-store'
15+
import type { Address, PeerStore } from '@libp2p/interface/peer-store'
1616
import type { Transport } from '@libp2p/interface/transport'
1717
import type { TransportManager } from '@libp2p/interface-internal/transport-manager'
1818

@@ -330,4 +330,99 @@ describe('dial queue', () => {
330330

331331
resolvers.delete('dnsaddr')
332332
})
333+
334+
const ensureDialOrder = async (peerId: PeerId, orderedAddresses: Address[], testType: 'lastFailure' | 'lastSuccess'): Promise<void> => {
335+
components.transportManager.transportForMultiaddr.returns(stubInterface<Transport>())
336+
337+
const duplicatedOrderedAddresses = [...orderedAddresses]
338+
339+
const dialer = new DialQueue(components, {
340+
maxParallelDials: 1
341+
})
342+
343+
// reverse order of addresses
344+
orderedAddresses.reverse()
345+
346+
const sortedAddresses = await dialer.calculateMultiaddrs(peerId, orderedAddresses)
347+
348+
expect(sortedAddresses.map(address => address[testType])).to.deep.equal(duplicatedOrderedAddresses.map(address => address[testType]))
349+
}
350+
351+
it('should sort already succesfully dialled addresses by most recently sucessful', async () => {
352+
const peerId = await createEd25519PeerId()
353+
const orderedAddresses: Address[] = [
354+
{
355+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1231'),
356+
lastSuccess: Date.now()
357+
},
358+
{
359+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1232'),
360+
lastSuccess: Date.now() - 1000
361+
},
362+
{
363+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234'),
364+
lastSuccess: Date.now() - 2000
365+
}
366+
]
367+
368+
await ensureDialOrder(peerId, orderedAddresses, 'lastSuccess')
369+
})
370+
371+
it('should sort addresses which failed to be dialled by least recent failure', async () => {
372+
const peerId = await createEd25519PeerId()
373+
const orderedAddresses: Address[] = [
374+
{
375+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1231'),
376+
lastFailure: Date.now() - 2000
377+
},
378+
{
379+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1232'),
380+
lastFailure: Date.now() - 1000
381+
},
382+
{
383+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234'),
384+
lastFailure: Date.now()
385+
}
386+
]
387+
388+
await ensureDialOrder(peerId, orderedAddresses, 'lastFailure')
389+
})
390+
391+
it('should dial new addresses before addresses which have failed to be dialled', async () => {
392+
const peerId = await createEd25519PeerId()
393+
const orderedAddresses: Address[] = [
394+
{
395+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1231')
396+
},
397+
{
398+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1232'),
399+
lastFailure: Date.now() - 2000
400+
},
401+
{
402+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234'),
403+
lastFailure: Date.now() - 1000
404+
}
405+
]
406+
407+
await ensureDialOrder(peerId, orderedAddresses, 'lastFailure')
408+
})
409+
410+
it('should dial succesfull dialled addresses first, then new addresses, then failed addresses', async () => {
411+
const peerId = await createEd25519PeerId()
412+
const orderedAddresses: Address[] = [
413+
{
414+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1231'),
415+
lastSuccess: Date.now() - 5000
416+
},
417+
{
418+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1232')
419+
},
420+
{
421+
multiaddr: multiaddr('/ip4/127.0.0.1/tcp/1234'),
422+
lastFailure: Date.now() - 6000
423+
}
424+
]
425+
426+
await ensureDialOrder(peerId, orderedAddresses, 'lastSuccess')
427+
})
333428
})

0 commit comments

Comments
 (0)