Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpc-js: Add backoff to DNS resolution attempts #2024

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/grpc-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@grpc/grpc-js",
"version": "1.5.0",
"version": "1.5.1",
"description": "gRPC Library for Node - pure JS implementation",
"homepage": "https://grpc.io/",
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",
Expand Down
33 changes: 31 additions & 2 deletions packages/grpc-js/src/resolver-dns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { SubchannelAddress, TcpSubchannelAddress } from './subchannel-address';
import { GrpcUri, uriToString, splitHostPort } from './uri-parser';
import { isIPv6, isIPv4 } from 'net';
import { ChannelOptions } from './channel-options';
import { BackoffOptions, BackoffTimeout } from './backoff-timeout';

const TRACER_NAME = 'dns_resolver';

Expand Down Expand Up @@ -85,6 +86,8 @@ class DnsResolver implements Resolver {
private latestServiceConfigError: StatusObject | null = null;
private percentage: number;
private defaultResolutionError: StatusObject;
private backoff: BackoffTimeout;
private continueResolving = false;
constructor(
private target: GrpcUri,
private listener: ResolverListener,
Expand Down Expand Up @@ -119,6 +122,18 @@ class DnsResolver implements Resolver {
details: `Name resolution failed for target ${uriToString(this.target)}`,
metadata: new Metadata(),
};

const backoffOptions: BackoffOptions = {
initialDelay: channelOptions['grpc.initial_reconnect_backoff_ms'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the key doesn't exist? is it oK?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then the value will be undefined, so the BackoffTimeout will fall back to the default value, which is 1000 ms.

maxDelay: channelOptions['grpc.max_reconnect_backoff_ms'],
};

this.backoff = new BackoffTimeout(() => {
if (this.continueResolving) {
this.startResolutionWithBackoff();
}
}, backoffOptions);
this.backoff.unref();
}

/**
Expand All @@ -140,6 +155,7 @@ class DnsResolver implements Resolver {
return;
}
if (this.dnsHostname === null) {
trace('Failed to parse DNS address ' + uriToString(this.target));
setImmediate(() => {
this.listener.onError({
code: Status.UNAVAILABLE,
Expand All @@ -148,6 +164,7 @@ class DnsResolver implements Resolver {
});
});
} else {
trace('Looking up DNS hostname ' + this.dnsHostname);
/* We clear out latestLookupResult here to ensure that it contains the
* latest result since the last time we started resolving. That way, the
* TXT resolution handler can use it, but only if it finishes second. We
Expand All @@ -164,6 +181,7 @@ class DnsResolver implements Resolver {
this.pendingLookupPromise.then(
(addressList) => {
this.pendingLookupPromise = null;
this.backoff.reset();
const ip4Addresses: dns.LookupAddress[] = addressList.filter(
(addr) => addr.family === 4
);
Expand Down Expand Up @@ -263,10 +281,21 @@ class DnsResolver implements Resolver {
}
}

private startResolutionWithBackoff() {
this.startResolution();
this.backoff.runOnce();
}

updateResolution() {
trace('Resolution update requested for target ' + uriToString(this.target));
/* If there is a pending lookup, just let it finish. Otherwise, if the
* backoff timer is running, do another lookup when it ends, and if not,
* do another lookup immeidately. */
if (this.pendingLookupPromise === null) {
this.startResolution();
if (this.backoff.isRunning()) {
this.continueResolving = true;
} else {
this.startResolutionWithBackoff();
}
}
}

Expand Down