/* * Copyright(C) 1999-2010 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.client.operation; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.alibaba.doris.client.AccessException; import com.alibaba.doris.client.DataSourceManager; import com.alibaba.doris.client.DataSourceRouter; import com.alibaba.doris.client.DorisClientException; import com.alibaba.doris.client.cn.OperationDataConverter; import com.alibaba.doris.client.net.DataSource; import com.alibaba.doris.client.operation.failover.CallbackHandlerFactory; import com.alibaba.doris.client.operation.failover.LogicCallback; import com.alibaba.doris.client.operation.failover.LogicCallbackHandler; import com.alibaba.doris.client.operation.failover.PeerCallback; import com.alibaba.doris.client.operation.failover.PeerCallbackHandler; import com.alibaba.doris.client.operation.failover.impl.DefaultLogicCallback; import com.alibaba.doris.client.operation.result.DataSourceOpResult; import com.alibaba.doris.common.ConsistentErrorType; import com.alibaba.doris.common.StoreNode; import com.alibaba.doris.common.adminservice.AdminServiceFactory; import com.alibaba.doris.common.configer.RouteTableConfiger; import com.alibaba.doris.common.data.CompareStatus; import com.alibaba.doris.common.data.Key; import com.alibaba.doris.common.data.Value; import com.alibaba.doris.common.route.DorisRouterException; import com.alibaba.doris.common.route.RouteTable; import com.alibaba.doris.common.route.VirtualRouter; /** * Operation * * @author Kun He (Raymond He), kun.hek@alibaba-inc.com * @since 1.0 2011-4-22 */ public abstract class AbstractOperation implements Operation { private Log logger = LogFactory.getLog(AbstractOperation.class); protected DataSourceManager dataSourceManager; protected CallbackHandlerFactory callbackHandlerFactory; protected OperationDataConverter operationDataConverter; public abstract String getName(); public void setDataSourceManager(DataSourceManager dataSourceManager) { this.dataSourceManager = dataSourceManager; } public DataSourceManager getDataSourceManager() { return dataSourceManager; } public void setFailoverHandlerFactory(CallbackHandlerFactory callbackHandlerFactory) { this.callbackHandlerFactory = callbackHandlerFactory; } public void setOperationDataConverter(OperationDataConverter operationDataConverter) { this.operationDataConverter = operationDataConverter; } public List<List<DataSourceOpResult>> doLogicExecute(List<OperationDataSourceGroup> operationDataSourceGroups, List<OperationData> operationDatas) throws AccessException { if (operationDataSourceGroups.isEmpty() || operationDatas.isEmpty()) { throw new AccessException("operationDataSourceGroups or operationDatas is empty"); } int size = operationDataSourceGroups.size(); List<LogicCallback> logicCallbacks = new ArrayList<LogicCallback>(size); LogicCallbackHandler callbackHandler = callbackHandlerFactory.getLogicFailoverHandler(); callbackHandler.setDataSourceManager(dataSourceManager); for (int i = 0; i < size; i++) { // callbackHandler.setOperationData(operationDatas.get(i)); final OperationDataSourceGroup group = operationDataSourceGroups.get(i); // callbackHandler.setOperationDataSourceGroup(group); LogicCallback logicCallback = new DefaultLogicCallback(operationDatas.get(i)) { @Override public LogicCallback execute() throws AccessException { List<DataSourceOpFuture> dsFutureList = new ArrayList<DataSourceOpFuture>( group.getDataSources().size()); for (final DataSource dataSource : group.getDataSources()) { // distributed computing logic here. PeerCallback peerCallback = doPeerExecute(dataSource, operationData); // OperationFuture<?> future = peerCallback.getOperationFuture(); DataSourceOpFuture dsopFuture = new DataSourceOpFuture(dataSource, peerCallback); dsFutureList.add(dsopFuture); } this.dataSourceOpFutures = dsFutureList; return this; } }; logicCallbacks.add(logicCallback); } List<List<DataSourceOpResult>> dataSourceOpResults = callbackHandler.doLogicExecute(logicCallbacks, operationDataSourceGroups); // resultList.add(dataSourceOpResults); return dataSourceOpResults; } /** * Operation on single node datasource. * * @param dataSource * @param operationData * @throws AccessException */ public PeerCallback doPeerExecute(final DataSource dataSource, final OperationData operationData) throws AccessException { final PeerCallbackHandler callbackHandler = callbackHandlerFactory.getPeerFailoverHandler(); callbackHandler.setDataSourceManager(dataSourceManager); callbackHandler.setOperationData(operationData); callbackHandler.setDataSource(dataSource); PeerCallback callback = generatePeerCallback(dataSource, operationData); return callbackHandler.doPeerExecute(callback); } protected abstract PeerCallback generatePeerCallback(final DataSource dataSource, final OperationData operationData); public void execute(OperationData operationData) { List<Object> args = operationData.getArgs(); if (args.get(0) == null) { throw new IllegalArgumentException("Argument 0 ( key) can't be null"); } DataSourceRouter dataSourceRouter = dataSourceManager.getDataSourceRouter(); RouteTableConfiger routeTableConfiger = dataSourceRouter.getRouteTableConfiger(); RouteTable routeTable = routeTableConfiger.getRouteTable(); if (routeTable == null) { throw new DorisClientException("Can't get RouteTable or connect to AdminServer."); } long routeVersion = routeTable.getVersion(); VirtualRouter virtualRouter = dataSourceManager.getDataSourceRouter().getVirtualRouter(); operationDataConverter.setVirtualRouter(virtualRouter); List<OperationDataSourceGroup> operationDataSourceGroups = null; try { operationDataSourceGroups = new ArrayList<OperationDataSourceGroup>(); List<OperationData> operationDatas = new ArrayList<OperationData>(); buildLogicParam(operationDataSourceGroups, operationDatas, operationData, operationDataConverter, routeVersion); List<List<DataSourceOpResult>> dsOpResults = doLogicExecute(operationDataSourceGroups, operationDatas); mergeOperationResult(dsOpResults, operationDatas); if (operationDatas.size() > 1) { List<Object> rl = new ArrayList<Object>(operationDatas.size()); for (OperationData od : operationDatas) { rl.add(od.getResult()); } operationData.setResult(rl); } } catch (AccessException e) { if (logger.isErrorEnabled()) { logger.error(e.getMessage(), e); } reportConsisitentError(operationData, operationDataSourceGroups, e.getMessage()); throw new DorisClientException(e); } catch (DorisRouterException e) { if (logger.isErrorEnabled()) { logger.error(e.getMessage(), e); } reportConsisitentError(operationData, operationDataSourceGroups, e.getMessage()); throw new DorisClientException(e); } } protected void reportConsisitentError(OperationData operationData, List<OperationDataSourceGroup> operationDataSourceGroups, String exceptionMsg) { } protected void doReportConsistentError(OperationData operationData, List<OperationDataSourceGroup> operationDataSourceGroups, String exceptionMsg) { for (OperationDataSourceGroup dataSourceGroup : operationDataSourceGroups) { StringBuilder phisicalIpsBuilder = new StringBuilder(); List<StoreNode> storeNodes = dataSourceGroup.getNodes(); for (StoreNode storeNode : storeNodes) { String ip = storeNode.getIp(); int port = storeNode.getPort(); phisicalIpsBuilder.append(ip).append(":").append(port).append(";"); } String timestampStr = ""; Value v = (Value) operationData.getArgs().get(1); if (v != null) { timestampStr = Long.toString(v.getTimestamp()); } AdminServiceFactory.getConsistentErrorReportService().report(operationData.getNamespace().getId(), operationData.getKey().getKey(), phisicalIpsBuilder.toString(), exceptionMsg, ConsistentErrorType.write, timestampStr); } } protected abstract void buildLogicParam(List<OperationDataSourceGroup> operationDataSourceGroups, List<OperationData> operationDatas, OperationData operationData, OperationDataConverter operationDataConverter, long routeVersion) throws DorisRouterException; protected void buildSimpleLogicParam(List<OperationDataSourceGroup> operationDataSourceGroups, List<OperationData> operationDatas, OperationData operationData, OperationDataConverter operationDataConverter, long routeVersion) throws DorisRouterException { int opCount = getOperationCount(operationData); Key phKey = operationDataConverter.buildKey(operationData, routeVersion); operationData.setKey(phKey); OperationDataSourceGroup operationDataSourceGroup = dataSourceManager.getOperationDataSourceGroup(getOperationType(operationData), opCount, phKey.getPhysicalKey()); operationDataSourceGroups.add(operationDataSourceGroup); operationDatas.add(operationData); } /** * 合并结果 * * @param dsOpResults * @param operationData */ protected void mergeOperationResult(List<List<DataSourceOpResult>> dsOpResults, List<OperationData> operationDatas) { int size = dsOpResults.size(); for (int i = 0; i < size; i++) { for (DataSourceOpResult dataSourceOpResult : dsOpResults.get(i)) { if (dataSourceOpResult.getResult() != null) { operationDatas.get(i).setResult(dataSourceOpResult.getResult()); break; } } } } /** * 多读场景下,合并put,puts,delete操作结果<br/> * put操作需要比较所有节点,只要有一个节点put成功即返回成功。<br/> * 为防止put时一个node成功一个node失败,如果返回失败,但是get的时候却能成功返回结果。 * * @param dsOpResults * @param operationData */ protected void needOneNodeSuccess(List<List<DataSourceOpResult>> dsOpResults, List<OperationData> operationDatas) { int size = dsOpResults.size(); for (int i = 0; i < size; i++) { boolean success = false; for (DataSourceOpResult dataSourceOpResult : dsOpResults.get(i)) { if (dataSourceOpResult.getResult() != null && (Boolean) dataSourceOpResult.getResult()) { success = true; break; } } if (success) { operationDatas.get(i).setResult(Boolean.TRUE); } else { operationDatas.get(i).setResult(Boolean.FALSE); } } } /** * 单读场景下合并put,puts,delete操作结果<br/> * put操作需要比较所有节点,只有所有你哦的都操作成功才返回成功。<br/> * 为防止put时一个node成功一个node失败,如果返回成功,但是get的时候恰好路由到put操作失败的节点上,就会有问题。 * * @param dsOpResults * @param operationData */ protected void needAllNodeSuccess(List<List<DataSourceOpResult>> dsOpResults, List<OperationData> operationDatas) { int size = dsOpResults.size(); for (int i = 0; i < size; i++) { boolean success = true; for (DataSourceOpResult dataSourceOpResult : dsOpResults.get(i)) { if (dataSourceOpResult.getResult() == null || !((Boolean) dataSourceOpResult.getResult()).booleanValue()) { success = false; break; } } if (success) { operationDatas.get(i).setResult(Boolean.TRUE); } else { operationDatas.get(i).setResult(Boolean.FALSE); } } } /** * 多读场景下,比较get结果,并用最新的value去更新过时节点数据 * * @param dsOpResults * @param operationDatas */ protected void compareAndUpdateResult(List<List<DataSourceOpResult>> dsOpResults, List<OperationData> operationDatas) { int size = dsOpResults.size(); for (int i = 0; i < size; i++) { List<DataSourceOpResult> latestResults = new ArrayList<DataSourceOpResult>(dsOpResults.get(i).size()); List<DataSourceOpResult> overdueResults = new ArrayList<DataSourceOpResult>(dsOpResults.get(i).size()); // 比较所有节点的返回值 for (DataSourceOpResult dataSourceOpResult : dsOpResults.get(i)) { if (latestResults.isEmpty()) { latestResults.add(dataSourceOpResult); } else { Value value = (Value) dataSourceOpResult.getResult(); CompareStatus compareVersion = value.compareVersion((Value) latestResults.get(0).getResult()); if (CompareStatus.AFTER.equals(compareVersion)) { overdueResults.addAll(latestResults); latestResults.clear(); latestResults.add(dataSourceOpResult); } else if (CompareStatus.BEFORE.equals(compareVersion)) { overdueResults.add(dataSourceOpResult); } else { latestResults.add(dataSourceOpResult); } } } // 设置结果为最近更新的value Value latestValue = (Value) latestResults.get(0).getResult(); operationDatas.get(i).setResult(latestValue); if (overdueResults.size() > 0) { Key key = operationDatas.get(i).getKey(); // 更新过期数据节点为最近的value try { for (DataSourceOpResult overdule : overdueResults) { overdule.getDataSource().getConnection().cas(key, latestValue); } } catch (Exception e) { logger.error("failed to fix key:" + key.getKey() + ", with latest value:" + latestValue, e); } finally { StringBuilder phisicalIpsBuilder = new StringBuilder(); for (DataSourceOpResult dataSourceOpResult : dsOpResults.get(i)) { String ip = dataSourceOpResult.getDataSource().getIp(); int port = dataSourceOpResult.getDataSource().getPort(); phisicalIpsBuilder.append(ip).append(":").append(port).append(";"); } // send report to admin AdminServiceFactory.getConsistentErrorReportService().report(operationDatas.get(i).getNamespace().getId(), key.getKey(), phisicalIpsBuilder.toString(), "read unconsistent data", ConsistentErrorType.read, Long.toString(latestValue.getTimestamp())); } } } } public int getOperationCount(OperationData operationData) { return operationData.getNamespace().getCopyCount(); } }