Package voldemort.server.quota

Source Code of voldemort.server.quota.ExceededQuotaSlopTest

package voldemort.server.quota;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import voldemort.ServerTestUtils;
import voldemort.VoldemortException;
import voldemort.client.ClientConfig;
import voldemort.client.SocketStoreClientFactory;
import voldemort.client.StoreClient;
import voldemort.client.protocol.admin.AdminClient;
import voldemort.client.protocol.admin.AdminClientConfig;
import voldemort.cluster.Cluster;
import voldemort.routing.BaseStoreRoutingPlan;
import voldemort.server.VoldemortServer;
import voldemort.server.scheduler.slop.StreamingSlopPusherJob;
import voldemort.store.StoreDefinition;
import voldemort.store.quota.QuotaType;
import voldemort.store.quota.QuotaUtils;
import voldemort.store.routed.NodeValue;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.utils.Pair;
import voldemort.versioning.VectorClock;
import voldemort.versioning.VectorClockUtils;
import voldemort.versioning.Versioned;
import voldemort.xml.StoreDefinitionsMapper;

public class ExceededQuotaSlopTest {

    private SocketStoreClientFactory factory;
    private StoreClient<String, String> storeClient;
    private Cluster cluster;
    private AdminClient adminClient;
    private VoldemortServer[] servers = new VoldemortServer[2];
    private int partitionMap[][] = { { 0 }, { 1 } };
    private HashMap<Integer, Integer> partitionToNodeMap;
    private static final String quotaStoreName = "voldsys$_store_quotas";
    private static final String encodingType = "UTF-8";
    private static final Integer SLOP_FREQUENCY_MS = 5000;
    private final Logger logger = Logger.getLogger(ExceededQuotaSlopTest.class);

    String storesxml = "test/common/voldemort/config/single-store-with-handoff-strategy.xml";
    String storeName = "test";

    @Before
    public void setup() throws IOException {
        fillPartitionsToNodeMap();
        startVoldemortTwoNodeCluster();
    }

    public void fillPartitionsToNodeMap() {
        partitionToNodeMap = new HashMap<Integer, Integer>();
        int partitionId = 0;
        for(int i = 0; i < servers.length; i++) {
            for(int j = 0; j < partitionMap[i].length; j++) {
                partitionToNodeMap.put(partitionId++, i);
            }
        }
    }

    public void startVoldemortTwoNodeCluster() throws IOException {
        Properties serverProperties = new Properties();
        serverProperties.setProperty("pusher.type", StreamingSlopPusherJob.TYPE_NAME);
        serverProperties.setProperty("slop.frequency.ms", SLOP_FREQUENCY_MS.toString());
        serverProperties.setProperty("auto.purge.dead.slops", "true");
        serverProperties.setProperty("enable.quota.limiting", "true");
        cluster = ServerTestUtils.startVoldemortCluster(servers,
                                                        partitionMap,
                                                        serverProperties,
                                                        storesxml);
    }

    public void setGetPutQuotasForEachServer() throws Exception {
        Properties adminProperties = new Properties();
        adminProperties.setProperty("max_connections", "2");
        adminClient = new AdminClient(cluster,
                                      new AdminClientConfig(adminProperties),
                                      new ClientConfig());

        Map<Pair<Integer, QuotaType>, Integer> throughPutMap = new HashMap<Pair<Integer, QuotaType>, Integer>();
        // Set Node0 Quota
        throughPutMap.put(new Pair<Integer, QuotaType>(0, QuotaType.PUT_THROUGHPUT), 5);
        throughPutMap.put(new Pair<Integer, QuotaType>(0, QuotaType.GET_THROUGHPUT), 20);

        // Set Node1 Quota
        throughPutMap.put(new Pair<Integer, QuotaType>(1, QuotaType.PUT_THROUGHPUT), 2);
        throughPutMap.put(new Pair<Integer, QuotaType>(1, QuotaType.GET_THROUGHPUT), 20);

        for(Entry<Pair<Integer, QuotaType>, Integer> throughPut: throughPutMap.entrySet()) {

            int nodeId = throughPut.getKey().getFirst();
            QuotaType type = throughPut.getKey().getSecond();
            int value = throughPut.getValue();

            VectorClock clock = VectorClockUtils.makeClockWithCurrentTime(cluster.getNodeIds());
            NodeValue<ByteArray, byte[]> operationValue = new NodeValue<ByteArray, byte[]>(nodeId,
                                                                                           new ByteArray(getKeyBytes(type)),
                                                                                           new Versioned<byte[]>(ByteUtils.getBytes(Integer.toString(value),
                                                                                                                                    encodingType),
                                                                                                                 clock));
            try {
                adminClient.storeOps.putNodeKeyValue(quotaStoreName, operationValue);
            } catch(Exception e) {
                throw new Exception("Exception when setting put quota for node " + nodeId
                                    + " Operation " + type + "." + e.getMessage());
            }
        }
    }

    private byte[] getKeyBytes(QuotaType quotaType) {
        return ByteUtils.getBytes(QuotaUtils.makeQuotaKey(storeName,
                                                          QuotaType.valueOf(quotaType.toString())),
                                  encodingType);
    }

