Package voldemort.store.routed

Source Code of voldemort.store.routed.RoutedStoreTest

/*
* Copyright 2008-2013 LinkedIn, Inc
*
* 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 voldemort.store.routed;

import static voldemort.FailureDetectorTestUtils.recordException;
import static voldemort.FailureDetectorTestUtils.recordSuccess;
import static voldemort.TestUtils.getClock;
import static voldemort.VoldemortTestConstants.getNineNodeCluster;
import static voldemort.cluster.failuredetector.FailureDetectorUtils.create;
import static voldemort.cluster.failuredetector.MutableStoreVerifier.create;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import voldemort.ServerTestUtils;
import voldemort.TestUtils;
import voldemort.VoldemortException;
import voldemort.VoldemortTestConstants;
import voldemort.client.RoutingTier;
import voldemort.client.TimeoutConfig;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.cluster.Zone;
import voldemort.cluster.failuredetector.BannagePeriodFailureDetector;
import voldemort.cluster.failuredetector.FailureDetector;
import voldemort.cluster.failuredetector.FailureDetectorConfig;
import voldemort.common.VoldemortOpCode;
import voldemort.routing.BaseStoreRoutingPlan;
import voldemort.routing.RoutingStrategy;
import voldemort.routing.RoutingStrategyFactory;
import voldemort.routing.RoutingStrategyType;
import voldemort.serialization.SerializerDefinition;
import voldemort.store.AbstractByteArrayStoreTest;
import voldemort.store.FailingReadsStore;
import voldemort.store.FailingStore;
import voldemort.store.InsufficientOperationalNodesException;
import voldemort.store.InsufficientZoneResponsesException;
import voldemort.store.SleepyStore;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.store.StoreDefinitionBuilder;
import voldemort.store.UnreachableStoreException;
import voldemort.store.memory.InMemoryStorageEngine;
import voldemort.store.slop.strategy.HintedHandoffStrategyType;
import voldemort.store.stats.StatTrackingStore;
import voldemort.store.stats.Tracked;
import voldemort.store.versioned.InconsistencyResolvingStore;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.utils.Time;
import voldemort.utils.Utils;
import voldemort.versioning.Occurred;
import voldemort.versioning.VectorClock;
import voldemort.versioning.VectorClockInconsistencyResolver;
import voldemort.versioning.Version;
import voldemort.versioning.Versioned;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
* Basic tests for RoutedStore
*
*/
public class RoutedStoreTest extends AbstractByteArrayStoreTest {

    public static final int BANNAGE_PERIOD = 1000;
    public static final int SLEEPY_TIME = 200;
    public static final int OPERATION_TIMEOUT = 60;

    private Cluster cluster;
    private StoreDefinition storeDef;
    private final ByteArray aKey = TestUtils.toByteArray("jay");
    private final byte[] aValue = "kreps".getBytes();
    private final byte[] aTransform = "transform".getBytes();
    private FailureDetector failureDetector;
    private ExecutorService routedStoreThreadPool;

