package com.alibaba.doris.dataserver.migrator.task;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.doris.client.net.Connection;
import com.alibaba.doris.client.net.NetException;
import com.alibaba.doris.client.net.OperationFuture;
import com.alibaba.doris.common.data.ActionPair;
import com.alibaba.doris.common.data.Key;
import com.alibaba.doris.common.data.Pair;
import com.alibaba.doris.common.data.Value;
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.task.migrate.ExitPair;
import com.alibaba.doris.dataserver.store.Storage;
/**
* @author ajun Email:jack.yuj@alibaba-inc.com
*/
public class MigrateDataThread implements Callable<Integer> {
public MigrateDataThread(BaseMigrationTask migrationTask, String targetMachine, BlockingQueue<Pair> dataQueue) {
this.migrationTask = migrationTask;
this.migrationManager = migrationTask.getMigrationManager();
this.migrationActionData = migrationTask.getMigrationActionData();
this.storage = migrationManager.getStorage();
this.dataQueue = dataQueue;
this.targetMachine = targetMachine;
this.connectionManager = migrationTask.getConnectionManager();
}
public Integer call() throws Exception {
// 获取和目标机器的连接
Connection connection = connectionManager.getConnection(targetMachine);
int mcount = 0;
long start = System.currentTimeMillis();
long totalTime = 0;
while (true) {
Pair pair = dataQueue.take();
if (pair instanceof ExitPair) {
break;
}
mcount++;
if (migrationTask.isCancel()) {
if (logger.isInfoEnabled()) {
logger.info("Migrate Task has been cancelled.Terminate it.");
}
break;
}
// TODO: 待puts实现后,可以批量传输,以加快迁移速度
// 迁移一条数据到目标机器
long begin = System.currentTimeMillis();
try {
migrateDataEntry(connection, pair);
totalTime += System.currentTimeMillis() - begin;
} catch (MigrationException e) {
if (e.getCause() instanceof NetException) {
logger.error("Fail to Migrate data " + pair.getKey().getPhysicalKey() + ". Cause:" + e.getCause(),
e.getCause());
Thread.sleep(RE_CONNECTE_INTERVAL);
connection = connectionManager.getConnection(targetMachine);
if (!connection.isConnected()) {
Thread.sleep(RE_CONNECTE_INTERVAL);
connection = connectionManager.getConnection(targetMachine);
if (!connection.isConnected()) {
throw new MigrationException("Migration failed bacause of target connection close."
+ targetMachine);
}
}
migrateDataEntry(connection, pair);
} else {
logger.error("Fail to Migrate data " + pair.getKey().getPhysicalKey() + ". Cause:" + e, e);
throw e;
}
}
}
long end = System.currentTimeMillis();
long ellapse = end - start;
if (logger.isDebugEnabled()) {
logger.debug("--------Migrate data on storage by vnodes , to " + targetMachine + ",record count:" + mcount
+ ", time:" + ellapse + " write remote data(ms):" + totalTime);
}
return mcount;
}
/**
* 迁移一条数据
*
* @param connection
* @param key
* @param value
* @throws MigrationException
*/
protected void migrateDataEntry(Connection connection, Pair pair) throws MigrationException {
Key key = pair.getKey();
Value value = pair.getValue();
Throwable failCause = null;
try {
migrateDataEntry0(connection, pair, key, value);
return;
} catch (Throwable t) {
failCause = t;
}
throw new MigrationException("Fail to migrate data entry, cause:" + failCause, failCause);
}
/**
* migrateDataEntry0
*
* @param connection
* @param pair
* @param key
* @param value
* @throws InterruptedException
* @throws ExecutionException
*/
private void migrateDataEntry0(Connection connection, Pair pair, Key key, Value value) throws InterruptedException,
ExecutionException {
// 取出要迁移的数据
if (pair instanceof ActionPair) {
ActionPair ap = (ActionPair) pair;
if (ap.getActionType() == ActionPair.Type.SET) {
OperationFuture<Boolean> future = connection.cas(key, value);
if (!future.get()) {
if (logger.isDebugEnabled()) {
logger.debug("Migrating log item (cas), result is false. key=" + key + " value=" + value);
}
}
} else if (ap.getActionType() == ActionPair.Type.DELETE) {
OperationFuture<Boolean> future = connection.cad(key, value);
if (!future.get()) {
if (logger.isDebugEnabled()) {
logger.debug("Migrating log item (cad), result is false. key=" + key + " value=" + value);
}
}
}
} else {
OperationFuture<Boolean> future = connection.cas(key, value);
if (!future.get()) {
if (logger.isDebugEnabled()) {
logger.debug("Migrating item (cas), result is false. key=" + key + " value=" + value);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Migrating item (cas), result is true. key=" + key + " value=" + value);
}
}
}
}
protected Logger logger = LoggerFactory.getLogger(MigrateDataThread.class);
protected int retryCount = 3;
protected BaseMigrationTask migrationTask;
protected MigrationManager migrationManager;
protected Storage storage;
protected MigrationActionData migrationActionData;
private BlockingQueue<Pair> dataQueue;
private String targetMachine;
private ConnectionManager connectionManager;
private static final int RE_CONNECTE_INTERVAL = 1000;
}