This is an automated email from the ASF dual-hosted git repository.
ycai pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-sidecar.git
The following commit(s) were added to refs/heads/trunk by this push:
new 42359c1c CASSSIDECAR-192: Fix CassandraInstance not found error (#180)
42359c1c is described below
commit 42359c1c7c390b1e4e31e744128c017bfc393c1c
Author: Yifan Cai <[email protected]>
AuthorDate: Mon Feb 3 12:26:20 2025 -0800
CASSSIDECAR-192: Fix CassandraInstance not found error (#180)
Patch by Yifan Cai; Reviewed by Francisco Guerrero for CASSSIDECAR-192
---
.circleci/config.yml | 6 +-
CHANGES.txt | 1 +
build.gradle | 27 +++-
gradle/common/integrationTestTask.gradle | 1 -
.../sidecar/cluster/InstancesMetadata.java | 14 +-
.../sidecar/cluster/InstancesMetadataImpl.java | 123 ++++++++++++----
.../sidecar/cluster/instance/InstanceMetadata.java | 19 ++-
.../cluster/instance/InstanceMetadataImpl.java | 43 +++++-
....java => NoSuchCassandraInstanceException.java} | 12 +-
.../sidecar/metrics/SidecarMetricsImpl.java | 4 +-
.../cassandra/sidecar/routes/AbstractHandler.java | 4 +-
.../sidecar/utils/InstanceMetadataFetcher.java | 20 +--
.../sidecar/testing/IntegrationTestModule.java | 10 +-
.../sidecar/cluster/InstancesMetadataImplTest.java | 161 +++++++++++++++++++++
.../cluster/instance/InstanceMetadataImplTest.java | 55 ++++++-
15 files changed, 426 insertions(+), 74 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 973505f2..6bf814de 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -121,7 +121,7 @@ jobs:
- attach_workspace:
at: dtest-jars
- run: ./scripts/install-shaded-dtest-jar-local.sh
- - run: ./gradlew --no-daemon -PdtestVersion=4.1.8
-Dcassandra.sidecar.versions_to_test="4.0" checkstyleIntegrationTest
spotbugsIntegrationTest integrationTestLightWeight --stacktrace
+ - run: ./gradlew --no-daemon -PdtestVersion=4.1.8
-Dcassandra.sidecar.versions_to_test="4.0" checkstyleIntegrationTest
integrationTestLightWeight --stacktrace
- store_artifacts:
path: build/reports
@@ -173,7 +173,7 @@ jobs:
- attach_workspace:
at: dtest-jars
- run: ./scripts/install-shaded-dtest-jar-local.sh
- - run: ./gradlew --no-daemon -PdtestVersion=5.0.3
-Dcassandra.sidecar.versions_to_test="5.0" checkstyleIntegrationTest
spotbugsIntegrationTest integrationTestLightWeight --stacktrace
+ - run: ./gradlew --no-daemon -PdtestVersion=5.0.3
-Dcassandra.sidecar.versions_to_test="5.0" checkstyleIntegrationTest
integrationTestLightWeight --stacktrace
- store_artifacts:
path: build/reports
@@ -225,7 +225,7 @@ jobs:
- attach_workspace:
at: dtest-jars
- run: ./scripts/install-shaded-dtest-jar-local.sh
- - run: ./gradlew --no-daemon -PdtestVersion=5.1
-Dcassandra.sidecar.versions_to_test="5.1" checkstyleIntegrationTest
spotbugsIntegrationTest integrationTestLightWeight --stacktrace
+ - run: ./gradlew --no-daemon -PdtestVersion=5.1
-Dcassandra.sidecar.versions_to_test="5.1" checkstyleIntegrationTest
integrationTestLightWeight --stacktrace
- store_artifacts:
path: build/reports
diff --git a/CHANGES.txt b/CHANGES.txt
index 6cc3557e..b5177189 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
1.0.0
-----
+ * Fix Cassandra instance not found error (CASSSIDECAR-192)
* Implemented Schema Reporter for Integration with DataHub (CASSSIDECAR-191)
* Add sidecar endpoint to retrieve stream stats (CASSSIDECAR-180)
* Add sidecar endpoint to retrieve cassandra gossip health (CASSSIDECAR-173)
diff --git a/build.gradle b/build.gradle
index f04390d2..599f90b1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,7 +63,21 @@ def codeCheckTasks = task("codeCheckTasks")
allprojects {
apply plugin: 'jacoco'
apply plugin: 'checkstyle'
- apply plugin: "com.github.spotbugs"
+
+ // do not run spot-bug on test-specific sub-projects
+ if (!it.name.startsWith('integration-') && !it.name.startsWith("test-"))
+ {
+ apply plugin: "com.github.spotbugs"
+ spotbugs {
+ toolVersion = '4.2.3'
+ excludeFilter = file("${project.rootDir}/spotbugs-exclude.xml")
+ }
+ codeCheckTasks.dependsOn(tasks.withType(SpotBugsTask))
+ tasks.withType(Test) {
+ // define the order of the tasks, if SpotBugsTask runs
+ shouldRunAfter(tasks.withType(SpotBugsTask))
+ }
+ }
repositories {
mavenCentral()
@@ -76,20 +90,17 @@ allprojects {
toolVersion '7.8.1'
configFile file("${project.rootDir}/checkstyle.xml")
}
- spotbugs {
- toolVersion = '4.2.3'
- excludeFilter = file("${project.rootDir}/spotbugs-exclude.xml")
- }
+ // must run the dependant tasks in order to run codeCheckTasks
+ codeCheckTasks.dependsOn(tasks.withType(Javadoc))
codeCheckTasks.dependsOn(tasks.withType(Checkstyle))
codeCheckTasks.dependsOn(tasks.withType(RatTask))
- codeCheckTasks.dependsOn(tasks.withType(SpotBugsTask))
tasks.withType(Test) {
+ // define the order of the tasks, if the below tasks runs
shouldRunAfter(codeCheckTasks)
shouldRunAfter(tasks.withType(Checkstyle))
shouldRunAfter(tasks.withType(RatTask))
- shouldRunAfter(tasks.withType(SpotBugsTask))
}
}
@@ -315,7 +326,7 @@ rat {
installDist.dependsOn copyJolokia
check.dependsOn codeCheckTasks
if (JavaVersion.current().isJava11Compatible()) {
- build.dependsOn copyJolokia, copyDocs
+ build.dependsOn codeCheckTasks, copyJolokia, copyDocs
}
run.dependsOn build
diff --git a/gradle/common/integrationTestTask.gradle
b/gradle/common/integrationTestTask.gradle
index f5bdab8b..38547b61 100644
--- a/gradle/common/integrationTestTask.gradle
+++ b/gradle/common/integrationTestTask.gradle
@@ -95,4 +95,3 @@ task("integrationTest").dependsOn integrationTestLightWeight,
integrationTestHea
compileIntegrationTestJava.onlyIf { "true" !=
System.getenv("skipIntegrationTest") }
checkstyleIntegrationTest.onlyIf { "true" !=
System.getenv("skipIntegrationTest") }
-spotbugsIntegrationTest.onlyIf { "true" !=
System.getenv("skipIntegrationTest") }
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadata.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadata.java
index f9d91c9d..a8bcf1c4 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadata.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadata.java
@@ -21,7 +21,7 @@ package org.apache.cassandra.sidecar.cluster;
import java.util.List;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.jetbrains.annotations.NotNull;
/**
@@ -43,16 +43,18 @@ public interface InstancesMetadata
*
* @param id instance's id
* @return instance meta information
- * @throws NoSuchSidecarInstanceException when the instance with {@code
id} does not exist
+ * @throws NoSuchCassandraInstanceException when the instance with {@code
id} does not exist
*/
- InstanceMetadata instanceFromId(int id) throws
NoSuchSidecarInstanceException;
+ @NotNull
+ InstanceMetadata instanceFromId(int id) throws
NoSuchCassandraInstanceException;
/**
* Lookup instance metadata by host name.
*
- * @param host host address of instance
+ * @param hostOrIpAddress hostname or IP address of instance
* @return instance meta information
- * @throws NoSuchSidecarInstanceException when the instance for {@code
host} does not exist
+ * @throws NoSuchCassandraInstanceException when the instance for {@code
host} does not exist
*/
- InstanceMetadata instanceFromHost(String host) throws
NoSuchSidecarInstanceException;
+ @NotNull
+ InstanceMetadata instanceFromHost(String hostOrIpAddress) throws
NoSuchCassandraInstanceException;
}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImpl.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImpl.java
index 6a42e915..3016b214 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImpl.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImpl.java
@@ -20,14 +20,18 @@ package org.apache.cassandra.sidecar.cluster;
import java.net.UnknownHostException;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
import org.apache.cassandra.sidecar.common.server.dns.DnsResolver;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.jetbrains.annotations.NotNull;
/**
@@ -35,10 +39,15 @@ import org.jetbrains.annotations.NotNull;
*/
public class InstancesMetadataImpl implements InstancesMetadata
{
- private final Map<Integer, InstanceMetadata> idToInstanceMetadata;
- private final Map<String, InstanceMetadata> hostToInstanceMetadata;
- private final List<InstanceMetadata> instanceMetadataList;
+ private static final Logger LOGGER =
LoggerFactory.getLogger(InstancesMetadataImpl.class);
+ private static final long ONE_SECOND = TimeUnit.SECONDS.toMillis(1);
+
+ private final AtomicLong lastUpdateTimestamp;
private final DnsResolver dnsResolver;
+ private final List<InstanceMetadata> instanceMetadataList;
+ private final Map<Integer, InstanceMetadata> idToInstanceMetadata;
+ private final Map<String, InstanceMetadata> hostNameToInstanceMetadata;
+ private volatile Map<String, InstanceMetadata> ipToInstanceMetadata;
public InstancesMetadataImpl(InstanceMetadata instanceMetadata,
DnsResolver dnsResolver)
{
@@ -47,14 +56,22 @@ public class InstancesMetadataImpl implements
InstancesMetadata
public InstancesMetadataImpl(List<InstanceMetadata> instanceMetadataList,
DnsResolver dnsResolver)
{
- this.idToInstanceMetadata = instanceMetadataList.stream()
-
.collect(Collectors.toMap(InstanceMetadata::id,
-
Function.identity()));
- this.hostToInstanceMetadata = instanceMetadataList.stream()
-
.collect(Collectors.toMap(InstanceMetadata::host,
-
Function.identity()));
- this.instanceMetadataList = instanceMetadataList;
this.dnsResolver = dnsResolver;
+ this.instanceMetadataList = instanceMetadataList;
+ this.idToInstanceMetadata = new HashMap<>(instanceMetadataList.size());
+ this.ipToInstanceMetadata = new HashMap<>(instanceMetadataList.size());
+ this.hostNameToInstanceMetadata = new
HashMap<>(instanceMetadataList.size());
+ for (InstanceMetadata instanceMetadata : instanceMetadataList)
+ {
+ this.idToInstanceMetadata.put(instanceMetadata.id(),
instanceMetadata);
+ if (instanceMetadata.ipAddress() != null)
+ {
+ this.ipToInstanceMetadata.put(instanceMetadata.ipAddress(),
instanceMetadata);
+ }
+ // 'host' could be IP already, in such case,
hostNameToInstanceMetadata map is identical to ipToInstanceMetadata
+ this.hostNameToInstanceMetadata.put(instanceMetadata.host(),
instanceMetadata);
+ }
+ this.lastUpdateTimestamp = new AtomicLong(0); // The field stores the
timestamp value, but let's not query system clock for initialization
}
@Override
@@ -64,37 +81,91 @@ public class InstancesMetadataImpl implements
InstancesMetadata
}
@Override
- public InstanceMetadata instanceFromId(int id) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instanceFromId(int id) throws
NoSuchCassandraInstanceException
{
InstanceMetadata instanceMetadata = idToInstanceMetadata.get(id);
if (instanceMetadata == null)
{
- throw new NoSuchSidecarInstanceException("Instance id '" + id + "'
not found");
+ throw new NoSuchCassandraInstanceException("Instance id '" + id +
"' not found");
}
return instanceMetadata;
}
@Override
- public InstanceMetadata instanceFromHost(String host) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instanceFromHost(String hostOrIpAddress) throws
NoSuchCassandraInstanceException
{
- InstanceMetadata instanceMetadata = hostToInstanceMetadata.get(host);
- if (instanceMetadata == null)
+ // if the input string is hostname string, resolve the ip address and
loop up again
+ InstanceMetadata instanceMetadata =
hostNameToInstanceMetadata.get(hostOrIpAddress);
+ if (instanceMetadata != null)
+ {
+ return instanceMetadata;
+ }
+
+ // the input string is probably IP address string
+ instanceMetadata = ipToInstanceMetadata.get(hostOrIpAddress);
+ if (instanceMetadata != null)
+ {
+ return instanceMetadata;
+ }
+
+ // maybe the input string is host, try to resolve it
+ String ipAddress = hostOrIpAddress;
+ try
{
+ ipAddress = dnsResolver.resolve(hostOrIpAddress);
+ }
+ catch (UnknownHostException e)
+ {
+ // Do not exit; try to look up again after updating the
IpToInstanceMetadata map
+ LOGGER.debug("Failed to resolve IP address for {}",
hostOrIpAddress);
+ }
+
+ // maybe the IP of the hostname has been updated in the DNS, and
client is using the updated IP
+ // update the ipToInstanceMetadata and look up with the IP address
again
+ maybeUpdateIpToInstanceMetadata();
+ instanceMetadata = ipToInstanceMetadata.get(ipAddress);
+ if (instanceMetadata != null)
+ {
+ return instanceMetadata;
+ }
+
+ throw new NoSuchCassandraInstanceException("Instance with host address
'" + hostOrIpAddress + "' not found");
+ }
+
+ // Update the IpToInstanceMetadata if it has been enough time since the
last update
+ //
+ // Memory visibility effects:
+ // The method does not guarantee the caller see the latest value in the
IpToInstanceMetadata when the method exits.
+ // However, once the reference of IpToInstanceMetadata is updated, the
subsequent procedures should see the updated value
+ private void maybeUpdateIpToInstanceMetadata()
+ {
+ long now = System.currentTimeMillis();
+ long lastValue = lastUpdateTimestamp.get();
+ if (now - lastValue <= ONE_SECOND // update at most once a second
+ || !lastUpdateTimestamp.compareAndSet(lastValue, now)) // another
thread has updated the timestamp. let it update the map
+ {
+ return;
+ }
+
+ Map<String, InstanceMetadata> updated = new
HashMap<>(instanceMetadataList.size());
+ for (InstanceMetadata instanceMetadata : instanceMetadataList)
+ {
+ String ipAddress = instanceMetadata.ipAddress(); // existing ip
try
{
- instanceMetadata =
hostToInstanceMetadata.get(dnsResolver.resolve(host));
+ ipAddress = instanceMetadata.refreshIpAddress(); // updated ip
}
- catch (UnknownHostException cause)
+ catch (UnknownHostException uhe)
{
- throw new NoSuchSidecarInstanceException("Instance with host
address '" + host + "' not found, "
- + "and an error
occurred when attempting to resolve its "
- + "IP address.",
cause);
+ // log a warning and continue; Do not update the ipAddress for
this instance
+ LOGGER.warn("Failed to resolve IP address from host. Going to
use the existing IP address. host={}",
+ instanceMetadata.host(), uhe);
}
- if (instanceMetadata == null)
+ if (ipAddress != null)
{
- throw new NoSuchSidecarInstanceException("Instance with host
address '" + host + "' not found");
+ updated.put(ipAddress, instanceMetadata);
}
}
- return instanceMetadata;
+ this.ipToInstanceMetadata = updated;
}
}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadata.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadata.java
index cb7065ee..5abcea8b 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadata.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadata.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.sidecar.cluster.instance;
+import java.net.UnknownHostException;
import java.util.List;
import org.apache.cassandra.sidecar.cluster.CassandraAdapterDelegate;
@@ -37,10 +38,26 @@ public interface InstanceMetadata
int id();
/**
- * @return the host address of the Cassandra instance
+ * @return the hostname or IP address of the Cassandra instance
*/
String host();
+ /**
+ * @return the IP address of the Cassandra instance. When no IP address is
resolved, it returns null
+ */
+ @Nullable
+ String ipAddress();
+
+ /**
+ * Resolve the ipAddress and update.
+ * @return the IP address resolved
+ * @throws UnknownHostException when hostname cannot be resolved to IP
address
+ */
+ default String refreshIpAddress() throws UnknownHostException
+ {
+ return ipAddress();
+ }
+
/**
* @return the native transport port number of the Cassandra instance
*/
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImpl.java
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImpl.java
index 7fc79413..729bc8d4 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImpl.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImpl.java
@@ -19,6 +19,7 @@
package org.apache.cassandra.sidecar.cluster.instance;
import java.io.File;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -28,6 +29,7 @@ import java.util.stream.Collectors;
import com.codahale.metrics.MetricRegistry;
import org.apache.cassandra.sidecar.cluster.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.common.DataObjectBuilder;
+import org.apache.cassandra.sidecar.common.server.dns.DnsResolver;
import org.apache.cassandra.sidecar.common.utils.Preconditions;
import org.apache.cassandra.sidecar.exceptions.CassandraUnavailableException;
import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
@@ -63,11 +65,15 @@ public class InstanceMetadataImpl implements
InstanceMetadata
@Nullable
private final CassandraAdapterDelegate delegate;
private final InstanceMetrics metrics;
+ private final DnsResolver dnsResolver;
+ private volatile String ipAddress;
protected InstanceMetadataImpl(Builder builder)
{
id = builder.id;
host = builder.host;
+ dnsResolver = builder.dnsResolver;
+ ipAddress = builder.ipAddress;
port = builder.port;
delegate = builder.delegate;
metrics = builder.metrics;
@@ -96,6 +102,20 @@ public class InstanceMetadataImpl implements
InstanceMetadata
return host;
}
+ @Nullable
+ @Override
+ public String ipAddress()
+ {
+ return ipAddress;
+ }
+
+ @Override
+ public String refreshIpAddress() throws UnknownHostException
+ {
+ this.ipAddress = dnsResolver.resolve(host);
+ return ipAddress;
+ }
+
@Override
public int port()
{
@@ -187,8 +207,10 @@ public class InstanceMetadataImpl implements
InstanceMetadata
*/
public static class Builder implements DataObjectBuilder<Builder,
InstanceMetadataImpl>
{
+ protected DnsResolver dnsResolver;
protected Integer id;
protected String host;
+ protected String ipAddress;
protected int port;
protected String storageDir;
protected List<String> dataDirs;
@@ -210,6 +232,7 @@ public class InstanceMetadataImpl implements
InstanceMetadata
{
id = instanceMetadata.id;
host = instanceMetadata.host;
+ ipAddress = instanceMetadata.ipAddress;
port = instanceMetadata.port;
dataDirs = new ArrayList<>(instanceMetadata.dataDirs);
stagingDir = instanceMetadata.stagingDir;
@@ -240,14 +263,30 @@ public class InstanceMetadataImpl implements
InstanceMetadata
}
/**
- * Sets the {@code host} and returns a reference to this Builder
enabling method chaining.
+ * Sets the {@code host} and the {@code ipAddress} resolved by {@link
DnsResolver#DEFAULT}
+ * and returns a reference to this Builder enabling method chaining.
*
* @param host the {@code host} to set
* @return a reference to this Builder
*/
public Builder host(String host)
{
- return update(b -> b.host = host);
+ return host(host, DnsResolver.DEFAULT);
+ }
+
+ /**
+ * Sets the {@code host} and the {@code ipAddress} resolved by
dnsResolver
+ * and returns a reference to this Builder enabling method chaining.
+ *
+ * @param host the {@code host} to set
+ * @return a reference to this Builder
+ */
+ public Builder host(String host, DnsResolver dnsResolver)
+ {
+ return update(b -> {
+ b.host = host;
+ b.dnsResolver = dnsResolver;
+ });
}
/**
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchSidecarInstanceException.java
b/server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchCassandraInstanceException.java
similarity index 78%
rename from
server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchSidecarInstanceException.java
rename to
server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchCassandraInstanceException.java
index ce91fa73..972ec120 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchSidecarInstanceException.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/exceptions/NoSuchCassandraInstanceException.java
@@ -21,24 +21,24 @@ package org.apache.cassandra.sidecar.exceptions;
import java.util.NoSuchElementException;
/**
- * Thrown when the Sidecar instance is not found in the metadata
+ * Thrown when the Cassandra instance is not found in the metadata
*/
-public class NoSuchSidecarInstanceException extends NoSuchElementException
+public class NoSuchCassandraInstanceException extends NoSuchElementException
{
/**
- * Constructs a {@link NoSuchSidecarInstanceException}, saving a reference
+ * Constructs a {@link NoSuchCassandraInstanceException}, saving a
reference
* to the error message string {@code errorMessage} for later retrieval by
the
* {@code getMessage} method.
*
* @param errorMessage the detail message.
*/
- public NoSuchSidecarInstanceException(String errorMessage)
+ public NoSuchCassandraInstanceException(String errorMessage)
{
super(errorMessage);
}
/**
- * Constructs a {@link NoSuchSidecarInstanceException} with the specified
detail
+ * Constructs a {@link NoSuchCassandraInstanceException} with the
specified detail
* message and cause.
*
* @param message the detail message (which is saved for later retrieval
@@ -48,7 +48,7 @@ public class NoSuchSidecarInstanceException extends
NoSuchElementException
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
- public NoSuchSidecarInstanceException(String message, Throwable cause)
+ public NoSuchCassandraInstanceException(String message, Throwable cause)
{
super(message);
initCause(cause);
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/metrics/SidecarMetricsImpl.java
b/server/src/main/java/org/apache/cassandra/sidecar/metrics/SidecarMetricsImpl.java
index 501ce62c..0e1d278c 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/metrics/SidecarMetricsImpl.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/metrics/SidecarMetricsImpl.java
@@ -20,7 +20,7 @@ package org.apache.cassandra.sidecar.metrics;
import com.codahale.metrics.MetricRegistry;
import org.apache.cassandra.sidecar.exceptions.CassandraUnavailableException;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.apache.cassandra.sidecar.metrics.instance.InstanceMetrics;
import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
@@ -55,7 +55,7 @@ public class SidecarMetricsImpl implements SidecarMetrics
}
@Override
- public InstanceMetrics instance(String host) throws
NoSuchSidecarInstanceException, CassandraUnavailableException
+ public InstanceMetrics instance(String host) throws
NoSuchCassandraInstanceException, CassandraUnavailableException
{
return instanceMetadataFetcher.instance(host).metrics();
}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
b/server/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
index 976256a0..e36bbe6d 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/routes/AbstractHandler.java
@@ -36,7 +36,7 @@ import org.apache.cassandra.sidecar.common.server.data.Name;
import org.apache.cassandra.sidecar.common.server.data.QualifiedTableName;
import
org.apache.cassandra.sidecar.common.server.exceptions.JmxAuthenticationException;
import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.apache.cassandra.sidecar.utils.CassandraInputValidator;
import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
@@ -209,7 +209,7 @@ public abstract class AbstractHandler<T> implements
Handler<RoutingContext>
return wrapHttpException(HttpResponseStatus.SERVICE_UNAVAILABLE,
cause.getMessage(), cause);
}
- if (cause instanceof NoSuchSidecarInstanceException)
+ if (cause instanceof NoSuchCassandraInstanceException)
{
return wrapHttpException(HttpResponseStatus.MISDIRECTED_REQUEST,
cause.getMessage(), cause);
}
diff --git
a/server/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
b/server/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
index c12b1710..9c6242b9 100644
---
a/server/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
+++
b/server/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
@@ -27,7 +27,7 @@ import
org.apache.cassandra.sidecar.cluster.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.cluster.InstancesMetadata;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
import org.apache.cassandra.sidecar.exceptions.CassandraUnavailableException;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,12 +49,12 @@ public class InstanceMetadataFetcher
* Returns the {@link InstanceMetadata} for the given {@code host}. When
the {@code host} is {@code null},
* returns the first instance from the list of configured instances.
*
- * @param host the Cassandra instance host
+ * @param host the Cassandra instance hostname or IP address
* @return the {@link InstanceMetadata} for the given {@code host}, or the
first instance when {@code host} is
* {@code null}
*/
@NotNull
- public InstanceMetadata instance(@Nullable String host) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instance(@Nullable String host) throws
NoSuchCassandraInstanceException
{
return host == null
? firstInstance()
@@ -68,10 +68,10 @@ public class InstanceMetadataFetcher
* @param instanceId the identifier for the Cassandra instance
* @return the {@link InstanceMetadata} for the given {@code instanceId},
or the first instance when
* {@code instanceId} is {@code null}
- * @throws NoSuchSidecarInstanceException when the Cassandra instance with
{@code instanceId} does not exist
+ * @throws NoSuchCassandraInstanceException when the Cassandra instance
with {@code instanceId} does not exist
*/
@NotNull
- public InstanceMetadata instance(int instanceId) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instance(int instanceId) throws
NoSuchCassandraInstanceException
{
return instancesMetadata.instanceFromId(instanceId);
}
@@ -80,14 +80,14 @@ public class InstanceMetadataFetcher
* Returns the {@link CassandraAdapterDelegate} for the given {@code
host}. When the {@code host} is {@code null},
* returns the delegate for the first instance from the list of configured
instances.
*
- * @param host the Cassandra instance host
+ * @param host the Cassandra instance hostname or IP address
* @return the {@link CassandraAdapterDelegate} for the given {@code
host}, or the first instance when {@code host}
* is {@code null}
- * @throws NoSuchSidecarInstanceException when the Cassandra instance with
{@code host} does not exist
+ * @throws NoSuchCassandraInstanceException when the Cassandra instance
with {@code host} does not exist
* @throws CassandraUnavailableException when Cassandra is not yet
connected
*/
@NotNull
- public CassandraAdapterDelegate delegate(@Nullable String host) throws
NoSuchSidecarInstanceException, CassandraUnavailableException
+ public CassandraAdapterDelegate delegate(@Nullable String host) throws
NoSuchCassandraInstanceException, CassandraUnavailableException
{
return instance(host).delegate();
}
@@ -97,11 +97,11 @@ public class InstanceMetadataFetcher
*
* @param instanceId the identifier for the Cassandra instance
* @return the {@link CassandraAdapterDelegate} for the given {@code
instanceId}
- * @throws NoSuchSidecarInstanceException when the Cassandra instance with
{@code instanceId} does not exist
+ * @throws NoSuchCassandraInstanceException when the Cassandra instance
with {@code instanceId} does not exist
* @throws CassandraUnavailableException when Cassandra is not yet
connected
*/
@NotNull
- public CassandraAdapterDelegate delegate(int instanceId) throws
NoSuchSidecarInstanceException, CassandraUnavailableException
+ public CassandraAdapterDelegate delegate(int instanceId) throws
NoSuchCassandraInstanceException, CassandraUnavailableException
{
return instance(instanceId).delegate();
}
diff --git
a/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
b/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
index dc341770..df165815 100644
---
a/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
+++
b/server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java
@@ -52,7 +52,7 @@ import
org.apache.cassandra.sidecar.config.yaml.SidecarConfigurationImpl;
import org.apache.cassandra.sidecar.config.yaml.SslConfigurationImpl;
import org.apache.cassandra.sidecar.config.yaml.TestServiceConfiguration;
import org.apache.cassandra.sidecar.coordination.ClusterLease;
-import org.apache.cassandra.sidecar.exceptions.NoSuchSidecarInstanceException;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
import org.jetbrains.annotations.NotNull;
import static
org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_SERVER_STOP;
@@ -213,10 +213,10 @@ public class IntegrationTestModule extends AbstractModule
*
* @param id instance's id
* @return instance meta information
- * @throws NoSuchSidecarInstanceException when the instance with
{@code id} does not exist
+ * @throws NoSuchCassandraInstanceException when the instance with
{@code id} does not exist
*/
@Override
- public InstanceMetadata instanceFromId(int id) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instanceFromId(int id) throws
NoSuchCassandraInstanceException
{
return cassandraTestContext.instancesMetadata().instanceFromId(id);
}
@@ -226,10 +226,10 @@ public class IntegrationTestModule extends AbstractModule
*
* @param host host address of instance
* @return instance meta information
- * @throws NoSuchSidecarInstanceException when the instance for {@code
host} does not exist
+ * @throws NoSuchCassandraInstanceException when the instance for
{@code host} does not exist
*/
@Override
- public InstanceMetadata instanceFromHost(String host) throws
NoSuchSidecarInstanceException
+ public InstanceMetadata instanceFromHost(String host) throws
NoSuchCassandraInstanceException
{
return
cassandraTestContext.instancesMetadata().instanceFromHost(host);
}
diff --git
a/server/src/test/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImplTest.java
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImplTest.java
new file mode 100644
index 00000000..16832286
--- /dev/null
+++
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/InstancesMetadataImplTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cassandra.sidecar.cluster;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import com.codahale.metrics.MetricRegistry;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadataImpl;
+import org.apache.cassandra.sidecar.common.server.dns.DnsResolver;
+import
org.apache.cassandra.sidecar.exceptions.NoSuchCassandraInstanceException;
+
+import static org.apache.cassandra.testing.utils.AssertionUtils.loopAssert;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.fail;
+
+class InstancesMetadataImplTest
+{
+ static final MetricRegistry METRIC_REGISTRY = new MetricRegistry();
+
+ final AtomicReference<String> localhost1NewIp = new
AtomicReference<>(null);
+ final DnsResolver localhostResolver = new DnsResolver()
+ {
+ @Override
+ public String resolve(String hostname)
+ {
+ String ipPrefix = "127.0.0.";
+ if (hostname.startsWith("127."))
+ {
+ // it is ip address already
+ return hostname;
+ }
+
+ String trimmed = hostname.replace("localhost", "");
+ if (trimmed.isEmpty())
+ {
+ return ipPrefix + '1';
+ }
+ int digit = Integer.parseInt(trimmed);
+ if (digit == 1 && localhost1NewIp.get() != null)
+ {
+ return localhost1NewIp.get();
+ }
+ return ipPrefix + digit;
+ }
+
+ @Override
+ public String reverseResolve(String address)
+ {
+ return ""; // not examined in this test
+ }
+ };
+
+ @TempDir
+ Path tempDir;
+
+ @Test
+ void testLookupByHostName()
+ {
+ List<InstanceMetadata> instances = Arrays.asList(instance(1,
"127.0.0.1"),
+ instance(2,
"127.0.0.2"),
+ instance(3,
"127.0.0.3"));
+ InstancesMetadataImpl instancesMetadata = new
InstancesMetadataImpl(instances, localhostResolver);
+
assertThat(instancesMetadata.instanceFromHost("localhost").id()).isEqualTo(1);
+
assertThat(instancesMetadata.instanceFromHost("localhost1").id()).isEqualTo(1);
+
assertThat(instancesMetadata.instanceFromHost("localhost2").id()).isEqualTo(2);
+
assertThat(instancesMetadata.instanceFromHost("localhost3").id()).isEqualTo(3);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.1").id()).isEqualTo(1);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.2").id()).isEqualTo(2);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.3").id()).isEqualTo(3);
+
+ assertThatThrownBy(() ->
instancesMetadata.instanceFromHost("localhost999"))
+ .isExactlyInstanceOf(NoSuchCassandraInstanceException.class)
+ .hasMessage("Instance with host address 'localhost999' not found");
+ }
+
+ @Test
+ void testLookupByIPAddress()
+ {
+ List<InstanceMetadata> instances = Arrays.asList(instance(1,
"localhost1"),
+ instance(2,
"localhost2"),
+ instance(3,
"localhost3"));
+ InstancesMetadataImpl instancesMetadata = new
InstancesMetadataImpl(instances, localhostResolver);
+
assertThat(instancesMetadata.instanceFromHost("localhost1").id()).isEqualTo(1);
+
assertThat(instancesMetadata.instanceFromHost("localhost2").id()).isEqualTo(2);
+
assertThat(instancesMetadata.instanceFromHost("localhost3").id()).isEqualTo(3);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.1").id()).isEqualTo(1);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.2").id()).isEqualTo(2);
+
assertThat(instancesMetadata.instanceFromHost("127.0.0.3").id()).isEqualTo(3);
+
+ String newIp = "127.1.2.3";
+ assertThatThrownBy(() -> instancesMetadata.instanceFromHost(newIp))
+ .isExactlyInstanceOf(NoSuchCassandraInstanceException.class)
+ .hasMessage("Instance with host address '127.1.2.3' not found");
+
+ localhost1NewIp.set(newIp);
+ loopAssert(2, 100, () -> {
+ // wait for the cache to be updated
+ try
+ {
+
assertThat(instancesMetadata.instanceFromHost(newIp).id()).isEqualTo(1);
+ }
+ catch (NoSuchCassandraInstanceException e)
+ {
+ fail(e.getMessage());
+ // continue
+ }
+ });
+ }
+
+ @Test
+ void testLookupById()
+ {
+ List<InstanceMetadata> instances = Arrays.asList(instance(1,
"localhost1"),
+ instance(2,
"localhost2"),
+ instance(3,
"localhost3"));
+ InstancesMetadataImpl instancesMetadata = new
InstancesMetadataImpl(instances, localhostResolver);
+
assertThat(instancesMetadata.instanceFromId(1).host()).isEqualTo("localhost1");
+
assertThat(instancesMetadata.instanceFromId(2).host()).isEqualTo("localhost2");
+
assertThat(instancesMetadata.instanceFromId(3).host()).isEqualTo("localhost3");
+
+ assertThatThrownBy(() -> instancesMetadata.instanceFromId(123))
+ .isExactlyInstanceOf(NoSuchCassandraInstanceException.class)
+ .hasMessage("Instance id '123' not found");
+ }
+
+ InstanceMetadata instance(int id, String hostNameOrIp)
+ {
+ String root = tempDir.toString();
+ return InstanceMetadataImpl.builder()
+ .id(id)
+ .host(hostNameOrIp, localhostResolver)
+ .port(9042)
+ .storageDir(root)
+ .metricRegistry(METRIC_REGISTRY)
+ .build();
+ }
+}
diff --git
a/server/src/test/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImplTest.java
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImplTest.java
index fa4a024f..07029a9e 100644
---
a/server/src/test/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImplTest.java
+++
b/server/src/test/java/org/apache/cassandra/sidecar/cluster/instance/InstanceMetadataImplTest.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.sidecar.cluster.instance;
+import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,15 +29,17 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import com.codahale.metrics.MetricRegistry;
+import org.apache.cassandra.sidecar.common.server.dns.DnsResolver;
import static org.assertj.core.api.Assertions.assertThat;
import static
org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
class InstanceMetadataImplTest
{
private static final int ID = 123;
- private static final String HOST = "testhost";
+ private static final String HOST = "localhost";
private static final int PORT = 12345;
private static final String DATA_DIR_1 = "test/data/data1";
private static final String DATA_DIR_2 = "test/data/data2";
@@ -270,7 +273,33 @@ class InstanceMetadataImplTest
assertThat(metadata.savedCachesDir()).isEqualTo(rootDir +
"/saved_caches");
}
- InstanceMetadataImpl.Builder getInstanceMetadataBuilder(String rootDir)
+ @Test
+ void testResolveIpAddress() throws Exception
+ {
+ String rootDir = tempDir.toString();
+ InstanceMetadataImpl instance =
getInstanceMetadataBuilder(rootDir).host("localhost").build();
+ instance.refreshIpAddress();
+ assertThat(instance.ipAddress()).isEqualTo("127.0.0.1");
+
+ String host = "cassandra.sidecar.org";
+ instance = getInstanceMetadataBuilder(rootDir).host(host,
createDnsResolver(host, "127.0.0.1")).build();
+ instance.refreshIpAddress();
+ assertThat(instance.ipAddress()).isEqualTo("127.0.0.1");
+ }
+
+ @Test
+ void testIpAddressResolutionFails()
+ {
+ String rootDir = tempDir.toString();
+ InstanceMetadataImpl instanceMetadata =
getInstanceMetadataBuilder(rootDir)
+ .host("my_host",
createDnsResolver("localhost", "127.0.0.1"))
+ .build();
+ assertThatThrownBy(instanceMetadata::refreshIpAddress)
+ .isExactlyInstanceOf(UnknownHostException.class)
+ .hasMessage("my_host");
+ }
+
+ static InstanceMetadataImpl.Builder getInstanceMetadataBuilder(String
rootDir)
{
List<String> dataDirs = new ArrayList<>();
dataDirs.add(rootDir + "/" + DATA_DIR_1);
@@ -289,4 +318,26 @@ class InstanceMetadataImplTest
.localSystemDataFileDir(rootDir + "/" +
LOCAL_SYSTEM_DATA_FILE_DIR)
.metricRegistry(METRIC_REGISTRY);
}
+
+ private DnsResolver createDnsResolver(String hostName, String ipAddress)
+ {
+ return new DnsResolver()
+ {
+ @Override
+ public String resolve(String toResolve) throws UnknownHostException
+ {
+ if (toResolve.equalsIgnoreCase(hostName))
+ {
+ return ipAddress;
+ }
+ throw new UnknownHostException(toResolve);
+ }
+
+ @Override
+ public String reverseResolve(String address) throws
UnknownHostException
+ {
+ return ""; // won't be examined
+ }
+ };
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]