Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ protected static class GssApiAuthenticator extends BaseDseAuthenticator {
private SaslClient saslClient;
private EndPoint endPoint;

@SuppressWarnings("deprecation") // resolve() is deprecated in favour of resolveAll();
// Kerberos authentication needs a single canonical hostname for SASL service name resolution.
protected GssApiAuthenticator(
GssApiOptions options, EndPoint endPoint, String serverAuthenticator) {
super(serverAuthenticator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ private InsightsStatusData createStatusData() {
.build();
}

@SuppressWarnings("deprecation") // resolve() is deprecated in favour of resolveAll();
// address reporting needs a single canonical address per node.
private Map<String, SessionStateForNode> getConnectedNodes() {
Map<Node, ChannelPool> pools = driverContext.getPoolManager().getPools();
return pools.entrySet().stream()
Expand All @@ -302,6 +304,8 @@ private SessionStateForNode constructSessionStateForNode(Map.Entry<Node, Channel
entry.getKey().getOpenConnections(), entry.getValue().getInFlight());
}

@SuppressWarnings("deprecation") // resolve() is deprecated in favour of resolveAll();
// address reporting needs a single canonical address per node.
private InsightsStartupData createStartupData() {
Map<String, String> startupOptions = driverContext.getStartupOptions();
return InsightsStartupData.builder()
Expand Down Expand Up @@ -454,6 +458,8 @@ private PoolSizeByHostDistance getPoolSizeByHostDistance() {
0);
}

@SuppressWarnings("deprecation") // resolve() is deprecated in favour of resolveAll();
// address reporting needs a single canonical address for the control connection.
private String getControlConnectionSocketAddress() {
SocketAddress controlConnectionAddress = controlConnection.channel().getEndPoint().resolve();
return AddressFormatter.nullSafeToString(controlConnectionAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,8 +701,13 @@ public enum DefaultDriverOption implements DriverOption {
CONTROL_CONNECTION_AGREEMENT_WARN("advanced.control-connection.schema-agreement.warn-on-failure"),

/**
* Whether to forcibly add original contact points held by MetadataManager to the reconnection
* plan, in case there is no live nodes available according to LBP. Experimental.
* Whether to append the original contact points held by MetadataManager to the reconnection plan,
* after the live nodes reported by the load balancing policy. Defaults to {@code true}.
*
* <p>This is also the driver's DNS re-resolution path: contact points are expanded to their
* current DNS IPs at query-plan time, whereas metadata nodes hold an already-resolved endpoint
* that is never re-resolved. Keeping this enabled lets control-connection reconnects re-resolve
* the original hostnames and pick up new IPs once the live-node plan is exhausted.
*
* <p>Value-type: boolean
*/
Expand Down Expand Up @@ -837,7 +842,11 @@ public enum DefaultDriverOption implements DriverOption {
* Whether to resolve the addresses passed to `basic.contact-points`.
*
* <p>Value-type: boolean
*
* @deprecated Contact points are now always kept as unresolved hostnames and expanded to all
* their DNS-mapped IPs lazily at connection time. Setting this option has no effect.
*/
@Deprecated
Comment thread
nikagra marked this conversation as resolved.
RESOLVE_CONTACT_POINTS("advanced.resolve-contact-points"),

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ protected static void fillWithDriverDefaults(OptionsMap map) {
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_INTERVAL, Duration.ofMillis(200));
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_TIMEOUT, Duration.ofSeconds(10));
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_WARN, true);
map.put(TypedDriverOption.CONTROL_CONNECTION_RECONNECT_CONTACT_POINTS, false);
map.put(TypedDriverOption.CONTROL_CONNECTION_RECONNECT_CONTACT_POINTS, true);
map.put(TypedDriverOption.PREPARE_ON_ALL_NODES, true);
map.put(TypedDriverOption.REPREPARE_ENABLED, true);
map.put(TypedDriverOption.REPREPARE_CHECK_SYSTEM_TABLE, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,15 @@ public String toString() {
public static final TypedDriverOption<Boolean> CONTROL_CONNECTION_AGREEMENT_WARN =
new TypedDriverOption<>(
DefaultDriverOption.CONTROL_CONNECTION_AGREEMENT_WARN, GenericType.BOOLEAN);
/** Whether to forcibly try original contacts if no live nodes are available */
/**
* Whether to append the original contact points to the control-connection reconnection plan,
* after the live nodes reported by the load balancing policy (defaults to {@code true}).
*
* <p>Contact points are appended as-is (unresolved hostnames); each is expanded to all of its
* current DNS IPs at connection time via {@code EndPoint.resolveAll()}, which is also the
* driver's DNS re-resolution mechanism. The append is skipped for topology monitors that
* re-resolve node addresses themselves (such as the cloud/proxy monitors).
*/
public static final TypedDriverOption<Boolean> CONTROL_CONNECTION_RECONNECT_CONTACT_POINTS =
new TypedDriverOption<>(
DefaultDriverOption.CONTROL_CONNECTION_RECONNECT_CONTACT_POINTS, GenericType.BOOLEAN);
Expand Down Expand Up @@ -664,7 +672,13 @@ public String toString() {
/** The coalescer reschedule interval. */
public static final TypedDriverOption<Duration> COALESCER_INTERVAL =
new TypedDriverOption<>(DefaultDriverOption.COALESCER_INTERVAL, GenericType.DURATION);
/** Whether to resolve the addresses passed to `basic.contact-points`. */
/**
* Whether to resolve the addresses passed to `basic.contact-points`.
*
* @deprecated Contact points are now always kept as unresolved hostnames and expanded to all
* their DNS-mapped IPs lazily at connection time. Setting this option has no effect.
*/
@Deprecated
public static final TypedDriverOption<Boolean> RESOLVE_CONTACT_POINTS =
new TypedDriverOption<>(DefaultDriverOption.RESOLVE_CONTACT_POINTS, GenericType.BOOLEAN);
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,75 @@
package com.datastax.oss.driver.api.core.metadata;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;

/**
* Encapsulates the information needed to open connections to a node.
*
* <p>By default, the driver assumes plain TCP connections, and this is just a wrapper around an
* {@link InetSocketAddress}. However, more complex deployment scenarios might use a custom
* {@link java.net.InetSocketAddress}. However, more complex deployment scenarios might use a custom
* implementation that contains additional information; for example, if the nodes are accessed
* through a proxy with SNI routing, an SNI server name is needed in addition to the proxy address.
*/
public interface EndPoint {

/**
* Resolves this instance to a socket address.
* Resolves this instance to a single socket address.
*
* <p>This will be called each time the driver opens a new connection to the node. The returned
* address cannot be null.
*
* @deprecated Use {@link #resolveAll(Executor)} instead. When a hostname maps to multiple IPs
* (e.g. in dynamic DNS environments) only one address is returned here, causing the driver to
* miss fallback IPs when the first one is unreachable. {@code resolveAll(Executor)} returns
* the full set, resolved asynchronously off the calling thread.
*/
@Deprecated
@NonNull
SocketAddress resolve();

/**
* Resolves this instance to all known socket addresses, asynchronously.
*
* <p>This is called each time the driver opens a new connection to the node. For endpoints backed
* by a plain IP address the returned array contains exactly one element. For endpoints whose
* hostname resolves to multiple IPs (e.g. a DNS round-robin entry) all addresses are returned so
* that the driver can try each one in sequence and fall back gracefully when individual IPs are
* unreachable.
*
* <p>Resolution is asynchronous on purpose: name resolution can block (e.g. {@link
* java.net.InetAddress#getAllByName(String)}), and the driver calls this from its admin event
* loop, which must never block. Implementations whose resolution may block <b>must</b> run it on
* the supplied {@code executor} rather than on the calling thread (see the default
* implementation). Implementations that resolve from memory (e.g. an already-resolved address or
* an in-memory lookup) may return an {@linkplain CompletableFuture#completedFuture(Object)
* already completed stage} and ignore the executor.
*
* <p>The default implementation offloads {@link #resolve()} to {@code executor} and wraps the
* result in a single-element array. Implementations that can supply multiple addresses should
* override this method.
*
* <p>The returned stage must not be null and must complete with a non-null, non-empty array.
*
* @param executor the executor to run potentially-blocking resolution on; must not be null. The
* driver supplies a dedicated resolver executor so that blocking name resolution never runs
* on an event loop.
* @apiNote <b>Timeout note:</b> {@link
* com.datastax.oss.driver.internal.core.channel.ChannelFactory} tries each address in
* sequence. If a hostname resolves to N addresses and each attempt times out, the worst-case
* connection time before declaring a node unreachable is {@code N ×
* advanced.connection.connect-timeout}. In practice DNS round-robin entries have only a small
* number of records, so this is rarely a concern, but callers should be aware of this when
* configuring connect timeouts.
*/
@NonNull
default CompletionStage<SocketAddress[]> resolveAll(@NonNull Executor executor) {
return CompletableFuture.supplyAsync(() -> new SocketAddress[] {resolve()}, executor);
}

/**
* Returns an alternate string representation for use in node-level metric names.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,15 @@ protected DriverConfigLoader defaultConfigLoader(@Nullable ClassLoader classLoad
* <p>Contact points can also be provided statically in the configuration. If both are specified,
* they will be merged. If both are absent, the driver will default to 127.0.0.1:9042.
*
* <p>Contrary to the configuration, DNS names with multiple A-records will not be handled here.
* If you need that, extract them manually with {@link java.net.InetAddress#getAllByName(String)}
* before calling this method. Similarly, if you need connect addresses to stay unresolved, make
* sure you pass unresolved instances here (see {@code advanced.resolve-contact-points} in the
* configuration for more explanations).
* <p>The driver automatically expands any contact point backed by an unresolved hostname to all
* its DNS-mapped IPs at connection time (via {@code EndPoint.resolveAll()}), so passing a single
* hostname is sufficient to try all its IPs on initial connect. This applies equally to hostnames
* provided here programmatically (build an unresolved {@link InetSocketAddress} with {@link
* InetSocketAddress#createUnresolved(String, int)} to opt in) and to hostnames specified in the
* configuration. An already-resolved address passed here (the common case when constructing an
* {@code InetSocketAddress} directly from a hostname, which resolves eagerly) is used as
* provided, with no further expansion. The {@code advanced.resolve-contact-points} option is
* deprecated and has no effect.
*/
@NonNull
public SelfT addContactPoints(@NonNull Collection<InetSocketAddress> contactPoints) {
Expand Down Expand Up @@ -957,11 +961,10 @@ protected final CompletionStage<CqlSession> buildDefaultSessionAsync() {
programmaticArguments = programmaticArgumentsBuilder.build();
}

boolean resolveAddresses =
defaultConfig.getBoolean(DefaultDriverOption.RESOLVE_CONTACT_POINTS, false);

// RESOLVE_CONTACT_POINTS is deprecated: contact points are always kept as unresolved
// hostnames and expanded to all their DNS IPs at connection time via EndPoint.resolveAll().
Set<EndPoint> contactPoints =
ContactPoints.merge(programmaticContactPoints, configContactPoints, resolveAddresses);
ContactPoints.merge(programmaticContactPoints, configContactPoints, false);

if (keyspace == null && defaultConfig.isDefined(DefaultDriverOption.SESSION_KEYSPACE)) {
keyspace =
Expand Down
Loading
Loading