@libp2p/kad-dht 9.1.0 → 9.1.1
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/dist/index.min.js +11 -11
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +3 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/kad-dht.d.ts.map +1 -1
- package/dist/src/kad-dht.js +16 -3
- package/dist/src/kad-dht.js.map +1 -1
- package/dist/src/peer-routing/index.d.ts +2 -2
- package/dist/src/peer-routing/index.d.ts.map +1 -1
- package/dist/src/query/manager.d.ts +9 -1
- package/dist/src/query/manager.d.ts.map +1 -1
- package/dist/src/query/manager.js +14 -0
- package/dist/src/query/manager.js.map +1 -1
- package/dist/src/query-self.d.ts +10 -1
- package/dist/src/query-self.d.ts.map +1 -1
- package/dist/src/query-self.js +62 -14
- package/dist/src/query-self.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +4 -1
- package/src/index.ts +15 -0
- package/src/kad-dht.ts +19 -3
- package/src/peer-routing/index.ts +2 -2
- package/src/query/manager.ts +28 -1
- package/src/query-self.ts +106 -36
|
@@ -13,9 +13,9 @@ import { Libp2pRecord } from '@libp2p/record'
|
|
|
13
13
|
import { logger } from '@libp2p/logger'
|
|
14
14
|
import { keys } from '@libp2p/crypto'
|
|
15
15
|
import { peerIdFromKeys } from '@libp2p/peer-id'
|
|
16
|
-
import type { DHTRecord, DialingPeerEvent, FinalPeerEvent, QueryEvent,
|
|
16
|
+
import type { DHTRecord, DialingPeerEvent, FinalPeerEvent, QueryEvent, Validators } from '@libp2p/interface-dht'
|
|
17
17
|
import type { RoutingTable } from '../routing-table/index.js'
|
|
18
|
-
import type { QueryManager } from '../query/manager.js'
|
|
18
|
+
import type { QueryManager, QueryOptions } from '../query/manager.js'
|
|
19
19
|
import type { Network } from '../network.js'
|
|
20
20
|
import type { Logger } from '@libp2p/logger'
|
|
21
21
|
import type { AbortOptions } from '@libp2p/interfaces'
|
package/src/query/manager.ts
CHANGED
|
@@ -11,9 +11,12 @@ import { logger } from '@libp2p/logger'
|
|
|
11
11
|
import type { PeerId } from '@libp2p/interface-peer-id'
|
|
12
12
|
import type { Startable } from '@libp2p/interfaces/startable'
|
|
13
13
|
import type { QueryFunc } from './types.js'
|
|
14
|
-
import type { QueryEvent
|
|
14
|
+
import type { QueryEvent } from '@libp2p/interface-dht'
|
|
15
15
|
import { PeerSet } from '@libp2p/peer-collections'
|
|
16
16
|
import type { Metric, Metrics } from '@libp2p/interface-metrics'
|
|
17
|
+
import type { DeferredPromise } from 'p-defer'
|
|
18
|
+
import type { AbortOptions } from '@libp2p/interfaces'
|
|
19
|
+
import { AbortError } from '@libp2p/interfaces/errors'
|
|
17
20
|
|
|
18
21
|
export interface CleanUpEvents {
|
|
19
22
|
'cleanup': CustomEvent
|
|
@@ -23,6 +26,7 @@ export interface QueryManagerInit {
|
|
|
23
26
|
lan?: boolean
|
|
24
27
|
disjointPaths?: number
|
|
25
28
|
alpha?: number
|
|
29
|
+
initialQuerySelfHasRun: DeferredPromise<void>
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export interface QueryManagerComponents {
|
|
@@ -30,6 +34,11 @@ export interface QueryManagerComponents {
|
|
|
30
34
|
metrics?: Metrics
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
export interface QueryOptions extends AbortOptions {
|
|
38
|
+
queryFuncTimeout?: number
|
|
39
|
+
isSelfQuery?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
/**
|
|
34
43
|
* Keeps track of all running queries
|
|
35
44
|
*/
|
|
@@ -46,6 +55,8 @@ export class QueryManager implements Startable {
|
|
|
46
55
|
queryTime: Metric
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
private initialQuerySelfHasRun?: DeferredPromise<void>
|
|
59
|
+
|
|
49
60
|
constructor (components: QueryManagerComponents, init: QueryManagerInit) {
|
|
50
61
|
const { lan = false, disjointPaths = K, alpha = ALPHA } = init
|
|
51
62
|
|
|
@@ -55,6 +66,7 @@ export class QueryManager implements Startable {
|
|
|
55
66
|
this.alpha = alpha ?? ALPHA
|
|
56
67
|
this.lan = lan
|
|
57
68
|
this.queries = 0
|
|
69
|
+
this.initialQuerySelfHasRun = init.initialQuerySelfHasRun
|
|
58
70
|
|
|
59
71
|
// allow us to stop queries on shut down
|
|
60
72
|
this.shutDownController = new AbortController()
|
|
@@ -131,6 +143,21 @@ export class QueryManager implements Startable {
|
|
|
131
143
|
const cleanUp = new EventEmitter<CleanUpEvents>()
|
|
132
144
|
|
|
133
145
|
try {
|
|
146
|
+
if (options.isSelfQuery !== true && this.initialQuerySelfHasRun != null) {
|
|
147
|
+
log('waiting for initial query-self query before continuing')
|
|
148
|
+
|
|
149
|
+
await Promise.race([
|
|
150
|
+
new Promise((resolve, reject) => {
|
|
151
|
+
signal.addEventListener('abort', () => {
|
|
152
|
+
reject(new AbortError('Query was aborted before self-query ran'))
|
|
153
|
+
})
|
|
154
|
+
}),
|
|
155
|
+
this.initialQuerySelfHasRun.promise
|
|
156
|
+
])
|
|
157
|
+
|
|
158
|
+
this.initialQuerySelfHasRun = undefined
|
|
159
|
+
}
|
|
160
|
+
|
|
134
161
|
log('query:start')
|
|
135
162
|
this.queries++
|
|
136
163
|
this.metrics?.runningQueries.update(this.queries)
|
package/src/query-self.ts
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import { setMaxListeners } from 'events'
|
|
2
2
|
import take from 'it-take'
|
|
3
3
|
import length from 'it-length'
|
|
4
|
-
import { QUERY_SELF_INTERVAL, QUERY_SELF_TIMEOUT, K } from './constants.js'
|
|
4
|
+
import { QUERY_SELF_INTERVAL, QUERY_SELF_TIMEOUT, K, QUERY_SELF_INITIAL_INTERVAL } from './constants.js'
|
|
5
5
|
import { anySignal } from 'any-signal'
|
|
6
6
|
import { logger, Logger } from '@libp2p/logger'
|
|
7
7
|
import type { PeerRouting } from './peer-routing/index.js'
|
|
8
8
|
import type { Startable } from '@libp2p/interfaces/startable'
|
|
9
9
|
import { pipe } from 'it-pipe'
|
|
10
10
|
import type { KadDHTComponents } from './index.js'
|
|
11
|
+
import type { DeferredPromise } from 'p-defer'
|
|
12
|
+
import type { RoutingTable } from './routing-table/index.js'
|
|
11
13
|
|
|
12
14
|
export interface QuerySelfInit {
|
|
13
15
|
lan: boolean
|
|
14
16
|
peerRouting: PeerRouting
|
|
17
|
+
routingTable: RoutingTable
|
|
15
18
|
count?: number
|
|
16
19
|
interval?: number
|
|
20
|
+
initialInterval?: number
|
|
17
21
|
queryTimeout?: number
|
|
22
|
+
initialQuerySelfHasRun: DeferredPromise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
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
|
+
}
|
|
18
37
|
}
|
|
19
38
|
|
|
20
39
|
/**
|
|
@@ -24,40 +43,51 @@ export class QuerySelf implements Startable {
|
|
|
24
43
|
private readonly log: Logger
|
|
25
44
|
private readonly components: KadDHTComponents
|
|
26
45
|
private readonly peerRouting: PeerRouting
|
|
46
|
+
private readonly routingTable: RoutingTable
|
|
27
47
|
private readonly count: number
|
|
28
48
|
private readonly interval: number
|
|
49
|
+
private readonly initialInterval: number
|
|
29
50
|
private readonly queryTimeout: number
|
|
51
|
+
private started: boolean
|
|
30
52
|
private running: boolean
|
|
31
53
|
private timeoutId?: NodeJS.Timer
|
|
32
54
|
private controller?: AbortController
|
|
55
|
+
private initialQuerySelfHasRun?: DeferredPromise<void>
|
|
33
56
|
|
|
34
57
|
constructor (components: KadDHTComponents, init: QuerySelfInit) {
|
|
35
|
-
const { peerRouting, lan, count, interval, queryTimeout } = init
|
|
58
|
+
const { peerRouting, lan, count, interval, queryTimeout, routingTable } = init
|
|
36
59
|
|
|
37
60
|
this.components = components
|
|
38
61
|
this.log = logger(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:query-self`)
|
|
39
62
|
this.running = false
|
|
63
|
+
this.started = false
|
|
40
64
|
this.peerRouting = peerRouting
|
|
65
|
+
this.routingTable = routingTable
|
|
41
66
|
this.count = count ?? K
|
|
42
67
|
this.interval = interval ?? QUERY_SELF_INTERVAL
|
|
68
|
+
this.initialInterval = init.initialInterval ?? QUERY_SELF_INITIAL_INTERVAL
|
|
43
69
|
this.queryTimeout = queryTimeout ?? QUERY_SELF_TIMEOUT
|
|
70
|
+
this.initialQuerySelfHasRun = init.initialQuerySelfHasRun
|
|
71
|
+
|
|
72
|
+
this.querySelf = debounce(this.querySelf.bind(this), 100)
|
|
44
73
|
}
|
|
45
74
|
|
|
46
75
|
isStarted (): boolean {
|
|
47
|
-
return this.
|
|
76
|
+
return this.started
|
|
48
77
|
}
|
|
49
78
|
|
|
50
79
|
async start (): Promise<void> {
|
|
51
|
-
if (this.
|
|
80
|
+
if (this.started) {
|
|
52
81
|
return
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
84
|
+
this.started = true
|
|
85
|
+
clearTimeout(this.timeoutId)
|
|
86
|
+
this.timeoutId = setTimeout(this.querySelf.bind(this), this.initialInterval)
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
async stop (): Promise<void> {
|
|
60
|
-
this.
|
|
90
|
+
this.started = false
|
|
61
91
|
|
|
62
92
|
if (this.timeoutId != null) {
|
|
63
93
|
clearTimeout(this.timeoutId)
|
|
@@ -68,36 +98,76 @@ export class QuerySelf implements Startable {
|
|
|
68
98
|
}
|
|
69
99
|
}
|
|
70
100
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
|
|
101
|
+
querySelf (): void {
|
|
102
|
+
if (!this.started) {
|
|
103
|
+
this.log('skip self-query because we are not started')
|
|
104
|
+
return
|
|
105
|
+
}
|
|
75
106
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}),
|
|
88
|
-
(source) => take(source, this.count),
|
|
89
|
-
async (source) => await length(source)
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
this.log('query ran successfully - found %d peers', found)
|
|
93
|
-
} catch (err: any) {
|
|
94
|
-
this.log('query error', err)
|
|
95
|
-
} finally {
|
|
96
|
-
this.timeoutId = setTimeout(this._querySelf.bind(this), this.interval)
|
|
97
|
-
signal.clear()
|
|
107
|
+
if (this.running) {
|
|
108
|
+
this.log('skip self-query because we are already running, will run again in %dms', this.interval)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.routingTable.size === 0) {
|
|
113
|
+
let nextInterval = this.interval
|
|
114
|
+
|
|
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
|
|
98
118
|
}
|
|
99
|
-
|
|
100
|
-
this.log('query
|
|
101
|
-
|
|
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
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.running = true
|
|
127
|
+
|
|
128
|
+
Promise.resolve()
|
|
129
|
+
.then(async () => {
|
|
130
|
+
this.controller = new AbortController()
|
|
131
|
+
const signal = anySignal([this.controller.signal, AbortSignal.timeout(this.queryTimeout)])
|
|
132
|
+
|
|
133
|
+
// this controller will get used for lots of dial attempts so make sure we don't cause warnings to be logged
|
|
134
|
+
try {
|
|
135
|
+
if (setMaxListeners != null) {
|
|
136
|
+
setMaxListeners(Infinity, signal)
|
|
137
|
+
}
|
|
138
|
+
} catch {} // fails on node < 15.4
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
this.log('run self-query, look for %d peers timing out after %dms', this.count, this.queryTimeout)
|
|
142
|
+
|
|
143
|
+
const found = await pipe(
|
|
144
|
+
this.peerRouting.getClosestPeers(this.components.peerId.toBytes(), {
|
|
145
|
+
signal,
|
|
146
|
+
isSelfQuery: true
|
|
147
|
+
}),
|
|
148
|
+
(source) => take(source, this.count),
|
|
149
|
+
async (source) => await length(source)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
this.log('self-query ran successfully - found %d peers', found)
|
|
153
|
+
|
|
154
|
+
if (this.initialQuerySelfHasRun != null) {
|
|
155
|
+
this.initialQuerySelfHasRun.resolve()
|
|
156
|
+
this.initialQuerySelfHasRun = undefined
|
|
157
|
+
}
|
|
158
|
+
} catch (err: any) {
|
|
159
|
+
this.log.error('self-query error', err)
|
|
160
|
+
} finally {
|
|
161
|
+
signal.clear()
|
|
162
|
+
}
|
|
163
|
+
}).catch(err => {
|
|
164
|
+
this.log('self-query error', err)
|
|
165
|
+
}).finally(() => {
|
|
166
|
+
this.running = false
|
|
167
|
+
|
|
168
|
+
this.log('running self-query again in %dms', this.interval)
|
|
169
|
+
clearTimeout(this.timeoutId)
|
|
170
|
+
this.timeoutId = setTimeout(this.querySelf.bind(this), this.interval)
|
|
171
|
+
})
|
|
102
172
|
}
|
|
103
173
|
}
|