package com.alibaba.doris.dataserver;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.alibaba.doris.cli.CommandLineHandler;
import com.alibaba.doris.cli.Option;
import com.alibaba.doris.client.net.Connection;
import com.alibaba.doris.client.net.ConnectionFactory;
import com.alibaba.doris.client.net.OperationFuture;
import com.alibaba.doris.common.data.ActionPair;
import com.alibaba.doris.common.data.Pair;
import com.alibaba.doris.dataserver.migrator.task.migrate.ExitPair;
import com.alibaba.doris.dataserver.store.Storage;
import com.alibaba.doris.dataserver.store.StorageConfig;
import com.alibaba.doris.dataserver.store.bdb.BDBStorageDriver;
import com.alibaba.doris.dataserver.store.log.LogStorageDriver;
/**
* 节点数据迁移工具: 遍历指定目录下面的数据,并将数据迁移到指定的目标节点;<br>
* srcPath:设置要迁移数据的原目录; <br>
* targetIp: 设置目标节点的服务器ip和端口号;<br>
* [threads]:设置远程写入线程数量;默认10个;<BR>
* [namespace]: 设置要迁移数据属于指定的namespace;默认迁移所有namespace的数据;<BR>
*
* @author ajun Email:jack.yuj@alibaba-inc.com
*/
public class NodeMigrateTool extends CommandLineHandler {
public static void main(String[] args) {
NodeMigrateTool migrationTool = new NodeMigrateTool();
migrationTool.handle(args);
}
public NodeMigrateTool() {
options.add(new Option("-srcPath", "srcPath", "The source path of migration data."));
options.add(new Option("-configFile", "configFile", "The file path of storage configuration."));
options.add(new Option("-targetIp", "targetIp", "The target dataserver ip."));
options.add(new Option("-storageType", "Storage type",
"Setting the storage type: bdb or log. Default value is bdb.", false, true, "bdb"));
options.add(new Option("-threads", "threads", "Setting the migrationg threads.", false, true, "10"));
options.add(new Option("-namespace", "Name space", "Setting the name space which will migrating.", false, true,
null));
}
@Override
public void prepareParameters() {
srcPath = commandLine.getValue("-srcPath");
targetIp = commandLine.getValue("-targetIp");
threads = commandLine.getInt("-threads");
namespace = commandLine.getValue("-namespace");
storageType = commandLine.getValue("-storageType");
configFile = commandLine.getValue("-configFile");
System.out.println("--------------------configuration--------------------");
System.out.println("srcPath=" + srcPath);
System.out.println("targetIp=" + targetIp);
System.out.println("threads=" + threads);
System.out.println("namespace=" + namespace);
System.out.println("storageType=" + storageType);
System.out.println("configFile=" + configFile);
System.out.println("-----------------------------------------------------");
}
@Override
public void handleCommand() {
List<MigrateDataThread> migrateThread = new ArrayList<MigrateDataThread>(threads);
dataQueue = new ArrayBlockingQueue<Pair>(10000);
// 启动迁移数据线程
List<Future<TaskResult>> taskFuterList = startMigratingThread(migrateThread);
// 启动定时打印统计数据线程;
startPrinterThread(migrateThread);
// 读取存储层数据;
iteratorStorage();
long totalRecord = 0;
// 阻塞并等待各子任务的执行结果。
for (int index = 0; index < taskFuterList.size(); index++) {
Future<TaskResult> future = taskFuterList.get(index);
try {
totalRecord += future.get().successDataCount;
} catch (Exception e) {
e.printStackTrace();
}
}
ConnectionFactory.getInstance().releaseResources();
System.out.println("Finish migrating task. The total records is " + totalRecord);
}
protected void iteratorStorage() {
Storage storage = getStorage();
try {
storage.open();
Iterator<Pair> iterator = storage.iterator();
while (iterator.hasNext()) {
this.dataQueue.put(iterator.next());
}
for (int i = 0; i < threads; i++) {
dataQueue.put(new ExitPair());
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
storage.close();
}
}
private Storage getStorage() {
if ("log".equalsIgnoreCase(storageType)) {
System.out.println("Loading log storage driver.");
LogStorageDriver driver = new LogStorageDriver();
driver.init(getStorageConfig());
return driver.createStorage();
} else {
System.out.println("Loading bdb storage driver.");
BDBStorageDriver driver = new BDBStorageDriver();
driver.init(getStorageConfig());
return driver.createStorage();
}
}
private StorageConfig getStorageConfig() {
StorageConfig storageConfig = new StorageConfig();
storageConfig.setPropertiesFile(configFile);
storageConfig.setDatabasePath(srcPath);
return storageConfig;
}
protected List<Future<TaskResult>> startMigratingThread(List<MigrateDataThread> migrateThread) {
List<Future<TaskResult>> taskFuterList = new ArrayList<Future<TaskResult>>(threads);
ExecutorService executor = Executors.newFixedThreadPool(threads);
for (int i = 0; i < threads; i++) {
Callable<TaskResult> callableTask = new MigrateDataThread(dataQueue, targetIp);
taskFuterList.add(executor.submit(callableTask));
migrateThread.add((MigrateDataThread) callableTask);
}
return taskFuterList;
}
protected void startPrinterThread(final List<MigrateDataThread> migrateThread) {
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
long failedOpetionCount = 0;
long successDataCount = 0;
long totalTime = 0;
for (MigrateDataThread mThread : migrateThread) {
failedOpetionCount += mThread.failedOpetionCount;
successDataCount += mThread.successDataCount;
totalTime += mThread.totalTime;
}
System.out.println("Total:" + (failedOpetionCount + successDataCount) + " sucess:" + successDataCount
+ " failed:" + failedOpetionCount + " success incr:"
+ (successDataCount - prevSuccessDataCount));
prevSuccessDataCount = successDataCount;
prevFailedOperationCount = failedOpetionCount;
}
private long prevFailedOperationCount;
private long prevSuccessDataCount;
}, 1, 1, TimeUnit.SECONDS);
}
private static class MigrateDataThread implements Callable<TaskResult> {
public MigrateDataThread(BlockingQueue<Pair> dataQueue, String targetIp) {
this.dataQueue = dataQueue;
this.targetIp = targetIp;
initConnection();
}
private void initConnection() {
String[] pId = targetIp.split(":");
String ip = pId[0];
int port = Integer.valueOf(pId[1]).intValue();
// 和目标机器建立连接
InetSocketAddress remoteAddress = new InetSocketAddress(ip, port);
ConnectionFactory factory = ConnectionFactory.getInstance();
connection = factory.getConnection(remoteAddress);
}
public TaskResult call() {
try {
connection.open();
while (true) {
Pair pair = dataQueue.take();
if (pair instanceof ExitPair) {
break;
}
long begin = System.currentTimeMillis();
migrateDataEntry(pair);
totalTime += System.currentTimeMillis() - begin;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.close();
}
System.out.println("Exit thread....");
TaskResult result = new TaskResult();
result.failedOpetionCount = failedOpetionCount;
result.successDataCount = successDataCount;
result.totalTime = totalTime;
return result;
}
private void migrateDataEntry(Pair pair) throws InterruptedException, ExecutionException {
// 取出要迁移的数据
if (pair instanceof ActionPair) {
ActionPair ap = (ActionPair) pair;
if (ap.getActionType() == ActionPair.Type.SET) {
OperationFuture<Boolean> future = connection.cas(pair.getKey(), pair.getValue());
if (!future.get()) {
failedOpetionCount++;
} else {
successDataCount++;
}
} else if (ap.getActionType() == ActionPair.Type.DELETE) {
OperationFuture<Boolean> future = connection.cad(pair.getKey(), pair.getValue());
if (!future.get()) {
failedOpetionCount++;
} else {
successDataCount++;
}
}
} else {
OperationFuture<Boolean> future = connection.cas(pair.getKey(), pair.getValue());
if (!future.get()) {
failedOpetionCount++;
} else {
successDataCount++;
}
}
}
public long getTotalTime() {
return totalTime;
}
private Connection connection;
private volatile int successDataCount;
private volatile int failedOpetionCount;
private String targetIp;
private BlockingQueue<Pair> dataQueue;
private volatile long totalTime;
}
private static class TaskResult {
private int successDataCount;
private int failedOpetionCount;
private long totalTime;
}
private String configFile;
private String srcPath;
private String targetIp;
private int threads;
private String namespace;
private BlockingQueue<Pair> dataQueue;
private String storageType;
}