Skip to content

Commit

Permalink
Add support for direct DNS lookups behind a feature flag.
Browse files Browse the repository at this point in the history
  • Loading branch information
YoEight committed Oct 6, 2024
1 parent 12a1d4c commit 88daa6c
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.eventstore.dbclient;

public final class ClientFeatureFlags {
/**
* Enables direct DNS name resolution, retrieving all IP addresses associated with a given hostname. This
* functionality was initially implemented to support the now-deprecated TCP API. It is particularly useful in
* scenarios involving clusters, where node discovery is enabled.
*/
public static final String DnsLookup = "dns-lookup";
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.eventstore.dbclient;

import com.eventstore.dbclient.resolution.DeferredNodeResolution;
import com.eventstore.dbclient.resolution.DeprecatedNodeResolution;
import com.eventstore.dbclient.resolution.FixedSeedsNodeResolution;
import com.eventstore.dbclient.resolution.NodeResolution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
Expand All @@ -13,15 +18,18 @@
class ClusterDiscovery implements Discovery {
private static final Logger logger = LoggerFactory.getLogger(ClusterDiscovery.class);
private final NodeSelector nodeSelector;
private final List<InetSocketAddress> seeds;
private final NodeResolution resolution;

ClusterDiscovery(EventStoreDBClientSettings settings) {
this.nodeSelector = new NodeSelector(settings.getNodePreference());

if (settings.isDnsDiscover()) {
this.seeds = Collections.singletonList(settings.getHosts()[0]);
if (settings.getFeatures().contains(ClientFeatureFlags.DnsLookup))
this.resolution = new DeprecatedNodeResolution(settings.getHosts()[0]);
else
this.resolution = new DeferredNodeResolution(settings.getHosts()[0]);
} else {
this.seeds = Arrays.asList(settings.getHosts());
this.resolution = new FixedSeedsNodeResolution(settings.getHosts());
}
}

Expand All @@ -43,7 +51,7 @@ public CompletableFuture<Void> run(ConnectionState state) {
}

void discover(ConnectionState state) {
List<InetSocketAddress> candidates = new ArrayList<>(this.seeds);
List<InetSocketAddress> candidates = resolution.resolve();

if (candidates.size() > 1) {
Collections.shuffle(candidates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class ConnectionSettingsBuilder {
private Long _defaultDeadline = null;
private List<ClientInterceptor> _interceptors = new ArrayList<>();
private String _tlsCaFile = null;
private Set<String> _features = new HashSet<>();

ConnectionSettingsBuilder() {}

Expand All @@ -54,7 +55,8 @@ public EventStoreDBClientSettings buildConnectionSettings() {
_keepAliveInterval,
_defaultDeadline,
_interceptors,
_tlsCaFile);
_tlsCaFile,
_features);
}

/**
Expand Down Expand Up @@ -219,6 +221,22 @@ public ConnectionSettingsBuilder tlsCaFile(String filepath) {
return this;
}

/**
* Add feature flags.
*/
public ConnectionSettingsBuilder features(String... features) {
this._features.addAll(Arrays.asList(features));
return this;
}

/**
* Add feature flag.
*/
public ConnectionSettingsBuilder feature(String feature) {
this._features.add(feature);
return this;
}

void parseGossipSeed(String host) {
String[] hostParts = host.split(":");

Expand Down Expand Up @@ -436,6 +454,10 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder
userKeyFile = entry[1];
break;

case "feature":
builder._features.add(value);
break;

default:
logger.warn(String.format("Unknown setting '%s' is ignored", entry[0]));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Set;

/**
* Gathers all the settings related to a gRPC client with an EventStoreDB database.
Expand Down Expand Up @@ -39,6 +40,7 @@ public class EventStoreDBClientSettings {
private final Long defaultDeadline;
private final List<ClientInterceptor> interceptors;
private final String tlsCaFile;
private final Set<String> features;

/**
* If the dns discovery is enabled.
Expand Down Expand Up @@ -160,6 +162,11 @@ public String getTlsCaFile() {
return tlsCaFile;
}

/**
* Feature flags
*/
public Set<String> getFeatures() { return features; }

EventStoreDBClientSettings(
boolean dnsDiscover,
int maxDiscoverAttempts,
Expand All @@ -175,7 +182,8 @@ public String getTlsCaFile() {
long keepAliveInterval,
Long defaultDeadline,
List<ClientInterceptor> interceptors,
String tlsCaFile
String tlsCaFile,
Set<String> features
) {
this.dnsDiscover = dnsDiscover;
this.maxDiscoverAttempts = maxDiscoverAttempts;
Expand All @@ -192,6 +200,7 @@ public String getTlsCaFile() {
this.defaultDeadline = defaultDeadline;
this.interceptors = interceptors;
this.tlsCaFile = tlsCaFile;
this.features = features;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.eventstore.dbclient.resolution;

import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;

public class DeferredNodeResolution implements NodeResolution {
private final InetSocketAddress address;

public DeferredNodeResolution(InetSocketAddress address) {
this.address = address;
}

@Override
public List<InetSocketAddress> resolve() {
return Collections.singletonList(address);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.eventstore.dbclient.resolution;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DeprecatedNodeResolution implements NodeResolution {
private final InetSocketAddress address;

public DeprecatedNodeResolution(InetSocketAddress address) {
this.address = address;
}

@Override
public List<InetSocketAddress> resolve() {
try {
return Arrays.stream(InetAddress.getAllByName(address.getHostName()))
.map(addr -> new InetSocketAddress(addr, address.getPort()))
.collect(Collectors.toList());
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.eventstore.dbclient.resolution;

import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;

public class FixedSeedsNodeResolution implements NodeResolution {
private final InetSocketAddress[] seeds;

public FixedSeedsNodeResolution(InetSocketAddress[] seeds) {
this.seeds = seeds;
}

@Override
public List<InetSocketAddress> resolve() {
return Arrays.asList(seeds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.eventstore.dbclient.resolution;

import java.net.InetSocketAddress;
import java.util.List;

public interface NodeResolution {
List<InetSocketAddress> resolve();
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ public static Stream<Arguments> validConnectionStrings() {
Arguments.of(
"esdb://127.0.0.1:21573?userCertFile=/path/to/cert&userKeyFile=/path/to/key",
"{\"dnsDiscover\":false,\"maxDiscoverAttempts\":3,\"discoveryInterval\":500,\"gossipTimeout\":3000,\"nodePreference\":\"leader\",\"tls\":true,\"tlsVerifyCert\":true,\"throwOnAppendFailure\":true,\"hosts\":[{\"address\":\"127.0.0.1\",\"port\":21573}], \"defaultClientCertificate\": {\"clientCertFile\": \"/path/to/cert\", \"clientKeyFile\": \"/path/to/key\"}}"
),
Arguments.of(
"esdb://localhost?feature=foobar",
"{\"dnsDiscover\":false,\"maxDiscoverAttempts\":3,\"discoveryInterval\":500,\"gossipTimeout\":3000,\"nodePreference\":\"leader\",\"tls\":true,\"tlsVerifyCert\":true,\"throwOnAppendFailure\":true,\"hosts\":[{\"address\":\"localhost\",\"port\":2113}], \"features\": \"foobar\"}"
),
Arguments.of(
"esdb://localhost?feature=foobar&feature=baz",
"{\"dnsDiscover\":false,\"maxDiscoverAttempts\":3,\"discoveryInterval\":500,\"gossipTimeout\":3000,\"nodePreference\":\"leader\",\"tls\":true,\"tlsVerifyCert\":true,\"throwOnAppendFailure\":true,\"hosts\":[{\"address\":\"localhost\",\"port\":2113}], \"features\": [\"foobar\", \"baz\"]}"
)
);
}
Expand Down Expand Up @@ -215,6 +223,15 @@ private EventStoreDBClientSettings parseJson(String input) throws JsonProcessing
builder.addHost(new InetSocketAddress(host.get("address").asText(), host.get("port").asInt()));
});

if (tree.get("features") != null) {
JsonNode features = tree.get("features");

if (features.isArray())
features.elements().forEachRemaining(feature -> builder.feature(feature.asText()));
else
builder.feature(features.asText());
}

return builder.buildConnectionSettings();
}
}

0 comments on commit 88daa6c

Please sign in to comment.