    public RoutedStoreTest() {}

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();
        cluster = getNineNodeCluster();
    }

    @Override
    @After
    public void tearDown() throws Exception {
        super.tearDown();

        if(failureDetector != null)
            failureDetector.destroy();

        if(routedStoreThreadPool != null)
            routedStoreThreadPool.shutdown();
    }

    @Override
    public Store<ByteArray, byte[], byte[]> getStore() throws Exception {
        return new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(getStore(cluster,
                                                                                   cluster.getNumberOfNodes(),
                                                                                   cluster.getNumberOfNodes(),
                                                                                   4,
                                                                                   0),
                                                                          new VectorClockInconsistencyResolver<byte[]>());
    }

    private RoutedStoreFactory createFactory() {
        return new RoutedStoreFactory(this.routedStoreThreadPool);
    }

    private RoutedStoreConfig createConfig(TimeoutConfig timeoutConfig) {
        return new RoutedStoreConfig().setTimeoutConfig(timeoutConfig);
    }

    private RoutedStoreConfig createConfig(long timeout) {
        return new RoutedStoreConfig().setTimeoutConfig(new TimeoutConfig(timeout));
    }

    private RoutedStore getStore(Cluster cluster, int reads, int writes, int threads, int failing)
            throws Exception {
        return getStore(cluster,
                        reads,
                        writes,
                        threads,
                        failing,
                        0,
                        RoutingStrategyType.CONSISTENT_STRATEGY,
                        new VoldemortException());
    }

    private RoutedStore getStore(Cluster cluster,
                                 int reads,
                                 int writes,
                                 int threads,
                                 int failing,
                                 int sleepy,
                                 String strategy,
                                 VoldemortException e) throws Exception {
        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();
        int count = 0;
        for(Node n: cluster.getNodes()) {
            if(count >= cluster.getNumberOfNodes())
                throw new IllegalArgumentException(failing + " failing nodes, " + sleepy
                                                   + " sleepy nodes, but only "
                                                   + cluster.getNumberOfNodes()
                                                   + " nodes in the cluster.");

            Store<ByteArray, byte[], byte[]> subStore = null;

            if(count < failing)
                subStore = new FailingStore<ByteArray, byte[], byte[]>("test", e);
            else if(count < failing + sleepy)
                subStore = new SleepyStore<ByteArray, byte[], byte[]>(Long.MAX_VALUE,
                                                                      new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            else
                subStore = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test");

            subStores.put(n.getId(), subStore);

            count += 1;
        }

        setFailureDetector(subStores);
        this.storeDef = ServerTestUtils.getStoreDef("test",
                                                    reads + writes,
                                                    reads,
                                                    reads,
                                                    writes,
                                                    writes,
                                                    strategy);
        routedStoreThreadPool = Executors.newFixedThreadPool(threads);

        RoutedStoreFactory routedStoreFactory = createFactory();

        return routedStoreFactory.create(cluster,
                                         storeDef,
                                         subStores,
                                         failureDetector,
                                         createConfig(BANNAGE_PERIOD));
    }

    public Store<ByteArray, byte[], byte[]> getZonedStore() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        return new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(getStore(cluster,
                                                                                   cluster.getNumberOfNodes(),
                                                                                   cluster.getNumberOfNodes(),
                                                                                   cluster.getNumberOfZones() - 1,
                                                                                   cluster.getNumberOfZones() - 1,
                                                                                   4,
                                                                                   zoneReplicationFactor),
                                                                          new VectorClockInconsistencyResolver<byte[]>());
    }

    private RoutedStore getStore(Cluster cluster,
                                 int reads,
                                 int writes,
                                 int zonereads,
                                 int zonewrites,
                                 int threads,
                                 HashMap<Integer, Integer> zoneReplicationFactor) throws Exception {
        return getStore(cluster,
                        reads,
                        writes,
                        zonereads,
                        zonewrites,
                        threads,
                        null,
                        null,
                        zoneReplicationFactor,
                        RoutingStrategyType.ZONE_STRATEGY,
                        0,
                        BANNAGE_PERIOD,
                        null);

    }

    private RoutedStore getStore(Cluster cluster,
                                 int reads,
                                 int writes,
                                 int zonereads,
                                 int zonewrites,
                                 int threads,
                                 Set<Integer> failing,
                                 Set<Integer> sleepy,
                                 HashMap<Integer, Integer> zoneReplicationFactor,
                                 String strategy,
                                 long sleepMs,
                                 long timeOutMs,
                                 VoldemortException e) throws Exception {
        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();
        for(Node n: cluster.getNodes()) {
            Store<ByteArray, byte[], byte[]> subStore = null;

            if(failing != null && failing.contains(n.getId()))
                subStore = new FailingStore<ByteArray, byte[], byte[]>("test", e);
            else if(sleepy != null && sleepy.contains(n.getId()))
                subStore = new SleepyStore<ByteArray, byte[], byte[]>(sleepMs,
                                                                      new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            else
                subStore = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test");

            subStores.put(n.getId(), subStore);
        }

        setFailureDetector(subStores);
        this.storeDef = ServerTestUtils.getStoreDef("test",
                                                    reads,
                                                    reads,
                                                    writes,
                                                    writes,
                                                    zonereads,
                                                    zonewrites,
                                                    zoneReplicationFactor,
                                                    HintedHandoffStrategyType.PROXIMITY_STRATEGY,
                                                    strategy);
        routedStoreThreadPool = Executors.newFixedThreadPool(threads);
        RoutedStoreFactory routedStoreFactory = createFactory();

        return routedStoreFactory.create(cluster,
                                         storeDef,
                                         subStores,
                                         failureDetector,
                                         createConfig(timeOutMs));
    }

    private int countOccurances(RoutedStore routedStore, ByteArray key, Versioned<byte[]> value) {
        int count = 0;
        for(Store<ByteArray, byte[], byte[]> store: routedStore.getInnerStores().values())
            try {
                if(store.get(key, null).size() > 0
                   && Utils.deepEquals(store.get(key, null).get(0), value))
                    count += 1;
            } catch(VoldemortException e) {
                // This is normal for the failing store...
            }
        return count;
    }

    private void assertNEqual(RoutedStore routedStore,
                              int expected,
                              ByteArray key,
                              Versioned<byte[]> value) {
        int count = countOccurances(routedStore, key, value);
        assertEquals("Expected " + expected + " occurances of '" + key + "' with value '" + value
                     + "', but found " + count + ".", expected, count);
    }

    private void assertNOrMoreEqual(RoutedStore routedStore,
                                    int expected,
                                    ByteArray key,
                                    Versioned<byte[]> value) {
        int count = countOccurances(routedStore, key, value);
        assertTrue("Expected " + expected + " or more occurances of '" + key + "' with value '"
                   + value + "', but found " + count + ".", expected <= count);
    }

    /**
     * In case of Zoned cluster, there is a non trivial time required for the
     * delete. The custom timeout is used to account for this delay.
     */
    private void waitForOperationToComplete(long customSleepTime) {
        if(customSleepTime > 0) {
            try {
                Thread.sleep(customSleepTime);
            } catch(Exception e) {}
        }

    }

    private void testBasicOperations(int reads,
                                     int writes,
                                     int failures,
                                     int threads,
                                     RoutedStore customRoutedStore,
                                     long customSleepTime) throws Exception {

        RoutedStore routedStore = null;
        if(customRoutedStore == null) {
            routedStore = getStore(cluster, reads, writes, threads, failures);
        } else {
            routedStore = customRoutedStore;
        }

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());

        VectorClock clock = getClock(1);
        Versioned<byte[]> versioned = new Versioned<byte[]>(aValue, clock);
        routedStore.put(aKey, versioned, aTransform);

        waitForOperationToComplete(customSleepTime);
        assertNOrMoreEqual(routedStore, cluster.getNumberOfNodes() - failures, aKey, versioned);

        List<Versioned<byte[]>> found = store.get(aKey, aTransform);
        assertEquals(1, found.size());
        assertEquals(versioned, found.get(0));

        waitForOperationToComplete(customSleepTime);
        assertNOrMoreEqual(routedStore, cluster.getNumberOfNodes() - failures, aKey, versioned);

        assertTrue(routedStore.delete(aKey, versioned.getVersion()));

        waitForOperationToComplete(customSleepTime);
        assertNEqual(routedStore, 0, aKey, versioned);

        assertTrue(!routedStore.delete(aKey, versioned.getVersion()));
    }

    @Test
    public void testBasicOperationsSingleThreaded() throws Exception {
        testBasicOperations(cluster.getNumberOfNodes(), cluster.getNumberOfNodes(), 0, 1, null, 0);
    }

    /**
     * Test to ensure that the basic operations can be performed successfully
     * against a 3 zone cluster with a single thread.
     *
     * @throws Exception
     */
    @Test
    public void testBasicOperationsZZZSingleThreaded() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = #nodes in a zone
        // PW = RW = #nodes in a zone
        // Zone Reads = # Zones - 1
        // Zone Writes = # Zones - 1
        // Threads = 1
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodesInZone(0),
                                           cluster.getNumberOfNodesInZone(0),
                                           cluster.getNumberOfZones() - 1,
                                           cluster.getNumberOfZones() - 1,
                                           1,
                                           zoneReplicationFactor);

        testBasicOperations(cluster.getNumberOfNodes(),
                            cluster.getNumberOfNodes(),
                            0,
                            0,
                            routedStore,
                            500);
    }

    @Test
    public void testBasicOperationsMultiThreaded() throws Exception {
        testBasicOperations(cluster.getNumberOfNodes(), cluster.getNumberOfNodes(), 0, 4, null, 0);
    }

    /**
     * Test to ensure that the basic operations can be performed successfully
     * against a 3 zone cluster with multiple threads.
     *
     * @throws Exception
     */

    @Test
    public void testBasicOperationsZZZMultiThreaded() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = #nodes in a zone
        // PW = RW = #nodes in a zone
        // Zone Reads = # Zones - 1
        // Zone Writes = # Zones - 1
        // Threads = 4
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodesInZone(0),
                                           cluster.getNumberOfNodesInZone(0),
                                           cluster.getNumberOfZones() - 1,
                                           cluster.getNumberOfZones() - 1,
                                           4,
                                           zoneReplicationFactor);

        testBasicOperations(cluster.getNumberOfNodes(),
                            cluster.getNumberOfNodes(),
                            0,
                            0,
                            routedStore,
                            500);
    }

    @Test
    public void testBasicOperationsMultiThreadedWithFailures() throws Exception {
        testBasicOperations(cluster.getNumberOfNodes() - 2,
                            cluster.getNumberOfNodes() - 2,
                            2,
                            4,
                            null,
                            0);
    }

    /**
     * Test to ensure that the basic operations can be performed successfully
     * against a 3 zone cluster in the presence of failures.
     *
     * @throws Exception
     */

    @Test
    public void testBasicOperationsZZZMultiThreadedWithFailures() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // This configuration uses zone reads = 0 in order to avoid getting
        // InsufficientZoneResponsesException. Please check the comment at the
        // end of PerformSerialRequests.java to understand this.

        // PR = RR = #nodes in a zone - 1
        // PW = RW = #nodes in a zone - 1
        // Zone Reads = 0
        // Zone Writes = # Zones - 1
        // Failing nodes = 1 from each zone
        // Threads = 4
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           0,
                                           cluster.getNumberOfZones() - 1,
                                           4,
                                           Sets.newHashSet(1, 5, 6),
                                           null,
                                           zoneReplicationFactor,
                                           RoutingStrategyType.ZONE_STRATEGY,
                                           0,
                                           BANNAGE_PERIOD,
                                           new VoldemortException());

        testBasicOperations(cluster.getNumberOfNodes(),
                            cluster.getNumberOfNodes(),
                            3,
                            0,
                            routedStore,
                            1000);
    }

    /**
     * Test to ensure that the basic operations occur correctly, in the presence
     * of some sleepy nodes. NOTE: For some selection of the sleepy nodes, it is
     * possible that the operation will timeout. This particular set of sleepy
     * nodes is designed for the chosen key and the chosen global timeout.
     *
     * @throws Exception
     */
    @Test
    public void testBasicOperationsZZZMultiThreadedWithDelays() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = #nodes in a zone - 1
        // PW = RW = #nodes in a zone - 1
        // Zone Reads = 1
        // Zone Writes = 1
        // Sleepy nodes = 1 from each zone
        // Threads = 4
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           1,
                                           1,
                                           4,
                                           null,
                                           Sets.newHashSet(2, 4, 8),
                                           zoneReplicationFactor,
                                           RoutingStrategyType.ZONE_STRATEGY,
                                           SLEEPY_TIME,
                                           100,
                                           new VoldemortException());

        testBasicOperations(cluster.getNumberOfNodes(),
                            cluster.getNumberOfNodes(),
                            3,
                            0,
                            routedStore,
                            1000);
    }

    private void testBasicOperationFailure(int reads,
                                           int writes,
                                           int failures,
                                           int threads,
                                           RoutedStore customRoutedStore) throws Exception {
        VectorClock clock = getClock(1);
        Versioned<byte[]> versioned = new Versioned<byte[]>(aValue, clock);

        RoutedStore routedStore = null;
        if(customRoutedStore == null) {
            routedStore = getStore(cluster,
                                   reads,
                                   writes,
                                   threads,
                                   failures,
                                   0,
                                   RoutingStrategyType.TO_ALL_STRATEGY,
                                   new UnreachableStoreException("no go"));
        } else {
            routedStore = customRoutedStore;
        }

        try {
            routedStore.put(aKey, versioned, aTransform);
            fail("Put succeeded with too few operational nodes.");
        } catch(InsufficientOperationalNodesException e) {
            // expected
        }
        try {
            routedStore.get(aKey, aTransform);
            fail("Get succeeded with too few operational nodes.");
        } catch(InsufficientOperationalNodesException e) {
            // expected
        }
        try {
            routedStore.delete(aKey, versioned.getVersion());
            fail("Get succeeded with too few operational nodes.");
        } catch(InsufficientOperationalNodesException e) {
            // expected
        }
    }

    @Test
    public void testBasicOperationFailureMultiThreaded() throws Exception {
        testBasicOperationFailure(cluster.getNumberOfNodes() - 2,
                                  cluster.getNumberOfNodes() - 2,
                                  4,
                                  4,
                                  null);
    }

    /**
     * Test to ensure that the basic operations fail in the presence of bad
     * nodes in a 3 zone cluster.
     *
     * @throws Exception
     */
    @Test
    public void testBasicOperationFailureZZZMultiThreaded() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = 7
        // PW = RW = 7
        // Zone Reads = # Zones - 1
        // Zone Writes = # Zones - 1
        // Failing nodes = 1 from each zone
        // Threads = 4
        RoutedStore zonedRoutedStore = getStore(cluster,
                                                7,
                                                7,
                                                cluster.getNumberOfZones() - 1,
                                                cluster.getNumberOfZones() - 1,
                                                4,
                                                Sets.newHashSet(1, 5, 6),
                                                null,
                                                zoneReplicationFactor,
                                                RoutingStrategyType.ZONE_STRATEGY,
                                                0,
                                                BANNAGE_PERIOD,
                                                new UnreachableStoreException("no go"));

        testBasicOperationFailure(cluster.getNumberOfNodes() - 2,
                                  cluster.getNumberOfNodes() - 2,
                                  0,
                                  0,
                                  zonedRoutedStore);
    }

    /**
     * Test to ensure that the basic operations fail, in the presence of some
     * sleepy nodes and zone count reads and writes = 2 in a 3 zone cluster.
     *
     * @throws Exception
     */
    @Test
    public void testBasicOperationsFailureZZZMultiThreadedWithDelays() throws Exception {
        cluster = VoldemortTestConstants.getNineNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = #nodes in a zone - 1
        // PW = RW = #nodes in a zone - 1
        // Zone Reads = # Zones - 1
        // Zone Writes = # Zones - 1
        // Sleepy nodes = 1 from each zone
        // Threads = 4
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           cluster.getNumberOfNodesInZone(0) - 1,
                                           cluster.getNumberOfZones() - 1,
                                           cluster.getNumberOfZones() - 1,
                                           4,
                                           null,
                                           Sets.newHashSet(2, 4, 8),
                                           zoneReplicationFactor,
                                           RoutingStrategyType.ZONE_STRATEGY,
                                           SLEEPY_TIME,
                                           OPERATION_TIMEOUT,
                                           new VoldemortException());

        try {
            testBasicOperations(cluster.getNumberOfNodes(),
                                cluster.getNumberOfNodes(),
                                3,
                                0,
                                routedStore,
                                1000);
            fail("Too few successful zone responses. Should've failed.");
        } catch(InsufficientZoneResponsesException ize) {
            // Expected
        }
    }

    @Test
    public void testPutIncrementsVersion() throws Exception {
        Store<ByteArray, byte[], byte[]> store = getStore();
        VectorClock clock = new VectorClock();
        VectorClock copy = clock.clone();
        store.put(aKey, new Versioned<byte[]>(getValue(), clock), aTransform);
        List<Versioned<byte[]>> found = store.get(aKey, aTransform);
        assertEquals("Invalid number of items found.", 1, found.size());
        assertEquals("Version not incremented properly",
                     Occurred.BEFORE,
                     copy.compare(found.get(0).getVersion()));
    }

    @Test
    public void testPutIncrementsVersionZZZ() throws Exception {
        Store<ByteArray, byte[], byte[]> store = getZonedStore();
        VectorClock clock = new VectorClock();
        VectorClock copy = clock.clone();
        store.put(aKey, new Versioned<byte[]>(getValue(), clock), aTransform);
        List<Versioned<byte[]>> found = store.get(aKey, aTransform);
        assertEquals("Invalid number of items found.", 1, found.size());
        assertEquals("Version not incremented properly",
                     Occurred.BEFORE,
                     copy.compare(found.get(0).getVersion()));
    }

    @Test
    public void testObsoleteMasterFails() {
        // write me
    }

    @Test
    public void testZoneRouting() throws Exception {
        cluster = VoldemortTestConstants.getEightNodeClusterWithZones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, 2);
        zoneReplicationFactor.put(1, 2);

        long start;
        Versioned<byte[]> versioned = new Versioned<byte[]>(new byte[] { 1 });

        // Basic put with zone read = 0, zone write = 0 and timeout < cross-zone
        // latency
        Store<ByteArray, byte[], byte[]> s1 = getStore(cluster,
                                                       1,
                                                       1,
                                                       0,
                                                       0,
                                                       8,
                                                       null,
                                                       Sets.newHashSet(4, 5, 6, 7),
                                                       zoneReplicationFactor,
                                                       RoutingStrategyType.ZONE_STRATEGY,
                                                       SLEEPY_TIME,
                                                       OPERATION_TIMEOUT,
                                                       new VoldemortException());

        start = System.nanoTime();
        try {
            s1.put(new ByteArray("test".getBytes()), versioned, null);
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }
        // Putting extra key to test getAll
        s1.put(new ByteArray("test2".getBytes()), versioned, null);

        start = System.nanoTime();
        try {
            s1.get(new ByteArray("test".getBytes()), null);
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        start = System.nanoTime();
        try {
            List<Version> versions = s1.getVersions(new ByteArray("test".getBytes()));
            for(Version version: versions) {
                assertEquals(version.compare(versioned.getVersion()), Occurred.BEFORE);
            }
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        start = System.nanoTime();
        try {
            s1.delete(new ByteArray("test".getBytes()), versioned.getVersion());
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        // make sure sleepy stores processed the delete before checking,
        // otherwise, we might be bailing
        // out of the test too early for the delete to be processed.
        Thread.sleep(SLEEPY_TIME * 2);
        List<ByteArray> keys = Lists.newArrayList(new ByteArray("test".getBytes()),
                                                  new ByteArray("test2".getBytes()));

        Map<ByteArray, List<Versioned<byte[]>>> values = s1.getAll(keys, null);
        assertFalse("'test' did not get deleted.",
                    values.containsKey(new ByteArray("test".getBytes())));
        ByteUtils.compare(values.get(new ByteArray("test2".getBytes())).get(0).getValue(),
                          new byte[] { 1 });

        // Basic put with zone read = 1, zone write = 1
        Store<ByteArray, byte[], byte[]> s2 = getStore(cluster,
                                                       1,
                                                       1,
                                                       1,
                                                       1,
                                                       8,
                                                       null,
                                                       Sets.newHashSet(4, 5, 6, 7),
                                                       zoneReplicationFactor,
                                                       RoutingStrategyType.ZONE_STRATEGY,
                                                       SLEEPY_TIME,
                                                       BANNAGE_PERIOD,
                                                       new VoldemortException());

        start = System.nanoTime();

        try {
            s2.put(new ByteArray("test".getBytes()), versioned, null);
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " > " + SLEEPY_TIME, elapsed >= SLEEPY_TIME);
        }
        s2.put(new ByteArray("test2".getBytes()), versioned, null);

        try {
            s2.get(new ByteArray("test".getBytes()), null);
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * Why would you want responses from two zones and wait for only one
             * response...
             */
        }

        try {
            s2.getVersions(new ByteArray("test".getBytes()));
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * Why would you want responses from two zones and wait for only one
             * response...
             */
        }

        try {
            s2.delete(new ByteArray("test".getBytes()), null);
        } catch(InsufficientZoneResponsesException e) {
            /*
             * Why would you want responses from two zones and wait for only one
             * response...
             */
        }

        values = s2.getAll(keys, null);
        assertFalse("'test' did not get deleted.",
                    values.containsKey(new ByteArray("test".getBytes())));
        ByteUtils.compare(values.get(new ByteArray("test2".getBytes())).get(0).getValue(),
                          new byte[] { 1 });

        // Basic put with zone read = 0, zone write = 0 and failures in other
        // dc, but should still work
        Store<ByteArray, byte[], byte[]> s3 = getStore(cluster,
                                                       1,
                                                       1,
                                                       0,
                                                       0,
                                                       8,
                                                       Sets.newHashSet(4, 5, 6, 7),
                                                       null,
                                                       zoneReplicationFactor,
                                                       RoutingStrategyType.ZONE_STRATEGY,
                                                       SLEEPY_TIME,
                                                       BANNAGE_PERIOD,
                                                       new VoldemortException());

        start = System.nanoTime();
        try {
            s3.put(new ByteArray("test".getBytes()), versioned, null);
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }
        // Putting extra key to test getAll
        s3.put(new ByteArray("test2".getBytes()), versioned, null);

        start = System.nanoTime();
        try {
            List<Version> versions = s3.getVersions(new ByteArray("test".getBytes()));
            for(Version version: versions) {
                assertEquals(version.compare(versioned.getVersion()), Occurred.BEFORE);
            }
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        start = System.nanoTime();
        try {
            s3.get(new ByteArray("test".getBytes()), null);
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        start = System.nanoTime();
        try {
            s3.delete(new ByteArray("test".getBytes()), versioned.getVersion());
        } finally {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        // Basic put with zone read = 1, zone write = 1 and failures in other
        // dc, should not work
        Store<ByteArray, byte[], byte[]> s4 = getStore(cluster,
                                                       2,
                                                       2,
                                                       1,
                                                       1,
                                                       8,
                                                       Sets.newHashSet(4, 5, 6, 7),
                                                       null,
                                                       zoneReplicationFactor,
                                                       RoutingStrategyType.ZONE_STRATEGY,
                                                       SLEEPY_TIME,
                                                       BANNAGE_PERIOD,
                                                       new VoldemortException());

        try {
            s4.put(new ByteArray("test".getBytes()), new Versioned<byte[]>(new byte[] { 1 }), null);
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * The other zone is down and you expect a result from both zones
             */
        }

        try {
            s4.getVersions(new ByteArray("test".getBytes()));
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * The other zone is down and you expect a result from both zones
             */
        }

        try {
            s4.get(new ByteArray("test".getBytes()), null);
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * The other zone is down and you expect a result from both zones
             */
        }

        try {
            s4.delete(new ByteArray("test".getBytes()), versioned.getVersion());
            fail("Should have shown exception");
        } catch(InsufficientZoneResponsesException e) {
            /*
             * The other zone is down and you expect a result from both zones
             */
        }

    }

    @Test
    public void testOnlyNodeFailuresDisableNode() throws Exception {
        // test put
        cluster = getNineNodeCluster();

        Store<ByteArray, byte[], byte[]> s1 = getStore(cluster,
                                                       1,
                                                       9,
                                                       9,
                                                       9,
                                                       0,
                                                       RoutingStrategyType.TO_ALL_STRATEGY,
                                                       new VoldemortException());
        try {
            s1.put(aKey, new Versioned<byte[]>(aValue), aTransform);
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(9);

        cluster = getNineNodeCluster();

        Store<ByteArray, byte[], byte[]> s2 = getStore(cluster,
                                                       1,
                                                       9,
                                                       9,
                                                       9,
                                                       0,
                                                       RoutingStrategyType.TO_ALL_STRATEGY,
                                                       new UnreachableStoreException("no go"));
        try {
            s2.put(aKey, new Versioned<byte[]>(aValue), aTransform);
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(0);

        // test get
        cluster = getNineNodeCluster();

        s1 = getStore(cluster,
                      1,
                      9,
                      9,
                      9,
                      0,
                      RoutingStrategyType.TO_ALL_STRATEGY,
                      new VoldemortException());
        try {
            s1.get(aKey, aTransform);
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(9);

        cluster = getNineNodeCluster();

        s2 = getStore(cluster,
                      1,
                      9,
                      9,
                      9,
                      0,
                      RoutingStrategyType.TO_ALL_STRATEGY,
                      new UnreachableStoreException("no go"));
        try {
            s2.get(aKey, aTransform);
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(0);

        // test delete
        cluster = getNineNodeCluster();

        s1 = getStore(cluster,
                      1,
                      9,
                      9,
                      9,
                      0,
                      RoutingStrategyType.TO_ALL_STRATEGY,
                      new VoldemortException());
        try {
            s1.delete(aKey, new VectorClock());
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(9);

        cluster = getNineNodeCluster();

        s2 = getStore(cluster,
                      1,
                      9,
                      9,
                      9,
                      0,
                      RoutingStrategyType.TO_ALL_STRATEGY,
                      new UnreachableStoreException("no go"));
        try {
            s2.delete(aKey, new VectorClock());
            fail("Failure is expected");
        } catch(InsufficientOperationalNodesException e) { /* expected */
        }
        assertOperationalNodes(0);
    }

    @Test
    public void testGetVersions2() throws Exception {
        List<ByteArray> keys = getKeys(2);
        ByteArray key = keys.get(0);
        byte[] value = getValue();
        Store<ByteArray, byte[], byte[]> store = getStore();
        store.put(key, Versioned.value(value), null);
        List<Versioned<byte[]>> versioneds = store.get(key, null);
        List<Version> versions = store.getVersions(key);
        assertEquals(1, versioneds.size());
        assertEquals(9, versions.size());
        for(int i = 0; i < versions.size(); i++)
            assertEquals(versioneds.get(0).getVersion(), versions.get(i));

        assertEquals(0, store.getVersions(keys.get(1)).size());
    }

    @Test
    public void testGetVersions2ZZZ() throws Exception {
        List<ByteArray> keys = getKeys(2);
        ByteArray key = keys.get(0);
        byte[] value = getValue();
        Store<ByteArray, byte[], byte[]> store = getZonedStore();
        store.put(key, Versioned.value(value), null);
        List<Versioned<byte[]>> versioneds = store.get(key, null);
        List<Version> versions = store.getVersions(key);
        assertEquals(1, versioneds.size());
        assertEquals(9, versions.size());
        for(int i = 0; i < versions.size(); i++)
            assertEquals(versioneds.get(0).getVersion(), versions.get(i));

        assertEquals(0, store.getVersions(keys.get(1)).size());
    }

    /**
     * Util function to test getAll with one node down
     *
     * @param store The Routed store object used to perform the put and getall
     * @throws Exception
     */
    private void getAllWithNodeDown(Store<ByteArray, byte[], byte[]> store) throws Exception {

        Map<ByteArray, byte[]> expectedValues = Maps.newHashMap();
        for(byte i = 1; i < 11; ++i) {
            ByteArray key = new ByteArray(new byte[] { i });
            byte[] value = new byte[] { (byte) (i + 50) };
            store.put(key, Versioned.value(value), null);
            expectedValues.put(key, value);
        }

        recordException(failureDetector, cluster.getNodes().iterator().next());

        Map<ByteArray, List<Versioned<byte[]>>> all = store.getAll(expectedValues.keySet(), null);
        assertEquals(expectedValues.size(), all.size());
        for(Map.Entry<ByteArray, List<Versioned<byte[]>>> mapEntry: all.entrySet()) {
            byte[] value = expectedValues.get(mapEntry.getKey());
            assertEquals(new ByteArray(value), new ByteArray(mapEntry.getValue().get(0).getValue()));
        }
    }

    /**
     * Tests that getAll works correctly with a node down in a two node cluster.
     */
    @Test
    public void testGetAllWithNodeDown() throws Exception {
        cluster = VoldemortTestConstants.getTwoNodeCluster();

        RoutedStore routedStore = getStore(cluster, 1, 2, 1, 0);
        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());
        getAllWithNodeDown(store);
    }

    /**
     * Tests that getAll works correctly with a node down in a three node three
     * zone cluster.
     */
    @Test
    public void testGetAllWithNodeDownZZZ() throws Exception {
        cluster = VoldemortTestConstants.getThreeNodeClusterWith3Zones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = 2
        // PW = RW = 3
        // Zone Reads = 0
        // Zone Writes = 0
        // Threads = 1
        RoutedStore routedStore = getStore(cluster,
                                           cluster.getNumberOfNodes() - 1,
                                           cluster.getNumberOfNodes(),
                                           0,
                                           0,
                                           1,
                                           zoneReplicationFactor);

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());
        getAllWithNodeDown(store);
    }

    /**
     * Tests that getAll returns partial results
     */
    @Test
    public void testPartialGetAll() throws Exception {
        // create a store with rf=1 i.e disjoint partitions
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
                                                                 .setReplicationFactor(1)
                                                                 .setPreferredReads(1)
                                                                 .setRequiredReads(1)
                                                                 .setPreferredWrites(1)
                                                                 .setRequiredWrites(1)
                                                                 .build();

        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        // create nodes with varying speeds - 100ms, 200ms, 300ms
        for(int i = 0; i < 3; i++) {
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(100 * (i + 1),
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);
            nodes.add(new Node(i, "none", 0, 0, 0, partitions));
        }
        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);

        TimeoutConfig timeoutConfig = new TimeoutConfig(1500, true);
        // This means, the getall will only succeed on two of the nodes
        timeoutConfig.setOperationTimeout(VoldemortOpCode.GET_ALL_OP_CODE, 250);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeoutConfig));
        /* do some puts so we have some data to test getalls */
        Map<ByteArray, byte[]> expectedValues = Maps.newHashMap();
        for(byte i = 1; i < 11; ++i) {
            ByteArray key = new ByteArray(new byte[] { i });
            byte[] value = new byte[] { (byte) (i + 50) };
            routedStore.put(key, Versioned.value(value), null);
            expectedValues.put(key, value);
        }

        /* 1. positive test; if partial is on, should get something back */
        Map<ByteArray, List<Versioned<byte[]>>> all = routedStore.getAll(expectedValues.keySet(),
                                                                         null);
        assert (expectedValues.size() > all.size());

        /* 2. negative test; if partial is off, should fail the whole operation */
        timeoutConfig.setPartialGetAllAllowed(false);
        try {
            all = routedStore.getAll(expectedValues.keySet(), null);
            fail("Should have failed");
        } catch(Exception e) {

        }
    }

    /**
     * Tests that getAll returns partial results in a 3 zone cluster (with a
     * node down).
     */
    @Test
    public void testPartialGetAllZZZ() throws Exception {

        // Set replication factors for a 3 zone cluster
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, 1);
        zoneReplicationFactor.put(1, 1);
        zoneReplicationFactor.put(2, 1);

        // Create a store with RF=3, Required reads = 3 and zone count reads = 2
        // This ensures that a GET operation requires a response from all 3
        // nodes (from the respective 3 zones)
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.ZONE_STRATEGY)
                                                                 .setHintedHandoffStrategy(HintedHandoffStrategyType.PROXIMITY_STRATEGY)
                                                                 .setReplicationFactor(3)
                                                                 .setPreferredReads(3)
                                                                 .setRequiredReads(3)
                                                                 .setPreferredWrites(1)
                                                                 .setRequiredWrites(1)
                                                                 .setZoneCountReads(2)
                                                                 .setZoneCountWrites(1)
                                                                 .setZoneReplicationFactor(zoneReplicationFactor)
                                                                 .build();

        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        // create nodes with varying speeds - 100ms, 200ms, 300ms
        for(int i = 0; i < 3; i++) {
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(100 * (i + 1),
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);

            // Create zoned nodes - one in each zone (0, 1, 2)
            nodes.add(new Node(i, "none", 0, 0, 0, i, partitions));
        }
        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);

        TimeoutConfig timeoutConfig = new TimeoutConfig(1500, true);
        // This means, the getall will only succeed on two of the nodes
        timeoutConfig.setOperationTimeout(VoldemortOpCode.GET_ALL_OP_CODE, 250);
        RoutedStoreFactory routedStoreFactory = createFactory();

        List<Zone> zones = Lists.newArrayList();

        for(int i = 0; i < 3; i++) {
            LinkedList<Integer> zoneProximityList = Lists.newLinkedList();
            Set<Integer> zoneIds = Sets.newHashSet(0, 1, 2);
            zoneIds.remove(i);
            zoneProximityList.addAll(zoneIds);
            zones.add(new Zone(i, zoneProximityList));
        }

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes, zones),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeoutConfig));
        /* do some puts so we have some data to test getalls */
        Map<ByteArray, byte[]> expectedValues = Maps.newHashMap();
        for(byte i = 1; i < 11; ++i) {
            ByteArray key = new ByteArray(new byte[] { i });
            byte[] value = new byte[] { (byte) (i + 50) };
            routedStore.put(key, Versioned.value(value), null);
            expectedValues.put(key, value);
        }

        /* 1. positive test; if partial is on, should get something back */
        Map<ByteArray, List<Versioned<byte[]>>> all = routedStore.getAll(expectedValues.keySet(),
                                                                         null);
        assert (expectedValues.size() > all.size());

        /* 2. negative test; if partial is off, should fail the whole operation */
        timeoutConfig.setPartialGetAllAllowed(false);
        try {
            all = routedStore.getAll(expectedValues.keySet(), null);
            fail("Should have failed");
        } catch(Exception e) {
            // Expected
        }
    }

    @Test
    public void testGetAllWithFailingStore() throws Exception {
        cluster = VoldemortTestConstants.getTwoNodeCluster();

        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               2,
                                                               1,
                                                               1,
                                                               2,
                                                               2,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        int id1 = Iterables.get(cluster.getNodes(), 0).getId();
        int id2 = Iterables.get(cluster.getNodes(), 1).getId();
        subStores.put(id1, new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
        subStores.put(id2, new FailingReadsStore<ByteArray, byte[], byte[]>("test"));

        setFailureDetector(subStores);
        routedStoreThreadPool = Executors.newFixedThreadPool(1);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(BANNAGE_PERIOD));

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());

        Map<ByteArray, byte[]> expectedValues = Maps.newHashMap();
        for(byte i = 1; i < 11; ++i) {
            ByteArray key = new ByteArray(new byte[] { i });
            byte[] value = new byte[] { (byte) (i + 50) };
            store.put(key, Versioned.value(value), null);
            expectedValues.put(key, value);
        }

        Map<ByteArray, List<Versioned<byte[]>>> all = store.getAll(expectedValues.keySet(), null);
        assertEquals(expectedValues.size(), all.size());
        for(Map.Entry<ByteArray, List<Versioned<byte[]>>> mapEntry: all.entrySet()) {
            byte[] value = expectedValues.get(mapEntry.getKey());
            assertEquals(new ByteArray(value), new ByteArray(mapEntry.getValue().get(0).getValue()));
        }
    }

    /**
     * Test to ensure that get all works in a 3 zone cluster with 2 nodes per
     * zone and 1 node down in each zone.
     */
    @Test
    public void testGetAllWithFailingStoreZZZ() throws Exception {
        cluster = VoldemortTestConstants.getSixNodeClusterWith3Zones();
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = 3
        // PW = RW = 6
        // Zone Reads = 2
        // Zone Writes = 2
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               3,
                                                               3,
                                                               6,
                                                               6,
                                                               2,
                                                               2,
                                                               zoneReplicationFactor,
                                                               HintedHandoffStrategyType.PROXIMITY_STRATEGY,
                                                               RoutingStrategyType.ZONE_STRATEGY);

        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        for(int id = 0; id < 6; id++) {
            // Mark all the even nodes as normal and odd as read-failing
            // This ensures that one node in each zone is read-failing and the
            // other is normal
            if(id % 2 == 0) {
                subStores.put(id, new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            } else {
                subStores.put(id, new FailingReadsStore<ByteArray, byte[], byte[]>("test"));
            }
        }

        setFailureDetector(subStores);
        routedStoreThreadPool = Executors.newFixedThreadPool(1);
        RoutedStoreFactory routedStoreFactory = createFactory();
        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(BANNAGE_PERIOD));

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());

        Map<ByteArray, byte[]> expectedValues = Maps.newHashMap();
        for(byte i = 1; i < 11; ++i) {
            ByteArray key = new ByteArray(new byte[] { i });
            byte[] value = new byte[] { (byte) (i + 50) };
            store.put(key, Versioned.value(value), null);
            expectedValues.put(key, value);
        }

        Map<ByteArray, List<Versioned<byte[]>>> all = store.getAll(expectedValues.keySet(), null);
        assertEquals(expectedValues.size(), all.size());
        for(Map.Entry<ByteArray, List<Versioned<byte[]>>> mapEntry: all.entrySet()) {
            byte[] value = expectedValues.get(mapEntry.getKey());
            assertEquals(new ByteArray(value), new ByteArray(mapEntry.getValue().get(0).getValue()));
        }
    }

    /**
     * One node up, two preferred reads and one required read. See:
     *
     * http://github.com/voldemort/voldemort/issues#issue/18
     */
    @Test
    public void testGetAllWithMorePreferredReadsThanNodes() throws Exception {
        cluster = VoldemortTestConstants.getTwoNodeCluster();

        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               2,
                                                               2,
                                                               1,
                                                               2,
                                                               2,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        int id1 = Iterables.get(cluster.getNodes(), 0).getId();
        int id2 = Iterables.get(cluster.getNodes(), 1).getId();
        subStores.put(id1, new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
        subStores.put(id2, new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));

        setFailureDetector(subStores);

        routedStoreThreadPool = Executors.newFixedThreadPool(1);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(BANNAGE_PERIOD));

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());
        store.put(aKey, Versioned.value(aValue), aTransform);
        recordException(failureDetector, cluster.getNodes().iterator().next());
        Map<ByteArray, List<Versioned<byte[]>>> all = store.getAll(Arrays.asList(aKey),
                                                                   Collections.singletonMap(aKey,
                                                                                            aTransform));
        assertEquals(1, all.size());
        assertTrue(Arrays.equals(aValue, all.values().iterator().next().get(0).getValue()));
    }

    /**
     * See Issue #89: Sequential retrieval in RoutedStore.get doesn't consider
     * repairReads.
     */
    @Test
    public void testReadRepairWithFailures() throws Exception {
        cluster = getNineNodeCluster();

        RoutedStore routedStore = getStore(cluster, 2, 2, 1, 0);
        BaseStoreRoutingPlan routingPlan = new BaseStoreRoutingPlan(cluster, this.storeDef);
        List<Integer> replicatingNodes = routingPlan.getReplicationNodeList(aKey.get());
        // This is node 1
        Node primaryNode = Iterables.get(cluster.getNodes(), replicatingNodes.get(0));
        // This is node 6
        Node secondaryNode = Iterables.get(cluster.getNodes(), replicatingNodes.get(1));

        // Disable primary node so that the first put happens with 6 as the
        // pseudo master
        recordException(failureDetector, primaryNode);
        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());
        store.put(aKey, new Versioned<byte[]>(aValue), null);

        byte[] anotherValue = "john".getBytes();

        /*
         * Disable the secondary node and enable primary node to prevent the
         * secondary from getting the new version
         */
        recordException(failureDetector, secondaryNode);
        recordSuccess(failureDetector, primaryNode);
        // Generate the clock based off secondary so that the resulting clock
        // will be [1:1, 6:1] across the replicas, except for the secondary
        // which will be [6:1]
        VectorClock clock = getClock(6);
        store.put(aKey, new Versioned<byte[]>(anotherValue, clock), null);

        // Enable secondary and disable primary, the following get should cause
        // a read repair on the secondary in the code path that is only executed
        // if there are failures. This should repair the secondary with the
        // superceding clock [1:1,6:1]
        recordException(failureDetector, primaryNode);
        recordSuccess(failureDetector, secondaryNode);
        List<Versioned<byte[]>> versioneds = store.get(aKey, null);
        assertEquals(1, versioneds.size());
        assertEquals(new ByteArray(anotherValue), new ByteArray(versioneds.get(0).getValue()));

        // Read repairs are done asynchronously, so we sleep for a short period.
        // It may be a good idea to use a synchronous executor service.
        Thread.sleep(500);
        for(Map.Entry<Integer, Store<ByteArray, byte[], byte[]>> innerStoreEntry: routedStore.getInnerStores()
                                                                                             .entrySet()) {
            // Only look at the nodes in the pref list
            if(replicatingNodes.contains(innerStoreEntry.getKey())) {
                List<Versioned<byte[]>> innerVersioneds = innerStoreEntry.getValue()
                                                                         .get(aKey, null);
                assertEquals(1, versioneds.size());
                assertEquals(new ByteArray(anotherValue), new ByteArray(innerVersioneds.get(0)
                                                                                       .getValue()));
            }
        }
    }

    /**
     * Test to ensure that read repair happens correctly across zones in case of
     * inconsistent writes in a 3 zone cluster.
     *
     * @throws Exception
     */
    @Test
    public void testReadRepairWithFailuresZZZ() throws Exception {
        cluster = VoldemortTestConstants.getSixNodeClusterWith3Zones();
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // PR = RR = 6
        // PW = RW = 4
        // Zone Reads = # Zones - 1
        // Zone Writes = 1
        // Threads = 1
        RoutedStore routedStore = getStore(cluster,
                                           6,
                                           4,
                                           cluster.getNumberOfZones() - 1,
                                           1,
                                           1,
                                           zoneReplicationFactor);

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());

        BaseStoreRoutingPlan routingPlan = new BaseStoreRoutingPlan(cluster, this.storeDef);
        List<Integer> replicatingNodes = routingPlan.getReplicationNodeList(aKey.get());

        try {
            // Do the initial put with all nodes up
            store.put(aKey, new Versioned<byte[]>(aValue), null);

            List<Version> initialVersions = store.getVersions(aKey);
            assertEquals(6, initialVersions.size());

            Version mainVersion = initialVersions.get(0);
            for(int i = 1; i < initialVersions.size(); i++) {
                assertEquals(mainVersion, initialVersions.get(i));
            }

            // Do another put with all nodes in the zone 0 marked as
            // unavailable. This will force the put to use a different pseudo
            // master than before.
            byte[] anotherValue = "john".getBytes();

            // In this cluster, nodes 0 and 1 are in Zone 0. Mark them
            // unavailable
            recordException(failureDetector, cluster.getNodeById(0));
            recordException(failureDetector, cluster.getNodeById(1));
            Version newVersion = ((VectorClock) mainVersion).clone();
            store.put(aKey, new Versioned<byte[]>(anotherValue, newVersion), null);

            waitForOperationToComplete(500);

            // Mark the nodes in Zone 0 as available and do a get. The Required
            // reads = 4 and Zone count reads = 2 will force the client to read
            // from all the zones and do the essential read repairs.
            recordSuccess(failureDetector, cluster.getNodeById(0));
            recordSuccess(failureDetector, cluster.getNodeById(1));
            List<Versioned<byte[]>> versioneds = store.get(aKey, null);
            assertEquals(1, versioneds.size());
            assertEquals(new ByteArray(anotherValue), new ByteArray(versioneds.get(0).getValue()));

            // Read repairs are done asynchronously, so we sleep for a short
            // period. It may be a good idea to use a synchronous executor
            // service.
            Thread.sleep(500);
            for(Map.Entry<Integer, Store<ByteArray, byte[], byte[]>> innerStoreEntry: routedStore.getInnerStores()
                                                                                                 .entrySet()) {
                // Only look at the nodes in the pref list
                if(replicatingNodes.contains(innerStoreEntry.getKey())) {
                    List<Versioned<byte[]>> innerVersioneds = innerStoreEntry.getValue().get(aKey,
                                                                                             null);
                    assertEquals(1, versioneds.size());
                    assertEquals(new ByteArray(anotherValue),
                                 new ByteArray(innerVersioneds.get(0).getValue()));
                }
            }

        } catch(VoldemortException ve) {
            fail("Unexpected error occurred : " + ve);
        }
    }

    /**
     * See issue #134: RoutedStore put() doesn't wait for enough attempts to
     * succeed
     *
     * This issue would only happen with one node down and another that was slow
     * to respond.
     */
    @Test
    public void testPutWithOneNodeDownAndOneNodeSlow() throws Exception {
        cluster = VoldemortTestConstants.getThreeNodeCluster();
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               3,
                                                               2,
                                                               2,
                                                               2,
                                                               2,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        /* The key used causes the nodes selected for writing to be [2, 0, 1] */
        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        int id1 = Iterables.get(cluster.getNodes(), 0).getId();
        int id2 = Iterables.get(cluster.getNodes(), 1).getId();
        int id3 = Iterables.get(cluster.getNodes(), 2).getId();

        subStores.put(id3, new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
        subStores.put(id1, new FailingStore<ByteArray, byte[], byte[]>("test"));
        /*
         * The bug would only show itself if the second successful required
         * write was slow (but still within the timeout).
         */
        subStores.put(id2,
                      new SleepyStore<ByteArray, byte[], byte[]>(100,
                                                                 new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test")));

        setFailureDetector(subStores);

        routedStoreThreadPool = Executors.newFixedThreadPool(1);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(BANNAGE_PERIOD));

        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());
        store.put(aKey, new Versioned<byte[]>(aValue), aTransform);
    }

    /**
     * See issue #134: RoutedStore put() doesn't wait for enough attempts to
     * succeed
     *
     * This issue would only happen with one node down and another that was slow
     * to respond in a 3 zone cluster.
     */
    @Test
    public void testPutWithOneNodeDownAndOneNodeSlowZZZ() throws Exception {
        cluster = VoldemortTestConstants.getSixNodeClusterWith3Zones();
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(1, cluster.getNumberOfNodesInZone(0));
        zoneReplicationFactor.put(2, cluster.getNumberOfNodesInZone(0));

        // The replication set for aKey is [1, 2, 0, 3, 5, 4]
        // As per problem statement, set node 2 as the failing node and node 0
        // as the sleepy node

        // PR = RR = 4
        // PW = RW = 4
        // Zone Reads = 0
        // Zone Writes = 0
        // Failing nodes = Node 2
        // Sleepy node = Node 0
        // Threads = 4
        RoutedStore routedStore = getStore(cluster,
                                           4,
                                           4,
                                           0,
                                           0,
                                           4,
                                           Sets.newHashSet(2),
                                           Sets.newHashSet(0),
                                           zoneReplicationFactor,
                                           RoutingStrategyType.ZONE_STRATEGY,
                                           0,
                                           BANNAGE_PERIOD,
                                           new VoldemortException());
        Store<ByteArray, byte[], byte[]> store = new InconsistencyResolvingStore<ByteArray, byte[], byte[]>(routedStore,
                                                                                                            new VectorClockInconsistencyResolver<byte[]>());

        try {
            store.put(aKey, new Versioned<byte[]>(aValue), aTransform);
        } catch(VoldemortException ve) {
            fail("Unknown exception occurred : " + ve);
        }
    }

    @Test
    public void testPutTimeout() throws Exception {
        int timeout = 50;
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
                                                                 .setReplicationFactor(3)
                                                                 .setPreferredReads(3)
                                                                 .setRequiredReads(3)
                                                                 .setPreferredWrites(3)
                                                                 .setRequiredWrites(3)
                                                                 .build();
        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        int totalDelay = 0;
        for(int i = 0; i < 3; i++) {
            int delay = 4 + i * timeout;
            totalDelay += delay;
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(delay,
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);
            nodes.add(new Node(i, "none", 0, 0, 0, partitions));
        }

        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeout));

        long start = System.nanoTime();
        try {
            routedStore.put(new ByteArray("test".getBytes()),
                            new Versioned<byte[]>(new byte[] { 1 }),
                            null);
            fail("Should have thrown");
        } catch(InsufficientOperationalNodesException e) {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + totalDelay, elapsed < totalDelay);
        }
    }

    @Test
    public void testGetTimeout() throws Exception {
        int timeout = 50;
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
                                                                 .setReplicationFactor(3)
                                                                 .setPreferredReads(3)
                                                                 .setRequiredReads(3)
                                                                 .setPreferredWrites(3)
                                                                 .setRequiredWrites(3)
                                                                 .build();
        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        int totalDelay = 0;
        for(int i = 0; i < 3; i++) {
            int delay = 4 + i * timeout;
            totalDelay += delay;
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(delay,
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);
            nodes.add(new Node(i, "none", 0, 0, 0, partitions));
        }

        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeout));

        long start = System.nanoTime();
        try {
            routedStore.get(new ByteArray("test".getBytes()), null);
            fail("Should have thrown");
        } catch(InsufficientOperationalNodesException e) {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + totalDelay, elapsed < totalDelay);
        }
    }

    @Test
    public void testGetAndPutTimeoutZZZ() throws Exception {
        int timeout = 50;

        // Set replication factors for a 3 zone cluster
        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, 1);
        zoneReplicationFactor.put(1, 1);
        zoneReplicationFactor.put(2, 1);

        // Create a store with RF=3, Required reads = 3 and zone count reads = 2
        // This ensures that a GET operation requires a response from all 3
        // nodes (from the respective 3 zones)
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.ZONE_STRATEGY)
                                                                 .setHintedHandoffStrategy(HintedHandoffStrategyType.PROXIMITY_STRATEGY)
                                                                 .setReplicationFactor(3)
                                                                 .setPreferredReads(3)
                                                                 .setRequiredReads(3)
                                                                 .setPreferredWrites(3)
                                                                 .setRequiredWrites(3)
                                                                 .setZoneCountReads(2)
                                                                 .setZoneCountWrites(2)
                                                                 .setZoneReplicationFactor(zoneReplicationFactor)
                                                                 .build();

        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        int totalDelay = 0;
        for(int i = 0; i < 3; i++) {
            int delay = 4 + i * timeout;
            totalDelay += delay;
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(delay,
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);
            nodes.add(new Node(i, "none", 0, 0, 0, i, partitions));
        }

        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);
        RoutedStoreFactory routedStoreFactory = createFactory();

        List<Zone> zones = Lists.newArrayList();

        for(int i = 0; i < 3; i++) {
            LinkedList<Integer> zoneProximityList = Lists.newLinkedList();
            Set<Integer> zoneIds = Sets.newHashSet(0, 1, 2);
            zoneIds.remove(i);
            zoneProximityList.addAll(zoneIds);
            zones.add(new Zone(i, zoneProximityList));
        }

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes, zones),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeout));

        long start = System.nanoTime();
        try {
            routedStore.get(new ByteArray("test".getBytes()), null);
            fail("Should have thrown");
        } catch(InsufficientOperationalNodesException e) {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + totalDelay, elapsed < totalDelay);
        }

        start = System.nanoTime();
        try {
            routedStore.put(new ByteArray("test".getBytes()),
                            new Versioned<byte[]>(new byte[] { 1 }),
                            null);
            fail("Should have thrown");
        } catch(InsufficientOperationalNodesException e) {
            long elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + totalDelay, elapsed < totalDelay);
        }
    }

    @Test
    public void testOperationSpecificTimeouts() throws Exception {
        StoreDefinition definition = new StoreDefinitionBuilder().setName("test")
                                                                 .setType("foo")
                                                                 .setKeySerializer(new SerializerDefinition("test"))
                                                                 .setValueSerializer(new SerializerDefinition("test"))
                                                                 .setRoutingPolicy(RoutingTier.CLIENT)
                                                                 .setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
                                                                 .setReplicationFactor(3)
                                                                 .setPreferredReads(3)
                                                                 .setRequiredReads(3)
                                                                 .setPreferredWrites(3)
                                                                 .setRequiredWrites(3)
                                                                 .build();
        Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
        List<Node> nodes = new ArrayList<Node>();
        for(int i = 0; i < 3; i++) {
            Store<ByteArray, byte[], byte[]> store = new SleepyStore<ByteArray, byte[], byte[]>(200,
                                                                                                new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            stores.put(i, store);
            List<Integer> partitions = Arrays.asList(i);
            nodes.add(new Node(i, "none", 0, 0, 0, partitions));
        }

        setFailureDetector(stores);

        routedStoreThreadPool = Executors.newFixedThreadPool(3);
        // with a 500ms general timeout and a 100ms get timeout, only get should
        // fail
        TimeoutConfig timeoutConfig = new TimeoutConfig(1500, false);
        timeoutConfig.setOperationTimeout(VoldemortOpCode.GET_OP_CODE, 100);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(new Cluster("test", nodes),
                                                            definition,
                                                            stores,
                                                            failureDetector,
                                                            createConfig(timeoutConfig));
        try {
            routedStore.put(new ByteArray("test".getBytes()),
                            new Versioned<byte[]>(new byte[] { 1 }),
                            null);
        } catch(InsufficientOperationalNodesException e) {
            fail("Should not have failed");
        }

        try {
            routedStore.get(new ByteArray("test".getBytes()), null);
            fail("Should have thrown");
        } catch(InsufficientOperationalNodesException e) {

        }
    }

    /**
     * See Issue #211: Unnecessary read repairs during getAll with more than one
     * key
     */
    @Test
    public void testNoReadRepair() throws Exception {
        cluster = VoldemortTestConstants.getThreeNodeCluster();
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               3,
                                                               2,
                                                               1,
                                                               3,
                                                               2,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        /* We just need to keep a store from one node */
        StatTrackingStore statTrackingStore = null;
        for(int i = 0; i < 3; ++i) {
            int id = Iterables.get(cluster.getNodes(), i).getId();
            statTrackingStore = new StatTrackingStore(new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"),
                                                      null);
            subStores.put(id, statTrackingStore);

        }
        setFailureDetector(subStores);

        routedStoreThreadPool = Executors.newFixedThreadPool(1);
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(BANNAGE_PERIOD));

        ByteArray key1 = aKey;
        routedStore.put(key1, Versioned.value("value1".getBytes()), null);
        ByteArray key2 = TestUtils.toByteArray("voldemort");
        routedStore.put(key2, Versioned.value("value2".getBytes()), null);

        long putCount = statTrackingStore.getStats().getCount(Tracked.PUT);
        routedStore.getAll(Arrays.asList(key1, key2), null);
        /* Read repair happens asynchronously, so we wait a bit */
        Thread.sleep(500);
        assertEquals("put count should remain the same if there are no read repairs",
                     putCount,
                     statTrackingStore.getStats().getCount(Tracked.PUT));
    }

    @Test
    public void testTardyResponsesNotIncludedInResult() throws Exception {
        cluster = VoldemortTestConstants.getThreeNodeCluster();
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               3,
                                                               3,
                                                               2,
                                                               3,
                                                               1,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        int sleepTimeMs = 500;
        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        for(Node node: cluster.getNodes()) {
            Store<ByteArray, byte[], byte[]> store = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test");

            if(subStores.isEmpty()) {
                store = new SleepyStore<ByteArray, byte[], byte[]>(sleepTimeMs, store);
            }

            subStores.put(node.getId(), store);
        }

        setFailureDetector(subStores);

        routedStoreThreadPool = Executors.newFixedThreadPool(cluster.getNumberOfNodes());
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(10000L));

        routedStore.put(aKey, Versioned.value(aValue), null);

        routedStoreFactory = createFactory();

        routedStore = routedStoreFactory.create(cluster,
                                                storeDef,
                                                subStores,
                                                failureDetector,
                                                createConfig(sleepTimeMs / 2));

        List<Versioned<byte[]>> versioneds = routedStore.get(aKey, null);
        assertEquals(2, versioneds.size());

        // Let's make sure that if the response *does* come in late, that it
        // doesn't alter the return value.
        Thread.sleep(sleepTimeMs * 2);
        assertEquals(2, versioneds.size());
    }

    @Test
    public void testSlowStoreDowngradesFromPreferredToRequired() throws Exception {
        cluster = VoldemortTestConstants.getThreeNodeCluster();
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               3,
                                                               3,
                                                               2,
                                                               3,
                                                               1,
                                                               RoutingStrategyType.CONSISTENT_STRATEGY);

        int sleepTimeMs = 500;
        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();

        for(Node node: cluster.getNodes()) {
            Store<ByteArray, byte[], byte[]> store = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test");

            if(subStores.isEmpty()) {
                store = new SleepyStore<ByteArray, byte[], byte[]>(sleepTimeMs, store);
            }

            subStores.put(node.getId(), store);
        }

        setFailureDetector(subStores);

        routedStoreThreadPool = Executors.newFixedThreadPool(cluster.getNumberOfNodes());
        RoutedStoreFactory routedStoreFactory = createFactory();

        RoutedStore routedStore = routedStoreFactory.create(cluster,
                                                            storeDef,
                                                            subStores,
                                                            failureDetector,
                                                            createConfig(10000L));

        routedStore.put(aKey, Versioned.value(aValue), null);

        routedStoreFactory = createFactory();

        routedStore = routedStoreFactory.create(cluster,
                                                storeDef,
                                                subStores,
                                                failureDetector,
                                                createConfig(sleepTimeMs / 2));

        List<Versioned<byte[]>> versioneds = routedStore.get(aKey, null);
        assertEquals(2, versioneds.size());
    }

    @Test
    public void testPutDeleteZoneRouting() throws Exception {
        cluster = VoldemortTestConstants.getEightNodeClusterWithZones();

        HashMap<Integer, Integer> zoneReplicationFactor = Maps.newHashMap();
        zoneReplicationFactor.put(0, 2);
        zoneReplicationFactor.put(1, 2);

        Versioned<byte[]> versioned = new Versioned<byte[]>(new byte[] { 1 });

        Map<Integer, Store<ByteArray, byte[], byte[]>> subStores = Maps.newHashMap();
        Set<Integer> sleepy = Sets.newHashSet(4, 5, 6, 7);
        for(Node n: cluster.getNodes()) {
            Store<ByteArray, byte[], byte[]> subStore = null;

            if(sleepy != null && sleepy.contains(n.getId()))
                subStore = new SleepyStore<ByteArray, byte[], byte[]>(SLEEPY_TIME,
                                                                      new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test"));
            else
                subStore = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test");

            subStores.put(n.getId(), subStore);
        }

        setFailureDetector(subStores);
        StoreDefinition storeDef = ServerTestUtils.getStoreDef("test",
                                                               1,
                                                               1,
                                                               1,
                                                               1,
                                                               0,
                                                               0,
                                                               zoneReplicationFactor,
                                                               HintedHandoffStrategyType.PROXIMITY_STRATEGY,
                                                               RoutingStrategyType.ZONE_STRATEGY);
        routedStoreThreadPool = Executors.newFixedThreadPool(8);
        RoutedStoreFactory routedStoreFactory = createFactory();

        Store<ByteArray, byte[], byte[]> s1 = routedStoreFactory.create(cluster,
                                                                        storeDef,
                                                                        subStores,
                                                                        failureDetector,
                                                                        createConfig(OPERATION_TIMEOUT));

        RoutingStrategy routingStrategy = new RoutingStrategyFactory().updateRoutingStrategy(storeDef,
                                                                                             cluster);

        List<Node> nodesRoutedTo = routingStrategy.routeRequest("test".getBytes());
        long start = System.nanoTime(), elapsed;
        try {
            s1.put(new ByteArray("test".getBytes()), versioned, null);
        } finally {
            elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        Thread.sleep(SLEEPY_TIME - elapsed);

        for(Node node: nodesRoutedTo) {
            assertEquals(subStores.get(node.getId())
                                  .get(new ByteArray("test".getBytes()), null)
                                  .get(0), versioned);
        }

        // make sure the failure detector adds back any previously failed nodes
        Thread.sleep(BANNAGE_PERIOD + 100);
        start = System.nanoTime();
        try {
            s1.delete(new ByteArray("test".getBytes()), versioned.getVersion());
        } finally {
            elapsed = (System.nanoTime() - start) / Time.NS_PER_MS;
            assertTrue(elapsed + " < " + SLEEPY_TIME, elapsed < SLEEPY_TIME);
        }

        Thread.sleep(SLEEPY_TIME - elapsed);

        for(Node node: nodesRoutedTo) {
            assertEquals(subStores.get(node.getId())
                                  .get(new ByteArray("test".getBytes()), null)
                                  .size(), 0);
        }

    }

    private void assertOperationalNodes(int expected) {
        int found = 0;
        for(Node n: cluster.getNodes())
            if(failureDetector.isAvailable(n))
                found++;
        assertEquals("Number of operational nodes not what was expected.", expected, found);
    }

    /**
     * Function to set the failure detector class.
     *
     * Note: We set this to BannagePeriodFailureDetector which is not supported
     * or recommended anymore because of the inefficiency in checking for
     * liveness and the potential issue of marking all the nodes as unavailable.
     * However, for the purpose of these tests, BannagePeriodFailureDetector is
     * useful to deterministically set a node as available or unavailable.
     *
     * @param subStores Stores used to check if node is up
     * @throws Exception
     */
    private void setFailureDetector(Map<Integer, Store<ByteArray, byte[], byte[]>> subStores)
            throws Exception {
        // Destroy any previous failure detector before creating the next one
        // (the final one is destroyed in tearDown).
        if(failureDetector != null)
            failureDetector.destroy();

        FailureDetectorConfig failureDetectorConfig = new FailureDetectorConfig().setImplementationClassName(BannagePeriodFailureDetector.class.getName())
                                                                                 .setBannagePeriod(BANNAGE_PERIOD)
                                                                                 .setCluster(cluster)
                                                                                 .setStoreVerifier(create(subStores));
        failureDetector = create(failureDetectorConfig, false);
    }
}
TOP

Related Classes of voldemort.store.routed.RoutedStoreTest

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.