Package voldemort.store.routed

Source Code of voldemort.store.routed.HintedHandoffSendHintTest$SocketStoreClientFactoryForTest

package voldemort.store.routed;

import static org.junit.Assert.assertTrue;
import static voldemort.VoldemortTestConstants.getNineNodeCluster;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import voldemort.ServerTestUtils;
import voldemort.TestUtils;
import voldemort.client.ClientConfig;
import voldemort.client.RoutingTier;
import voldemort.client.TimeoutConfig;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.cluster.failuredetector.FailureDetector;
import voldemort.cluster.failuredetector.FailureDetectorConfig;
import voldemort.cluster.failuredetector.FailureDetectorUtils;
import voldemort.cluster.failuredetector.MutableStoreVerifier;
import voldemort.cluster.failuredetector.ThresholdFailureDetector;
import voldemort.common.service.ServiceType;
import voldemort.common.service.VoldemortService;
import voldemort.routing.RoutingStrategy;
import voldemort.routing.RoutingStrategyFactory;
import voldemort.routing.RoutingStrategyType;
import voldemort.serialization.ByteArraySerializer;
import voldemort.serialization.IdentitySerializer;
import voldemort.serialization.Serializer;
import voldemort.serialization.SerializerDefinition;
import voldemort.serialization.SlopSerializer;
import voldemort.server.RequestRoutingType;
import voldemort.server.VoldemortConfig;
import voldemort.server.VoldemortServer;
import voldemort.server.storage.StorageService;
import voldemort.store.ForceFailStore;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.store.StoreDefinitionBuilder;
import voldemort.store.UnreachableStoreException;
import voldemort.store.memory.InMemoryStorageConfiguration;
import voldemort.store.nonblockingstore.NonblockingStore;
import voldemort.store.serialized.SerializingStore;
import voldemort.store.slop.Slop;
import voldemort.store.slop.SlopStorageEngine;
import voldemort.store.slop.strategy.HintedHandoffStrategyType;
import voldemort.store.socket.SocketStore;
import voldemort.store.socket.SocketStoreFactory;
import voldemort.store.socket.clientrequest.ClientRequestExecutorPool;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.versioning.Versioned;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

@RunWith(Parameterized.class)
public class HintedHandoffSendHintTest {

    private final static Logger logger = Logger.getLogger(HintedHandoffSendHintTest.class);

    private final static String STORE_NAME = "test";
    private final static String SLOP_STORE_NAME = "slop";

    private final static int NUM_NODES_TOTAL = 9;
    private final static int NUM_NODES_FAILED = 4;

    private int REPLICATION_FACTOR = 3;
    private final static int P_READS = 1;
    private final static int R_READS = 1;
    private int P_WRITES = 1;
    private int R_WRITES = 1;

    private final static int KEY_LENGTH = 32;
    private final static int VALUE_LENGTH = 32;
    private final static int SOCKET_TIMEOUT_MS = 500;

    private final Class<? extends FailureDetector> failureDetectorCls = ThresholdFailureDetector.class;
    private final HintedHandoffStrategyType hintRoutingStrategy;

    private final Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = new ConcurrentHashMap<Integer, Store<ByteArray, byte[], byte[]>>();
    private final Map<Integer, ForceFailStore<ByteArray, byte[], byte[]>> forceFailStores = new ConcurrentHashMap<Integer, ForceFailStore<ByteArray, byte[], byte[]>>();
    private final Map<Integer, Store<ByteArray, Slop, byte[]>> slopStores = new ConcurrentHashMap<Integer, Store<ByteArray, Slop, byte[]>>();
    private final Map<Integer, NonblockingStore> socketTestStores = new HashMap<Integer, NonblockingStore>();
    private final Map<Integer, NonblockingStore> socketSlopStores = new HashMap<Integer, NonblockingStore>();
    private final Map<Integer, SlopStorageEngine> slopStorageEngines = new ConcurrentHashMap<Integer, SlopStorageEngine>();
    private final Multimap<ByteArray, Integer> keysToNodes = HashMultimap.create();
    private final Map<ByteArray, ByteArray> keyValues = Maps.newHashMap();
    private final Map<Integer, VoldemortServer> voldemortServers = new HashMap<Integer, VoldemortServer>();

