Skip to content

Commit 25004ef

Browse files
kruscheclaude
andcommitted
Development: Fix Hazelcast client mode for build agents
Fixes several issues with the Hazelcast client mode implementation: 1. IPv6 address formatting: Core node addresses are now correctly formatted as [ipv6address]:port instead of ipv6address:port 2. Async startup: Build agents now always start asynchronously and connect to the cluster in the background, preventing startup blocking when core nodes are temporarily unavailable 3. Client address resolution: HazelcastDistributedDataProviderService now correctly handles Hazelcast clients by using getLocalEndpoint() instead of getLocalMember() which only works for cluster members Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent aac636a commit 25004ef

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

src/main/java/de/tum/cit/aet/artemis/core/config/CacheConfiguration.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,11 @@ private HazelcastInstance createHazelcastClient() {
401401
}
402402

403403
// Connection strategy configuration for resilience:
404-
// - asyncStart=true: Don't block startup if no core nodes available yet
404+
// - asyncStart=true: Don't block startup, connect in background
405405
// - reconnectMode=ON: Automatically reconnect if connection is lost
406406
// This allows build agents to start even when core nodes are temporarily unavailable
407407
// and reconnect automatically when they become available.
408-
boolean hasInitialAddresses = !discoveredAddresses.isEmpty();
409-
clientConfig.getConnectionStrategyConfig().setAsyncStart(!hasInitialAddresses) // Block only if we have addresses to connect to
408+
clientConfig.getConnectionStrategyConfig().setAsyncStart(true) // Never block startup - connect in background
410409
.setReconnectMode(ClientConnectionStrategyConfig.ReconnectMode.ON);
411410

412411
// Connection retry configuration - aggressive retry to handle temporary core node unavailability
@@ -426,11 +425,11 @@ private HazelcastInstance createHazelcastClient() {
426425
// Mark this instance as a client in the service registry
427426
hazelcastConnection.registerAsClient();
428427

429-
if (hasInitialAddresses) {
428+
if (!discoveredAddresses.isEmpty()) {
430429
log.info("Creating Hazelcast client to connect to core cluster at: {}", discoveredAddresses);
431430
}
432431
else {
433-
log.warn("No core nodes found in service registry. Hazelcast client will start asynchronously and retry connection.");
432+
log.warn("No core nodes found in service registry. Hazelcast client will retry connection in background.");
434433
}
435434

436435
return HazelcastClient.newHazelcastClient(clientConfig);

src/main/java/de/tum/cit/aet/artemis/core/config/HazelcastConnection.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,20 @@ private boolean isCurrentInstance(ServiceInstance instance) {
286286

287287
/**
288288
* Formats a service instance address as "host:port" for Hazelcast connection.
289+
* For IPv6 addresses, the format is "[ipv6address]:port".
289290
*
290291
* @param instance the service instance
291292
* @return the formatted address string
292293
*/
293294
private String formatInstanceAddress(ServiceInstance instance) {
294-
String host = instance.getHost().replace("[", "").replace("]", ""); // Handle IPv6 brackets
295+
String host = instance.getHost().replace("[", "").replace("]", ""); // Remove any existing brackets
295296
String port = instance.getMetadata().getOrDefault("hazelcast.port", String.valueOf(hazelcastPort));
297+
298+
// Check if this is an IPv6 address (contains colons)
299+
if (host.contains(":")) {
300+
// IPv6 addresses must be wrapped in brackets before the port
301+
return "[" + host + "]:" + port;
302+
}
296303
return host + ":" + port;
297304
}
298305

src/main/java/de/tum/cit/aet/artemis/programming/service/localci/distributed/hazelcast/HazelcastDistributedDataProviderService.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.tum.cit.aet.artemis.programming.service.localci.distributed.hazelcast;
22

3+
import java.net.InetSocketAddress;
34
import java.util.ArrayList;
45
import java.util.Set;
56
import java.util.stream.Collectors;
@@ -9,6 +10,7 @@
910
import org.springframework.context.annotation.Lazy;
1011
import org.springframework.stereotype.Service;
1112

13+
import com.hazelcast.client.impl.clientside.HazelcastClientProxy;
1214
import com.hazelcast.cluster.Member;
1315
import com.hazelcast.core.HazelcastInstance;
1416
import com.hazelcast.core.HazelcastInstanceNotActiveException;
@@ -68,13 +70,27 @@ public boolean isInstanceRunning() {
6870
}
6971

7072
/**
71-
* @return the address of the local Hazelcast member
73+
* Returns the address of the local Hazelcast instance.
74+
* For cluster members, returns the member's cluster address.
75+
* For clients (e.g., build agents in client mode), returns the client's local endpoint address.
76+
*
77+
* @return the address as a string in the format "[host]:port"
7278
*/
7379
@Override
7480
public String getLocalMemberAddress() {
7581
if (!isInstanceRunning()) {
7682
throw new HazelcastInstanceNotActiveException();
7783
}
84+
85+
// Check if this is a Hazelcast client (build agents in client mode)
86+
if (hazelcastInstance instanceof HazelcastClientProxy) {
87+
// For clients, use getLocalEndpoint() which provides the client's socket address
88+
InetSocketAddress socketAddress = (InetSocketAddress) hazelcastInstance.getLocalEndpoint().getSocketAddress();
89+
// Format consistently with cluster member addresses: [host]:port
90+
return "[" + socketAddress.getAddress().getHostAddress() + "]:" + socketAddress.getPort();
91+
}
92+
93+
// For cluster members, use the traditional approach
7894
return hazelcastInstance.getCluster().getLocalMember().getAddress().toString();
7995
}
8096

0 commit comments

Comments
 (0)