Skip to content
Merged
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
@@ -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 DNS_LOOKUP = "dns-lookup";
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
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;

Expand All @@ -13,15 +17,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.DNS_LOOKUP))
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 +50,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();
}
}
Loading