All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zulia.server.node.MembershipTask Maven / Gradle / Ivy

There is a newer version: 1.6.4
Show newest version
package io.zulia.server.node;

import io.zulia.message.ZuliaBase.Node;
import io.zulia.server.config.NodeService;
import io.zulia.server.config.ZuliaConfig;
import io.zulia.server.index.ZuliaIndexManager;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public abstract class MembershipTask extends TimerTask {

	//a node is no longer considered alive if it has not updated its heartbeat for this number of seconds
	public static final int MAX_HEARTBEAT_LAG_SECONDS = 30;

	private final NodeService nodeService;
	private final ZuliaConfig zuliaConfig;

	private Map otherNodeMap;

	private static final Logger LOG = Logger.getLogger(ZuliaIndexManager.class.getName());

	public MembershipTask(ZuliaConfig zuliaConfig, NodeService nodeService) {
		this.nodeService = nodeService;
		this.zuliaConfig = zuliaConfig;
		this.otherNodeMap = new HashMap<>();
	}

	@Override
	public void run() {

		try {
			//update this node's heartbeat
			nodeService.updateHeartbeat(zuliaConfig.getServerAddress(), zuliaConfig.getServicePort());

			//get list of other cluster members and sort by heartbeat time descending
			//members never start with have heartbeat time of 0
			ArrayList nodesList = new ArrayList<>(nodeService.getNodes());
			nodesList.sort(Comparator.comparingLong(Node::getHeartbeat).reversed());

			Map newOtherNodeMap = new HashMap<>();

			long latest = nodesList.get(0).getHeartbeat();
			for (Node node : nodesList) {
				if (latest - node.getHeartbeat() < MAX_HEARTBEAT_LAG_SECONDS * 1000) {
					if (node.getServerAddress().equals(zuliaConfig.getServerAddress()) && (node.getServicePort() == zuliaConfig.getServicePort())) {
						//skip this server
					}
					else {
						newOtherNodeMap.put(node.getServerAddress() + ":" + node.getServicePort(), node);
					}
				}
			}

			List removedNodesList = Collections.emptyList();
			List newNodesList = Collections.emptyList();

			{
				Set removedNodes = new HashSet<>(otherNodeMap.keySet());
				removedNodes.removeAll(newOtherNodeMap.keySet());

				if (!removedNodes.isEmpty()) {
					removedNodesList = removedNodes.stream().map(otherNodeMap::get).collect(Collectors.toList());
				}

				Set newNodes = new HashSet<>(newOtherNodeMap.keySet());
				newNodes.removeAll(otherNodeMap.keySet());

				if (!newNodes.isEmpty()) {
					newNodesList = newNodes.stream().map(newOtherNodeMap::get).collect(Collectors.toList());
				}

			}

			otherNodeMap = newOtherNodeMap;

			if (!newNodesList.isEmpty() || !removedNodesList.isEmpty()) {
				ArrayList otherNodes = new ArrayList<>(otherNodeMap.values());

				for (Node removedNode : removedNodesList) {
					handleNodeRemove(otherNodes, removedNode);
				}

				for (Node newNode : newNodesList) {
					handleNodeAdded(otherNodes, newNode);
				}
			}

		}
		catch (Throwable t) {
			LOG.log(Level.SEVERE, "Update membership failed: ", t);
		}
	}

	protected abstract void handleNodeRemove(Collection currentOtherNodesActive, Node removedNode);

	protected abstract void handleNodeAdded(Collection currentOtherNodesActive, Node newNode);

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy