-
Notifications
You must be signed in to change notification settings - Fork 186
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
DnsServiceDiscoverer
: add total resolution timeout
#2908
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
import io.servicetalk.client.api.ServiceDiscovererEvent; | ||
import io.servicetalk.transport.api.HostAndPort; | ||
import io.servicetalk.transport.api.IoExecutor; | ||
import io.servicetalk.utils.internal.DurationUtils; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -124,6 +125,8 @@ public final class DefaultDnsServiceDiscovererBuilder implements DnsServiceDisco | |
private IoExecutor ioExecutor; | ||
@Nullable | ||
private Duration queryTimeout; | ||
@Nullable | ||
private Duration resolutionTimeout; | ||
private int consolidateCacheSize = DEFAULT_CONSOLIDATE_CACHE_SIZE; | ||
private int minTTLSeconds = DEFAULT_MIN_TTL_POLL_SECONDS; | ||
private int maxTTLSeconds = DEFAULT_MAX_TTL_POLL_SECONDS; | ||
|
@@ -258,16 +261,23 @@ public DefaultDnsServiceDiscovererBuilder ndots(final int ndots) { | |
} | ||
|
||
@Override | ||
public DefaultDnsServiceDiscovererBuilder queryTimeout(final Duration queryTimeout) { | ||
this.queryTimeout = queryTimeout; | ||
public DefaultDnsServiceDiscovererBuilder queryTimeout(final @Nullable Duration queryTimeout) { | ||
this.queryTimeout = queryTimeout == null ? null : DurationUtils.ensureNonNegative(queryTimeout, "queryTimeout"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public DefaultDnsServiceDiscovererBuilder resolutionTimeout(final @Nullable Duration resolutionTimeout) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO the name between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I debated between adding "total" or not in the name as well. Decided to drop it for 2 reasons:
|
||
this.resolutionTimeout = resolutionTimeout == null ? null : | ||
DurationUtils.ensureNonNegative(resolutionTimeout, "resolutionTimeout"); | ||
return this; | ||
} | ||
|
||
@Override | ||
public DefaultDnsServiceDiscovererBuilder dnsResolverAddressTypes( | ||
@Nullable final DnsResolverAddressTypes dnsResolverAddressTypes) { | ||
this.dnsResolverAddressTypes = dnsResolverAddressTypes != null ? dnsResolverAddressTypes : | ||
systemDefault(); | ||
DEFAULT_DNS_RESOLVER_ADDRESS_TYPES; | ||
return this; | ||
} | ||
|
||
|
@@ -385,8 +395,8 @@ DnsClient build() { | |
ttlJitter.toNanos(), | ||
srvConcurrency, completeOncePreferredResolved, srvFilterDuplicateEvents, | ||
srvHostNameRepeatInitialDelay, srvHostNameRepeatJitter, maxUdpPayloadSize, ndots, optResourceEnabled, | ||
queryTimeout, dnsResolverAddressTypes, localAddress, dnsServerAddressStreamProvider, observer, | ||
missingRecordStatus, nxInvalidation); | ||
queryTimeout, resolutionTimeout, dnsResolverAddressTypes, localAddress, dnsServerAddressStreamProvider, | ||
observer, missingRecordStatus, nxInvalidation); | ||
return filterFactory == null ? rawClient : filterFactory.create(rawClient); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright © 2024 Apple Inc. and the ServiceTalk project authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.servicetalk.dns.discovery.netty; | ||
|
||
import io.servicetalk.concurrent.internal.ThrowableUtils; | ||
|
||
import java.net.UnknownHostException; | ||
|
||
final class DnsNameResolverTimeoutException extends UnknownHostException { | ||
private static final long serialVersionUID = 3089160074512305891L; | ||
|
||
private DnsNameResolverTimeoutException(final String name, final String recordType, final long timeoutMs) { | ||
super("Resolution for '" + name + "' [" + recordType + "] timed out after " + timeoutMs + " milliseconds"); | ||
} | ||
|
||
@Override | ||
public Throwable fillInStackTrace() { | ||
return this; | ||
} | ||
|
||
static DnsNameResolverTimeoutException newInstance(final String name, final long timeoutMs, final String recordType, | ||
final Class<?> clazz, final String method) { | ||
return ThrowableUtils.unknownStackTrace(new DnsNameResolverTimeoutException(name, recordType, timeoutMs), | ||
clazz, method); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,8 +41,8 @@ public interface DnsServiceDiscovererBuilder { | |
* @return {@code this}. | ||
*/ | ||
default DnsServiceDiscovererBuilder consolidateCacheSize(int consolidateCacheSize) { | ||
throw new UnsupportedOperationException("DnsServiceDiscovererBuilder#consolidateCacheSize(int) is not " + | ||
"supported by " + getClass()); | ||
throw new UnsupportedOperationException( | ||
"DnsServiceDiscovererBuilder#consolidateCacheSize(int) is not supported by " + getClass()); | ||
} | ||
|
||
/** | ||
|
@@ -123,8 +123,8 @@ default DnsServiceDiscovererBuilder consolidateCacheSize(int consolidateCacheSiz | |
*/ | ||
default DnsServiceDiscovererBuilder ttl(int minSeconds, int maxSeconds, int minCacheSeconds, int maxCacheSeconds, | ||
int negativeCacheSeconds) { | ||
throw new UnsupportedOperationException("DnsServiceDiscovererBuilder#ttl(int, int, int, int, int) is not " + | ||
"supported by " + getClass()); | ||
throw new UnsupportedOperationException( | ||
"DnsServiceDiscovererBuilder#ttl(int, int, int, int, int) is not supported by " + getClass()); | ||
} | ||
|
||
/** | ||
|
@@ -146,8 +146,8 @@ default DnsServiceDiscovererBuilder ttl(int minSeconds, int maxSeconds, int minC | |
* @return {@code this}. | ||
*/ | ||
default DnsServiceDiscovererBuilder localAddress(@Nullable SocketAddress localAddress) { | ||
throw new UnsupportedOperationException("DnsServiceDiscovererBuilder#localAddress(SocketAddress) is not " + | ||
"supported by " + getClass()); | ||
throw new UnsupportedOperationException( | ||
"DnsServiceDiscovererBuilder#localAddress(SocketAddress) is not supported by " + getClass()); | ||
} | ||
|
||
/** | ||
|
@@ -182,19 +182,46 @@ DnsServiceDiscovererBuilder dnsServerAddressStreamProvider( | |
|
||
/** | ||
* Set the number of dots which must appear in a name before an initial absolute query is made. | ||
* <p> | ||
* If not set, the default value is read from {@code ndots} option of {@code /etc/resolv.conf}). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if it is not present in the resolv.conf? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. docs for resolv.conf clarify the behavior if value is not specified in the file: https://man7.org/linux/man-pages/man5/resolv.conf.5.html |
||
* | ||
* @param ndots the ndots value. | ||
* @return {@code this}. | ||
*/ | ||
DnsServiceDiscovererBuilder ndots(int ndots); | ||
|
||
/** | ||
* Sets the timeout of each DNS query performed by this service discoverer. | ||
* Sets the timeout of each DNS query performed by this service discoverer as part of a resolution request. | ||
* <p> | ||
* Zero ({@code 0}) disables the timeout. If not set, the default value is read from {@code timeout} option of | ||
* {@code /etc/resolv.conf}). Similar to linux systems, this value may be silently capped. | ||
* | ||
* @param queryTimeout the query timeout value | ||
* @return {@code this}. | ||
* @return {@code this} | ||
* @see #resolutionTimeout(Duration) | ||
*/ | ||
DnsServiceDiscovererBuilder queryTimeout(@Nullable Duration queryTimeout); | ||
|
||
/** | ||
* Sets the total timeout of each DNS resolution performed by this service discoverer. | ||
* <p> | ||
* Each resolution may execute one or more DNS queries, like following multiple CNAME(s) or trying different search | ||
* domains. This is the total timeout for all intermediate queries involved in a single resolution request. Note, | ||
* that <a href="https://tools.ietf.org/html/rfc2782">SRV</a> resolutions may generate independent resolutions for | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a slightly different conversations, but I wonder if this is the right behavior. Even if technically it's two different resolutions, from a user perspective I still want to apply a "total" timeout, right? Since in the end they are just individual queries against a nameserver where we need a total timeout to cap? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, I don't like this fact either but it's what it's. the way SRV resolutions are implemented right now makes it too complicated to apply a timeout at SRV+all_A_resolutions level. requires a big refactoring. |
||
* {@code A/AAAA} records. In this case, this timeout will be applied to an {@code SRV} resolution and each | ||
* {@code A/AAAA} resolution independently. | ||
* <p> | ||
* Zero ({@code 0}) disables the timeout. If not set, it defaults to {@link #queryTimeout(Duration) query timeout} | ||
* value multiplied by {@code 2}. | ||
* | ||
* @param resolutionTimeout the query timeout value | ||
* @return {@code this} | ||
* @see #queryTimeout(Duration) | ||
*/ | ||
DnsServiceDiscovererBuilder queryTimeout(Duration queryTimeout); | ||
default DnsServiceDiscovererBuilder resolutionTimeout(@Nullable Duration resolutionTimeout) { | ||
throw new UnsupportedOperationException( | ||
"DnsServiceDiscovererBuilder#resolutionTimeout(Duration) is not supported by " + getClass()); | ||
} | ||
|
||
/** | ||
* Sets the list of the protocol families of the address resolved. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open for alternative suggestions. Originally, I planned to use
resolver.queryTimeoutMillis() * resolver.maxQueriesPerResolve()
, but Netty's default values are:which results in 80 sec and seems unreasonably high.
Decided to go with x2 because because the linux default for
attempts
(maxQueriesPerResolve) is 2 (see https://man7.org/linux/man-pages/man5/resolv.conf.5.html).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fwiw, this seems reasonable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable to me as well 👍