@@ -12,6 +12,7 @@ const log = debug("waku:connection-manager");
12
12
13
13
export const DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED = 1 ;
14
14
export const DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER = 3 ;
15
+ export const DEFAULT_MAX_PARALLEL_DIALS = 3 ;
15
16
16
17
export class ConnectionManager {
17
18
private static instances = new Map < string , ConnectionManager > ( ) ;
@@ -21,6 +22,9 @@ export class ConnectionManager {
21
22
private dialAttemptsForPeer : Map < string , number > = new Map ( ) ;
22
23
private dialErrorsForPeer : Map < string , any > = new Map ( ) ;
23
24
25
+ private currentActiveDialCount = 0 ;
26
+ private pendingPeerDialQueue : Array < PeerId > = [ ] ;
27
+
24
28
public static create (
25
29
peerId : string ,
26
30
libp2p : Libp2p ,
@@ -52,6 +56,7 @@ export class ConnectionManager {
52
56
this . options = {
53
57
maxDialAttemptsForPeer : DEFAULT_MAX_DIAL_ATTEMPTS_FOR_PEER ,
54
58
maxBootstrapPeersAllowed : DEFAULT_MAX_BOOTSTRAP_PEERS_ALLOWED ,
59
+ maxParallelDials : DEFAULT_MAX_PARALLEL_DIALS ,
55
60
...options ,
56
61
} ;
57
62
@@ -60,6 +65,31 @@ export class ConnectionManager {
60
65
this . run ( )
61
66
. then ( ( ) => log ( `Connection Manager is now running` ) )
62
67
. catch ( ( error ) => log ( `Unexpected error while running service` , error ) ) ;
68
+
69
+ // libp2p emits `peer:discovery` events during its initialization
70
+ // which means that before the ConnectionManager is initialized, some peers may have been discovered
71
+ // we will dial the peers in peerStore ONCE before we start to listen to the `peer:discovery` events within the ConnectionManager
72
+ this . dialPeerStorePeers ( ) ;
73
+ }
74
+
75
+ private async dialPeerStorePeers ( ) : Promise < void > {
76
+ const peerInfos = await this . libp2pComponents . peerStore . all ( ) ;
77
+ const dialPromises = [ ] ;
78
+ for ( const peerInfo of peerInfos ) {
79
+ if (
80
+ this . libp2pComponents
81
+ . getConnections ( )
82
+ . find ( ( c ) => c . remotePeer === peerInfo . id )
83
+ )
84
+ continue ;
85
+
86
+ dialPromises . push ( this . attemptDial ( peerInfo . id ) ) ;
87
+ }
88
+ try {
89
+ await Promise . all ( dialPromises ) ;
90
+ } catch ( error ) {
91
+ log ( `Unexpected error while dialing peer store peers` , error ) ;
92
+ }
63
93
}
64
94
65
95
private async run ( ) : Promise < void > {
@@ -86,6 +116,7 @@ export class ConnectionManager {
86
116
}
87
117
88
118
private async dialPeer ( peerId : PeerId ) : Promise < void > {
119
+ this . currentActiveDialCount += 1 ;
89
120
let dialAttempt = 0 ;
90
121
while ( dialAttempt <= this . options . maxDialAttemptsForPeer ) {
91
122
try {
@@ -105,6 +136,7 @@ export class ConnectionManager {
105
136
return ;
106
137
} catch ( e ) {
107
138
const error = e as AggregateError ;
139
+
108
140
this . dialErrorsForPeer . set ( peerId . toString ( ) , error ) ;
109
141
log ( `Error dialing peer ${ peerId . toString ( ) } - ${ error . errors } ` ) ;
110
142
@@ -128,6 +160,33 @@ export class ConnectionManager {
128
160
return await this . libp2pComponents . peerStore . delete ( peerId ) ;
129
161
} catch ( error ) {
130
162
throw `Error deleting undialable peer ${ peerId . toString ( ) } from peer store - ${ error } ` ;
163
+ } finally {
164
+ this . currentActiveDialCount -= 1 ;
165
+ this . processDialQueue ( ) ;
166
+ }
167
+ }
168
+
169
+ async dropConnection ( peerId : PeerId ) : Promise < void > {
170
+ try {
171
+ await this . libp2pComponents . hangUp ( peerId ) ;
172
+ log ( `Dropped connection with peer ${ peerId . toString ( ) } ` ) ;
173
+ } catch ( error ) {
174
+ log (
175
+ `Error dropping connection with peer ${ peerId . toString ( ) } - ${ error } `
176
+ ) ;
177
+ }
178
+ }
179
+
180
+ private async processDialQueue ( ) : Promise < void > {
181
+ if (
182
+ this . pendingPeerDialQueue . length > 0 &&
183
+ this . currentActiveDialCount < this . options . maxParallelDials
184
+ ) {
185
+ const peerId = this . pendingPeerDialQueue . shift ( ) ;
186
+ if ( ! peerId ) return ;
187
+ this . attemptDial ( peerId ) . catch ( ( error ) => {
188
+ log ( error ) ;
189
+ } ) ;
131
190
}
132
191
}
133
192
@@ -164,21 +223,50 @@ export class ConnectionManager {
164
223
) ;
165
224
}
166
225
226
+ private async attemptDial ( peerId : PeerId ) : Promise < void > {
227
+ if ( this . currentActiveDialCount >= this . options . maxParallelDials ) {
228
+ this . pendingPeerDialQueue . push ( peerId ) ;
229
+ return ;
230
+ }
231
+
232
+ if ( ! ( await this . shouldDialPeer ( peerId ) ) ) return ;
233
+
234
+ this . dialPeer ( peerId ) . catch ( ( err ) => {
235
+ throw `Error dialing peer ${ peerId . toString ( ) } : ${ err } ` ;
236
+ } ) ;
237
+ }
238
+
167
239
private onEventHandlers = {
168
240
"peer:discovery" : async ( evt : CustomEvent < PeerInfo > ) : Promise < void > => {
169
241
const { id : peerId } = evt . detail ;
170
- if ( ! ( await this . shouldDialPeer ( peerId ) ) ) return ;
171
242
172
- this . dialPeer ( peerId ) . catch ( ( err ) =>
243
+ this . attemptDial ( peerId ) . catch ( ( err ) =>
173
244
log ( `Error dialing peer ${ peerId . toString ( ) } : ${ err } ` )
174
245
) ;
175
246
} ,
176
- "peer:connect" : ( evt : CustomEvent < Connection > ) : void => {
177
- {
178
- this . keepAliveManager . start (
179
- evt . detail . remotePeer ,
180
- this . libp2pComponents . ping . bind ( this )
181
- ) ;
247
+ "peer:connect" : async ( evt : CustomEvent < Connection > ) : Promise < void > => {
248
+ const { remotePeer : peerId } = evt . detail ;
249
+
250
+ this . keepAliveManager . start (
251
+ peerId ,
252
+ this . libp2pComponents . ping . bind ( this )
253
+ ) ;
254
+
255
+ const isBootstrap = ( await this . getTagNamesForPeer ( peerId ) ) . includes (
256
+ Tags . BOOTSTRAP
257
+ ) ;
258
+
259
+ if ( isBootstrap ) {
260
+ const bootstrapConnections = this . libp2pComponents
261
+ . getConnections ( )
262
+ . filter ( ( conn ) => conn . tags . includes ( Tags . BOOTSTRAP ) ) ;
263
+
264
+ // If we have too many bootstrap connections, drop one
265
+ if (
266
+ bootstrapConnections . length > this . options . maxBootstrapPeersAllowed
267
+ ) {
268
+ await this . dropConnection ( peerId ) ;
269
+ }
182
270
}
183
271
} ,
184
272
"peer:disconnect" : ( ) => {
0 commit comments