@@ -4,10 +4,12 @@ import { anySignal } from 'any-signal'
44import length from 'it-length'
55import { pipe } from 'it-pipe'
66import take from 'it-take'
7+ import pDefer from 'p-defer'
8+ import { pEvent } from 'p-event'
79import { QUERY_SELF_INTERVAL , QUERY_SELF_TIMEOUT , K , QUERY_SELF_INITIAL_INTERVAL } from './constants.js'
8- import type { KadDHTComponents } from './index.js'
910import type { PeerRouting } from './peer-routing/index.js'
1011import type { RoutingTable } from './routing-table/index.js'
12+ import type { PeerId } from '@libp2p/interface-peer-id'
1113import type { Startable } from '@libp2p/interfaces/startable'
1214import type { DeferredPromise } from 'p-defer'
1315
@@ -22,44 +24,33 @@ export interface QuerySelfInit {
2224 initialQuerySelfHasRun : DeferredPromise < void >
2325}
2426
25- function debounce ( func : ( ) => void , wait : number ) : ( ) => void {
26- let timeout : ReturnType < typeof setTimeout > | undefined
27-
28- return function ( ) {
29- const later = function ( ) : void {
30- timeout = undefined
31- func ( )
32- }
33-
34- clearTimeout ( timeout )
35- timeout = setTimeout ( later , wait )
36- }
27+ export interface QuerySelfComponents {
28+ peerId : PeerId
3729}
3830
3931/**
4032 * Receives notifications of new peers joining the network that support the DHT protocol
4133 */
4234export class QuerySelf implements Startable {
4335 private readonly log : Logger
44- private readonly components : KadDHTComponents
36+ private readonly components : QuerySelfComponents
4537 private readonly peerRouting : PeerRouting
4638 private readonly routingTable : RoutingTable
4739 private readonly count : number
4840 private readonly interval : number
4941 private readonly initialInterval : number
5042 private readonly queryTimeout : number
5143 private started : boolean
52- private running : boolean
5344 private timeoutId ?: NodeJS . Timer
5445 private controller ?: AbortController
5546 private initialQuerySelfHasRun ?: DeferredPromise < void >
47+ private querySelfPromise ?: DeferredPromise < void >
5648
57- constructor ( components : KadDHTComponents , init : QuerySelfInit ) {
49+ constructor ( components : QuerySelfComponents , init : QuerySelfInit ) {
5850 const { peerRouting, lan, count, interval, queryTimeout, routingTable } = init
5951
6052 this . components = components
6153 this . log = logger ( `libp2p:kad-dht:${ lan ? 'lan' : 'wan' } :query-self` )
62- this . running = false
6354 this . started = false
6455 this . peerRouting = peerRouting
6556 this . routingTable = routingTable
@@ -68,25 +59,28 @@ export class QuerySelf implements Startable {
6859 this . initialInterval = init . initialInterval ?? QUERY_SELF_INITIAL_INTERVAL
6960 this . queryTimeout = queryTimeout ?? QUERY_SELF_TIMEOUT
7061 this . initialQuerySelfHasRun = init . initialQuerySelfHasRun
71-
72- this . querySelf = debounce ( this . querySelf . bind ( this ) , 100 )
7362 }
7463
7564 isStarted ( ) : boolean {
7665 return this . started
7766 }
7867
79- async start ( ) : Promise < void > {
68+ start ( ) : void {
8069 if ( this . started ) {
8170 return
8271 }
8372
8473 this . started = true
8574 clearTimeout ( this . timeoutId )
86- this . timeoutId = setTimeout ( this . querySelf . bind ( this ) , this . initialInterval )
75+ this . timeoutId = setTimeout ( ( ) => {
76+ this . querySelf ( )
77+ . catch ( err => {
78+ this . log . error ( 'error running self-query' , err )
79+ } )
80+ } , this . initialInterval )
8781 }
8882
89- async stop ( ) : Promise < void > {
83+ stop ( ) : void {
9084 this . started = false
9185
9286 if ( this . timeoutId != null ) {
@@ -98,84 +92,68 @@ export class QuerySelf implements Startable {
9892 }
9993 }
10094
101- querySelf ( ) : void {
95+ async querySelf ( ) : Promise < void > {
10296 if ( ! this . started ) {
10397 this . log ( 'skip self-query because we are not started' )
10498 return
10599 }
106100
107- if ( this . running ) {
108- this . log ( 'skip self-query because we are already running, will run again in %dms' , this . interval )
109- return
101+ if ( this . querySelfPromise != null ) {
102+ this . log ( 'joining existing self query' )
103+ return this . querySelfPromise . promise
110104 }
111105
112- if ( this . routingTable . size === 0 ) {
113- let nextInterval = this . interval
106+ this . querySelfPromise = pDefer ( )
114107
115- if ( this . initialQuerySelfHasRun != null ) {
116- // if we've not yet run the first self query, shorten the interval until we try again
117- nextInterval = this . initialInterval
118- }
119-
120- this . log ( 'skip self-query because routing table is empty, will run again in %dms' , nextInterval )
121- clearTimeout ( this . timeoutId )
122- this . timeoutId = setTimeout ( this . querySelf . bind ( this ) , nextInterval )
123- return
108+ if ( this . routingTable . size === 0 ) {
109+ // wait to discover at least one DHT peer
110+ await pEvent ( this . routingTable , 'peer:add' )
124111 }
125112
126- this . running = true
113+ if ( this . started ) {
114+ this . controller = new AbortController ( )
115+ const signal = anySignal ( [ this . controller . signal , AbortSignal . timeout ( this . queryTimeout ) ] )
127116
128- Promise . resolve ( )
129- . then ( async ( ) => {
130- if ( ! this . started ) {
131- this . log ( 'not running self-query - node stopped before query started' )
132- return
117+ // this controller will get used for lots of dial attempts so make sure we don't cause warnings to be logged
118+ try {
119+ if ( setMaxListeners != null ) {
120+ setMaxListeners ( Infinity , signal )
133121 }
122+ } catch { } // fails on node < 15.4
134123
135- this . controller = new AbortController ( )
136- const signal = anySignal ( [ this . controller . signal , AbortSignal . timeout ( this . queryTimeout ) ] )
137-
138- // this controller will get used for lots of dial attempts so make sure we don't cause warnings to be logged
139- try {
140- if ( setMaxListeners != null ) {
141- setMaxListeners ( Infinity , signal )
142- }
143- } catch { } // fails on node < 15.4
144-
145- try {
146- this . log ( 'run self-query, look for %d peers timing out after %dms' , this . count , this . queryTimeout )
147-
148- const found = await pipe (
149- this . peerRouting . getClosestPeers ( this . components . peerId . toBytes ( ) , {
150- signal,
151- isSelfQuery : true
152- } ) ,
153- ( source ) => take ( source , this . count ) ,
154- async ( source ) => length ( source )
155- )
156-
157- this . log ( 'self-query ran successfully - found %d peers' , found )
158-
159- if ( this . initialQuerySelfHasRun != null ) {
160- this . initialQuerySelfHasRun . resolve ( )
161- this . initialQuerySelfHasRun = undefined
162- }
163- } catch ( err : any ) {
164- this . log . error ( 'self-query error' , err )
165- } finally {
166- signal . clear ( )
167- }
168- } ) . catch ( err => {
169- this . log ( 'self-query error' , err )
170- } ) . finally ( ( ) => {
171- this . running = false
124+ try {
125+ this . log ( 'run self-query, look for %d peers timing out after %dms' , this . count , this . queryTimeout )
126+
127+ const found = await pipe (
128+ this . peerRouting . getClosestPeers ( this . components . peerId . toBytes ( ) , {
129+ signal,
130+ isSelfQuery : true
131+ } ) ,
132+ ( source ) => take ( source , this . count ) ,
133+ async ( source ) => length ( source )
134+ )
172135
173- clearTimeout ( this . timeoutId )
136+ this . log ( 'self-query ran successfully - found %d peers' , found )
174137
175- if ( this . started ) {
176- this . log ( 'running self-query again in %dms' , this . interval )
177- this . timeoutId = setTimeout ( this . querySelf . bind ( this ) , this . interval )
138+ if ( this . initialQuerySelfHasRun != null ) {
139+ this . initialQuerySelfHasRun . resolve ( )
140+ this . initialQuerySelfHasRun = undefined
178141 }
179- } )
142+ } catch ( err : any ) {
143+ this . log . error ( 'self-query error' , err )
144+ } finally {
145+ signal . clear ( )
146+ }
147+ }
148+
149+ this . querySelfPromise . resolve ( )
150+ this . querySelfPromise = undefined
151+
152+ this . timeoutId = setTimeout ( ( ) => {
153+ this . querySelf ( )
154+ . catch ( err => {
155+ this . log . error ( 'error running self-query' , err )
156+ } )
157+ } , this . interval )
180158 }
181159}
0 commit comments