/** * Project: doris.admin.service.common-0.1.0-SNAPSHOT File Created at 2011-5-24 $Id$ Copyright 1999-2100 Alibaba.com * Corporation Limited. All rights reserved. This software is the confidential and proprietary information of Alibaba * Company. ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into with Alibaba.com. */ package com.alibaba.doris.admin.service.failover.node.check; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.alibaba.doris.admin.core.AdminServiceLocator; import com.alibaba.doris.admin.dataobject.PhysicalNodeDO; import com.alibaba.doris.admin.monitor.MonitorEnum; import com.alibaba.doris.admin.monitor.SystemLogMonitor; import com.alibaba.doris.admin.service.AdminNodeService; import com.alibaba.doris.admin.service.PropertiesService; import com.alibaba.doris.admin.service.common.AdminServiceException; import com.alibaba.doris.admin.service.common.node.NodesManager; import com.alibaba.doris.admin.service.common.route.DorisConfigServiceException; import com.alibaba.doris.admin.service.common.route.RouteConfigProcessor; import com.alibaba.doris.admin.service.failover.processor.FailoverProcessor; import com.alibaba.doris.admin.service.failover.processor.ForeverFailoverProcessor; import com.alibaba.doris.admin.service.failover.processor.TempFailoverProcessor; import com.alibaba.doris.client.net.Connection; import com.alibaba.doris.client.net.OperationFuture; import com.alibaba.doris.client.net.command.CheckCommand.CheckType; import com.alibaba.doris.client.net.command.CheckCommand.Type; import com.alibaba.doris.client.net.command.result.CheckResult; import com.alibaba.doris.common.AdminServiceConstants; import com.alibaba.doris.common.MonitorWarnConstants; import com.alibaba.doris.common.NodeRouteStatus; import com.alibaba.doris.common.StoreNode; import com.alibaba.doris.common.StoreNodeSequenceEnum; /** * @author mian.hem */ public class NodeCheckThread extends Thread { private static final Log log = LogFactory.getLog(NodeCheckThread.class); private NodesManager nodeManager = NodesManager.getInstance(); private NodeCheckManager nodeCheckManager = null; private AdminNodeService nodeService = AdminServiceLocator.getAdminNodeService(); private Map<String, Long> tempFailTimes = new HashMap<String, Long>(); private boolean stopped = false; private static PropertiesService propertyService = AdminServiceLocator.getPropertiesService(); private long sleepTime = propertyService.getProperty( "nodeCheckInterval", Long.TYPE, AdminServiceConstants.NODE_DEFAULT_HEART_BEAT_INTERVAL); private long foreverFailTime = propertyService.getProperty( "foreverFailTime", Long.TYPE, AdminServiceConstants.NODE_DEFAULT_FOREVER_FAIL_TIME); public static long nodeCheckTimeout = propertyService.getProperty( "nodeCheckTimeout", Long.TYPE, AdminServiceConstants.NODE_CHECK_TIMEOUT_DEFAULT); public static int nodeCheckRetries = propertyService.getProperty( "nodeCheckRetries", Integer.TYPE, AdminServiceConstants.NODE_CHECK_RETRIES_DEFAULT); public NodeCheckThread(NodeCheckManager nodeCheckManager) { if (nodeCheckManager == null) { throw new IllegalArgumentException("nodeCheckManager cannot be null."); } this.nodeCheckManager = nodeCheckManager; } public void over() { this.stopped = true; this.interrupt(); } public void run() { while (!stopped) { if (log.isDebugEnabled()) { log.debug(" node checking starts..."); } try { verifyNode(); sleep(sleepTime); } catch (Throwable e) { log.error("node check thread exception:", e); } if (log.isDebugEnabled()) { log.debug(" node checking ends..."); } } } private void verifyNode() { try { Collection<PhysicalNodeDO> allnodes = nodeManager.getAllNodeList(); if (log.isDebugEnabled()) { log.debug("There are " + allnodes.size() + " nodes to check"); } if (allnodes != null && !allnodes.isEmpty()) { List<NodeCheckResult> checkResults = new ArrayList<NodeCheckResult>(); for (PhysicalNodeDO pNode : allnodes) { NodeCheckResult nodeCheckResult = new NodeCheckResult(pNode, null, nodeCheckRetries); checkResults.add(nodeCheckResult); } checkAllNodeHealth(checkResults); for (NodeCheckResult checkRslt : checkResults) { NodeHealth originalNodeHealth = checkRslt.getOriginalNodeHealth(); NodeHealth nodeHealth = checkRslt.getCurrentNodeHealth(); String physicalId = checkRslt.getPyhsicalNode().getPhysicalId(); // SYSTEM LOGGING if (originalNodeHealth != nodeHealth) { if (nodeHealth == NodeHealth.NG) { SystemLogMonitor.info(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_HEALTH_CHANGE_TO_NG + physicalId); } else { SystemLogMonitor.info(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_HEALTH_CHANGE_TO_OK + physicalId); } } if (nodeHealth == null) { if (log.isWarnEnabled()) { log.warn("Cannot get the node health status for node with pid=" + physicalId); } continue; } StoreNodeSequenceEnum storeSequence = StoreNodeSequenceEnum.getTypeByValue(checkRslt.getPyhsicalNode().getSerialId()); if (storeSequence == StoreNodeSequenceEnum.TEMP_SEQUENCE) { checkRsltForTempSeq(nodeHealth, checkRslt.getPyhsicalNode()); } else { checkRsltForNormalSeq(originalNodeHealth, nodeHealth, checkRslt.getPyhsicalNode()); } } } } catch (Throwable e) { log.error("fail to verify nodes:", e); } } private void checkRsltForTempSeq(NodeHealth nodeHealth, PhysicalNodeDO physicalNodeDO) { String physicalId = physicalNodeDO.getPhysicalId(); StoreNode node = nodeManager.getStoreNode(physicalId); if (node == null) { log.error("Cannot find node for node with pid=" + physicalId); return; } NodeRouteStatus routeStatus = node.getStatus(); // the node route status should be consistent with node health status for temp sequence if ((nodeHealth == NodeHealth.OK && routeStatus == NodeRouteStatus.TEMP_FAILED) || (nodeHealth == NodeHealth.NG && routeStatus == NodeRouteStatus.OK)) { NodeRouteStatus status = (nodeHealth == NodeHealth.OK) ? NodeRouteStatus.OK : NodeRouteStatus.TEMP_FAILED; if (log.isInfoEnabled()) { log.info("update node status for node \"" + physicalId + "\" by set status to " + status); } nodeService.updatePhysicalNodeStatus(physicalId, status.getValue()); if (nodeHealth == NodeHealth.NG) { SystemLogMonitor.error(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_TEMP_FAILED + ":" + physicalId); } else { SystemLogMonitor.info(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_TEMP_FAILURE_RESOLVED + ":" + physicalId); } } } private void checkRsltForNormalSeq(NodeHealth originalNodeHealth, NodeHealth nodeHealth, PhysicalNodeDO physicalNodeDO) { updateTempFailedTimes(nodeHealth, physicalNodeDO); // node health changes, fire corresponding events base on nodeHealth onNodeHealthStatusChange(physicalNodeDO, nodeHealth, originalNodeHealth); } private void updateTempFailedTimes(NodeHealth nodeHealth, PhysicalNodeDO physicalNodeDO) { String physicalId = physicalNodeDO.getPhysicalId(); if (NodeHealth.NG == nodeHealth) { Long times = tempFailTimes.get(physicalId); if (times == null) { // first time temp failed. if (log.isDebugEnabled()) { log.debug("temp failed first time. for node :pid =" + physicalId); } tempFailTimes.put(physicalId, 1L); } else { tempFailTimes.put(physicalId, ++times); if (log.isDebugEnabled()) { log.debug("temp failed first time. for node :pid =" + tempFailTimes.get(physicalId)); } } } else { tempFailTimes.put(physicalId, null); } } private void onNodeHealthStatusChange(PhysicalNodeDO physicalNodeDO, NodeHealth nodeHealth, NodeHealth originalNodeHealth) { String physicalId = physicalNodeDO.getPhysicalId(); boolean isNormalNode = StoreNodeSequenceEnum.isNormalSequence(String.valueOf(physicalNodeDO.getSerialId())); if (nodeHealth == NodeHealth.NG) { StoreNode node = nodeManager.getStoreNode(physicalId); if (node == null) { log.error("Cannot find node for node with pid=" + physicalId); return; } NodeRouteStatus routeStatus = node.getStatus(); if (routeStatus != NodeRouteStatus.TEMP_FAILED) { // 1. from NodeHealth.OK to NodeHealth.NG, generate new route configuration instance int status = NodeRouteStatus.TEMP_FAILED.getValue(); if (log.isInfoEnabled()) { log.info("update node status for node \"" + physicalId + "\" by set status to " + NodeRouteStatus.TEMP_FAILED); } nodeService.updatePhysicalNodeStatus(physicalId, status); try { RouteConfigProcessor.getInstance().refresh(); } catch (DorisConfigServiceException e) { log.error("failed refresh route config", e); // message 'Re-Gen Route failed:' is used as dragoon warning rule, should be consistent. SystemLogMonitor.error(MonitorEnum.ROUTER, MonitorWarnConstants.RE_GEN_ROUTE_FAILED + ":" + physicalId, e); } if (log.isInfoEnabled()) { log.info("refresh and generated new route config instance."); } // message 'Node Temp Failed:' is used as dragoon warning rule, should be consistent. SystemLogMonitor.error(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_TEMP_FAILED + ":" + physicalId); } boolean foreverFailed = checkForeverFail(physicalId); if (foreverFailed) { SystemLogMonitor.error(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_FOREVER_FAILED + ":" + physicalId); } if (foreverFailed && isNormalNode) { if (log.isInfoEnabled()) { log.info("resolve type: forever failed resolve for node :" + physicalId); } FailoverProcessor failoverProcessor = ForeverFailoverProcessor.getInstance(); try { failoverProcessor.failResolve(physicalId); if (log.isInfoEnabled()) { log.info("resolve starts for node with physical id \"" + physicalId + "\"."); } } catch (AdminServiceException e) { log.error("resolve starts for node with physical id \"" + physicalId + "\", but failed."); SystemLogMonitor.error(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_FOREVER_FAILURE_RESOLVE_FAILED + ":" + physicalId, e); } // reset counter tempFailTimes.put(physicalId, null); } } else if (originalNodeHealth == NodeHealth.NG && isNormalNode) { // 2. from NodeHealth.NG to NodeHealth.OK, start fail over recover FailoverProcessor failoverProcessor = TempFailoverProcessor.getInstance(); // boolean foreverFailed = checkForeverFail(physicalId); // if (foreverFailed) { // if (log.isInfoEnabled()) { // log.info("resolve type: forever failed resolve for node :" + physicalId); // } // failoverProcessor = ForeverFailoverProcessor.getInstance(); // } else { if (log.isInfoEnabled()) { log.info("resolve type: temp failed resolve for node :" + physicalId); } // } try { failoverProcessor.failResolve(physicalId); if (log.isInfoEnabled()) { log.info("resolve starts for node with physical id \"" + physicalId + "\"."); } } catch (AdminServiceException e) { log.error("resolve starts for node with physical id \"" + physicalId + "\", but failed."); // message 'TempFailure Resolve Failed:' is used as dragoon warning rule, should be consistent. SystemLogMonitor.error(MonitorEnum.NODE_HEALTH, MonitorWarnConstants.NODE_TEMP_FAILURE_RESOLVE_FAILED + ":" + physicalId, e); } if (log.isDebugEnabled()) { log.debug("temp failed counter is reset for node :pid =" + physicalId); } } } private boolean checkForeverFail(String phId) { Long times = tempFailTimes.get(phId); if (times != null) { return (times * sleepTime) > foreverFailTime; } return false; } private void checkAllNodeHealth(List<NodeCheckResult> checkResults) { for (NodeCheckResult checkResult : checkResults) { PhysicalNodeDO pNode = checkResult.getPyhsicalNode(); try { Connection conn = NodesManager.getInstance().getNodeConnection(pNode.getPhysicalId()); StoreNodeSequenceEnum nodeSeq = StoreNodeSequenceEnum.getTypeByValue(pNode.getSerialId()); Type checkType = null; switch (nodeSeq) { case TEMP_SEQUENCE: // 临时节点 checkType = CheckType.CHECK_TEMP_NODE; break; case STANDBY_SEQUENCE: // 备用节点 checkType = CheckType.CHECK_STANDBY_NODE; break; case UNUSE_SEQUENCE: // 待用节点 checkType = null; break; default: //正常(Data)节点 checkType = CheckType.CHECK_NORMAL_NODE; break; } OperationFuture<CheckResult> future = conn.check(checkType); checkResult.setResultFuture(future); } catch (Throwable e) { // From 0.1.3: log.error("Failed to start \"check\" commmond:", e); log.error("Failed to start \"check\" commmond, message:" + e.getMessage()); } } List<NodeCheckResult> recheckList = processNodeHealthResult(checkResults); if (!recheckList.isEmpty()) { checkAllNodeHealth(recheckList); } } private List<NodeCheckResult> processNodeHealthResult(List<NodeCheckResult> checkResults) { List<NodeCheckResult> needReCheckResults = new ArrayList<NodeCheckResult>(); for (NodeCheckResult chkRslt : checkResults) { // get the original node health String physicalId = chkRslt.getPyhsicalNode().getPhysicalId(); if (log.isDebugEnabled()) { log.debug("start node checking : pid=" + physicalId); } NodeHealth nodeHealth = NodeHealth.NG; try { CheckResult checkResult = null; try { OperationFuture<CheckResult> future = chkRslt.getResultFuture(); if (future != null) { long start = System.currentTimeMillis(); checkResult = future.get(nodeCheckTimeout, TimeUnit.MILLISECONDS); if (checkResult == null && log.isDebugEnabled()) { long end = System.currentTimeMillis(); log.warn("Time:[" + (end - start) + "]check failed, need to check data server log...==>pid=" + physicalId); } } } catch (Exception e) { log.error("check failed for node :" + physicalId); log.error(e.getMessage(), e); checkResult = null; } if (checkResult == null) { nodeHealth = NodeHealth.NG; log.error("Check result is null for node :" + physicalId); } else if (checkResult.isSuccess()) { nodeHealth = NodeHealth.OK; } else { nodeHealth = NodeHealth.NG; log.warn("Check result is NG for node :" + physicalId + ", Message:" + checkResult.getMessage()); } } catch (Exception e) { log.debug("fail to check node :" + physicalId, e); nodeHealth = NodeHealth.NG; } if (nodeHealth == NodeHealth.NG) { log.error("Check result is NG for node :" + physicalId); } else { chkRslt.setRetries(0); } int retries = chkRslt.getRetries(); if (retries > 0) { if (log.isDebugEnabled()) { log.debug("This is the " + (nodeCheckRetries - chkRslt.getRetries() + 1) + "times' retry for node :" + physicalId); } chkRslt.setRetries(--retries); needReCheckResults.add(chkRslt); } else { NodeHealth originalNodeHealth = nodeCheckManager.checkNode(physicalId, false); if (log.isDebugEnabled()) { log.debug("checkresult (previous) is \"" + originalNodeHealth + "\" for node : pid=" + physicalId); } nodeCheckManager.updateNodeHealth(physicalId, nodeHealth); if (log.isDebugEnabled()) { log.debug("checkresult is (current)\"" + nodeHealth + "\" for node : pid=" + physicalId); } chkRslt.setCurrentNodeHealth(nodeHealth); chkRslt.setOriginalNodeHealth(originalNodeHealth); } } return needReCheckResults; } }