/*
* 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.filter;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.doris.algorithm.HashFunction;
import com.alibaba.doris.algorithm.KetamaHashFunction;
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.client.net.exception.RouteVersionOutOfDateException;
import com.alibaba.doris.common.data.Key;
import com.alibaba.doris.common.data.Value;
import com.alibaba.doris.common.route.VirtualRouter;
import com.alibaba.doris.dataserver.Module;
import com.alibaba.doris.dataserver.ModuleContext;
import com.alibaba.doris.dataserver.action.ActionType;
import com.alibaba.doris.dataserver.action.data.ActionData;
import com.alibaba.doris.dataserver.action.data.BaseActionType;
import com.alibaba.doris.dataserver.action.data.ErrorActionData;
import com.alibaba.doris.dataserver.config.ModuleConstances;
import com.alibaba.doris.dataserver.core.Request;
import com.alibaba.doris.dataserver.core.RequestFilter;
import com.alibaba.doris.dataserver.core.RequestFilterChian;
import com.alibaba.doris.dataserver.core.Response;
import com.alibaba.doris.dataserver.migrator.MigrationManager;
import com.alibaba.doris.dataserver.migrator.connection.ConnectionManager;
import com.alibaba.doris.dataserver.migrator.task.MigrationTask;
/**
* 数据迁移. MigrationFilter
*
* @author Kun He (Raymond He), kun.hek@alibaba-inc.com
* @since 1.0 2011-5-25
*/
public class MigrationFilter implements RequestFilter {
protected static final Logger logger = LoggerFactory.getLogger(MigrationFilter.class);
private MigrationManager migrationManager;
private VirtualRouter virtualRouter;
private Object lock = new Object();
protected HashFunction hashFunction = new KetamaHashFunction();
public void setHashFunction(HashFunction hashFunction) {
this.hashFunction = hashFunction;
}
/**
* 迁移过滤器。 迁移状态中,使用代理写模式向 key 的迁移目标机多写一份.
*
* @see com.alibaba.doris.dataserver.core.RequestFilter#doFilter(com.alibaba.doris.dataserver.core.Request,
* com.alibaba.doris.dataserver.core.Response, com.alibaba.doris.dataserver.core.RequestFilterChian)
*/
public void doFilter(Request request, Response response, RequestFilterChian filterChain) {
OperationFuture<Boolean> proxyOperateFuture = null;
ActionData actionData = request.getActionData();
if (isNeedProxyOperation(actionData)) {
initMigrationManager(request);
// 如果正在迁移, 使用代理写模式
// 判断当前节点的迁移状态 MIGRATING 或 MIGRATE_NODE_FINISHED 表示正在迁移中,需要开启写代理. 当 MigrationManager
// 获取到最新的路由后,migrationManager.getMigrateStatus() 变为 ALL_FINISHED 状态,可以关闭代理写状态.
if (migrationManager.haveMigrationTask()) {
try {
proxyOperateFuture = handleProxyOperate(request, actionData);
if (logger.isDebugEnabled()) {
logger.debug("After handleProxyOperate. Do local storage operation.");
}
// 迁移过程中需要执行本地写,其它情况都直接代理到远程。
// if (NodeMigrateStatus.MIGRATING == migrationManager.getMigrateStatus()) {
// filterChain.doFilter(request, response);
// checkProxyOperationResult(proxyOperateFuture, actionData);
// return;
// }
} catch (NetException e) {
// 代理写过程中出现网络异常,忽略,因为底层迁移逻辑也会发生异常,此时迁移状态应该变成迁移失败(NORMAL)
// try {
// Thread.sleep(10);
// } catch (InterruptedException e1) {
// }
logger.error("The remote proxy operation is failed. " + request.getKey(), e);
}
}
}
filterChain.doFilter(request, response);
// 延迟获取远程代理操作的结果,使得远程操作和本地操作并行执行。
try {
checkProxyOperationResult(proxyOperateFuture, actionData);
} catch (RouteVersionOutOfDateException e) {
// 如果client的路由版本号过期,返回调用异常。
ErrorActionData error = new ErrorActionData(ErrorActionData.VERSION_OUT_OF_DATE);
// 设置server端的路由版本号
error.setErrorMessage(((RouteVersionOutOfDateException) e).getNewRouteTable());
// Note:此处覆盖action中写入response的结果;
response.write(error);
}
}
private void initMigrationManager(Request request) {
if (migrationManager == null) {
synchronized (lock) {
if (migrationManager == null) {
Module module = request.getApplicationContext().getModuleByName(ModuleConstances.MIGRATION_MODULE);
ModuleContext moduleContext = module.getModuleContext();
migrationManager = (MigrationManager) moduleContext.getAttribute(MigrationManager._MigrationManager);
virtualRouter = migrationManager.getVirtualRouter();
}
}
}
}
private void checkProxyOperationResult(OperationFuture<Boolean> proxyOperateFuture, ActionData actionData) {
try {
if (proxyOperateFuture != null) {
Boolean result = proxyOperateFuture.get();
if (null != result) {
// 远程代理操作失败,会导致当前请求返回失败信息。
if (result.booleanValue() == false) {
throw new ProxyOperationException("The proxy " + actionData.getActionType().getName()
+ " operation is failed.");
}
}
}
} catch (NetException e) {
// 代理写过程中发生的网络写通信异常忽略;
logger.error("Proxy " + actionData.getActionType().getName() + " error", e);
if (e instanceof RouteVersionOutOfDateException) {
throw e;
}
} catch (InterruptedException e) {
throw new ProxyOperationException("The proxy " + actionData.getActionType().getName()
+ " operation is failed. Error message:" + e.getLocalizedMessage());
} catch (ExecutionException e) {
throw new ProxyOperationException("The proxy " + actionData.getActionType().getName()
+ " operation is failed. Error message:" + e.getLocalizedMessage());
}
}
/**
* @param request
* @param actionData
*/
private OperationFuture<Boolean> handleProxyOperate(Request request, ActionData actionData) {
Key key = request.getKey();
// 遍历所有的在迁移中的任务,查看当前请求是否需要代理;
Map<String, MigrationTask> activeTaskMap = migrationManager.getMigrationTaskScheduler().getActiveTaskMap();
Iterator<MigrationTask> taskIterator = activeTaskMap.values().iterator();
while (taskIterator.hasNext()) {
MigrationTask task = taskIterator.next();
// 根据key,计算出是否需要执行代理;
String targetPhId = computeTargetPhNo(key, task);
if (null != targetPhId) {
// 如果需要代理,则获取连接,代理操作;
ConnectionManager connectionManager = task.getConnectionManager();
if (!connectionManager.isOpen()) {
logger.error("Prepare to Proxy operate, but connection manager is close. Give up proxy operate, data loss may occur.");
throw new ProxyOperationException("The target connection of migration is closed.");
// return null;
}
return proxyOperation(connectionManager, actionData.getActionType(), key, request.getValue(),
targetPhId);
}
}
return null;
}
/**
* getActionType
*
* @param actionData
* @return
*/
public boolean isNeedProxyOperation(ActionData actionData) {
ActionType type = actionData.getActionType();
return BaseActionType.SET == type || BaseActionType.DELETE == type;
}
/**
* @param connectionManager
* @param actionData
* @param key
* @param value
*/
private OperationFuture<Boolean> proxyOperation(ConnectionManager connectionManager, ActionType actionType,
Key key, Value value, String targetPhId) {
OperationFuture<Boolean> future = null;
if (actionType == BaseActionType.SET) {
future = proxySet(connectionManager, targetPhId, key, value);
if (logger.isDebugEnabled()) {
logger.debug("Proxy set to " + targetPhId + ", key=" + key);
}
} else if (actionType == BaseActionType.DELETE) {
future = proxyDelete(connectionManager, targetPhId, key);
if (logger.isDebugEnabled()) {
logger.debug("Proxy delete to " + targetPhId + ", key=" + key);
}
}
return future;
}
/**
* proxyDelete
*
* @param connectionManager
* @param targetPhId
* @param key
*/
private OperationFuture<Boolean> proxyDelete(ConnectionManager connectionManager, String targetPhId, Key key) {
OperationFuture<Boolean> future = null;
Connection connection = connectionManager.getConnection(targetPhId);
try {
if (null != connection) {
future = connection.delete(key);
}
if (logger.isDebugEnabled()) {
logger.debug("Proxy delete - key=" + key.getPhysicalKey());
}
} catch (Exception e) {
logger.error("Fail to proxy delete, key=" + key.getPhysicalKey() + ", cause:" + e, e);
throw new ProxyOperationException(e);
}
return future;
}
/**
* proxySet
*
* @param connectionManager
* @param targetPhId
* @param key
* @param value
*/
private OperationFuture<Boolean> proxySet(ConnectionManager connectionManager, String targetPhId, Key key,
Value value) {
OperationFuture<Boolean> future = null;
Connection connection = connectionManager.getConnection(targetPhId);
try {
if (null != connection) {
future = connection.put(key, value);
}
if (logger.isDebugEnabled()) {
logger.debug("Proxy set - key=" + key.getPhysicalKey());
}
} catch (Exception e) {
logger.error("Fail to proxy set, key=" + key.getPhysicalKey() + ", cause:" + e.getMessage(), e);
logger.error("Retrying to set.");
key.setRouteVersion(0);
// set key vnode = -1.
future = connection.put(key, value);
}
return future;
}
/**
* 计算目标机器节点编号
*
* @return
*/
private String computeTargetPhNo(Key key, MigrationTask task) {
String phKey = key.getPhysicalKey();
int targetVNo = key.getVNode();
if (targetVNo == -1) {
targetVNo = virtualRouter.findVirtualNode(phKey);
}
return task.getProxyTarget(targetVNo);
}
}