    private HashMap<String, String> generateKeysForMasterNode(int numKeys) throws IOException {
        // Assumes master node is 0 and generates keys that goes to master node
        HashMap<String, String> keyValuePairs = new HashMap<String, String>();
        StoreDefinitionsMapper storedDefMapper = new StoreDefinitionsMapper();
        List<StoreDefinition> storeDefs = storedDefMapper.readStoreList(new File(storesxml));
        StoreDefinition testStoreDef = storeDefs.get(0);
        BaseStoreRoutingPlan baseStoreRoutingPlan = new BaseStoreRoutingPlan(cluster, testStoreDef);

        /*
         * Generating simple key values pairs of the form 3:3 where key and
         * value are same but route to the Master node's partition
         */

        int key = 0;
        int partiionId = -1;
        for(int count = 0; count < numKeys;) {
            byte[] keyBytes = Integer.toString(key).getBytes();
            partiionId = baseStoreRoutingPlan.getMasterPartitionId(keyBytes);
            if(partitionToNodeMap.get(partiionId) == 0) {
                keyValuePairs.put(String.valueOf(key), String.valueOf(key));
                count++;
            }
            key++;
        }
        return keyValuePairs;
    }

    @Test
    public void testAsyncWritesSloppedOnQuotaExceeed() throws Exception {

        // Set quotas on each server
        setGetPutQuotasForEachServer();

        /**
         * Look at the comment on this method
         *
         * @see voldemort.utils.pool.QueuedKeyedResourcePool.processQueueLoop(K)
         *      to see why bumping the number generateKeysForMasterNode will
         *      fail this test.
         */

        // This test is non-deterministic.
        // 1) The QuotaException is thrown by SerialPut, but parallelPut ignores
        // QuotaException and throws InsufficientOperationalNodesException
        // as the QuotaExceptions are silently (warning logs) eaten away.
        // 2) When you increase the key/value pairs beyond 100, slops start
        // randomly failing as there are only 2 nodes and backlog of slops on
        // other node causes the slop to be dropped
        // But when you set this <= 100, no Put receives a QuotaException

        // Correct way is creating a mock SocketStore which can inject
        // failures of QuotaException and test the pipeline actions and handling
        // by node. The Server side needs a mock time where you can freeze the
        // time and see if it fails after the quota. But saving these ones for
        // later.

        HashMap<String, String> keyValuePairsToMasterNode = generateKeysForMasterNode(100);

        String bootStrapUrl = "tcp://" + cluster.getNodeById(0).getHost() + ":"
                              + cluster.getNodeById(0).getSocketPort();
        factory = new SocketStoreClientFactory(new ClientConfig().setBootstrapUrls(bootStrapUrl));
        storeClient = factory.getStoreClient(storeName);

        int numPutExceptions = 0;

        ArrayList<String> putKeysThatSucceeded = new ArrayList<String>();

        for(Entry<String, String> entry: keyValuePairsToMasterNode.entrySet()) {
            try {
                // do a put on node 0
                storeClient.put(entry.getKey(), entry.getValue());
                putKeysThatSucceeded.add(entry.getValue());
            } catch(VoldemortException e) {
                logger.warn(e, e);
                numPutExceptions++;
            }
        }

        logger.info("#Puts that failed due to Exception: " + numPutExceptions);

        // wait for the slop pushed to finish its job
        Thread.sleep(2 * SLOP_FREQUENCY_MS);

        // check the secondary node (node id:1) to see if all successful put
        // keys exist
        for(String val: putKeysThatSucceeded) {
            int nodeId = 1;
            // do a get on node 1
            List<Versioned<byte[]>> valueBytes = adminClient.storeOps.getNodeKey(storeName,
                                                                                 nodeId,
                                                                                 new ByteArray(ByteUtils.getBytes(val,
                                                                                                                  encodingType)));

            assertEquals("Expect 1 value from PUT " + val, 1, valueBytes.size());
            assertEquals("GET returned different value than put",
                         val,
                         ByteUtils.getString(valueBytes.get(0).getValue(), encodingType));
        }

        int numDeleteExceptions = 0;
        ArrayList<String> deleteKeysThatSucceeded = new ArrayList<String>();
        for(Entry<String, String> entry: keyValuePairsToMasterNode.entrySet()) {
            try {
                // do a put on node 0
                storeClient.delete(entry.getKey());
                deleteKeysThatSucceeded.add(entry.getValue());
            } catch(VoldemortException e) {
                logger.warn(e, e);
                numDeleteExceptions++;
            }
        }
        logger.info("#Deletes that failed due to Exceptions: " + numDeleteExceptions);

        // wait for the slop pushed to finish its job
        Thread.sleep(2 * SLOP_FREQUENCY_MS);

        for(String val: deleteKeysThatSucceeded) {
            for(int nodeId: cluster.getNodeIds()) {
                // do a get on node 1
                List<Versioned<byte[]>> valueBytes = adminClient.storeOps.getNodeKey(storeName,
                                                                                     nodeId,
                                                                                     new ByteArray(ByteUtils.getBytes(val,
                                                                                                                      encodingType)));

                assertTrue("Deleted value should be null or zero on node " + nodeId,
                           valueBytes == null || valueBytes.size() == 0);
            }
        }

    }

    @After
    public void tearDown() {
        for(VoldemortServer server: servers) {
            server.stop();
        }
        adminClient.close();
    }
}
TOP

Related Classes of voldemort.server.quota.ExceededQuotaSlopTest

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.