diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
index 214a7e373..8a96afa2e 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -6,7 +6,7 @@
com.aerospike
aerospike-parent
- 9.0.3
+ 9.0.4
aerospike-benchmarks
jar
diff --git a/client/pom.xml b/client/pom.xml
index 6aac92422..a659f5561 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -6,7 +6,7 @@
com.aerospike
aerospike-parent
- 9.0.3
+ 9.0.4
aerospike-client${crypto.type}-jdk21
jar
diff --git a/client/src/com/aerospike/client/async/AsyncTxnMonitor.java b/client/src/com/aerospike/client/async/AsyncTxnMonitor.java
index 81a7c004c..7e786bcd9 100644
--- a/client/src/com/aerospike/client/async/AsyncTxnMonitor.java
+++ b/client/src/com/aerospike/client/async/AsyncTxnMonitor.java
@@ -43,6 +43,8 @@ public static void execute(EventLoop eventLoop, Cluster cluster, WritePolicy pol
}
Txn txn = policy.txn;
+ txn.verifyCommand();
+
Key cmdKey = command.key;
if (txn.getWrites().contains(cmdKey)) {
diff --git a/client/src/com/aerospike/client/cluster/Cluster.java b/client/src/com/aerospike/client/cluster/Cluster.java
index 740e42679..5e8b6ce64 100644
--- a/client/src/com/aerospike/client/cluster/Cluster.java
+++ b/client/src/com/aerospike/client/cluster/Cluster.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2024 Aerospike, Inc.
+ * Copyright 2012-2025 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -67,10 +68,6 @@ public class Cluster implements Runnable, Closeable {
// Initial host nodes specified by user.
private volatile Host[] seeds;
- // All host aliases for all nodes in cluster.
- // Only accessed within cluster tend thread.
- protected final HashMap aliases;
-
// Map of active nodes in cluster.
// Only accessed within cluster tend thread.
protected final HashMap nodesMap;
@@ -314,7 +311,6 @@ else if (policy.user != null && policy.user.length() > 0) {
rackIds = new int[] {policy.rackId};
}
- aliases = new HashMap();
nodesMap = new HashMap();
nodes = new Node[0];
partitionMap = new HashMap();
@@ -560,8 +556,8 @@ private final void tend(boolean failIfNotConnected, boolean isInit) {
findNodesToRemove(peers);
// Remove nodes in a batch.
- if (peers.removeList.size() > 0) {
- removeNodes(peers.removeList);
+ if (peers.removeNodes.size() > 0) {
+ removeNodes(peers.removeNodes);
}
}
@@ -751,13 +747,13 @@ protected Node createNode(NodeValidator nv) {
}
private final void findNodesToRemove(Peers peers) {
- int refreshCount = peers.refreshCount;
- ArrayList removeList = peers.removeList;
+ int refreshCount = peers.refreshCount;
+ HashSet removeNodes = peers.removeNodes;
for (Node node : nodes) {
if (! node.isActive()) {
// Inactive nodes must be removed.
- removeList.add(node);
+ removeNodes.add(node);
continue;
}
@@ -765,7 +761,7 @@ private final void findNodesToRemove(Peers peers) {
// All node info requests failed and this node had 5 consecutive failures.
// Remove node. If no nodes are left, seeds will be tried in next cluster
// tend iteration.
- removeList.add(node);
+ removeNodes.add(node);
continue;
}
@@ -777,12 +773,12 @@ private final void findNodesToRemove(Peers peers) {
if (! findNodeInPartitionMap(node)) {
// Node doesn't have any partitions mapped to it.
// There is no point in keeping it in the cluster.
- removeList.add(node);
+ removeNodes.add(node);
}
}
else {
// Node not responding. Remove it.
- removeList.add(node);
+ removeNodes.add(node);
}
}
}
@@ -857,15 +853,9 @@ private final void addNode(Node node) {
}
nodesMap.put(node.getName(), node);
-
- // Add node's aliases to global alias set.
- // Aliases are only used in tend thread, so synchronization is not necessary.
- for (Host alias : node.aliases) {
- aliases.put(alias, node);
- }
}
- private final void removeNodes(List nodesToRemove) {
+ private final void removeNodes(HashSet nodesToRemove) {
// There is no need to delete nodes from partitionWriteMap because the nodes
// have already been set to inactive. Further connection requests will result
// in an exception and a different node will be tried.
@@ -875,13 +865,6 @@ private final void removeNodes(List nodesToRemove) {
// Remove node from map.
nodesMap.remove(node.getName());
- // Remove node's aliases from cluster alias set.
- // Aliases are only used in tend thread, so synchronization is not necessary.
- for (Host alias : node.aliases) {
- // Log.debug("Remove alias " + alias);
- aliases.remove(alias);
- }
-
if (metricsEnabled) {
// Flush node metrics before removal.
try {
@@ -901,7 +884,7 @@ private final void removeNodes(List nodesToRemove) {
/**
* Remove nodes using copy on write semantics.
*/
- private final void removeNodesCopy(List nodesToRemove) {
+ private final void removeNodesCopy(HashSet nodesToRemove) {
// Create temporary nodes array.
// Since nodes are only marked for deletion using node references in the nodes array,
// and the tend thread is the only thread modifying nodes, we are guaranteed that nodes
@@ -911,7 +894,7 @@ private final void removeNodesCopy(List nodesToRemove) {
// Add nodes that are not in remove list.
for (Node node : nodes) {
- if (findNode(node, nodesToRemove)) {
+ if (nodesToRemove.contains(node)) {
if (Log.infoEnabled()) {
Log.info("Remove node " + node);
}
@@ -937,15 +920,6 @@ private final void removeNodesCopy(List nodesToRemove) {
nodes = nodeArray;
}
- private final static boolean findNode(Node search, List nodeList) {
- for (Node node : nodeList) {
- if (node.equals(search)) {
- return true;
- }
- }
- return false;
- }
-
public final boolean isConnected() {
// Must copy array reference for copy on write semantics to work.
Node[] nodeArray = nodes;
diff --git a/client/src/com/aerospike/client/cluster/Node.java b/client/src/com/aerospike/client/cluster/Node.java
index 61730040b..487bc6794 100644
--- a/client/src/com/aerospike/client/cluster/Node.java
+++ b/client/src/com/aerospike/client/cluster/Node.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2024 Aerospike, Inc.
+ * Copyright 2012-2025 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
@@ -18,12 +18,12 @@
import java.io.Closeable;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -67,8 +67,8 @@ public class Node implements Closeable {
protected final Cluster cluster;
private final String name;
- private final Host host;
- protected final List aliases;
+ private String hostname; // Optional hostname.
+ private final Host host; // Host with IP address name.
protected final InetSocketAddress address;
private final Pool[] connectionPools;
private final AsyncPool[] asyncConnectionPools;
@@ -104,7 +104,6 @@ public class Node implements Closeable {
public Node(Cluster cluster, NodeValidator nv) {
this.cluster = cluster;
this.name = nv.name;
- this.aliases = nv.aliases;
this.host = nv.primaryHost;
this.address = nv.primaryAddress;
this.tendConnection = nv.primaryConn;
@@ -455,10 +454,13 @@ protected final void refreshPeers(Peers peers) {
// Create new node.
Node node = cluster.createNode(nv);
peers.nodes.put(nv.name, node);
- nodeValidated = true;
+ nodeValidated = true;
if (peer.replaceNode != null) {
- peers.removeList.add(peer.replaceNode);
+ if (Log.infoEnabled()) {
+ Log.info("Replace node: " + peer.replaceNode);
+ }
+ peers.removeNodes.add(peer.replaceNode);
}
break;
}
@@ -499,14 +501,37 @@ private static boolean findPeerNode(Cluster cluster, Peers peers, Peer peer) {
node.referenceCount++;
return true;
}
-
+
// Match peer hosts with the node host.
for (Host h : peer.hosts) {
- if (h.equals(node.host)) {
- // Main node host is also the same as one of the peer hosts.
- // Peer should not be added.
- node.referenceCount++;
- return true;
+ if (h.port == node.host.port) {
+ // Check for IP address (node.host.name is an IP address) or hostname if it exists.
+ if (h.name.equals(node.host.name) || (node.hostname != null && h.name.equals(node.hostname))) {
+ // Main node host is also the same as one of the peer hosts.
+ // Peer should not be added.
+ node.referenceCount++;
+ return true;
+ }
+
+ // Peer name might be a hostname. Get peer IP addresses and check with node IP address.
+ try {
+ InetAddress[] addresses = InetAddress.getAllByName(h.name);
+
+ for (InetAddress address : addresses) {
+ if (address.equals(node.address.getAddress()) ||
+ address.isLoopbackAddress()) {
+ // Set peer hostname for faster future lookups.
+ node.hostname = h.name;
+ node.referenceCount++;
+ return true;
+ }
+ }
+ }
+ catch (Throwable t) {
+ // Peer name is invalid. replaceNode may be set, but that node will
+ // not be replaced because NodeValidator will reject it.
+ Log.error("Invalid peer received by cluster tend: " + h.name);
+ }
}
}
peer.replaceNode = node;
diff --git a/client/src/com/aerospike/client/cluster/NodeValidator.java b/client/src/com/aerospike/client/cluster/NodeValidator.java
index 4237e1575..e34130815 100644
--- a/client/src/com/aerospike/client/cluster/NodeValidator.java
+++ b/client/src/com/aerospike/client/cluster/NodeValidator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 Aerospike, Inc.
+ * Copyright 2012-2025 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
@@ -34,7 +34,6 @@
public final class NodeValidator {
Node fallback;
String name;
- List aliases;
Host primaryHost;
InetSocketAddress primaryAddress;
Connection primaryConn;
@@ -49,7 +48,6 @@ public final class NodeValidator {
*/
public Node seedNode(Cluster cluster, Host host, Peers peers) throws Throwable {
name = null;
- aliases = null;
primaryHost = null;
primaryAddress = null;
primaryConn = null;
@@ -64,11 +62,6 @@ public Node seedNode(Cluster cluster, Host host, Peers peers) throws Throwable {
try {
validateAddress(cluster, address, host.tlsName, host.port, true);
- // Only add address alias when not set by load balancer detection logic.
- if (this.aliases == null) {
- setAliases(address, host.tlsName, host.port);
- }
-
Node node = new Node(cluster, this);
if (validatePeers(peers, node)) {
@@ -144,7 +137,6 @@ public void validateNode(Cluster cluster, Host host) throws Throwable {
for (InetAddress address : addresses) {
try {
validateAddress(cluster, address, host.tlsName, host.port, false);
- setAliases(address, host.tlsName, host.port);
return;
}
catch (Throwable e) {
@@ -399,7 +391,6 @@ private void setAddress(Cluster cluster, HashMap map, String addr
}
// Authenticated connection. Set real host.
- setAliases(address, tlsName, h.port);
this.primaryHost = new Host(address.getHostAddress(), tlsName, h.port);
this.primaryAddress = socketAddress;
this.primaryConn.close();
@@ -428,12 +419,6 @@ private void setAddress(Cluster cluster, HashMap map, String addr
}
}
- private void setAliases(InetAddress address, String tlsName, int port) {
- // Add capacity for current address plus IPV6 address and hostname.
- this.aliases = new ArrayList(3);
- this.aliases.add(new Host(address.getHostAddress(), tlsName, port));
- }
-
private static final class SwitchClear {
private InetAddress clearAddress;
private InetSocketAddress clearSocketAddress;
diff --git a/client/src/com/aerospike/client/cluster/Peers.java b/client/src/com/aerospike/client/cluster/Peers.java
index 9c38f8a49..4c7e58af4 100644
--- a/client/src/com/aerospike/client/cluster/Peers.java
+++ b/client/src/com/aerospike/client/cluster/Peers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2024 Aerospike, Inc.
+ * Copyright 2012-2025 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
@@ -26,7 +26,7 @@
public final class Peers {
public final ArrayList peers;
public final HashMap nodes;
- public final ArrayList removeList;
+ public final HashSet removeNodes;
private final HashSet invalidHosts;
public int refreshCount;
public boolean genChanged;
@@ -34,7 +34,7 @@ public final class Peers {
public Peers(int peerCapacity) {
peers = new ArrayList(peerCapacity);
nodes = new HashMap(16);
- removeList = new ArrayList();
+ removeNodes = new HashSet(8);
invalidHosts = new HashSet(8);
}
diff --git a/client/src/com/aerospike/client/command/Command.java b/client/src/com/aerospike/client/command/Command.java
index ed08982dc..869902141 100644
--- a/client/src/com/aerospike/client/command/Command.java
+++ b/client/src/com/aerospike/client/command/Command.java
@@ -2314,7 +2314,7 @@ private final void writeHeaderWrite(WritePolicy policy, int writeAttr, int field
dataBuffer[9] = (byte)readAttr;
dataBuffer[10] = (byte)writeAttr;
dataBuffer[11] = (byte)infoAttr;
- dataBuffer[12] = (byte)txnAttr;;
+ dataBuffer[12] = (byte)txnAttr;
dataBuffer[13] = 0; // clear the result code
Buffer.intToBytes(generation, dataBuffer, 14);
Buffer.intToBytes(policy.expiration, dataBuffer, 18);
diff --git a/client/src/com/aerospike/client/command/TxnMonitor.java b/client/src/com/aerospike/client/command/TxnMonitor.java
index 6bde1abf3..41632924e 100644
--- a/client/src/com/aerospike/client/command/TxnMonitor.java
+++ b/client/src/com/aerospike/client/command/TxnMonitor.java
@@ -42,6 +42,7 @@ public final class TxnMonitor {
public static void addKey(Cluster cluster, WritePolicy policy, Key cmdKey) {
Txn txn = policy.txn;
+ txn.verifyCommand();
if (txn.getWrites().contains(cmdKey)) {
// Transaction monitor already contains this key.
@@ -66,7 +67,6 @@ public static void addKeys(Cluster cluster, BatchPolicy policy, List
com.aerospike
aerospike-parent
- 9.0.3
+ 9.0.4
aerospike-examples
jar
diff --git a/pom.xml b/pom.xml
index 779a6a1f9..5b6ed8308 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.aerospike
aerospike-parent
aerospike-parent
- 9.0.3
+ 9.0.4
pom
https://github.com/aerospike/aerospike-client-java
@@ -38,7 +38,7 @@
2.18.1
3.2.0
- 4.1.116.Final
+ 4.1.117.Final
3.0.1
0.4
1.9.0
@@ -78,7 +78,7 @@
io.netty.incubator
netty-incubator-transport-native-io_uring
linux-x86_64
- 0.0.25.Final
+ 0.0.26.Final
diff --git a/test/pom.xml b/test/pom.xml
index 44f967494..e5660ce3f 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -6,7 +6,7 @@
com.aerospike
aerospike-parent
- 9.0.3
+ 9.0.4
aerospike-client-test
jar
diff --git a/test/src/com/aerospike/test/sync/basic/TestBatch.java b/test/src/com/aerospike/test/sync/basic/TestBatch.java
index da222116e..24811e764 100644
--- a/test/src/com/aerospike/test/sync/basic/TestBatch.java
+++ b/test/src/com/aerospike/test/sync/basic/TestBatch.java
@@ -414,6 +414,8 @@ public void batchDeleteSingleNotFound() {
@Test
public void batchReadTTL() {
+ org.junit.Assume.assumeTrue(args.hasTtl);
+
// WARNING: This test takes a long time to run due to sleeps.
// Define keys
Key key1 = new Key(args.namespace, args.set, 88888);
@@ -442,9 +444,9 @@ public void batchReadTTL() {
boolean rv = client.operate(null, list);
- assertTrue(rv);
assertEquals(ResultCode.OK, br1.resultCode);
assertEquals(ResultCode.OK, br2.resultCode);
+ assertTrue(rv);
// Read records again, but don't reset read ttl.
Util.sleep(3000);
@@ -468,6 +470,7 @@ public void batchReadTTL() {
// Read record after it expires, showing it's gone.
Util.sleep(8000);
rv = client.operate(null, list);
+
assertEquals(ResultCode.KEY_NOT_FOUND_ERROR, br1.resultCode);
assertEquals(ResultCode.KEY_NOT_FOUND_ERROR, br2.resultCode);
assertFalse(rv);