@@ -45,6 +45,8 @@ function trace(text: string): void {
45
45
*/
46
46
const DEFAULT_PORT = 443 ;
47
47
48
+ const DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS = 30_000 ;
49
+
48
50
const resolveTxtPromise = util . promisify ( dns . resolveTxt ) ;
49
51
const dnsLookupPromise = util . promisify ( dns . lookup ) ;
50
52
@@ -79,6 +81,12 @@ class DnsResolver implements Resolver {
79
81
private readonly ipResult : SubchannelAddress [ ] | null ;
80
82
private readonly dnsHostname : string | null ;
81
83
private readonly port : number | null ;
84
+ /**
85
+ * Minimum time between resolutions, measured as the time between starting
86
+ * successive resolution requests. Only applies to successful resolutions.
87
+ * Failures are handled by the backoff timer.
88
+ */
89
+ private readonly minTimeBetweenResolutionsMs : number ;
82
90
private pendingLookupPromise : Promise < dns . LookupAddress [ ] > | null = null ;
83
91
private pendingTxtPromise : Promise < string [ ] [ ] > | null = null ;
84
92
private latestLookupResult : TcpSubchannelAddress [ ] | null = null ;
@@ -88,6 +96,8 @@ class DnsResolver implements Resolver {
88
96
private defaultResolutionError : StatusObject ;
89
97
private backoff : BackoffTimeout ;
90
98
private continueResolving = false ;
99
+ private nextResolutionTimer : NodeJS . Timer ;
100
+ private isNextResolutionTimerRunning = false ;
91
101
constructor (
92
102
private target : GrpcUri ,
93
103
private listener : ResolverListener ,
@@ -134,6 +144,10 @@ class DnsResolver implements Resolver {
134
144
}
135
145
} , backoffOptions ) ;
136
146
this . backoff . unref ( ) ;
147
+
148
+ this . minTimeBetweenResolutionsMs = channelOptions [ 'grpc.dns_min_time_between_resolutions_ms' ] ?? DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS ;
149
+ this . nextResolutionTimer = setTimeout ( ( ) => { } , 0 ) ;
150
+ clearTimeout ( this . nextResolutionTimer ) ;
137
151
}
138
152
139
153
/**
@@ -183,6 +197,7 @@ class DnsResolver implements Resolver {
183
197
( addressList ) => {
184
198
this . pendingLookupPromise = null ;
185
199
this . backoff . reset ( ) ;
200
+ this . backoff . stop ( ) ;
186
201
const ip4Addresses : dns . LookupAddress [ ] = addressList . filter (
187
202
( addr ) => addr . family === 4
188
203
) ;
@@ -229,6 +244,7 @@ class DnsResolver implements Resolver {
229
244
( err as Error ) . message
230
245
) ;
231
246
this . pendingLookupPromise = null ;
247
+ this . stopNextResolutionTimer ( ) ;
232
248
this . listener . onError ( this . defaultResolutionError ) ;
233
249
}
234
250
) ;
@@ -282,17 +298,34 @@ class DnsResolver implements Resolver {
282
298
}
283
299
}
284
300
301
+ private startNextResolutionTimer ( ) {
302
+ this . nextResolutionTimer = setTimeout ( ( ) => {
303
+ this . stopNextResolutionTimer ( ) ;
304
+ if ( this . continueResolving ) {
305
+ this . startResolutionWithBackoff ( ) ;
306
+ }
307
+ } , this . minTimeBetweenResolutionsMs ) . unref ?.( ) ;
308
+ this . isNextResolutionTimerRunning = true ;
309
+ }
310
+
311
+ private stopNextResolutionTimer ( ) {
312
+ clearTimeout ( this . nextResolutionTimer ) ;
313
+ this . isNextResolutionTimerRunning = false ;
314
+ }
315
+
285
316
private startResolutionWithBackoff ( ) {
286
317
this . startResolution ( ) ;
287
318
this . backoff . runOnce ( ) ;
319
+ this . startNextResolutionTimer ( ) ;
288
320
}
289
321
290
322
updateResolution ( ) {
291
323
/* If there is a pending lookup, just let it finish. Otherwise, if the
292
- * backoff timer is running, do another lookup when it ends, and if not,
293
- * do another lookup immeidately. */
324
+ * nextResolutionTimer or backoff timer is running, set the
325
+ * continueResolving flag to resolve when whichever of those timers
326
+ * fires. Otherwise, start resolving immediately. */
294
327
if ( this . pendingLookupPromise === null ) {
295
- if ( this . backoff . isRunning ( ) ) {
328
+ if ( this . isNextResolutionTimerRunning || this . backoff . isRunning ( ) ) {
296
329
this . continueResolving = true ;
297
330
} else {
298
331
this . startResolutionWithBackoff ( ) ;
@@ -301,9 +334,9 @@ class DnsResolver implements Resolver {
301
334
}
302
335
303
336
destroy ( ) {
304
- /* Do nothing. There is not a practical way to cancel in-flight DNS
305
- * requests, and after this function is called we can expect that
306
- * updateResolution will not be called again. */
337
+ this . continueResolving = false ;
338
+ this . backoff . stop ( ) ;
339
+ this . stopNextResolutionTimer ( ) ;
307
340
}
308
341
309
342
/**
0 commit comments