    private Cluster cluster;
    private FailureDetector failureDetector;
    private StoreDefinition storeDef;
    private RoutingStrategy strategy;
    private RoutedStore routedStore;
    private final List<ByteArray> keyList = new ArrayList<ByteArray>();

    final static class SocketStoreClientFactoryForTest {

        private final String storeName;
        private final String slopStoreName;
        private final ClientRequestExecutorPool storeFactory;
        private final ClientConfig config;

        public SocketStoreClientFactoryForTest(String testStoreName, String slopStoreName) {
            this.storeName = testStoreName;
            this.slopStoreName = slopStoreName;
            config = new ClientConfig();
            storeFactory = new ClientRequestExecutorPool(config.getSelectors(),
                                                         config.getMaxConnectionsPerNode(),
                                                         config.getConnectionTimeout(TimeUnit.MILLISECONDS),
                                                         SOCKET_TIMEOUT_MS,
                                                         config.getSocketBufferSize(),
                                                         config.getSocketKeepAlive(),
                                                         false,
                                                         new String());
        }

        protected SocketStore getSocketTestStoreByNode(Node node) {
            return storeFactory.create(storeName,
                                       node.getHost(),
                                       node.getSocketPort(),
                                       config.getRequestFormatType(),
                                       RequestRoutingType.getRequestRoutingType(false, false));
        }

        protected SocketStore getSocketSlopStoreByNode(Node node) {
            return storeFactory.create(slopStoreName,
                                       node.getHost(),
                                       node.getSocketPort(),
                                       config.getRequestFormatType(),
                                       RequestRoutingType.getRequestRoutingType(false, false));
        }
    }

    public HintedHandoffSendHintTest(HintedHandoffStrategyType hintRoutingStrategy,
                                     int replicationFactor,
                                     int requiredWrites,
                                     int preferredWrites) {
        this.hintRoutingStrategy = hintRoutingStrategy;
        this.REPLICATION_FACTOR = replicationFactor;
        this.R_WRITES = requiredWrites;
        this.P_WRITES = preferredWrites;
    }

