core: For Android, ignores DNS cache

There is a known issue that causes DNS lookup issue when network
siwtchover on android. This issue is tracked separately in #4962.
This change simply disables DNS cache to avoid the issue on Android.
This commit is contained in:
Jihun Cho 2018-10-16 11:35:15 -07:00 committed by creamsoup
parent 0a7fa14042
commit b7dc501bbe
3 changed files with 69 additions and 25 deletions

View File

@ -98,6 +98,8 @@ final class DnsNameResolver extends NameResolver {
* <p>Default value is -1 (cache forever) if security manager is installed. If security manager is * <p>Default value is -1 (cache forever) if security manager is installed. If security manager is
* not installed, the ttl value is {@code null} which falls back to {@link * not installed, the ttl value is {@code null} which falls back to {@link
* #DEFAULT_NETWORK_CACHE_TTL_SECONDS gRPC default value}. * #DEFAULT_NETWORK_CACHE_TTL_SECONDS gRPC default value}.
*
* <p>For android, gRPC doesn't attempt to cache; this property value will be ignored.
*/ */
@VisibleForTesting @VisibleForTesting
static final String NETWORKADDRESS_CACHE_TTL_PROPERTY = "networkaddress.cache.ttl"; static final String NETWORKADDRESS_CACHE_TTL_PROPERTY = "networkaddress.cache.ttl";
@ -143,7 +145,7 @@ final class DnsNameResolver extends NameResolver {
DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params, DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params,
Resource<ExecutorService> executorResource, ProxyDetector proxyDetector, Resource<ExecutorService> executorResource, ProxyDetector proxyDetector,
Stopwatch stopwatch) { Stopwatch stopwatch, boolean isAndroid) {
// TODO: if a DNS server is provided as nsAuthority, use it. // TODO: if a DNS server is provided as nsAuthority, use it.
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java // https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
this.executorResource = executorResource; this.executorResource = executorResource;
@ -166,7 +168,7 @@ final class DnsNameResolver extends NameResolver {
port = nameUri.getPort(); port = nameUri.getPort();
} }
this.proxyDetector = proxyDetector; this.proxyDetector = proxyDetector;
this.resolveRunnable = new Resolve(this, stopwatch); this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid));
} }
@Override @Override
@ -196,10 +198,10 @@ final class DnsNameResolver extends NameResolver {
private final long cacheTtlNanos; private final long cacheTtlNanos;
private ResolutionResults cachedResolutionResults = null; private ResolutionResults cachedResolutionResults = null;
Resolve(DnsNameResolver resolver, Stopwatch stopwatch) { Resolve(DnsNameResolver resolver, Stopwatch stopwatch, long cacheTtlNanos) {
this.resolver = resolver; this.resolver = resolver;
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch"); this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch");
this.cacheTtlNanos = getNetworkAddressCacheTtlNanos(); this.cacheTtlNanos = cacheTtlNanos;
} }
@Override @Override
@ -230,23 +232,6 @@ final class DnsNameResolver extends NameResolver {
|| (cacheTtlNanos > 0 && stopwatch.elapsed(TimeUnit.NANOSECONDS) > cacheTtlNanos); || (cacheTtlNanos > 0 && stopwatch.elapsed(TimeUnit.NANOSECONDS) > cacheTtlNanos);
} }
/** Returns value of network address cache ttl property. */
private static long getNetworkAddressCacheTtlNanos() {
String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY);
long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS;
if (cacheTtlPropertyValue != null) {
try {
cacheTtl = Long.parseLong(cacheTtlPropertyValue);
} catch (NumberFormatException e) {
logger.log(
Level.WARNING,
"Property({0}) valid is not valid number format({1}), fall back to default({2})",
new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl});
}
}
return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl;
}
@VisibleForTesting @VisibleForTesting
void resolveInternal(Listener savedListener) { void resolveInternal(Listener savedListener) {
InetSocketAddress destination = InetSocketAddress destination =
@ -483,6 +468,31 @@ final class DnsNameResolver extends NameResolver {
ServiceConfigUtil.getList(serviceConfigChoice, SERVICE_CONFIG_CHOICE_CLIENT_HOSTNAME_KEY)); ServiceConfigUtil.getList(serviceConfigChoice, SERVICE_CONFIG_CHOICE_CLIENT_HOSTNAME_KEY));
} }
/**
* Returns value of network address cache ttl property if not Android environment. For android,
* DnsNameResolver does not cache the dns lookup result.
*/
private static long getNetworkAddressCacheTtlNanos(boolean isAndroid) {
if (isAndroid) {
// on Android, ignore dns cache.
return 0;
}
String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY);
long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS;
if (cacheTtlPropertyValue != null) {
try {
cacheTtl = Long.parseLong(cacheTtlPropertyValue);
} catch (NumberFormatException e) {
logger.log(
Level.WARNING,
"Property({0}) valid is not valid number format({1}), fall back to default({2})",
new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl});
}
}
return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl;
}
/** /**
* Determines if a given Service Config choice applies, and if so, returns it. * Determines if a given Service Config choice applies, and if so, returns it.
* *

View File

@ -19,6 +19,7 @@ package io.grpc.internal;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import io.grpc.Attributes; import io.grpc.Attributes;
import io.grpc.InternalServiceProviders;
import io.grpc.NameResolverProvider; import io.grpc.NameResolverProvider;
import java.net.URI; import java.net.URI;
@ -54,7 +55,8 @@ public final class DnsNameResolverProvider extends NameResolverProvider {
params, params,
GrpcUtil.SHARED_CHANNEL_EXECUTOR, GrpcUtil.SHARED_CHANNEL_EXECUTOR,
GrpcUtil.getDefaultProxyDetector(), GrpcUtil.getDefaultProxyDetector(),
Stopwatch.createUnstarted()); Stopwatch.createUnstarted(),
InternalServiceProviders.isAndroid(getClass().getClassLoader()));
} else { } else {
return null; return null;
} }

View File

@ -118,18 +118,34 @@ public class DnsNameResolverTest {
return newResolver(name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted()); return newResolver(name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted());
} }
private DnsNameResolver newResolver(String name, int port, boolean isAndroid) {
return
newResolver(
name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted(), isAndroid);
}
private DnsNameResolver newResolver( private DnsNameResolver newResolver(
String name, String name,
int port, int port,
ProxyDetector proxyDetector, ProxyDetector proxyDetector,
Stopwatch stopwatch) { Stopwatch stopwatch) {
return newResolver(name, port, proxyDetector, stopwatch, false);
}
private DnsNameResolver newResolver(
String name,
int port,
ProxyDetector proxyDetector,
Stopwatch stopwatch,
boolean isAndroid) {
DnsNameResolver dnsResolver = new DnsNameResolver( DnsNameResolver dnsResolver = new DnsNameResolver(
null, null,
name, name,
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(), Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
fakeExecutorResource, fakeExecutorResource,
proxyDetector, proxyDetector,
stopwatch); stopwatch,
isAndroid);
return dnsResolver; return dnsResolver;
} }
@ -200,14 +216,30 @@ public class DnsNameResolverTest {
} }
} }
@Test
public void resolve_androidIgnoresPropertyValue() throws Exception {
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(2));
resolveNeverCache(true);
}
@Test
public void resolve_androidIgnoresPropertyValueCacheForever() throws Exception {
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(-1));
resolveNeverCache(true);
}
@Test @Test
public void resolve_neverCache() throws Exception { public void resolve_neverCache() throws Exception {
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, "0"); System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, "0");
resolveNeverCache(false);
}
private void resolveNeverCache(boolean isAndroid) throws Exception {
final List<InetAddress> answer1 = createAddressList(2); final List<InetAddress> answer1 = createAddressList(2);
final List<InetAddress> answer2 = createAddressList(1); final List<InetAddress> answer2 = createAddressList(1);
String name = "foo.googleapis.com"; String name = "foo.googleapis.com";
DnsNameResolver resolver = newResolver(name, 81); DnsNameResolver resolver = newResolver(name, 81, isAndroid);
AddressResolver mockResolver = mock(AddressResolver.class); AddressResolver mockResolver = mock(AddressResolver.class);
when(mockResolver.resolveAddress(Matchers.anyString())).thenReturn(answer1).thenReturn(answer2); when(mockResolver.resolveAddress(Matchers.anyString())).thenReturn(answer1).thenReturn(answer2);
resolver.setAddressResolver(mockResolver); resolver.setAddressResolver(mockResolver);
@ -241,7 +273,7 @@ public class DnsNameResolverTest {
} }
}); });
new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted()).resolveInternal(mockListener); new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted(), 0).resolveInternal(mockListener);
ArgumentCaptor<Status> ac = ArgumentCaptor.forClass(Status.class); ArgumentCaptor<Status> ac = ArgumentCaptor.forClass(Status.class);
verify(mockListener).onError(ac.capture()); verify(mockListener).onError(ac.capture());