dependency "com.google.protobuf:protobuf-java:${protobufVersion}" | dependency "com.google.protobuf:protobuf-java:${protobufVersion}" | ||||
// Do not upgrade H2 to 1.4.200 because of instability: https://github.com/h2database/h2database/issues/2205 | // Do not upgrade H2 to 1.4.200 because of instability: https://github.com/h2database/h2database/issues/2205 | ||||
dependency 'com.h2database:h2:1.4.199' | dependency 'com.h2database:h2:1.4.199' | ||||
dependencySet(group: 'com.hazelcast', version: '3.12.12') { | |||||
dependencySet(group: 'com.hazelcast', version: '4.2') { | |||||
entry 'hazelcast' | entry 'hazelcast' | ||||
entry 'hazelcast-client' | |||||
} | } | ||||
dependency 'com.ibm.icu:icu4j:3.4.4' | dependency 'com.ibm.icu:icu4j:3.4.4' | ||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:9.2.0.jre11' | dependency 'com.microsoft.sqlserver:mssql-jdbc:9.2.0.jre11' |
import com.hazelcast.spi.exception.RetryableHazelcastException; | import com.hazelcast.spi.exception.RetryableHazelcastException; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import org.picocontainer.Startable; | import org.picocontainer.Startable; | ||||
import org.sonar.api.utils.log.Logger; | import org.sonar.api.utils.log.Logger; | ||||
@Override | @Override | ||||
public Set<String> getWorkerUUIDs() { | public Set<String> getWorkerUUIDs() { | ||||
Set<String> connectedWorkerUUIDs = hazelcastMember.getMemberUuids(); | |||||
Set<UUID> connectedWorkerUUIDs = hazelcastMember.getMemberUuids(); | |||||
return getClusteredWorkerUUIDs().entrySet().stream() | return getClusteredWorkerUUIDs().entrySet().stream() | ||||
.filter(e -> connectedWorkerUUIDs.contains(e.getKey())) | .filter(e -> connectedWorkerUUIDs.contains(e.getKey())) | ||||
} | } | ||||
} | } | ||||
private Map<String, Set<String>> getClusteredWorkerUUIDs() { | |||||
private Map<UUID, Set<String>> getClusteredWorkerUUIDs() { | |||||
return hazelcastMember.getReplicatedMap(WORKER_UUIDS); | return hazelcastMember.getReplicatedMap(WORKER_UUIDS); | ||||
} | } | ||||
} | } |
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import java.util.stream.Stream; | import java.util.stream.Stream; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import static org.sonar.process.cluster.hz.HazelcastObjects.WORKER_UUIDS; | import static org.sonar.process.cluster.hz.HazelcastObjects.WORKER_UUIDS; | ||||
public class CeDistributedInformationImplTest { | public class CeDistributedInformationImplTest { | ||||
private String clientUUID1 = "1"; | |||||
private String clientUUID2 = "2"; | |||||
private String clientUUID3 = "3"; | |||||
private Map workerMap = ImmutableMap.of( | |||||
clientUUID1, ImmutableSet.of("1", "2"), | |||||
clientUUID2, ImmutableSet.of("3"), | |||||
clientUUID3, ImmutableSet.of("4", "5", "6")); | |||||
private final UUID clientUUID1 = UUID.randomUUID(); | |||||
private final UUID clientUUID2 = UUID.randomUUID(); | |||||
private final UUID clientUUID3 = UUID.randomUUID(); | |||||
private HazelcastMember hzClientWrapper = mock(HazelcastMember.class); | |||||
private final String w1 = UUID.randomUUID().toString(); | |||||
private final String w2 = UUID.randomUUID().toString(); | |||||
private final String w3 = UUID.randomUUID().toString(); | |||||
private final String w4 = UUID.randomUUID().toString(); | |||||
private final String w5 = UUID.randomUUID().toString(); | |||||
private final String w6 = UUID.randomUUID().toString(); | |||||
private final Map<UUID, Set<String>> workerMap = ImmutableMap.of( | |||||
clientUUID1, ImmutableSet.of(w1, w2), | |||||
clientUUID2, ImmutableSet.of(w3), | |||||
clientUUID3, ImmutableSet.of(w4, w5, w6)); | |||||
private final HazelcastMember hzClientWrapper = mock(HazelcastMember.class); | |||||
@Test | @Test | ||||
public void getWorkerUUIDs_returns_union_of_workers_uuids_of_local_and_cluster_worker_uuids() { | public void getWorkerUUIDs_returns_union_of_workers_uuids_of_local_and_cluster_worker_uuids() { | ||||
when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | ||||
when(hzClientWrapper.getMemberUuids()).thenReturn(ImmutableSet.of(clientUUID1, clientUUID2, clientUUID3)); | when(hzClientWrapper.getMemberUuids()).thenReturn(ImmutableSet.of(clientUUID1, clientUUID2, clientUUID3)); | ||||
when(hzClientWrapper.getReplicatedMap(WORKER_UUIDS)).thenReturn(workerMap); | |||||
when(hzClientWrapper.<UUID, Set<String>>getReplicatedMap(WORKER_UUIDS)).thenReturn(workerMap); | |||||
CeDistributedInformation ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | CeDistributedInformation ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | ||||
assertThat(ceDistributedInformation.getWorkerUUIDs()).containsExactly("1", "2", "3", "4", "5", "6"); | |||||
assertThat(ceDistributedInformation.getWorkerUUIDs()).containsExactlyInAnyOrder(w1, w2, w3, w4, w5, w6); | |||||
} | } | ||||
@Test | @Test | ||||
public void getWorkerUUIDs_must_filter_absent_client() { | public void getWorkerUUIDs_must_filter_absent_client() { | ||||
when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | ||||
when(hzClientWrapper.getMemberUuids()).thenReturn(ImmutableSet.of(clientUUID1, clientUUID2)); | when(hzClientWrapper.getMemberUuids()).thenReturn(ImmutableSet.of(clientUUID1, clientUUID2)); | ||||
when(hzClientWrapper.getReplicatedMap(WORKER_UUIDS)).thenReturn(workerMap); | |||||
when(hzClientWrapper.<UUID, Set<String>>getReplicatedMap(WORKER_UUIDS)).thenReturn(workerMap); | |||||
CeDistributedInformation ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | CeDistributedInformation ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | ||||
assertThat(ceDistributedInformation.getWorkerUUIDs()).containsExactly("1", "2", "3"); | |||||
assertThat(ceDistributedInformation.getWorkerUUIDs()).containsExactlyInAnyOrder(w1, w2, w3); | |||||
} | } | ||||
@Test | @Test | ||||
public void broadcastWorkerUUIDs_adds_local_workerUUIDs_to_shared_map_under_key_of_localendpoint_uuid() { | public void broadcastWorkerUUIDs_adds_local_workerUUIDs_to_shared_map_under_key_of_localendpoint_uuid() { | ||||
Set<String> connectedClients = new HashSet<>(); | |||||
Map modifiableWorkerMap = new HashMap<>(); | |||||
Set<UUID> connectedClients = new HashSet<>(); | |||||
Map<UUID, Set<String>> modifiableWorkerMap = new HashMap<>(); | |||||
connectedClients.add(clientUUID1); | connectedClients.add(clientUUID1); | ||||
connectedClients.add(clientUUID2); | connectedClients.add(clientUUID2); | ||||
when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | ||||
when(hzClientWrapper.getMemberUuids()).thenReturn(connectedClients); | when(hzClientWrapper.getMemberUuids()).thenReturn(connectedClients); | ||||
when(hzClientWrapper.getReplicatedMap(WORKER_UUIDS)).thenReturn(modifiableWorkerMap); | |||||
when(hzClientWrapper.<UUID, Set<String>>getReplicatedMap(WORKER_UUIDS)).thenReturn(modifiableWorkerMap); | |||||
CeWorkerFactory ceWorkerFactory = mock(CeWorkerFactory.class); | CeWorkerFactory ceWorkerFactory = mock(CeWorkerFactory.class); | ||||
Set<CeWorker> ceWorkers = Stream.of("a10", "a11").map(uuid -> { | Set<CeWorker> ceWorkers = Stream.of("a10", "a11").map(uuid -> { | ||||
@Test | @Test | ||||
public void stop_must_remove_local_workerUUIDs() { | public void stop_must_remove_local_workerUUIDs() { | ||||
Set<String> connectedClients = new HashSet<>(); | |||||
Set<UUID> connectedClients = new HashSet<>(); | |||||
connectedClients.add(clientUUID1); | connectedClients.add(clientUUID1); | ||||
connectedClients.add(clientUUID2); | connectedClients.add(clientUUID2); | ||||
connectedClients.add(clientUUID3); | connectedClients.add(clientUUID3); | ||||
Map modifiableWorkerMap = new HashMap(workerMap); | |||||
Map<UUID, Set<String>> modifiableWorkerMap = new HashMap<>(workerMap); | |||||
when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | when(hzClientWrapper.getUuid()).thenReturn(clientUUID1); | ||||
when(hzClientWrapper.getMemberUuids()).thenReturn(connectedClients); | when(hzClientWrapper.getMemberUuids()).thenReturn(connectedClients); | ||||
when(hzClientWrapper.getReplicatedMap(WORKER_UUIDS)).thenReturn(modifiableWorkerMap); | |||||
when(hzClientWrapper.<UUID, Set<String>>getReplicatedMap(WORKER_UUIDS)).thenReturn(modifiableWorkerMap); | |||||
CeDistributedInformationImpl ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | CeDistributedInformationImpl ceDistributedInformation = new CeDistributedInformationImpl(hzClientWrapper, mock(CeWorkerFactory.class)); | ||||
ceDistributedInformation.stop(); | ceDistributedInformation.stop(); | ||||
assertThat(modifiableWorkerMap).containsExactly( | |||||
entry(clientUUID2, ImmutableSet.of("3")), | |||||
entry(clientUUID3, ImmutableSet.of("4", "5", "6"))); | |||||
assertThat(modifiableWorkerMap).containsExactlyInAnyOrderEntriesOf( | |||||
ImmutableMap.of(clientUUID2, ImmutableSet.of(w3), clientUUID3, ImmutableSet.of(w4, w5, w6))); | |||||
} | } | ||||
} | } |
package org.sonar.application.cluster; | package org.sonar.application.cluster; | ||||
import com.google.common.annotations.VisibleForTesting; | import com.google.common.annotations.VisibleForTesting; | ||||
import com.hazelcast.cluster.Address; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import com.hazelcast.cluster.memberselector.MemberSelectors; | import com.hazelcast.cluster.memberselector.MemberSelectors; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.nio.Address; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; |
*/ | */ | ||||
package org.sonar.application.cluster; | package org.sonar.application.cluster; | ||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MembershipEvent; | |||||
import com.hazelcast.cluster.MembershipListener; | |||||
import com.hazelcast.core.EntryEvent; | import com.hazelcast.core.EntryEvent; | ||||
import com.hazelcast.core.EntryListener; | import com.hazelcast.core.EntryListener; | ||||
import com.hazelcast.core.HazelcastInstanceNotActiveException; | import com.hazelcast.core.HazelcastInstanceNotActiveException; | ||||
import com.hazelcast.core.IAtomicReference; | |||||
import com.hazelcast.core.MapEvent; | |||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberAttributeEvent; | |||||
import com.hazelcast.core.MembershipEvent; | |||||
import com.hazelcast.core.MembershipListener; | |||||
import com.hazelcast.core.ReplicatedMap; | |||||
import com.hazelcast.cp.IAtomicReference; | |||||
import com.hazelcast.map.MapEvent; | |||||
import com.hazelcast.replicatedmap.ReplicatedMap; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.EnumMap; | import java.util.EnumMap; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.UUID; | |||||
import org.elasticsearch.cluster.health.ClusterHealthStatus; | import org.elasticsearch.cluster.health.ClusterHealthStatus; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
private final List<AppStateListener> listeners = new ArrayList<>(); | private final List<AppStateListener> listeners = new ArrayList<>(); | ||||
private final Map<ProcessId, Boolean> operationalLocalProcesses = new EnumMap<>(ProcessId.class); | private final Map<ProcessId, Boolean> operationalLocalProcesses = new EnumMap<>(ProcessId.class); | ||||
private final ReplicatedMap<ClusterProcess, Boolean> operationalProcesses; | private final ReplicatedMap<ClusterProcess, Boolean> operationalProcesses; | ||||
private final String operationalProcessListenerUUID; | |||||
private final String nodeDisconnectedListenerUUID; | |||||
private final UUID operationalProcessListenerUUID; | |||||
private final UUID nodeDisconnectedListenerUUID; | |||||
private final EsConnector esConnector; | private final EsConnector esConnector; | ||||
private HealthStateSharing healthStateSharing = null; | private HealthStateSharing healthStateSharing = null; | ||||
@Override | @Override | ||||
public boolean tryToLockWebLeader() { | public boolean tryToLockWebLeader() { | ||||
IAtomicReference<String> leader = hzMember.getAtomicReference(LEADER); | |||||
IAtomicReference<UUID> leader = hzMember.getAtomicReference(LEADER); | |||||
return leader.compareAndSet(null, hzMember.getUuid()); | return leader.compareAndSet(null, hzMember.getUuid()); | ||||
} | } | ||||
@Override | @Override | ||||
public Optional<String> getLeaderHostName() { | public Optional<String> getLeaderHostName() { | ||||
String leaderId = (String) hzMember.getAtomicReference(LEADER).get(); | |||||
if (leaderId != null) { | |||||
Optional<Member> leader = hzMember.getCluster().getMembers().stream().filter(m -> m.getUuid().equals(leaderId)).findFirst(); | |||||
UUID leaderUuid = (UUID) hzMember.getAtomicReference(LEADER).get(); | |||||
if (leaderUuid != null) { | |||||
Optional<Member> leader = hzMember.getCluster().getMembers().stream().filter(m -> m.getUuid().equals(leaderUuid)).findFirst(); | |||||
if (leader.isPresent()) { | if (leader.isPresent()) { | ||||
return Optional.of(leader.get().getAddress().getHost()); | return Optional.of(leader.get().getAddress().getHost()); | ||||
} | } | ||||
public void mapEvicted(MapEvent event) { | public void mapEvicted(MapEvent event) { | ||||
// Ignore it | // Ignore it | ||||
} | } | ||||
@Override | |||||
public void entryExpired(EntryEvent<ClusterProcess, Boolean> event) { | |||||
// Ignore it | |||||
} | |||||
} | } | ||||
private class NodeDisconnectedListener implements MembershipListener { | private class NodeDisconnectedListener implements MembershipListener { | ||||
removeOperationalProcess(membershipEvent.getMember().getUuid()); | removeOperationalProcess(membershipEvent.getMember().getUuid()); | ||||
} | } | ||||
@Override | |||||
public void memberAttributeChanged(MemberAttributeEvent memberAttributeEvent) { | |||||
// Nothing to do | |||||
} | |||||
private void removeOperationalProcess(String uuid) { | |||||
private void removeOperationalProcess(UUID uuid) { | |||||
for (ClusterProcess clusterProcess : operationalProcesses.keySet()) { | for (ClusterProcess clusterProcess : operationalProcesses.keySet()) { | ||||
if (clusterProcess.getNodeUuid().equals(uuid)) { | if (clusterProcess.getNodeUuid().equals(uuid)) { | ||||
LOGGER.debug("Set node process off for [{}:{}] : ", clusterProcess.getNodeUuid(), clusterProcess.getProcessId()); | LOGGER.debug("Set node process off for [{}:{}] : ", clusterProcess.getNodeUuid(), clusterProcess.getProcessId()); |
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.util.Objects; | import java.util.Objects; | ||||
import java.util.UUID; | |||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
public class ClusterProcess implements Serializable { | public class ClusterProcess implements Serializable { | ||||
private final ProcessId processId; | private final ProcessId processId; | ||||
private final String nodeUuid; | |||||
private final UUID nodeUuid; | |||||
public ClusterProcess(String nodeUuid, ProcessId processId) { | |||||
public ClusterProcess(UUID nodeUuid, ProcessId processId) { | |||||
this.processId = requireNonNull(processId); | this.processId = requireNonNull(processId); | ||||
this.nodeUuid = requireNonNull(nodeUuid); | this.nodeUuid = requireNonNull(nodeUuid); | ||||
} | } | ||||
return processId; | return processId; | ||||
} | } | ||||
public String getNodeUuid() { | |||||
public UUID getNodeUuid() { | |||||
return nodeUuid; | return nodeUuid; | ||||
} | } | ||||
package org.sonar.application.cluster; | package org.sonar.application.cluster; | ||||
import com.google.common.collect.ImmutableMap; | import com.google.common.collect.ImmutableMap; | ||||
import com.hazelcast.core.Cluster; | |||||
import com.hazelcast.core.IAtomicReference; | |||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.nio.Address; | |||||
import com.hazelcast.cluster.Address; | |||||
import com.hazelcast.cluster.Cluster; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import com.hazelcast.cp.IAtomicReference; | |||||
import java.net.InetAddress; | import java.net.InetAddress; | ||||
import java.net.UnknownHostException; | import java.net.UnknownHostException; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import java.util.function.Consumer; | import java.util.function.Consumer; | ||||
import org.junit.After; | import org.junit.After; | ||||
} | } | ||||
@Override | @Override | ||||
public String getUuid() { | |||||
public UUID getUuid() { | |||||
throw new IllegalStateException("not expected to be called"); | throw new IllegalStateException("not expected to be called"); | ||||
} | } | ||||
@Override | @Override | ||||
public Set<String> getMemberUuids() { | |||||
public Set<UUID> getMemberUuids() { | |||||
throw new IllegalStateException("not expected to be called"); | throw new IllegalStateException("not expected to be called"); | ||||
} | } | ||||
package org.sonar.application.cluster; | package org.sonar.application.cluster; | ||||
import java.net.InetAddress; | import java.net.InetAddress; | ||||
import java.util.Optional; | |||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.DisableOnDebug; | import org.junit.rules.DisableOnDebug; | ||||
} | } | ||||
} | } | ||||
@Test | |||||
public void return_hostname_if_node_is_leader() { | |||||
try (ClusterAppStateImpl underTest = createClusterAppState()) { | |||||
underTest.tryToLockWebLeader(); | |||||
Optional<String> hostname = underTest.getLeaderHostName(); | |||||
assertThat(hostname).isNotEmpty(); | |||||
} | |||||
} | |||||
@Test | |||||
public void return_null_if_node_is_not_leader() { | |||||
try (ClusterAppStateImpl underTest = createClusterAppState()) { | |||||
Optional<String> hostname = underTest.getLeaderHostName(); | |||||
assertThat(hostname).isEmpty(); | |||||
} | |||||
} | |||||
private ClusterAppStateImpl createClusterAppState() { | private ClusterAppStateImpl createClusterAppState() { | ||||
return new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class), mock(AppNodesClusterHostsConsistency.class)); | return new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class), mock(AppNodesClusterHostsConsistency.class)); | ||||
} | } |
*/ | */ | ||||
package org.sonar.application.cluster; | package org.sonar.application.cluster; | ||||
import java.util.UUID; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
public class ClusterProcessTest { | public class ClusterProcessTest { | ||||
@Test | @Test | ||||
public void test_equality() { | public void test_equality() { | ||||
ClusterProcess clusterProcess = new ClusterProcess("A", ProcessId.WEB_SERVER); | |||||
UUID nodeUuid = UUID.randomUUID(); | |||||
ClusterProcess clusterProcess = new ClusterProcess(nodeUuid, ProcessId.WEB_SERVER); | |||||
assertThat(clusterProcess) | assertThat(clusterProcess) | ||||
.isNotEqualTo(null) | .isNotEqualTo(null) | ||||
.isEqualTo(clusterProcess) | .isEqualTo(clusterProcess) | ||||
.isNotEqualTo(new ClusterProcess("B", ProcessId.WEB_SERVER)) | |||||
.isNotEqualTo(new ClusterProcess("A", ProcessId.ELASTICSEARCH)) | |||||
.isEqualTo(new ClusterProcess("A", ProcessId.WEB_SERVER)); | |||||
.isNotEqualTo(new ClusterProcess(UUID.randomUUID(), ProcessId.WEB_SERVER)) | |||||
.isNotEqualTo(new ClusterProcess(nodeUuid, ProcessId.ELASTICSEARCH)) | |||||
.isEqualTo(new ClusterProcess(nodeUuid, ProcessId.WEB_SERVER)); | |||||
} | } | ||||
} | } |
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.function.Predicate; | import java.util.function.Predicate; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
public void writeMine(NodeHealth nodeHealth) { | public void writeMine(NodeHealth nodeHealth) { | ||||
requireNonNull(nodeHealth, "nodeHealth can't be null"); | requireNonNull(nodeHealth, "nodeHealth can't be null"); | ||||
Map<String, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
Map<UUID, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
if (LOG.isTraceEnabled()) { | if (LOG.isTraceEnabled()) { | ||||
LOG.trace("Reading {} and adding {}", new HashMap<>(sqHealthState), nodeHealth); | LOG.trace("Reading {} and adding {}", new HashMap<>(sqHealthState), nodeHealth); | ||||
} | } | ||||
@Override | @Override | ||||
public void clearMine() { | public void clearMine() { | ||||
Map<String, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
String clientUUID = hzMember.getUuid(); | |||||
Map<UUID, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
UUID clientUUID = hzMember.getUuid(); | |||||
if (LOG.isTraceEnabled()) { | if (LOG.isTraceEnabled()) { | ||||
LOG.trace("Reading {} and clearing for {}", new HashMap<>(sqHealthState), clientUUID); | LOG.trace("Reading {} and clearing for {}", new HashMap<>(sqHealthState), clientUUID); | ||||
} | } | ||||
public Set<NodeHealth> readAll() { | public Set<NodeHealth> readAll() { | ||||
long clusterTime = hzMember.getClusterTime(); | long clusterTime = hzMember.getClusterTime(); | ||||
long timeout = clusterTime - TIMEOUT_30_SECONDS; | long timeout = clusterTime - TIMEOUT_30_SECONDS; | ||||
Map<String, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
Set<String> hzMemberUUIDs = hzMember.getMemberUuids(); | |||||
Map<UUID, TimestampedNodeHealth> sqHealthState = readReplicatedMap(); | |||||
Set<UUID> hzMemberUUIDs = hzMember.getMemberUuids(); | |||||
Set<NodeHealth> existingNodeHealths = sqHealthState.entrySet().stream() | Set<NodeHealth> existingNodeHealths = sqHealthState.entrySet().stream() | ||||
.filter(outOfDate(timeout)) | .filter(outOfDate(timeout)) | ||||
.filter(ofNonExistentMember(hzMemberUUIDs)) | .filter(ofNonExistentMember(hzMemberUUIDs)) | ||||
return ImmutableSet.copyOf(existingNodeHealths); | return ImmutableSet.copyOf(existingNodeHealths); | ||||
} | } | ||||
private static Predicate<Map.Entry<String, TimestampedNodeHealth>> outOfDate(long timeout) { | |||||
private static Predicate<Map.Entry<UUID, TimestampedNodeHealth>> outOfDate(long timeout) { | |||||
return entry -> { | return entry -> { | ||||
boolean res = entry.getValue().getTimestamp() > timeout; | boolean res = entry.getValue().getTimestamp() > timeout; | ||||
if (!res) { | if (!res) { | ||||
}; | }; | ||||
} | } | ||||
private static Predicate<Map.Entry<String, TimestampedNodeHealth>> ofNonExistentMember(Set<String> hzMemberUUIDs) { | |||||
private static Predicate<Map.Entry<UUID, TimestampedNodeHealth>> ofNonExistentMember(Set<UUID> hzMemberUUIDs) { | |||||
return entry -> { | return entry -> { | ||||
boolean res = hzMemberUUIDs.contains(entry.getKey()); | boolean res = hzMemberUUIDs.contains(entry.getKey()); | ||||
if (!res) { | if (!res) { | ||||
}; | }; | ||||
} | } | ||||
private Map<String, TimestampedNodeHealth> readReplicatedMap() { | |||||
private Map<UUID, TimestampedNodeHealth> readReplicatedMap() { | |||||
return hzMember.getReplicatedMap(HazelcastObjects.SQ_HEALTH_STATE); | return hzMember.getReplicatedMap(HazelcastObjects.SQ_HEALTH_STATE); | ||||
} | } | ||||
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.cluster.Member; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
public void propagateExceptions() { | public void propagateExceptions() { | ||||
if (!failedMembers.isEmpty()) { | if (!failedMembers.isEmpty()) { | ||||
String failedMemberNames = failedMembers.keySet().stream() | String failedMemberNames = failedMembers.keySet().stream() | ||||
.map(m -> m.getStringAttribute(NODE_NAME.getKey())) | |||||
.map(m -> m.getAttribute(NODE_NAME.getKey())) | |||||
.collect(Collectors.joining(", ")); | .collect(Collectors.joining(", ")); | ||||
throw new IllegalStateException("Distributed cluster action in cluster nodes " + failedMemberNames + " (other nodes may have timed out)", | throw new IllegalStateException("Distributed cluster action in cluster nodes " + failedMemberNames + " (other nodes may have timed out)", | ||||
failedMembers.values().iterator().next()); | failedMembers.values().iterator().next()); | ||||
if (!timedOutMembers.isEmpty()) { | if (!timedOutMembers.isEmpty()) { | ||||
String timedOutMemberNames = timedOutMembers.stream() | String timedOutMemberNames = timedOutMembers.stream() | ||||
.map(m -> m.getStringAttribute(NODE_NAME.getKey())) | |||||
.map(m -> m.getAttribute(NODE_NAME.getKey())) | |||||
.collect(Collectors.joining(", ")); | .collect(Collectors.joining(", ")); | ||||
throw new IllegalStateException("Distributed cluster action timed out in cluster nodes " + timedOutMemberNames); | throw new IllegalStateException("Distributed cluster action timed out in cluster nodes " + timedOutMemberNames); | ||||
} | } |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.cluster.Member; | |||||
import java.util.Map; | import java.util.Map; | ||||
@FunctionalInterface | @FunctionalInterface |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Cluster; | |||||
import com.hazelcast.core.IAtomicReference; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.cluster.Cluster; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import com.hazelcast.cp.IAtomicReference; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
/** | /** | ||||
* Gets the replicated map shared by the cluster and identified by name. | * Gets the replicated map shared by the cluster and identified by name. | ||||
* Result can be casted to {@link com.hazelcast.core.ReplicatedMap} if needed to | |||||
* Result can be casted to {@link com.hazelcast.replicatedmap.ReplicatedMap} if needed to | |||||
* benefit from listeners. | * benefit from listeners. | ||||
*/ | */ | ||||
<K, V> Map<K, V> getReplicatedMap(String name); | <K, V> Map<K, V> getReplicatedMap(String name); | ||||
String getUuid(); | |||||
UUID getUuid(); | |||||
/** | /** | ||||
* The UUIDs of all the members (both members and local clients of these members) currently connected to the | * The UUIDs of all the members (both members and local clients of these members) currently connected to the | ||||
* Hazelcast cluster. | * Hazelcast cluster. | ||||
*/ | */ | ||||
Set<String> getMemberUuids(); | |||||
Set<UUID> getMemberUuids(); | |||||
/** | /** | ||||
* Gets lock among the cluster, identified by name | * Gets lock among the cluster, identified by name |
import com.hazelcast.config.MemberAttributeConfig; | import com.hazelcast.config.MemberAttributeConfig; | ||||
import com.hazelcast.config.NetworkConfig; | import com.hazelcast.config.NetworkConfig; | ||||
import com.hazelcast.core.Hazelcast; | import com.hazelcast.core.Hazelcast; | ||||
import com.hazelcast.internal.util.AddressUtil; | |||||
import java.net.UnknownHostException; | import java.net.UnknownHostException; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import com.hazelcast.util.AddressUtil; | |||||
import org.sonar.api.utils.log.Logger; | |||||
import org.sonar.api.utils.log.Loggers; | |||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
import org.sonar.process.cluster.hz.HazelcastMember.Attribute; | import org.sonar.process.cluster.hz.HazelcastMember.Attribute; | ||||
import static java.util.Collections.singletonList; | import static java.util.Collections.singletonList; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HZ_PORT; | import static org.sonar.process.ProcessProperties.Property.CLUSTER_NODE_HZ_PORT; | ||||
import org.sonar.api.utils.log.Logger; | |||||
import org.sonar.api.utils.log.Loggers; | |||||
public class HazelcastMemberBuilder { | public class HazelcastMemberBuilder { | ||||
return Collections.singletonList(host.contains(":") ? host : format("%s:%s", host, CLUSTER_NODE_HZ_PORT.getDefaultValue())); | return Collections.singletonList(host.contains(":") ? host : format("%s:%s", host, CLUSTER_NODE_HZ_PORT.getDefaultValue())); | ||||
} else { | } else { | ||||
List<String> membersToAdd = new ArrayList<>(); | List<String> membersToAdd = new ArrayList<>(); | ||||
for (String memberIp : getAllByName(hostStripped)){ | |||||
for (String memberIp : getAllByName(hostStripped)) { | |||||
String prefix = memberIp.split("/")[1]; | String prefix = memberIp.split("/")[1]; | ||||
LOG.debug("Found IP for: " + hostStripped + " : " + prefix); | LOG.debug("Found IP for: " + hostStripped + " : " + prefix); | ||||
String memberPort = host.contains(":") ? host.split(":")[1] : CLUSTER_NODE_HZ_PORT.getDefaultValue(); | String memberPort = host.contains(":") ? host.split(":")[1] : CLUSTER_NODE_HZ_PORT.getDefaultValue(); | ||||
// Hazelcast does not fail when joining a cluster with different name. | // Hazelcast does not fail when joining a cluster with different name. | ||||
// Apparently this behavior exists since Hazelcast 3.8.2 (see note | // Apparently this behavior exists since Hazelcast 3.8.2 (see note | ||||
// at http://docs.hazelcast.org/docs/3.8.6/manual/html-single/index.html#creating-cluster-groups) | // at http://docs.hazelcast.org/docs/3.8.6/manual/html-single/index.html#creating-cluster-groups) | ||||
config.getGroupConfig().setName("SonarQube"); | |||||
config.setClusterName("SonarQube"); | |||||
// Configure network | // Configure network | ||||
NetworkConfig netConfig = config.getNetworkConfig(); | NetworkConfig netConfig = config.getNetworkConfig(); | ||||
.setProperty("hazelcast.logging.type", "slf4j"); | .setProperty("hazelcast.logging.type", "slf4j"); | ||||
MemberAttributeConfig attributes = config.getMemberAttributeConfig(); | MemberAttributeConfig attributes = config.getMemberAttributeConfig(); | ||||
attributes.setStringAttribute(Attribute.NODE_NAME.getKey(), requireNonNull(nodeName, "Node name is missing")); | |||||
attributes.setStringAttribute(Attribute.PROCESS_KEY.getKey(), requireNonNull(processId, "Process key is missing").getKey()); | |||||
attributes.setAttribute(Attribute.NODE_NAME.getKey(), requireNonNull(nodeName, "Node name is missing")); | |||||
attributes.setAttribute(Attribute.PROCESS_KEY.getKey(), requireNonNull(processId, "Process key is missing").getKey()); | |||||
return new HazelcastMemberImpl(Hazelcast.newHazelcastInstance(config)); | return new HazelcastMemberImpl(Hazelcast.newHazelcastInstance(config)); | ||||
} | } |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Cluster; | |||||
import com.hazelcast.cluster.Cluster; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import com.hazelcast.core.HazelcastInstance; | import com.hazelcast.core.HazelcastInstance; | ||||
import com.hazelcast.core.HazelcastInstanceNotActiveException; | import com.hazelcast.core.HazelcastInstanceNotActiveException; | ||||
import com.hazelcast.core.IAtomicReference; | |||||
import com.hazelcast.core.IExecutorService; | import com.hazelcast.core.IExecutorService; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.core.MultiExecutionCallback; | import com.hazelcast.core.MultiExecutionCallback; | ||||
import com.hazelcast.cp.IAtomicReference; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.UUID; | |||||
import java.util.concurrent.ExecutionException; | import java.util.concurrent.ExecutionException; | ||||
import java.util.concurrent.Future; | import java.util.concurrent.Future; | ||||
import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
@Override | @Override | ||||
public <E> IAtomicReference<E> getAtomicReference(String name) { | public <E> IAtomicReference<E> getAtomicReference(String name) { | ||||
return hzInstance.getAtomicReference(name); | |||||
return hzInstance.getCPSubsystem().getAtomicReference(name); | |||||
} | } | ||||
@Override | @Override | ||||
} | } | ||||
@Override | @Override | ||||
public String getUuid() { | |||||
public UUID getUuid() { | |||||
return hzInstance.getLocalEndpoint().getUuid(); | return hzInstance.getLocalEndpoint().getUuid(); | ||||
} | } | ||||
@Override | @Override | ||||
public Set<String> getMemberUuids() { | |||||
public Set<UUID> getMemberUuids() { | |||||
return hzInstance.getCluster().getMembers().stream().map(Member::getUuid).collect(Collectors.toSet()); | return hzInstance.getCluster().getMembers().stream().map(Member::getUuid).collect(Collectors.toSet()); | ||||
} | } | ||||
@Override | @Override | ||||
public Lock getLock(String s) { | public Lock getLock(String s) { | ||||
return hzInstance.getLock(s); | |||||
return hzInstance.getCPSubsystem().getLock(s); | |||||
} | } | ||||
@Override | @Override |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import java.util.List; | import java.util.List; | ||||
import org.sonar.process.ProcessId; | import org.sonar.process.ProcessId; | ||||
public static MemberSelector selectorForProcessIds(ProcessId... processIds) { | public static MemberSelector selectorForProcessIds(ProcessId... processIds) { | ||||
List<ProcessId> processIdList = asList(processIds); | List<ProcessId> processIdList = asList(processIds); | ||||
return member -> { | return member -> { | ||||
ProcessId memberProcessId = fromKey(member.getStringAttribute(PROCESS_KEY.getKey())); | |||||
ProcessId memberProcessId = fromKey(member.getAttribute(PROCESS_KEY.getKey())); | |||||
return processIdList.contains(memberProcessId); | return processIdList.contains(memberProcessId); | ||||
}; | }; | ||||
} | } |
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Random; | import java.util.Random; | ||||
import java.util.UUID; | |||||
import java.util.stream.IntStream; | import java.util.stream.IntStream; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
@Test | @Test | ||||
public void write_put_arg_into_map_sq_health_state_under_current_client_uuid() { | public void write_put_arg_into_map_sq_health_state_under_current_client_uuid() { | ||||
NodeHealth nodeHealth = randomNodeHealth(); | NodeHealth nodeHealth = randomNodeHealth(); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
long clusterTime = random.nextLong(); | long clusterTime = random.nextLong(); | ||||
String uuid = randomAlphanumeric(5); | |||||
UUID uuid = UUID.randomUUID(); | |||||
when(hazelcastMember.getUuid()).thenReturn(uuid); | when(hazelcastMember.getUuid()).thenReturn(uuid); | ||||
when(hazelcastMember.getClusterTime()).thenReturn(clusterTime); | when(hazelcastMember.getClusterTime()).thenReturn(clusterTime); | ||||
underTest.writeMine(nodeHealth); | underTest.writeMine(nodeHealth); | ||||
assertThat(map.size()).isEqualTo(1); | |||||
assertThat(map).hasSize(1); | |||||
assertThat(map.get(uuid)).isEqualTo(new TimestampedNodeHealth(nodeHealth, clusterTime)); | assertThat(map.get(uuid)).isEqualTo(new TimestampedNodeHealth(nodeHealth, clusterTime)); | ||||
assertThat(logging.getLogs()).isEmpty(); | assertThat(logging.getLogs()).isEmpty(); | ||||
} | } | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | Map<String, TimestampedNodeHealth> map = new HashMap<>(); | ||||
map.put(randomAlphanumeric(4), new TimestampedNodeHealth(randomNodeHealth(), random.nextLong())); | map.put(randomAlphanumeric(4), new TimestampedNodeHealth(randomNodeHealth(), random.nextLong())); | ||||
doReturn(new HashMap<>(map)).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(new HashMap<>(map)).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
String uuid = randomAlphanumeric(5); | |||||
UUID uuid = UUID.randomUUID(); | |||||
when(hazelcastMember.getUuid()).thenReturn(uuid); | when(hazelcastMember.getUuid()).thenReturn(uuid); | ||||
underTest.writeMine(newNodeHealth); | underTest.writeMine(newNodeHealth); | ||||
@Test | @Test | ||||
public void readAll_returns_all_NodeHealth_in_map_sq_health_state_for_existing_client_uuids_aged_less_than_30_seconds() { | public void readAll_returns_all_NodeHealth_in_map_sq_health_state_for_existing_client_uuids_aged_less_than_30_seconds() { | ||||
NodeHealth[] nodeHealths = IntStream.range(0, 1 + random.nextInt(6)).mapToObj(i -> randomNodeHealth()).toArray(NodeHealth[]::new); | NodeHealth[] nodeHealths = IntStream.range(0, 1 + random.nextInt(6)).mapToObj(i -> randomNodeHealth()).toArray(NodeHealth[]::new); | ||||
Map<String, TimestampedNodeHealth> allNodeHealths = new HashMap<>(); | |||||
Map<String, NodeHealth> expected = new HashMap<>(); | |||||
String randomUuidBase = randomAlphanumeric(5); | |||||
for (int i = 0; i < nodeHealths.length; i++) { | |||||
String memberUuid = randomUuidBase + i; | |||||
TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealths[i], clusterTime - random.nextInt(30 * 1000)); | |||||
Map<UUID, TimestampedNodeHealth> allNodeHealths = new HashMap<>(); | |||||
Map<UUID, NodeHealth> expected = new HashMap<>(); | |||||
for (NodeHealth nodeHealth : nodeHealths) { | |||||
UUID memberUuid = UUID.randomUUID(); | |||||
TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealth, clusterTime - random.nextInt(30 * 1000)); | |||||
allNodeHealths.put(memberUuid, timestampedNodeHealth); | allNodeHealths.put(memberUuid, timestampedNodeHealth); | ||||
if (random.nextBoolean()) { | if (random.nextBoolean()) { | ||||
expected.put(memberUuid, nodeHealths[i]); | |||||
expected.put(memberUuid, nodeHealth); | |||||
} | } | ||||
} | } | ||||
doReturn(allNodeHealths).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(allNodeHealths).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
@Test | @Test | ||||
public void readAll_ignores_NodeHealth_of_30_seconds_before_cluster_time() { | public void readAll_ignores_NodeHealth_of_30_seconds_before_cluster_time() { | ||||
NodeHealth nodeHealth = randomNodeHealth(); | NodeHealth nodeHealth = randomNodeHealth(); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
String memberUuid = randomAlphanumeric(5); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
UUID memberUuid = UUID.randomUUID(); | |||||
TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealth, clusterTime - 30 * 1000); | TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealth, clusterTime - 30 * 1000); | ||||
map.put(memberUuid, timestampedNodeHealth); | map.put(memberUuid, timestampedNodeHealth); | ||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
@Test | @Test | ||||
public void readAll_ignores_NodeHealth_of_more_than_30_seconds_before_cluster_time() { | public void readAll_ignores_NodeHealth_of_more_than_30_seconds_before_cluster_time() { | ||||
NodeHealth nodeHealth = randomNodeHealth(); | NodeHealth nodeHealth = randomNodeHealth(); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
String memberUuid = randomAlphanumeric(5); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
UUID memberUuid = UUID.randomUUID(); | |||||
TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealth, clusterTime - 30 * 1000 - random.nextInt(99)); | TimestampedNodeHealth timestampedNodeHealth = new TimestampedNodeHealth(nodeHealth, clusterTime - 30 * 1000 - random.nextInt(99)); | ||||
map.put(memberUuid, timestampedNodeHealth); | map.put(memberUuid, timestampedNodeHealth); | ||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
@Test | @Test | ||||
public void readAll_logs_map_sq_health_state_content_and_the_content_effectively_returned_if_TRACE() { | public void readAll_logs_map_sq_health_state_content_and_the_content_effectively_returned_if_TRACE() { | ||||
logging.setLevel(Level.TRACE); | logging.setLevel(Level.TRACE); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
String uuid = randomAlphanumeric(44); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
UUID uuid = UUID.randomUUID(); | |||||
NodeHealth nodeHealth = randomNodeHealth(); | NodeHealth nodeHealth = randomNodeHealth(); | ||||
map.put(uuid, new TimestampedNodeHealth(nodeHealth, clusterTime - 1)); | map.put(uuid, new TimestampedNodeHealth(nodeHealth, clusterTime - 1)); | ||||
when(hazelcastMember.getClusterTime()).thenReturn(clusterTime); | when(hazelcastMember.getClusterTime()).thenReturn(clusterTime); | ||||
@Test | @Test | ||||
public void readAll_logs_message_for_each_timed_out_NodeHealth_ignored_if_TRACE() { | public void readAll_logs_message_for_each_timed_out_NodeHealth_ignored_if_TRACE() { | ||||
logging.setLevel(Level.TRACE); | logging.setLevel(Level.TRACE); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
String memberUuid1 = randomAlphanumeric(44); | |||||
String memberUuid2 = randomAlphanumeric(44); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
UUID memberUuid1 = UUID.randomUUID(); | |||||
UUID memberUuid2 = UUID.randomUUID(); | |||||
map.put(memberUuid1, new TimestampedNodeHealth(randomNodeHealth(), clusterTime - 30 * 1000)); | map.put(memberUuid1, new TimestampedNodeHealth(randomNodeHealth(), clusterTime - 30 * 1000)); | ||||
map.put(memberUuid2, new TimestampedNodeHealth(randomNodeHealth(), clusterTime - 30 * 1000)); | map.put(memberUuid2, new TimestampedNodeHealth(randomNodeHealth(), clusterTime - 30 * 1000)); | ||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
@Test | @Test | ||||
public void clearMine_clears_entry_into_map_sq_health_state_under_current_client_uuid() { | public void clearMine_clears_entry_into_map_sq_health_state_under_current_client_uuid() { | ||||
Map<String, TimestampedNodeHealth> map = mock(Map.class); | |||||
Map<UUID, TimestampedNodeHealth> map = mock(Map.class); | |||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
String uuid = randomAlphanumeric(5); | |||||
UUID uuid = UUID.randomUUID(); | |||||
when(hazelcastMember.getUuid()).thenReturn(uuid); | when(hazelcastMember.getUuid()).thenReturn(uuid); | ||||
underTest.clearMine(); | underTest.clearMine(); | ||||
@Test | @Test | ||||
public void clearMine_logs_map_sq_health_state_and_current_client_uuid_if_TRACE() { | public void clearMine_logs_map_sq_health_state_and_current_client_uuid_if_TRACE() { | ||||
logging.setLevel(Level.TRACE); | logging.setLevel(Level.TRACE); | ||||
Map<String, TimestampedNodeHealth> map = new HashMap<>(); | |||||
map.put(randomAlphanumeric(4), new TimestampedNodeHealth(randomNodeHealth(), random.nextLong())); | |||||
Map<UUID, TimestampedNodeHealth> map = new HashMap<>(); | |||||
map.put(UUID.randomUUID(), new TimestampedNodeHealth(randomNodeHealth(), random.nextLong())); | |||||
doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | doReturn(map).when(hazelcastMember).getReplicatedMap(MAP_SQ_HEALTH_STATE); | ||||
String uuid = randomAlphanumeric(5); | |||||
UUID uuid = UUID.randomUUID(); | |||||
when(hazelcastMember.getUuid()).thenReturn(uuid); | when(hazelcastMember.getUuid()).thenReturn(uuid); | ||||
underTest.clearMine(); | underTest.clearMine(); |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.cluster.Member; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.UUID; | |||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.sonar.process.cluster.hz.HazelcastMember.Attribute.NODE_NAME; | import static org.sonar.process.cluster.hz.HazelcastMember.Attribute.NODE_NAME; | ||||
public class DistributedAnswerTest { | public class DistributedAnswerTest { | ||||
@Rule | @Rule | ||||
public ExpectedException expectedException = ExpectedException.none(); | public ExpectedException expectedException = ExpectedException.none(); | ||||
private Member member = newMember("member1"); | |||||
private DistributedAnswer underTest = new DistributedAnswer(); | |||||
private final Member member = newMember(UUID.randomUUID()); | |||||
private final DistributedAnswer<String> underTest = new DistributedAnswer<>(); | |||||
@Test | @Test | ||||
public void getMembers_return_all_members() { | public void getMembers_return_all_members() { | ||||
underTest.setAnswer(member, "foo"); | underTest.setAnswer(member, "foo"); | ||||
underTest.setTimedOut(newMember("bar")); | |||||
underTest.setFailed(newMember("baz"), new IOException("BOOM")); | |||||
underTest.setTimedOut(newMember(UUID.randomUUID())); | |||||
underTest.setFailed(newMember(UUID.randomUUID()), new IOException("BOOM")); | |||||
assertThat(underTest.getMembers()).hasSize(3); | assertThat(underTest.getMembers()).hasSize(3); | ||||
} | } | ||||
@Test | @Test | ||||
public void propagateExceptions_does_nothing_if_no_errors() { | public void propagateExceptions_does_nothing_if_no_errors() { | ||||
underTest.setAnswer(newMember("foo"), "bar"); | |||||
underTest.setAnswer(newMember(UUID.randomUUID()), "bar"); | |||||
// no errors | // no errors | ||||
underTest.propagateExceptions(); | underTest.propagateExceptions(); | ||||
@Test | @Test | ||||
public void propagateExceptions_throws_ISE_if_at_least_one_timeout() { | public void propagateExceptions_throws_ISE_if_at_least_one_timeout() { | ||||
underTest.setAnswer(newMember("bar"), "baz"); | |||||
underTest.setTimedOut(newMember("foo")); | |||||
UUID uuid = UUID.randomUUID(); | |||||
UUID otherUuid = UUID.randomUUID(); | |||||
underTest.setAnswer(newMember(uuid), "baz"); | |||||
underTest.setTimedOut(newMember(otherUuid)); | |||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Distributed cluster action timed out in cluster nodes foo"); | |||||
expectedException.expectMessage("Distributed cluster action timed out in cluster nodes " + otherUuid); | |||||
underTest.propagateExceptions(); | underTest.propagateExceptions(); | ||||
} | } | ||||
@Test | @Test | ||||
public void propagateExceptions_throws_ISE_if_at_least_one_failure() { | public void propagateExceptions_throws_ISE_if_at_least_one_failure() { | ||||
underTest.setAnswer(newMember("bar"), "baz"); | |||||
underTest.setFailed(newMember("foo"), new IOException("BOOM")); | |||||
UUID foo = UUID.randomUUID(); | |||||
UUID bar = UUID.randomUUID(); | |||||
underTest.setAnswer(newMember(bar), "baz"); | |||||
underTest.setFailed(newMember(foo), new IOException("BOOM")); | |||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Distributed cluster action in cluster nodes foo (other nodes may have timed out)"); | |||||
expectedException.expectMessage("Distributed cluster action in cluster nodes " + foo + " (other nodes may have timed out)"); | |||||
underTest.propagateExceptions(); | underTest.propagateExceptions(); | ||||
} | } | ||||
private static Member newMember(String uuid) { | |||||
private static Member newMember(UUID uuid) { | |||||
Member member = mock(Member.class); | Member member = mock(Member.class); | ||||
when(member.getUuid()).thenReturn(uuid); | when(member.getUuid()).thenReturn(uuid); | ||||
when(member.getStringAttribute(NODE_NAME.getKey())).thenReturn(uuid); | |||||
when(member.getAttribute(NODE_NAME.getKey())).thenReturn(uuid.toString()); | |||||
return member; | return member; | ||||
} | } | ||||
} | } |
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Set; | |||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60)); | public TestRule safeguardTimeout = new DisableOnDebug(Timeout.seconds(60)); | ||||
// use loopback for support of offline builds | // use loopback for support of offline builds | ||||
private InetAddress loopback = InetAddress.getLoopbackAddress(); | |||||
private InetAdressResolver inetAdressResolver = mock(InetAdressResolver.class); | |||||
private HazelcastMemberBuilder underTest = new HazelcastMemberBuilder(inetAdressResolver); | |||||
private final InetAddress loopback = InetAddress.getLoopbackAddress(); | |||||
private final InetAdressResolver inetAdressResolver = mock(InetAdressResolver.class); | |||||
private final HazelcastMemberBuilder underTest = new HazelcastMemberBuilder(inetAdressResolver); | |||||
@Before | @Before | ||||
public void before() throws UnknownHostException { | public void before() throws UnknownHostException { | ||||
.setNetworkInterface(loopback.getHostAddress()) | .setNetworkInterface(loopback.getHostAddress()) | ||||
.build(); | .build(); | ||||
assertThat(member.getUuid()).isNotEmpty(); | |||||
assertThat(member.getClusterTime()).isGreaterThan(0); | |||||
assertThat(member.getUuid()).isNotNull(); | |||||
assertThat(member.getClusterTime()).isPositive(); | |||||
assertThat(member.getCluster().getMembers()).hasSize(1); | assertThat(member.getCluster().getMembers()).hasSize(1); | ||||
assertThat(member.getMemberUuids()).containsOnlyOnce(member.getUuid()); | assertThat(member.getMemberUuids()).containsOnlyOnce(member.getUuid()); | ||||
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.memberselector.MemberSelectors; | import com.hazelcast.cluster.memberselector.MemberSelectors; | ||||
import com.hazelcast.core.Member; | |||||
import java.net.InetAddress; | import java.net.InetAddress; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.List; | import java.util.List; |
*/ | */ | ||||
package org.sonar.process.cluster.hz; | package org.sonar.process.cluster.hz; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
Member member = mock(Member.class); | Member member = mock(Member.class); | ||||
MemberSelector underTest = HazelcastMemberSelectors.selectorForProcessIds(COMPUTE_ENGINE); | MemberSelector underTest = HazelcastMemberSelectors.selectorForProcessIds(COMPUTE_ENGINE); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(COMPUTE_ENGINE.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(COMPUTE_ENGINE.getKey()); | |||||
assertThat(underTest.select(member)).isTrue(); | assertThat(underTest.select(member)).isTrue(); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(WEB_SERVER.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(WEB_SERVER.getKey()); | |||||
assertThat(underTest.select(member)).isFalse(); | assertThat(underTest.select(member)).isFalse(); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(APP.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(APP.getKey()); | |||||
assertThat(underTest.select(member)).isFalse(); | assertThat(underTest.select(member)).isFalse(); | ||||
} | } | ||||
Member member = mock(Member.class); | Member member = mock(Member.class); | ||||
MemberSelector underTest = HazelcastMemberSelectors.selectorForProcessIds(WEB_SERVER, APP); | MemberSelector underTest = HazelcastMemberSelectors.selectorForProcessIds(WEB_SERVER, APP); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(COMPUTE_ENGINE.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(COMPUTE_ENGINE.getKey()); | |||||
assertThat(underTest.select(member)).isFalse(); | assertThat(underTest.select(member)).isFalse(); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(WEB_SERVER.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(WEB_SERVER.getKey()); | |||||
assertThat(underTest.select(member)).isTrue(); | assertThat(underTest.select(member)).isTrue(); | ||||
when(member.getStringAttribute(PROCESS_KEY.getKey())).thenReturn(APP.getKey()); | |||||
when(member.getAttribute(PROCESS_KEY.getKey())).thenReturn(APP.getKey()); | |||||
assertThat(underTest.select(member)).isTrue(); | assertThat(underTest.select(member)).isTrue(); | ||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.server.platform.monitoring.cluster; | package org.sonar.server.platform.monitoring.cluster; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
MemberSelector memberSelector = HazelcastMemberSelectors.selectorForProcessIds(ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE); | MemberSelector memberSelector = HazelcastMemberSelectors.selectorForProcessIds(ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE); | ||||
DistributedAnswer<ProtobufSystemInfo.SystemInfo> distributedAnswer = hzMember.call(ProcessInfoProvider::provide, memberSelector, DISTRIBUTED_TIMEOUT_MS); | DistributedAnswer<ProtobufSystemInfo.SystemInfo> distributedAnswer = hzMember.call(ProcessInfoProvider::provide, memberSelector, DISTRIBUTED_TIMEOUT_MS); | ||||
for (Member member : distributedAnswer.getMembers()) { | for (Member member : distributedAnswer.getMembers()) { | ||||
String nodeName = member.getStringAttribute(NODE_NAME.getKey()); | |||||
String nodeName = member.getAttribute(NODE_NAME.getKey()); | |||||
NodeInfo nodeInfo = nodesByName.computeIfAbsent(nodeName, name -> { | NodeInfo nodeInfo = nodesByName.computeIfAbsent(nodeName, name -> { | ||||
NodeInfo info = new NodeInfo(name); | NodeInfo info = new NodeInfo(name); | ||||
info.setHost(member.getAddress().getHost()); | info.setHost(member.getAddress().getHost()); |
*/ | */ | ||||
package org.sonar.server.platform.monitoring.cluster; | package org.sonar.server.platform.monitoring.cluster; | ||||
import com.hazelcast.core.Member; | |||||
import com.hazelcast.core.MemberSelector; | |||||
import com.hazelcast.nio.Address; | |||||
import com.hazelcast.cluster.Address; | |||||
import com.hazelcast.cluster.Member; | |||||
import com.hazelcast.cluster.MemberSelector; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.net.InetAddress; | import java.net.InetAddress; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
public class AppNodesInfoLoaderImplTest { | public class AppNodesInfoLoaderImplTest { | ||||
private static final InetAddress AN_ADDRESS = InetAddress.getLoopbackAddress(); | private static final InetAddress AN_ADDRESS = InetAddress.getLoopbackAddress(); | ||||
@Rule | @Rule | ||||
public ExpectedException expectedException = ExpectedException.none(); | public ExpectedException expectedException = ExpectedException.none(); | ||||
private HazelcastMember hzMember = mock(HazelcastMember.class); | |||||
private AppNodesInfoLoaderImpl underTest = new AppNodesInfoLoaderImpl(hzMember); | |||||
private final HazelcastMember hzMember = mock(HazelcastMember.class); | |||||
private final AppNodesInfoLoaderImpl underTest = new AppNodesInfoLoaderImpl(hzMember); | |||||
@Test | @Test | ||||
public void load_info_from_all_nodes() throws Exception { | public void load_info_from_all_nodes() throws Exception { | ||||
private Member newMember(String name) { | private Member newMember(String name) { | ||||
Member member = mock(Member.class, Mockito.RETURNS_MOCKS); | Member member = mock(Member.class, Mockito.RETURNS_MOCKS); | ||||
when(member.getStringAttribute(HazelcastMember.Attribute.NODE_NAME.getKey())).thenReturn(name); | |||||
when(member.getAttribute(HazelcastMember.Attribute.NODE_NAME.getKey())).thenReturn(name); | |||||
when(member.getAddress()).thenReturn(new Address(AN_ADDRESS, 6789)); | when(member.getAddress()).thenReturn(new Address(AN_ADDRESS, 6789)); | ||||
return member; | return member; | ||||
} | } |