package com.flipkart.foxtrot.server.cluster; import com.flipkart.foxtrot.core.querystore.impl.HazelcastConnection; import com.google.common.collect.ImmutableList; import com.hazelcast.config.EvictionPolicy; import com.hazelcast.config.MapConfig; import com.hazelcast.core.IMap; import com.yammer.dropwizard.config.HttpConfiguration; import com.yammer.dropwizard.lifecycle.Managed; import com.yammer.metrics.core.HealthCheck; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.Inet4Address; import java.util.Collection; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ClusterManager implements Managed { private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class.getSimpleName()); private static final String MAP_NAME = "__FOXTROT_MEMBERS_MAP"; private static final int MAP_REFRESH_TIME = 5; private final ClusterMember clusterMember; private IMap<String, ClusterMember> members; private HazelcastConnection hazelcastConnection; private final List<HealthCheck> healthChecks; private ScheduledExecutorService executor; public ClusterManager(HazelcastConnection connection, List<HealthCheck> healthChecks, HttpConfiguration httpConfiguration) throws Exception { this.hazelcastConnection = connection; this.healthChecks = healthChecks; MapConfig mapConfig = new MapConfig(MAP_NAME); mapConfig.setTimeToLiveSeconds(MAP_REFRESH_TIME + 2); //Reduce jitter mapConfig.setBackupCount(1); mapConfig.setAsyncBackupCount(2); mapConfig.setEvictionPolicy(EvictionPolicy.NONE); hazelcastConnection.getHazelcastConfig().getMapConfigs().put(MAP_NAME, mapConfig); String hostname = Inet4Address.getLocalHost().getCanonicalHostName(); executor = Executors.newScheduledThreadPool(1); clusterMember = new ClusterMember(hostname, httpConfiguration.getPort()); } @Override public void start() throws Exception { members = hazelcastConnection.getHazelcast().getMap(MAP_NAME); executor.scheduleWithFixedDelay(new NodeDataUpdater( healthChecks, members, clusterMember), 0, MAP_REFRESH_TIME, TimeUnit.SECONDS); } @Override public void stop() throws Exception { members.remove(clusterMember.toString()); } public Collection<ClusterMember> getMembers() { return members.values(); } private static final class NodeDataUpdater implements Runnable { private final List<HealthCheck> healthChecks; private IMap<String, ClusterMember> members; private final ClusterMember clusterMember; private NodeDataUpdater(List<HealthCheck> healthChecks, IMap<String, ClusterMember> members, ClusterMember clusterMember) { this.healthChecks = ImmutableList.copyOf(healthChecks); this.members = members; this.clusterMember = clusterMember; } @Override public void run() { if (null == members) { logger.error("Map not yet initialized."); return; } try { boolean isHealthy = true; for (HealthCheck healthCheck : healthChecks) { isHealthy &= healthCheck.execute().isHealthy(); } if (isHealthy) { members.put(clusterMember.toString(), clusterMember); logger.debug("Service is healthy. Registering to map."); } } catch (Exception e) { logger.error("Error updating value in map: ", e); } } } }