/* * Copyright(C) 2010-2011 Alibaba Group Holding Limited 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.alibaba.doris.dataserver.migrator.task; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.doris.client.net.NetException; import com.alibaba.doris.common.MigrateTypeEnum; import com.alibaba.doris.common.migrate.NodeMigrateStatus; import com.alibaba.doris.common.route.MigrationRoutePair; import com.alibaba.doris.dataserver.migrator.MigrationException; import com.alibaba.doris.dataserver.migrator.MigrationManager; import com.alibaba.doris.dataserver.migrator.action.MigrationActionData; import com.alibaba.doris.dataserver.migrator.connection.ConnectionManager; import com.alibaba.doris.dataserver.migrator.event.MigrationEvent; import com.alibaba.doris.dataserver.migrator.event.MigrationListener; import com.alibaba.doris.dataserver.migrator.filter.MigrationVirtualNodeFinder; import com.alibaba.doris.dataserver.migrator.task.dataclean.DataCleanExecutor; import com.alibaba.doris.dataserver.migrator.utils.DateUtil; /** * MigrationTask * * @author Kun He (Raymond He), kun.hek@alibaba-inc.com * @since 1.0 2011-5-25 */ public abstract class BaseMigrationTask extends Thread implements MigrationTask { private static final Logger logger = LoggerFactory.getLogger(BaseMigrationTask.class); // 等待所有任务迁移完成,接受到删除数据指令的等待超时时间;默认为5小时 private static final long DEFAULT_DATACLEAN_SIGNAL_WAITTING_TIME_OUT = 5; // 前台代理连接管理器 private ConnectionManager proxyConnectionManager; protected String taskName; protected String taskKey; protected MigrateTypeEnum migrateType; protected String message; protected MigrationActionData migrationActionData; protected NodeMigrateStatus migrateStatus = NodeMigrateStatus.NORMAL; protected int progress = DEFAULT_PROCESS; protected boolean finished; final protected long createTime = System.currentTimeMillis(); protected long startTime; protected long cancelTime; protected long finishTime; protected MigrationManager migrationManager; protected DataCleanExecutor dataCleanExecutor; protected DataMigrationExecutor dataMigrationExecutor; protected List<MigrationListener> listeners = new ArrayList<MigrationListener>( 3); protected MigrationReportTimer migrationReportTimer; public static final int DEFAULT_PROCESS = -1; protected MigrationVirtualNodeFinder migrationVirtualNodeFinder; // targetNode // <-> // vnode // index // of // migrationActionData. protected ProgressComputer progressComputer; protected Semaphore cancelSignal = new Semaphore(0); protected Semaphore dataCleanStartSignal = new Semaphore(0); protected ReentrantLock controlLock = new ReentrantLock(); public Semaphore getDataCleanStartSignal() { return dataCleanStartSignal; } public BaseMigrationTask(String taskName, MigrationManager migrationManager) { super(taskName + "-" + new Date()); this.taskName = taskName; this.migrationManager = migrationManager; this.proxyConnectionManager = migrationManager.getMigrationConnectionManager(); } public String getTaskName() { return taskName; } public MigrateTypeEnum getMigrateType() { return this.migrateType; } public String getTaskKey() { return taskKey; } public void setMigrateType(MigrateTypeEnum migrateType) { this.migrateType = migrateType; } public void setMigrationActionData(MigrationActionData migrationActionData) { this.migrationActionData = migrationActionData; this.taskKey = BaseMigrationTask.buildTaskKey(this.migrationActionData); initMigrationVirtualNodeFinder(migrationActionData.getMigrationRoutePairs()); } public void setProgress(int progress) { this.progress = progress; } public int getProgress() { return progress; } public String getMessage() { return message; } public void setMigrationManager(MigrationManager migrationManager) { this.migrationManager = migrationManager; } protected void initMigrationVirtualNodeFinder(List<MigrationRoutePair> migrationRoutePairs) { this.migrationVirtualNodeFinder = new MigrationVirtualNodeFinder(migrationRoutePairs); } public MigrationVirtualNodeFinder getMigrationVirtualNodeFinder() { return migrationVirtualNodeFinder; } /** * 获取Cancel信号量. * * @return */ public Semaphore getCancelSignal() { return cancelSignal; } /** * 开始一个迁移任务 */ public void run() { // do try { // start0 if (begin() == false) { if (logger.isDebugEnabled()) { logger.debug("Fail to start new migration task thread. " + getMigrateType() + ", currrent state: " + migrateStatus); } return; } if (logger.isDebugEnabled()) { logger.debug("Start a migrate task " + getMigrateType() + ",route: " + migrationActionData.getMigrationRouteString()); } migrate(); // finish finish(); dataCleanPrepare(); } catch (MigrationException e) { logger.error("Migration Task Error:" + e, e); notifyMigrateError(e.toString()); } catch (Throwable t) { logger.error("Migration Task Error:" + t, t); notifyMigrateError(t.toString()); } finally { if (logger.isInfoEnabled()) { logger.info("Migration Task Thread Finished, and exit."); } finished = true; finishTime = System.currentTimeMillis(); if (migrationReportTimer != null) { migrationReportTimer.cancel(); } notifyExitMigrationTask(); // 通知退出任务后等待2s再释放所有连接; sleeps(2000); // 释放代理过程中用到的连接; releaseProxyConnection(); } } public boolean prepareTask() { if (!establishProxyConnection()) { return false; } return true; } /** * 数据清理准备,不立即开始清理,任务阻塞. 等待 All_Finished 指令收到后才开始. */ protected void dataCleanPrepare() throws MigrationException { try { if (logger.isWarnEnabled()) { logger.warn("Prepare data cleaning."); } if (!dataCleanStartSignal.tryAcquire(DEFAULT_DATACLEAN_SIGNAL_WAITTING_TIME_OUT, TimeUnit.HOURS)) { logger.error("Waiting data clean signal time out! After " + DEFAULT_DATACLEAN_SIGNAL_WAITTING_TIME_OUT + " hours."); throw new MigrationException("Waiting all_finished command time out!"); } // 当cancel任务时,不能删除本地的数据。 if (!isFinished()) { dataClean(); } else { logger.warn("To skip data cleaning."); } } catch (InterruptedException e) { logger.error("Waiting the start signal of data cleaning failed!", e); Thread.currentThread().interrupt(); } } /** * 开始迁移任务。为了和 thread 和 start 方法区别。 * * @return */ protected boolean begin() { controlLock.lock(); try { if (migrateStatus != NodeMigrateStatus.NORMAL) { if (logger.isDebugEnabled()) { logger.debug("Node status is " + NodeMigrateStatus.MIGRATING + " or " + NodeMigrateStatus.MIGRATE_NODE_FINISHED + ",Migraion task can't start!"); } return false; } // Notify listener, Before we set the status of task to be migrating, we should establish the connections of // Proxy first. notifyMigrateStart(); return true; } finally { controlLock.unlock(); } } protected boolean establishProxyConnection() { try { proxyConnectionManager.connect(migrationActionData.getMigrationRoutePairs()); } catch (NetException e) { String msg = "Can't establish migration connection. Give up migration task." + e; logger.error(msg); notifyMigrateError(e.getMessage()); return false; } logger.info("Succeed to create migraion connections."); return true; } protected void releaseProxyConnection() { proxyConnectionManager.close(); } /** * migrate * * @throws MigrationException * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#migrate() */ public void migrate() throws MigrationException { controlLock.lock(); try { migrate0(); } finally { controlLock.unlock(); } } private void migrate0() throws MigrationException { migrationReportTimer = getMigrationReportTimer(); migrationReportTimer.start(); dataMigrationExecutor = getDataMigrationExecutor(); dataMigrationExecutor.execute(); } protected DataMigrationExecutor getDataMigrationExecutor() { DataMigrationExecutor executor = new DataMigrationExecutor(); executor.setMigrationTask(this); return executor; } protected MigrationReportTimer getMigrationReportTimer() { MigrationReportTimer timer = new MigrationReportTimer(); timer.setMigrationTask(this); return timer; } protected DataCleanExecutor getDataCleanExecutor() { DataCleanExecutor executor = new DataCleanExecutor(); executor.setMigrationTask(this); return executor; } /** * 完成本次迁移任务 */ protected void finish() { controlLock.lock(); try { finishTime = System.currentTimeMillis(); notifyMigrateNodeFinish(); } finally { controlLock.unlock(); } } /** * 数据清理 */ private void dataClean0() { if (logger.isInfoEnabled()) { logger.info("Start migrate data clean."); } notifyDataCleaningStart(); // 通知启动数据清理后,等待5s开始实际启动数据删除任务; sleeps(5000); // 在执行器中,会调用 task 通知数据清理进度. try { dataCleanExecutor = getDataCleanExecutor(); if (null != dataCleanExecutor) { dataCleanExecutor.execute(); } notifyDataCleanFinish(); if (logger.isDebugEnabled()) { logger.debug("DataClean Finish................"); } } catch (MigrationException e) { notifyDataCleanError(e.getMessage()); } } public void sleeps(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private MigrationEvent createMigrationEvent() { MigrationEvent event = new MigrationEvent(); event.setMigrateType(getMigrateType()); event.setMigrationTask(this); event.setMigrateRoute(migrationActionData.getMigrationRoutePairs()); event.setServerPort(migrationManager.getPort()); return event; } /** * Cancel right now * * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#cancel() */ public void cancel() { cancel(0); } /** * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#cancel() */ public void cancel0() { controlLock.lock(); try { if (!finished) { migrateStatus = NodeMigrateStatus.CANCELLING; cancelTime = System.currentTimeMillis(); finished = true; // 如果正在迁移数据通知迁移任务取消; if (null != dataMigrationExecutor) { dataMigrationExecutor.cancel(); } // 如果在删除数据,则通知取消; if (null != dataCleanExecutor) { dataCleanExecutor.cancel(); } notifyMigrateCancel(); dataCleanStartSignal.release(); migrateStatus = NodeMigrateStatus.CANCELLED; } } finally { controlLock.unlock(); } } public boolean isFinished() { return finished; } /** * 在指定时间内cancel 迁移任务. * * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#cancel() */ public void cancel(long timeout) { controlLock.lock(); try { cancel0(); } finally { controlLock.unlock(); } } /** * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#isCancel() */ public boolean isCancel() { return migrateStatus == NodeMigrateStatus.CANCELLED || migrateStatus == NodeMigrateStatus.CANCELLING || finished == true; } /** * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#isNodeFinish() */ public boolean isNodeFinish() { return migrateStatus == NodeMigrateStatus.MIGRATE_NODE_FINISHED; } /** * 一次集群迁移整体结束,本Node回复NORMAL状态 */ public void allFinish() { allFinished0(); } /** * 通过信号量通知数据数据清理开始. */ public void dataCleanStart() { controlLock.lock(); try { dataCleanStartSignal.release(); if (logger.isInfoEnabled()) { logger.info("Migrate data clean start notify."); } } finally { controlLock.unlock(); } } private void allFinished0() { controlLock.lock(); try { boolean hasLatestRouteTable = migrationManager.refreshRouteTable(); if (!hasLatestRouteTable) { // 如果第一次刷新路由,没有路由更新信息,则等待5s继续查看;路由有可能被定时刷新线程刷新到最新; sleeps(5000); hasLatestRouteTable = migrationManager.refreshRouteTable(); if (!hasLatestRouteTable) { logger.error("Warring: Migration manager want to refresh the route table, but the route table has nothing changed."); } } if (logger.isInfoEnabled()) { logger.info("Migration allFinish operaiton complete. Task: " + this); } notifyMigrateAllFinish(); // 获取最新的RouteTable. if (logger.isInfoEnabled()) { logger.info("Data migration ALL_FINSIHED."); } if (logger.isInfoEnabled()) { logger.info("Data clean starting by task: " + this); } // 开始数据清理 this.dataCleanStart(); } finally { controlLock.unlock(); } } /** * 执行数据清理 */ public void dataClean() { controlLock.lock(); try { dataClean0(); } finally { controlLock.unlock(); } } /** * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#addListener(com.alibaba.doris.dataserver.migrator.event.MigrationListener) */ public void addListener(MigrationListener listener) { this.listeners.add(listener); } /** * @see com.alibaba.doris.dataserver.migrator.task.BaseMigrationTask#removeListener(com.alibaba.doris.dataserver.migrator.event.MigrationListener) */ public void removeListener(MigrationListener listener) { this.listeners.remove(listener); } /** * getMigrationManager */ public MigrationManager getMigrationManager() { return migrationManager; } /** * 通知迁移开始 */ protected void notifyMigrateStart() { controlLock.lock(); try { this.progress = 0; this.migrateStatus = NodeMigrateStatus.MIGRATING; this.startTime = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("notifyMigrateStart, migrateStatus: " + migrateStatus + ", progress: " + progress + ", task: " + this); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); event.setMigrateType(getMigrateType()); for (MigrationListener listener : listeners) { listener.onMigrationStart(event); } } finally { controlLock.unlock(); } } /** * 通知迁移取消 */ protected void notifyMigrateCancel() { controlLock.lock(); try { if (logger.isDebugEnabled()) { logger.debug("notifyMigrateCancel, current migrateStatus: " + migrateStatus + ", progress: " + progress + ", task: " + this); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onMigrationCancelled(event); } revert2NormalState(); } finally { controlLock.unlock(); } } protected void notifyMigrateNodeFinish() { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.MIGRATE_NODE_FINISHED; this.progress = 100; if (logger.isDebugEnabled()) { logger.debug("DataMigration Finish, notifyMigrateNodeFinish: migrateStatus: " + migrateStatus + ", progress: " + progress + ", task: " + this); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onMigrationNodeFinished(event); } } finally { controlLock.unlock(); } } /** * 通知迁移所有完成 */ protected void notifyMigrateAllFinish() { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.MIGRATE_ALL_FINISHED; this.progress = 100; if (logger.isDebugEnabled()) { logger.debug("DataMigration All Finish, notifyMigrateAllFinish: migrateStatus: " + migrateStatus + ", progress: " + progress + ", task: " + this); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onMigrationAllFinished(event); } } finally { controlLock.unlock(); } } /** * 通知迁移失败 * * @param t */ protected void notifyMigrateError(String message) { controlLock.lock(); try { MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); event.setMessage("Migrate Fail:" + message); event.setFailed(true); for (MigrationListener listener : listeners) { listener.onMigrationFail(event); } revert2NormalState(); } finally { controlLock.unlock(); } } protected void revert2NormalState() { // controlLock.lock(); // try { this.migrateStatus = NodeMigrateStatus.NORMAL; this.progress = -1; this.finishTime = System.currentTimeMillis(); MigrationEvent event2 = createMigrationEvent(); event2.setProgress(this.progress); event2.setMigrateStatus(this.migrateStatus); event2.setMessage("Migrate revert to normal state"); event2.setFailed(false); event2.setMigrateType(getMigrateType()); event2.setMigrationTask(this); // }finally { // controlLock.unlock(); // } } /** * 通知迁移进度. */ public void notifyMigrateProgress(int newProgress) { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.MIGRATING; if (newProgress == 100) { this.progress = 99; } else { this.progress = newProgress; } if (logger.isDebugEnabled()) { logger.debug("notifyMigrateProgress: migrateStatus: " + migrateStatus + ", progress: " + progress + ", task: " + this); } notifyMigrateOrDataCleanProgress(); } finally { controlLock.unlock(); } } public void notifyMigrateOrDataCleanProgress() { // controlLock.lock(); // try { if ((migrateStatus == NodeMigrateStatus.MIGRATING || migrateStatus == NodeMigrateStatus.DATACLEANING) && progress < 100) { if (logger.isInfoEnabled()) { logger.info(String.format("Progress Report - status: %s. Task:%s", progress, migrateStatus.toString(), this.toString())); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onMigraionProcessing(event); } } // }finally { // controlLock.unlock(); // } } /** * 通知系统正在退出当前迁移任务; */ protected void notifyExitMigrationTask() { controlLock.lock(); try { if (logger.isDebugEnabled()) { logger.debug("notifyExitMigrationTask."); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onExitMigrationTask(event); } } finally { controlLock.unlock(); } } /** * 通知数据清理开始. */ protected void notifyDataCleaningStart() { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.DATACLEANING; this.progress = 0; if (logger.isDebugEnabled()) { logger.debug("notifyDataCleaningStart: migrateStatus: " + migrateStatus + ", progress: " + progress); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onDataCleanStart(event); } } finally { controlLock.unlock(); } } protected void notifyDataCleanError(String string) { controlLock.lock(); try { MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); event.setMessage("DataClean Fail:" + message); event.setFailed(true); for (MigrationListener listener : listeners) { listener.onDataCleanError(event); } revert2NormalState(); } finally { controlLock.unlock(); } } /** * 通知数据清理完毕. */ protected void notifyDataCleanFinish() { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.DATACLEAN_FINISH; this.progress = 100; if (logger.isDebugEnabled()) { logger.debug("notifyDataCleanFinish: migrateStatus: " + migrateStatus + ", progress: " + progress); } if (logger.isDebugEnabled()) { logger.debug("notifyDataCleanFinish."); } MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onDataCleanFinish(event); } migrationManager.onDataCleanFinish(event); migrationManager.completeAndClearMigration(migrationActionData); } finally { controlLock.unlock(); } } /** * 通知数据清理进度 * * @param newProgress */ public void notifyDataCleanProcess(int newProgress) { controlLock.lock(); try { this.migrateStatus = NodeMigrateStatus.DATACLEANING; this.progress = newProgress; notifyDataCleanProcess(); } finally { controlLock.unlock(); } } public void notifyDataCleanProcess() { // controlLock.lock(); // try { if (migrateStatus == NodeMigrateStatus.DATACLEANING && progress < 100) { MigrationEvent event = createMigrationEvent(); event.setProgress(this.progress); event.setMigrateStatus(this.migrateStatus); for (MigrationListener listener : listeners) { listener.onDataCleanProcessing(event); } } // }finally { // controlLock.unlock(); // } } public long getStartTime() { return startTime; } public long getCancelTime() { return cancelTime; } public long getFinishTime() { return finishTime; } public long getCreateTime() { return createTime; } public NodeMigrateStatus getMigrateStatus() { return migrateStatus; } public void setMigrateStatus(NodeMigrateStatus status) { this.migrateStatus = status; } public List<MigrationListener> getListeners() { return listeners; } public MigrationActionData getMigrationActionData() { return migrationActionData; } public void setFinish(boolean b) { this.finished = true; } public static String buildTaskKey(MigrationActionData actionData) { MigrateTypeEnum migrateType = actionData.getSubcommand().getMigrateType(); String taskKey = migrateType + "-" + actionData.getHashKey(); return taskKey; } public static MigrateTypeEnum getMigrateType(MigrationActionData actionData) { MigrateTypeEnum migrateType = actionData.getSubcommand().getMigrateType(); return migrateType; } public String getProxyTarget(int vnode) { return migrationVirtualNodeFinder.getTargetNodeOfVirtualNode(vnode); } public ConnectionManager getConnectionManager() { return this.proxyConnectionManager; } @Override public String toString() { return new StringBuilder(128).append("[MigrateTask:").append(getMigrateType()).append(",TaskKey:").append( taskKey).append( ",Status:").append( migrateStatus).append( ",Progress:").append( progress).append( ",CreateTime:").append( createTime).append( ",StartTime:").append( DateUtil.formatDate(startTime)).append( ",FinishTime:").append( DateUtil.formatDate(finishTime)).append( "]").toString(); } }