Browse Source

SONAR-14625 Upgrade to Hazelcast 4.2

tags/8.9.0.43852
Zipeng WU 3 years ago
parent
commit
d8130b6764
23 changed files with 198 additions and 162 deletions
  1. 1
    2
      build.gradle
  2. 3
    2
      server/sonar-ce/src/main/java/org/sonar/ce/CeDistributedInformationImpl.java
  3. 29
    21
      server/sonar-ce/src/test/java/org/sonar/ce/CeDistributedInformationImplTest.java
  4. 3
    3
      server/sonar-main/src/main/java/org/sonar/application/cluster/AppNodesClusterHostsConsistency.java
  5. 19
    19
      server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterAppStateImpl.java
  6. 4
    3
      server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterProcess.java
  7. 8
    7
      server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java
  8. 18
    0
      server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterAppStateImplTest.java
  9. 6
    4
      server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterProcessTest.java
  10. 9
    8
      server/sonar-process/src/main/java/org/sonar/process/cluster/health/SharedHealthStateImpl.java
  11. 3
    3
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/DistributedAnswer.java
  12. 1
    1
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/DistributedCallback.java
  13. 7
    6
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java
  14. 7
    8
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberBuilder.java
  15. 9
    8
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java
  16. 2
    2
      server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberSelectors.java
  17. 25
    25
      server/sonar-process/src/test/java/org/sonar/process/cluster/health/SharedHealthStateImplTest.java
  18. 21
    15
      server/sonar-process/src/test/java/org/sonar/process/cluster/hz/DistributedAnswerTest.java
  19. 5
    6
      server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberBuilderTest.java
  20. 1
    1
      server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberImplTest.java
  21. 8
    8
      server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberSelectorsTest.java
  22. 3
    3
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImpl.java
  23. 6
    7
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImplTest.java

+ 1
- 2
build.gradle View File

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'

+ 3
- 2
server/sonar-ce/src/main/java/org/sonar/ce/CeDistributedInformationImpl.java View File

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);
} }
} }

+ 29
- 21
server/sonar-ce/src/test/java/org/sonar/ce/CeDistributedInformationImplTest.java View File

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)));
} }
} }

+ 3
- 3
server/sonar-main/src/main/java/org/sonar/application/cluster/AppNodesClusterHostsConsistency.java View File

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;

+ 19
- 19
server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterAppStateImpl.java View File

*/ */
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());

+ 4
- 3
server/sonar-main/src/main/java/org/sonar/application/cluster/ClusterProcess.java View File



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;
} }



+ 8
- 7
server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java View File

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");
} }



+ 18
- 0
server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterAppStateImplTest.java View File

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));
} }

+ 6
- 4
server/sonar-main/src/test/java/org/sonar/application/cluster/ClusterProcessTest.java View File

*/ */
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));
} }
} }

+ 9
- 8
server/sonar-process/src/main/java/org/sonar/process/cluster/health/SharedHealthStateImpl.java View File

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);
} }



+ 3
- 3
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/DistributedAnswer.java View File

*/ */
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);
} }

+ 1
- 1
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/DistributedCallback.java View File

*/ */
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

+ 7
- 6
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java View File

*/ */
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

+ 7
- 8
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberBuilder.java View File

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));
} }

+ 9
- 8
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java View File

*/ */
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

+ 2
- 2
server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberSelectors.java View File

*/ */
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);
}; };
} }

+ 25
- 25
server/sonar-process/src/test/java/org/sonar/process/cluster/health/SharedHealthStateImplTest.java View File

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();

+ 21
- 15
server/sonar-process/src/test/java/org/sonar/process/cluster/hz/DistributedAnswerTest.java View File

*/ */
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;
} }
} }

+ 5
- 6
server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberBuilderTest.java View File

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());



+ 1
- 1
server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberImplTest.java View File

*/ */
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;

+ 8
- 8
server/sonar-process/src/test/java/org/sonar/process/cluster/hz/HazelcastMemberSelectorsTest.java View File

*/ */
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();
} }
} }

+ 3
- 3
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImpl.java View File

*/ */
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());

+ 6
- 7
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/cluster/AppNodesInfoLoaderImplTest.java View File

*/ */
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;
} }

Loading…
Cancel
Save