    @Parameterized.Parameters
    public static Collection<Object[]> configs() {
        return Arrays.asList(new Object[][] {
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 3, 1, 1 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 3, 1, 2 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 3, 1, 3 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 3, 2, 2 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 3, 2, 3 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 2, 1, 1 },
                { HintedHandoffStrategyType.CONSISTENT_STRATEGY, 2, 1, 2 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 3, 1, 1 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 3, 1, 2 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 3, 1, 3 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 3, 2, 2 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 3, 2, 3 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 2, 1, 1 },
                { HintedHandoffStrategyType.PROXIMITY_STRATEGY, 2, 1, 2 } });
    }

    private StoreDefinition getStoreDef() {
        SerializerDefinition serDef = new SerializerDefinition("string");
        return new StoreDefinitionBuilder().setName(STORE_NAME)
                                           .setType(InMemoryStorageConfiguration.TYPE_NAME)
                                           .setKeySerializer(serDef)
                                           .setValueSerializer(serDef)
                                           .setRoutingPolicy(RoutingTier.CLIENT)
                                           .setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
                                           .setReplicationFactor(REPLICATION_FACTOR)
                                           .setPreferredReads(P_READS)
                                           .setRequiredReads(R_READS)
                                           .setPreferredWrites(P_WRITES)
                                           .setRequiredWrites(R_WRITES)
                                           .setHintedHandoffStrategy(this.hintRoutingStrategy)
                                           .build();
    }

    @Before
    public void setUp() throws Exception {
        if(logger.isDebugEnabled()) {
            logger.debug("Test Started: replication[" + REPLICATION_FACTOR + "], preferredW["
                         + P_WRITES + "], requiredW[" + R_WRITES + "]");
        }
        cluster = getNineNodeCluster();
        storeDef = getStoreDef();

        // create voldemort servers
        for(Integer nodeId = 0; nodeId < NUM_NODES_TOTAL; nodeId++) {
            SocketStoreFactory socketStoreFactory;
            socketStoreFactory = new ClientRequestExecutorPool(2, 10000, 100000, 1024);
            List<StoreDefinition> stores = new ArrayList<StoreDefinition>();
            stores.add(storeDef);
            VoldemortConfig config = ServerTestUtils.createServerConfigWithDefs(true,
                                                                                nodeId,
                                                                                TestUtils.createTempDir()
                                                                                         .getAbsolutePath(),
                                                                                cluster,
                                                                                stores,
                                                                                new Properties());
            config.setNioAdminConnectorSelectors(1);
            config.setNioConnectorSelectors(2);
            VoldemortServer vs = ServerTestUtils.startVoldemortServer(socketStoreFactory, config);
            VoldemortService vsrv = vs.getService(ServiceType.STORAGE);
            StorageService ss = (StorageService) vsrv;
            voldemortServers.put(nodeId, vs);

            slopStorageEngines.put(nodeId, ss.getStoreRepository().getSlopStore());
            slopStores.put(nodeId, SerializingStore.wrap(ss.getStoreRepository().getSlopStore(),
                                                         new ByteArraySerializer(),
                                                         new SlopSerializer(),
                                                         new IdentitySerializer()));
            // wrap original store with force fail store
            Store<ByteArray, byte[], byte[]> store = ss.getStoreRepository()
                                                       .removeLocalStore(STORE_NAME);
            UnreachableStoreException exception = new UnreachableStoreException("Force failed");
            ForceFailStore<ByteArray, byte[], byte[]> forceFailStore = new ForceFailStore<ByteArray, byte[], byte[]>(store,
                                                                                                                     exception);
            forceFailStores.put(nodeId, forceFailStore);
            ss.getStoreRepository().addLocalStore(forceFailStore);
        }

        strategy = new RoutingStrategyFactory().updateRoutingStrategy(storeDef, cluster);

        // create client socket stores and slop stores
        SocketStoreClientFactoryForTest clientSocketStoreFactory = new SocketStoreClientFactoryForTest(STORE_NAME,
                                                                                                       SLOP_STORE_NAME);
        Serializer<ByteArray> slopKeySerializer = new ByteArraySerializer();
        Serializer<Slop> slopValueSerializer = new SlopSerializer();
        Map<Integer, Store<ByteArray, byte[], byte[]>> testStores = subStores;
        Map<Integer, Store<ByteArray, Slop, byte[]>> slopStores = new HashMap<Integer, Store<ByteArray, Slop, byte[]>>();
        for(Node node: cluster.getNodes()) {
            // test store
            SocketStore socketTestStore = clientSocketStoreFactory.getSocketTestStoreByNode(node);
            socketTestStores.put(node.getId(), socketTestStore);
            testStores.put(node.getId(), socketTestStore);

            // slop store
            SocketStore socketSlopStore = clientSocketStoreFactory.getSocketSlopStoreByNode(node);
            Store<ByteArray, Slop, byte[]> slopStore = SerializingStore.wrap(socketSlopStore,
                                                                             slopKeySerializer,
                                                                             slopValueSerializer,
                                                                             new IdentitySerializer());
            socketSlopStores.put(node.getId(), socketSlopStore);
            slopStores.put(node.getId(), slopStore);
        }

        // set failure detector
        if(failureDetector != null)
            failureDetector.destroy();
        FailureDetectorConfig failureDetectorConfig = new FailureDetectorConfig();
        failureDetectorConfig.setImplementationClassName(failureDetectorCls.getName());
        failureDetectorConfig.setThreshold(50);
        failureDetectorConfig.setCluster(cluster);
        failureDetectorConfig.setStoreVerifier(MutableStoreVerifier.create(subStores));
        failureDetector = FailureDetectorUtils.create(failureDetectorConfig, false);

        // make routedStore
        RoutedStoreFactory factory = new RoutedStoreFactory();
        routedStore = factory.create(cluster,
                                     storeDef,
                                     testStores,
                                     socketTestStores,
                                     slopStores,
                                     socketSlopStores,
                                     failureDetector,
                                     new RoutedStoreConfig().setTimeoutConfig(new TimeoutConfig(1500L,
                                                                                                false)));

        // generate the keys
        for(int i = 0; i < 5; i++) {
            Set<Integer> nodesCovered = Sets.newHashSet();
            while(nodesCovered.size() < NUM_NODES_TOTAL) {
                ByteArray randomKey = new ByteArray(TestUtils.randomBytes(KEY_LENGTH));
                byte[] randomValue = TestUtils.randomBytes(VALUE_LENGTH);

                if(randomKey.length() > 0 && randomValue.length > 0) {
                    if(!keyList.contains(randomKey)) {
                        for(Node node: strategy.routeRequest(randomKey.get())) {
                            keysToNodes.put(randomKey, node.getId());
                            nodesCovered.add(node.getId());
                        }
                        logger.info("Inserting key [" + randomKey + "] to key list as id:"
                                    + keyList.size());
                        keyList.add(randomKey);
                        keyValues.put(randomKey, new ByteArray(randomValue));
                    }
                }
            }
        }
    }

    @After
    public void tearDown() throws Exception {
        if(failureDetector != null)
            failureDetector.destroy();
        for(VoldemortServer vs: voldemortServers.values()) {
            vs.stop();
        }
        routedStore.close();
        if(logger.isDebugEnabled()) {
            logger.debug("Test Ended: replication[" + REPLICATION_FACTOR + "], preferredW["
                         + P_WRITES + "], requiredW[" + R_WRITES + "]");
        }
    }

    @Test
    public void testHintedHandoff() throws Exception {
        Set<Integer> failedNodeSet = chooseFailedNodeSet(NUM_NODES_FAILED);
        Multimap<Integer, ByteArray> nodeToFailedKeysMap = doBatchPut(failedNodeSet);

        // wait for async operations
        // must be greater than socket timeout to ensure slop is registered
        logger.debug("Sleep for async operations to finish");
        Thread.sleep(Math.max(2000, SOCKET_TIMEOUT_MS * 2));

        Map<Integer, Map<ByteArray, byte[]>> nodeToSlopData = new HashMap<Integer, Map<ByteArray, byte[]>>();
        Set<ByteArray> slopKeys = makeSlopKeys(nodeToFailedKeysMap, Slop.Operation.PUT);
        for(Store<ByteArray, Slop, byte[]> slopStore: slopStores.values()) {
            Map<ByteArray, List<Versioned<Slop>>> getAllResult = slopStore.getAll(slopKeys, null);
            for(Map.Entry<ByteArray, List<Versioned<Slop>>> entry: getAllResult.entrySet()) {
                Slop slop = entry.getValue().get(0).getValue();
                Integer nodeId = slop.getNodeId();
                // get data
                if(!nodeToSlopData.containsKey(nodeId)) {
                    nodeToSlopData.put(nodeId, new HashMap<ByteArray, byte[]>());
                }
                Map<ByteArray, byte[]> perNodeSlopMap = nodeToSlopData.get(nodeId);
                perNodeSlopMap.put(slop.getKey(), slop.getValue());

                if(logger.isTraceEnabled())
                    logger.trace(slop);
            }
        }

        int errorCount = 0;
        for(Map.Entry<Integer, ByteArray> failedKey: nodeToFailedKeysMap.entries()) {
            Integer nodeId = failedKey.getKey();
            ByteArray key = failedKey.getValue();
            byte[] expected = keyValues.get(key).get();

            Integer id = keyList.indexOf(key);

            // check if map exist
            Map<ByteArray, byte[]> perNodeSlopMap = nodeToSlopData.get(nodeId);
            if(perNodeSlopMap == null) {
                logger.error("Slop does not have key[" + key + "][id:" + id + "]");
                errorCount++;
                continue;
            }

            byte[] actual = perNodeSlopMap.get(key);

            if(actual == null) {
                logger.error("Slop does not have key[" + key + "][nodeId:" + nodeId + "]");
                errorCount++;
            } else if(ByteUtils.compare(actual, expected) != 0) {
                logger.error("Slop key[" + key + "][nodeId:" + nodeId
                             + "] does not have the correct value");
                errorCount++;
            } else {
                logger.debug("Slop has key[" + key + "][nodeId:" + nodeId + "] with value["
                             + actual + "]");
            }
        }
        assertTrue(errorCount + " Slop(s) incorrect. See log for more info", errorCount == 0);
    }

    private Set<ByteArray> makeSlopKeys(Multimap<Integer, ByteArray> failedKeys,
                                        Slop.Operation operation) {
        Set<ByteArray> slopKeys = Sets.newHashSet();

        for(Map.Entry<Integer, ByteArray> entry: failedKeys.entries()) {
            byte[] opCode = new byte[] { operation.getOpCode() };
            byte[] spacer = new byte[] { (byte) 0 };
            byte[] storeName = ByteUtils.getBytes(STORE_NAME, "UTF-8");
            byte[] nodeIdBytes = new byte[ByteUtils.SIZE_OF_INT];
            ByteUtils.writeInt(nodeIdBytes, entry.getKey(), 0);
            ByteArray slopKey = new ByteArray(ByteUtils.cat(opCode,
                                                            spacer,
                                                            storeName,
                                                            spacer,
                                                            nodeIdBytes,
                                                            spacer,
                                                            entry.getValue().get()));
            slopKeys.add(slopKey);
        }
        return slopKeys;
    }

    private static Set<Integer> chooseFailedNodeSet(int count) {
        // decide failed nodes
        Set<Integer> failedNodes = new HashSet<Integer>();
        Random rand = new Random();
        while(failedNodes.size() < count && failedNodes.size() <= NUM_NODES_TOTAL) {
            int n = rand.nextInt(NUM_NODES_TOTAL);
            failedNodes.add(n);
        }

        if(logger.isDebugEnabled()) {
            logger.debug("Failing requests to " + failedNodes);
        }
        return failedNodes;
    }

    private Multimap<Integer, ByteArray> doBatchPut(Set<Integer> failedNodeSet) {
        for(Integer nodeId: failedNodeSet) {
            forceFailStores.get(nodeId).setFail(true);
        }
        // put keys into the nodes
        Multimap<Integer, ByteArray> nodeToFailedKeys = ArrayListMultimap.create();
        for(ByteArray key: keysToNodes.keySet()) {
            List<Node> nodes = strategy.routeRequest(key.get());
            List<Node> failedNodes = new ArrayList<Node>();
            for(Node node: nodes) {
                if(failedNodeSet != null && failedNodeSet.contains(node.getId())) {
                    failedNodes.add(node);
                }
            }
            // determine if write will succeed (if numGoodNodes > required)
            if((nodes.size() - failedNodes.size()) >= R_WRITES) {
                for(Node node: failedNodes) {
                    nodeToFailedKeys.put(node.getId(), key);
                    logger.debug("[key:" + key + "] to [nodeId:" + node.getId() + "] should fail");
                }
            } else {
                logger.debug("[key:" + key + "] should fail overall due to insufficient nodes");
            }

            try {
                Versioned<byte[]> versioned = new Versioned<byte[]>(keyValues.get(key).get());
                if(logger.isTraceEnabled())
                    logger.trace("PUT key [" + key + "] to store");
                routedStore.put(key, versioned, null);
            } catch(Exception e) {
                if(logger.isTraceEnabled())
                    logger.trace(e, e);
            }
        }
        return nodeToFailedKeys;
    }
}
TOP

Related Classes of voldemort.store.routed.HintedHandoffSendHintTest$SocketStoreClientFactoryForTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.