/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.internal.cluster.impl;
import com.hazelcast.core.Member;
import com.hazelcast.instance.MemberImpl;
import com.hazelcast.instance.Node;
import com.hazelcast.internal.cluster.Versions;
import com.hazelcast.internal.cluster.impl.operations.MemberRemoveOperation;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import com.hazelcast.spi.impl.NodeEngineImpl;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
import static java.lang.String.format;
/**
* Implements member removal logic used before 3.9
*/
@Deprecated
public class MembershipManagerCompat {
private final Node node;
private final NodeEngineImpl nodeEngine;
private final ClusterServiceImpl clusterService;
private final MembershipManager membershipManager;
private final Lock clusterServiceLock;
private final ILogger logger;
MembershipManagerCompat(Node node, ClusterServiceImpl clusterService, Lock clusterServiceLock) {
this.node = node;
this.clusterService = clusterService;
this.clusterServiceLock = clusterServiceLock;
this.nodeEngine = node.getNodeEngine();
this.membershipManager = clusterService.getMembershipManager();
this.logger = node.getLogger(getClass());
}
@SuppressWarnings("checkstyle:npathcomplexity")
public void removeMember(Address deadAddress, String uuid, String reason) {
if (clusterService.getClusterVersion().isGreaterOrEqual(Versions.V3_9)) {
throw new IllegalStateException("Should not be called on versions 3.9+");
}
if (!ensureMemberIsRemovable(deadAddress)) {
return;
}
clusterServiceLock.lock();
try {
if (!clusterService.isJoined()) {
logger.fine("Cannot remove " + deadAddress + " with uuid: " + uuid + " because this node is not joined...");
return;
}
MemberImpl member = membershipManager.getMember(deadAddress);
if (member == null || (uuid != null && !uuid.equals(member.getUuid()))) {
if (logger.isFineEnabled()) {
logger.fine("Cannot remove " + deadAddress + ", either member is not present "
+ "or uuid is not matching. Uuid: " + uuid + ", member: " + member);
}
return;
}
if (deadAddress.equals(clusterService.getMasterAddress())) {
assignNewMaster();
}
if (clusterService.isMaster()) {
clusterService.getClusterJoinManager().removeJoin(deadAddress);
}
Connection conn = node.connectionManager.getConnection(deadAddress);
if (conn != null) {
conn.close(reason, null);
}
removeMember(member);
clusterService.printMemberList();
} finally {
clusterServiceLock.unlock();
}
}
private void assignNewMaster() {
Address oldMasterAddress = clusterService.getMasterAddress();
if (clusterService.isJoined()) {
Collection<MemberImpl> members = membershipManager.getMembers();
Member newMaster = null;
int size = members.size();
if (size > 1) {
Iterator<MemberImpl> iterator = members.iterator();
Member member = iterator.next();
if (member.getAddress().equals(oldMasterAddress)) {
newMaster = iterator.next();
} else {
logger.severe(format("Old master %s is dead, but the first of member list is a different member %s!",
oldMasterAddress, member));
newMaster = member;
}
} else {
logger.warning(format("Old master %s is dead and this node is not master, "
+ "but member list contains only %d members: %s", oldMasterAddress, size, members));
}
logger.info(format("Old master %s left the cluster, assigning new master %s", oldMasterAddress, newMaster));
if (newMaster != null) {
clusterService.setMasterAddress(newMaster.getAddress());
} else {
clusterService.setMasterAddress(null);
}
} else {
clusterService.setMasterAddress(null);
}
if (logger.isFineEnabled()) {
logger.fine(format("Old master: %s, new master: %s ", oldMasterAddress, clusterService.getMasterAddress()));
}
ClusterHeartbeatManager clusterHeartbeatManager = clusterService.getClusterHeartbeatManager();
if (clusterService.isMaster()) {
clusterHeartbeatManager.resetMemberMasterConfirmations();
} else {
clusterHeartbeatManager.sendMasterConfirmation();
}
}
private void removeMember(MemberImpl deadMember) {
assert clusterService.getClusterVersion().isLessThan(Versions.V3_9);
logger.info("Removing " + deadMember);
clusterServiceLock.lock();
try {
ClusterHeartbeatManager clusterHeartbeatManager = clusterService.getClusterHeartbeatManager();
MemberMap currentMembers = membershipManager.getMemberMap();
if (currentMembers.contains(deadMember.getAddress())) {
clusterHeartbeatManager.removeMember(deadMember);
MemberMap newMembers = MemberMap.cloneExcluding(currentMembers, deadMember);
membershipManager.setMembers(newMembers);
if (clusterService.isMaster()) {
if (logger.isFineEnabled()) {
logger.fine(deadMember + " is dead, sending remove to all other members...");
}
sendMemberRemoveOperation(deadMember);
}
membershipManager.handleMemberRemove(newMembers, deadMember);
}
} finally {
clusterServiceLock.unlock();
}
}
private void sendMemberRemoveOperation(Member deadMember) {
for (Member member : membershipManager.getMembers()) {
Address address = member.getAddress();
if (!node.getThisAddress().equals(address) && !address.equals(deadMember.getAddress())) {
MemberRemoveOperation
op = new MemberRemoveOperation(deadMember.getAddress(), deadMember.getUuid());
nodeEngine.getOperationService().send(op, address);
}
}
}
private boolean ensureMemberIsRemovable(Address deadAddress) {
return clusterService.isJoined() && !deadAddress.equals(node.getThisAddress());
}
}