package com.sungardas.enhancedsnapshots.cluster; import com.sungardas.enhancedsnapshots.aws.dynamodb.model.EventEntry; import com.sungardas.enhancedsnapshots.aws.dynamodb.model.NodeEntry; import com.sungardas.enhancedsnapshots.aws.dynamodb.repository.EventsRepository; import com.sungardas.enhancedsnapshots.aws.dynamodb.repository.NodeRepository; import com.sungardas.enhancedsnapshots.components.ConfigurationMediator; import com.sungardas.enhancedsnapshots.components.logwatcher.LogsWatcherService; import com.sungardas.enhancedsnapshots.service.MasterService; import com.sungardas.enhancedsnapshots.service.SystemService; import com.sungardas.enhancedsnapshots.util.SystemUtils; import com.sungardas.enhancedsnapshots.ws.WebSocketConfig; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.StreamSupport; @Service public class ClusterEventService implements Runnable { private static final Logger LOG = LogManager.getLogger(ClusterEventService.class); @Autowired private EventsRepository eventsRepository; @Autowired private NodeRepository nodeRepository; @Autowired private SystemService systemService; private ExecutorService executor; @Value("${enhancedsnapshots.default.polling.rate}") private int pollingRate; private long lastCheckTime; @Autowired private ConfigurationMediator configurationMediator; @Autowired private LogsWatcherService logsWatcherService; @Autowired private MasterService masterService; @Autowired private WebSocketConfig webSocketConfig; @Autowired private List<ClusterEventListener> listeners; @Autowired private ClusterEventPublisher clusterEventPublisher; public void run() { while (configurationMediator.isClusterMode() && !Thread.interrupted()) { iterate(); sleep(); } } private void iterate() { List<EventEntry> events = eventsRepository.findByTimeGreaterThan(lastCheckTime); try { for (EventEntry eventEntry : events) { ClusterEvents event = eventEntry.getEvent(); switch (event) { case NODE_LAUNCHED: { NodeEntry nodeEntry = nodeRepository.findOne(eventEntry.getInstanceId()); LOG.info("node launched event: {}", eventEntry.toString()); listeners.forEach(l -> l.launched(eventEntry)); break; } case NODE_TERMINATED: { List<NodeEntry> masters = nodeRepository.findByMaster(true); if (masters.size() > 1) { LOG.warn("System has more than one master[{}]", masters); } // check whether terminated node was master one and current node should become a new master if (masters.size() == 0 && StreamSupport.stream(nodeRepository.findAll().spliterator(), false) .sorted(Comparator.comparing(node -> node.getNodeId())) .findFirst().get().getNodeId().toLowerCase().equals(SystemUtils.getInstanceId().toLowerCase())) { NodeEntry currentNode = nodeRepository.findOne(SystemUtils.getInstanceId()); currentNode.setMaster(true); masterService.init(); nodeRepository.save(currentNode); clusterEventPublisher.masterNodeChanged(); } LOG.info("Node terminated event: {}", eventEntry.toString()); listeners.forEach(l -> l.terminated(eventEntry)); break; } case SETTINGS_UPDATED: { systemService.refreshSystemConfiguration(); LOG.info("System settings synchronized with DB"); break; } case LOGS_WATCHER_STARTED: { logsWatcherService.start(); break; } case MASTER_NODE_CHANGED: { // All node in cluster use embedded broker on master node for User notification // in case master node was changed all nodes in cluster must reconfigure their WebSocket configuration webSocketConfig.updateWebSocketConfiguration(); break; } default: { LOG.warn("Unknown event type: {}", event); break; } } if (eventEntry.getTime() > lastCheckTime) { lastCheckTime = eventEntry.getTime(); } } } catch (Exception e) { LOG.error("Unable to process cluster event", e); } } @PostConstruct public void startListener() { if (configurationMediator.isClusterMode()) { lastCheckTime = System.currentTimeMillis() - 1000 * 60 * 5; LOG.info("ClusterEventService initialization started"); iterate(); LOG.info("ClusterEventService initialization finished"); executor = Executors.newSingleThreadExecutor(); executor.execute(this); LOG.info("Cluster events listener started."); } } @PreDestroy public void stopListener() { if (executor != null) { executor.shutdownNow(); LOG.info("Cluster events listener stopped."); } } private void sleep() { try { TimeUnit.MILLISECONDS.sleep(pollingRate); } catch (InterruptedException e) { e.printStackTrace(); } } }