All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dasasian.chok.operation.master.AbstractIndexOperation Maven / Gradle / Ivy

There is a newer version: 1.7
Show newest version
/**
 * Copyright (C) 2014 Dasasian ([email protected])
 *
 * 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.dasasian.chok.operation.master;

import com.dasasian.chok.master.MasterContext;
import com.dasasian.chok.operation.OperationId;
import com.dasasian.chok.operation.node.DeployResult;
import com.dasasian.chok.operation.node.OperationResult;
import com.dasasian.chok.operation.node.ShardDeployOperation;
import com.dasasian.chok.operation.node.ShardUndeployOperation;
import com.dasasian.chok.protocol.InteractionProtocol;
import com.dasasian.chok.protocol.ReplicationReport;
import com.dasasian.chok.protocol.metadata.IndexDeployError;
import com.dasasian.chok.protocol.metadata.IndexDeployError.ErrorType;
import com.dasasian.chok.protocol.metadata.IndexMetaData;
import com.dasasian.chok.protocol.metadata.IndexMetaData.Shard;
import com.dasasian.chok.util.CollectionUtil;
import com.dasasian.chok.util.One2ManyListMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.Map.Entry;

public abstract class AbstractIndexOperation implements MasterOperation {

    public static final char INDEX_SHARD_NAME_SEPARATOR = '#';
    private static final long serialVersionUID = 1L;
    private static final Logger LOG = LoggerFactory.getLogger(AbstractIndexOperation.class);
    private Map> _newShardsByNodeMap = new HashMap<>();

    public static String createShardName(String indexName, String shardPath) {
        int lastIndexOf = shardPath.lastIndexOf("/");
        if (lastIndexOf == -1) {
            lastIndexOf = 0;
        }
        String shardFolderName = shardPath.substring(lastIndexOf + 1, shardPath.length());
        if (shardFolderName.endsWith(".zip")) {
            shardFolderName = shardFolderName.substring(0, shardFolderName.length() - 4);
        }
        return indexName + INDEX_SHARD_NAME_SEPARATOR + shardFolderName;
    }

    public static String getIndexNameFromShardName(String shardName) {
        try {
            return shardName.substring(0, shardName.indexOf(INDEX_SHARD_NAME_SEPARATOR));
        } catch (IndexOutOfBoundsException e) {
            throw new IllegalArgumentException(shardName + " is not a valid shard name");
        }
    }

    protected List distributeIndexShards(MasterContext context, final IndexMetaData indexMD, Collection liveNodes, List runningOperations) throws IndexDeployException {
        if (liveNodes.isEmpty()) {
            throw new IndexDeployException(ErrorType.NO_NODES_AVAILIBLE, "no nodes availible");
        }

        InteractionProtocol protocol = context.getProtocol();
        Set shards = indexMD.getShards();

        // now distribute shards
        final Map> currentIndexShard2NodesMap = protocol.getShard2NodesMap(Shard.getShardNames(shards));
        Map> currentGlobalNode2ShardsMap = getCurrentNode2ShardMap(liveNodes, protocol.getShard2NodesMap(protocol.getShard2NodeShards()));
        addRunningDeployments(currentGlobalNode2ShardsMap, runningOperations);

        final Map> newNode2ShardMap = context.getDeployPolicy().createDistributionPlan(currentIndexShard2NodesMap, cloneMap(currentGlobalNode2ShardsMap), new ArrayList<>(liveNodes), indexMD.getReplicationLevel());

        // System.out.println(distributionMap);// node to shards
        Set nodes = newNode2ShardMap.keySet();
        List operationIds = new ArrayList<>(nodes.size());
        One2ManyListMap newShardsByNode = new One2ManyListMap<>();
        for (String node : nodes) {
            List nodeShards = newNode2ShardMap.get(node);
            List listOfAdded = CollectionUtil.getListOfAdded(currentGlobalNode2ShardsMap.get(node), nodeShards);
            if (!listOfAdded.isEmpty()) {
                ShardDeployOperation deployInstruction = new ShardDeployOperation();
                for (String shard : listOfAdded) {
                    deployInstruction.addShard(shard, indexMD.getShardPath(shard));
                    newShardsByNode.add(node, shard);
                }
                OperationId operationId = protocol.addNodeOperation(node, deployInstruction);
                operationIds.add(operationId);
            }
            List listOfRemoved = CollectionUtil.getListOfRemoved(currentGlobalNode2ShardsMap.get(node), nodeShards);
            if (!listOfRemoved.isEmpty()) {
                ShardUndeployOperation undeployInstruction = new ShardUndeployOperation(listOfRemoved);
                OperationId operationId = protocol.addNodeOperation(node, undeployInstruction);
                operationIds.add(operationId);
            }
        }
        _newShardsByNodeMap = newShardsByNode.asMap();
        return operationIds;
    }

    protected Map> getNewShardsByNodeMap() {
        return _newShardsByNodeMap;
    }

    private void addRunningDeployments(Map> currentNode2ShardsMap, List runningOperations) {
        for (MasterOperation masterOperation : runningOperations) {
            if (masterOperation instanceof AbstractIndexOperation) {
                AbstractIndexOperation indexOperation = (AbstractIndexOperation) masterOperation;
                for (Entry> entry : indexOperation.getNewShardsByNodeMap().entrySet()) {
                    List shardList = currentNode2ShardsMap.get(entry.getKey());
                    if (shardList == null) {
                        shardList = new ArrayList<>(entry.getValue().size());
                        currentNode2ShardsMap.put(entry.getKey(), shardList);
                    }
                    shardList.addAll(entry.getValue());
                }
            }
        }
    }

    private Map> cloneMap(Map> currentShard2NodesMap) {
        // return currentShard2NodesMap;
        Set>> entries = currentShard2NodesMap.entrySet();
        HashMap> clonedMap = new HashMap<>();
        for (Entry> e : entries) {
            clonedMap.put(e.getKey(), new ArrayList<>(e.getValue()));
        }
        return clonedMap;
    }

    private Map> getCurrentNode2ShardMap(Collection liveNodes, final Map> currentShard2NodesMap) {
        final Map> currentNodeToShardsMap = CollectionUtil.invertListMap(currentShard2NodesMap);
        for (String node : liveNodes) {
            if (!currentNodeToShardsMap.containsKey(node)) {
                currentNodeToShardsMap.put(node, new ArrayList(3));
            }
        }
        return currentNodeToShardsMap;
    }

    protected boolean canAndShouldRegulateReplication(InteractionProtocol protocol, IndexMetaData indexMD) {
        ReplicationReport replicationReport = protocol.getReplicationReport(indexMD);
        return canAndShouldRegulateReplication(protocol, replicationReport);
    }

    protected boolean canAndShouldRegulateReplication(InteractionProtocol protocol, ReplicationReport replicationReport) {
        List liveNodes = protocol.getLiveNodes();
        if (replicationReport.isBalanced()) {
            return false;
        }
        if (replicationReport.isUnderreplicated() && liveNodes.size() <= replicationReport.getMinimalShardReplicationCount()) {
            return false;
        }
        return true;
    }

    protected void handleMasterDeployException(InteractionProtocol protocol, IndexMetaData indexMD, Exception e) {
        ErrorType errorType;
        if (e instanceof IndexDeployException) {
            errorType = ((IndexDeployException) e).getErrorType();
        } else {
            errorType = ErrorType.UNKNOWN;
        }
        IndexDeployError deployError = new IndexDeployError(indexMD.getName(), errorType);
        deployError.setException(e);
        indexMD.setDeployError(deployError);
        protocol.updateIndexMD(indexMD);
    }

    protected void handleDeploymentComplete(MasterContext context, List results, IndexMetaData indexMD, boolean newIndex) {
        ReplicationReport replicationReport = context.getProtocol().getReplicationReport(indexMD);
        if (replicationReport.isDeployed()) {
            indexMD.setDeployError(null);
            updateShardMetaData(results, indexMD);
            // we ignore possible shard errors
            if (canAndShouldRegulateReplication(context.getProtocol(), replicationReport)) {
                context.getProtocol().addMasterOperation(new BalanceIndexOperation(indexMD.getName()));
            }
        } else {
            IndexDeployError deployError = new IndexDeployError(indexMD.getName(), ErrorType.SHARDS_NOT_DEPLOYABLE);
            for (OperationResult operationResult : results) {
                if (operationResult != null) {// node-crashed produces null
                    DeployResult deployResult = (DeployResult) operationResult;
                    for (Entry entry : deployResult.getShardExceptions().entrySet()) {
                        deployError.addShardError(entry.getKey(), entry.getValue());
                    }
                }
            }
            indexMD.setDeployError(deployError);
        }
        if (newIndex) {
            context.getProtocol().publishIndex(indexMD);
        } else {
            context.getProtocol().updateIndexMD(indexMD);
        }
    }

    private void updateShardMetaData(List results, IndexMetaData indexMD) {
        for (OperationResult operationResult : results) {
            if (operationResult != null) {// node-crashed produces null
                DeployResult deployResult = (DeployResult) operationResult;
                for (Entry> entry : deployResult.getShardMetaDataMaps().entrySet()) {
                    Map existingMap = indexMD.getShard(entry.getKey()).getMetaDataMap();
                    Map newMap = entry.getValue();
                    if (existingMap.size() > 0 && !existingMap.equals(newMap)) {
                        // maps from different nodes but for the same shard should have
                        // the same content
                        LOG.warn("new shard metadata differs from existing one. old: " + existingMap + " new: " + newMap);
                    }
                    existingMap.putAll(newMap);
                }
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy