Skip to content

Commit 4fb421b

Browse files
Alternative backup DNS request solution
1 parent e09d1ec commit 4fb421b

File tree

1 file changed

+117
-2
lines changed

1 file changed

+117
-2
lines changed

servicetalk-dns-discovery-netty/src/main/java/io/servicetalk/dns/discovery/netty/DefaultDnsClient.java

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import io.netty.channel.EventLoop;
4242
import io.netty.channel.socket.SocketChannel;
4343
import io.netty.handler.codec.dns.DefaultDnsQuestion;
44+
import io.netty.handler.codec.dns.DnsQuestion;
4445
import io.netty.handler.codec.dns.DnsRawRecord;
4546
import io.netty.handler.codec.dns.DnsRecord;
4647
import io.netty.resolver.ResolvedAddressTypes;
@@ -56,6 +57,7 @@
5657
import io.netty.util.ReferenceCountUtil;
5758
import io.netty.util.concurrent.Future;
5859
import io.netty.util.concurrent.FutureListener;
60+
import io.netty.util.concurrent.GenericFutureListener;
5961
import io.netty.util.concurrent.Promise;
6062
import org.slf4j.Logger;
6163
import org.slf4j.LoggerFactory;
@@ -75,6 +77,7 @@
7577
import java.util.List;
7678
import java.util.Map;
7779
import java.util.RandomAccess;
80+
import java.util.function.Function;
7881
import java.util.function.IntFunction;
7982
import javax.annotation.Nullable;
8083

@@ -127,7 +130,7 @@ final class DefaultDnsClient implements DnsClient {
127130
private static final Cancellable TERMINATED = () -> { };
128131

129132
private final EventLoopAwareNettyIoExecutor nettyIoExecutor;
130-
private final DnsNameResolver resolver;
133+
private final DnsNameResolverDelegate resolver;
131134
private final MinTtlCache ttlCache;
132135
private final long maxTTLNanos;
133136
private final long ttlJitterNanos;
@@ -144,6 +147,117 @@ final class DefaultDnsClient implements DnsClient {
144147
private final String id;
145148
private boolean closed;
146149

150+
private interface DnsNameResolverDelegate {
151+
void close();
152+
153+
Future<List<InetAddress>> resolveAll(String name);
154+
155+
Future<List<DnsRecord>> resolveAll(DnsQuestion name);
156+
157+
long queryTimeoutMillis();
158+
}
159+
160+
private static final class SingleResolver implements DnsNameResolverDelegate {
161+
private final DnsNameResolver nettyResolver;
162+
163+
SingleResolver(DnsNameResolver nettyResolver) {
164+
this.nettyResolver = nettyResolver;
165+
}
166+
167+
@Override
168+
public void close() {
169+
nettyResolver.close();
170+
}
171+
172+
@Override
173+
public Future<List<InetAddress>> resolveAll(String name) {
174+
return nettyResolver.resolveAll(name);
175+
}
176+
177+
@Override
178+
public Future<List<DnsRecord>> resolveAll(DnsQuestion question) {
179+
return nettyResolver.resolveAll(question);
180+
}
181+
182+
@Override
183+
public long queryTimeoutMillis() {
184+
return nettyResolver.queryTimeoutMillis();
185+
}
186+
}
187+
188+
private static final class BackupRequestResolver implements DnsNameResolverDelegate {
189+
190+
private final DnsNameResolver primary;
191+
private final DnsNameResolver backup;
192+
private final EventLoop eventLoop;
193+
194+
// TODO: we'll want to make sure we share the cache, make our backup request interval more flexible, and also
195+
// give ourselves some sort of budget so we don't overload DNS, but these are all possible.
196+
BackupRequestResolver(DnsNameResolverBuilder builder, EventLoop eventLoop) {
197+
primary = builder.build();
198+
backup = builder.consolidateCacheSize(0).build();
199+
this.eventLoop = eventLoop;
200+
}
201+
202+
@Override
203+
public void close() {
204+
try {
205+
primary.close();
206+
} finally {
207+
backup.close();
208+
}
209+
}
210+
211+
@Override
212+
public Future<List<InetAddress>> resolveAll(String name) {
213+
return compose(resolver -> resolver.resolveAll(name));
214+
}
215+
216+
@Override
217+
public Future<List<DnsRecord>> resolveAll(DnsQuestion name) {
218+
return compose(resolver -> resolver.resolveAll(name));
219+
}
220+
221+
@Override
222+
public long queryTimeoutMillis() {
223+
return primary.queryTimeoutMillis();
224+
}
225+
226+
private <T> Future<T> compose(Function<DnsNameResolver, Future<T>> query) {
227+
Promise<T> result = eventLoop.newPromise();
228+
Future<T> r1 = query.apply(primary);
229+
Future<?> timer = eventLoop.schedule(() -> join(query.apply(backup), result, null),
230+
50, MILLISECONDS);
231+
join(r1, result, timer);
232+
return result;
233+
}
234+
235+
private static <T> void join(Future<? extends T> future, Promise<T> promise, @Nullable Future<?> timer) {
236+
// transfer results from future to promise and cancellation from promise to future.
237+
future.addListener(new GenericFutureListener<Future<T>>() {
238+
@Override
239+
public void operationComplete(Future<T> future) {
240+
if (timer != null) {
241+
timer.cancel(true);
242+
}
243+
if (future.isSuccess()) {
244+
promise.trySuccess(future.getNow());
245+
} else if (!future.isCancelled()) {
246+
promise.tryFailure(future.cause());
247+
}
248+
}
249+
});
250+
promise.addListener(new GenericFutureListener<Future<T>>() {
251+
@Override
252+
public void operationComplete(Future<T> promise) {
253+
if (promise.isCancelled()) {
254+
future.cancel(true);
255+
}
256+
}
257+
});
258+
}
259+
}
260+
147261
DefaultDnsClient(final String id, final IoExecutor ioExecutor, final int consolidateCacheSize,
148262
final int minTTL, final int maxTTL, final int minCacheTTL, final int maxCacheTTL,
149263
final int negativeTTLCacheSeconds, final long ttlJitterNanos,
@@ -229,7 +343,8 @@ final class DefaultDnsClient implements DnsClient {
229343
if (dnsServerAddressStreamProvider != null) {
230344
builder.nameServerProvider(toNettyType(dnsServerAddressStreamProvider));
231345
}
232-
resolver = builder.build();
346+
// resolver = new SingleResolver(builder.build());
347+
resolver = new BackupRequestResolver(builder, eventLoop);
233348
this.resolutionTimeoutMillis = resolutionTimeout != null ? resolutionTimeout.toMillis() :
234349
// Default value is chosen based on a combination of default "timeout" and "attempts" options of
235350
// /etc/resolv.conf: https://man7.org/linux/man-pages/man5/resolv.conf.5.html

0 commit comments

Comments
 (0)