/*
* 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.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import voldemort.ROTestUtils;
import voldemort.ServerTestUtils;
import voldemort.TestUtils;
import voldemort.VoldemortException;
import voldemort.client.protocol.RequestFormatType;
import voldemort.client.protocol.admin.AdminClient;
import voldemort.client.protocol.admin.AdminClientConfig;
import voldemort.client.protocol.admin.QueryKeyResult;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.cluster.Zone;
import voldemort.routing.RoutingStrategy;
import voldemort.routing.RoutingStrategyFactory;
import voldemort.routing.RoutingStrategyType;
import voldemort.serialization.IdentitySerializer;
import voldemort.serialization.SerializerDefinition;
import voldemort.serialization.StringSerializer;
import voldemort.server.RequestRoutingType;
import voldemort.server.VoldemortServer;
import voldemort.store.InvalidMetadataException;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.store.StoreDefinitionBuilder;
import voldemort.store.bdb.BdbStorageConfiguration;
import voldemort.store.memory.InMemoryStorageConfiguration;
import voldemort.store.metadata.MetadataStore;
import voldemort.store.readonly.ReadOnlyStorageConfiguration;
import voldemort.store.readonly.ReadOnlyStorageEngine;
import voldemort.store.readonly.ReadOnlyStorageFormat;
import voldemort.store.readonly.ReadOnlyStorageMetadata;
import voldemort.store.serialized.SerializingStore;
import voldemort.store.slop.Slop;
import voldemort.store.slop.strategy.HintedHandoffStrategyType;
import voldemort.store.socket.SocketStoreFactory;
import voldemort.store.socket.clientrequest.ClientRequestExecutorPool;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.utils.Pair;
import voldemort.utils.StoreDefinitionUtils;
import voldemort.utils.UpdateClusterUtils;
import voldemort.utils.Utils;
import voldemort.versioning.VectorClock;
import voldemort.versioning.Version;
import voldemort.versioning.Versioned;
import voldemort.xml.StoreDefinitionsMapper;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
*/
@RunWith(Parameterized.class)
public class AdminServiceBasicTest {
private static int NUM_RUNS = 100;
private static int TEST_STREAM_KEYS_SIZE = 10000;
private static String testStoreName = "test-replication-memory";
private static final String STORE_NAME = "test-basic-replication-memory";
private static String storesXmlfile = "test/common/voldemort/config/stores.xml";
private SocketStoreFactory socketStoreFactory = new ClientRequestExecutorPool(2,
10000,
100000,
32 * 1024);
private static AtomicBoolean running = new AtomicBoolean(true);
private List<StoreDefinition> storeDefs;
private VoldemortServer[] servers;
private Cluster cluster;
private AdminClient adminClient;
private StoreClient<String, String> storeClient;
private final boolean useNio;
private final boolean onlineRetention;
public AdminServiceBasicTest(boolean useNio, boolean onlineRetention) {
this.useNio = useNio;
this.onlineRetention = onlineRetention;
}
@Parameters
public static Collection<Object[]> configs() {
return Arrays.asList(new Object[][] { { true, false }, { true, true }, { false, false },
{ false, true } });
}
@Before
public void setUp() throws IOException {
int numServers = 2;
servers = new VoldemortServer[numServers];
int partitionMap[][] = { { 0, 1, 2, 3 }, { 4, 5, 6, 7 } };
Properties serverProperties = new Properties();
serverProperties.setProperty("client.max.connections.per.node", "20");
serverProperties.setProperty("enforce.retention.policy.on.read",
Boolean.toString(onlineRetention));
cluster = ServerTestUtils.startVoldemortCluster(numServers,
servers,
partitionMap,
socketStoreFactory,
useNio,
null,
storesXmlfile,
serverProperties);
storeDefs = new StoreDefinitionsMapper().readStoreList(new File(storesXmlfile));
Properties adminProperties = new Properties();
adminProperties.setProperty("max_connections", "20");
adminClient = new AdminClient(cluster,
new AdminClientConfig(adminProperties),
new ClientConfig());
Node node = cluster.getNodeById(0);
String bootstrapUrl = "tcp://" + node.getHost() + ":" + node.getSocketPort();
StoreClientFactory storeClientFactory = new SocketStoreClientFactory(new ClientConfig().setBootstrapUrls(bootstrapUrl));
storeClient = storeClientFactory.getStoreClient(STORE_NAME);
}
/**
* Returns the corresponding server based on the node id
*
* @param nodeId The node id for which we're retrieving the server
* @return Voldemort server
*/
private VoldemortServer getServer(int nodeId) {
return servers[nodeId];
}
@After
public void tearDown() throws IOException {
adminClient.close();
for(VoldemortServer server: servers) {
ServerTestUtils.stopVoldemortServer(server);
}
socketStoreFactory.close();
}
private VoldemortServer getVoldemortServer(int nodeId) {
return servers[nodeId];
}
private AdminClient getAdminClient() {
return adminClient;
}
private Store<ByteArray, byte[], byte[]> getStore(int nodeID, String storeName) {
Store<ByteArray, byte[], byte[]> store = getVoldemortServer(nodeID).getStoreRepository()
.getStorageEngine(storeName);
assertNotSame("Store '" + storeName + "' should not be null", null, store);
return store;
}
private boolean isKeyPartition(ByteArray key,
int nodeId,
String storeName,
List<Integer> deletePartitionsList) {
RoutingStrategy routing = getVoldemortServer(nodeId).getMetadataStore()
.getRoutingStrategy(storeName);
for(int partition: routing.getPartitionList(key.get())) {
if(deletePartitionsList.contains(partition)) {
return true;
}
}
return false;
}
@Test
public void testUpdateClusterMetadata() {
Cluster updatedCluster = ServerTestUtils.getLocalCluster(4);
AdminClient client = getAdminClient();
for(int i = 0; i < NUM_RUNS; i++) {
VectorClock clock = ((VectorClock) client.metadataMgmtOps.getRemoteCluster(0)
.getVersion()).incremented(0,
System.currentTimeMillis());
client.metadataMgmtOps.updateRemoteCluster(0, updatedCluster, clock);
assertEquals("Cluster should match",
updatedCluster,
getVoldemortServer(0).getMetadataStore().getCluster());
assertEquals("AdminClient.getMetdata() should match",
client.metadataMgmtOps.getRemoteCluster(0).getValue(),
updatedCluster);
// version should match
assertEquals("versions should match as well.",
clock,
client.metadataMgmtOps.getRemoteCluster(0).getVersion());
}
}
/**
* Function to return the String representation of a Metadata key
* (stores.xml or an individual store)
*
* @param key specifies the metadata key to retrieve
* @return String representation of the value associated with the metadata
* key
*/
private String bootstrapMetadata(String metadataKey) {
Node serverNode = servers[0].getIdentityNode();
Store<ByteArray, byte[], byte[]> remoteStore = socketStoreFactory.create(MetadataStore.METADATA_STORE_NAME,
serverNode.getHost(),
serverNode.getSocketPort(),
RequestFormatType.VOLDEMORT_V2,
RequestRoutingType.NORMAL);
Store<String, String, byte[]> store = SerializingStore.wrap(remoteStore,
new StringSerializer("UTF-8"),
new StringSerializer("UTF-8"),
new IdentitySerializer());
List<Versioned<String>> found = store.get(metadataKey, null);
assertEquals(found.size(), 1);
String valueStr = found.get(0).getValue();
return valueStr;
}
/**
* Function to retrieve the set of store names contained in the given list
* of store definitions. This is used for comparing two store lists by only
* their names
*
* @param defs list of store definitions
* @return set of store names contained in the given list of store
* definitions
*/
private Set<String> getStoreNames(List<StoreDefinition> defs) {
Set<String> storeNameSet = new HashSet<String>();
for(StoreDefinition def: defs) {
storeNameSet.add(def.getName());
}
return storeNameSet;
}
private void doClientOperation() {
for(int i = 0; i < 100; i++) {
String key = "key-" + System.currentTimeMillis();
String value = "Value for " + key;
this.storeClient.put(key, value);
String returnedValue = this.storeClient.getValue(key);
assertEquals(returnedValue, value);
}
}
@Test
public void testFetchSingleStoreFromMetadataStore() throws Exception {
String storeName = "test-replication-memory";
String storeDefStr = bootstrapMetadata(storeName);
StoreDefinitionsMapper mapper = new StoreDefinitionsMapper();
List<StoreDefinition> storeDefList = mapper.readStoreList(new StringReader(storeDefStr));
assertEquals(storeDefList.size(), 1);
StoreDefinition storeDef = storeDefList.get(0);
assertEquals(storeDef.getName(), storeName);
}
@Test
public void testFetchAllStoresFromMetadataStore() throws Exception {
String storeName = MetadataStore.STORES_KEY;
String storeDefStr = bootstrapMetadata(storeName);
StoreDefinitionsMapper mapper = new StoreDefinitionsMapper();
List<StoreDefinition> storeDefList = mapper.readStoreList(new StringReader(storeDefStr));
assertEquals(storeDefList.size(), this.storeDefs.size());
Set<String> receivedStoreNames = getStoreNames(storeDefList);
Set<String> originalStoreNames = getStoreNames(this.storeDefs);
assertEquals(receivedStoreNames, originalStoreNames);
}
/**
* Function to update the given stores and then reset the stores.xml back to
* its original state. This is used for confirming that the updates only
* affect the specified stores in the server. Rest of the stores remain
* untouched.
*
* @param storesToBeUpdatedList specifies list of stores to be updated
*/
private void updateAndResetStoreDefinitions(List<StoreDefinition> storesToBeUpdatedList) {
// Track the names of the stores to be updated
Set<String> storesNamesToBeUpdated = getStoreNames(storesToBeUpdatedList);
// Keep track of the original store definitions for the specific stores
// about to be updated
List<StoreDefinition> originalStoreDefinitionsList = new ArrayList<StoreDefinition>();
for(StoreDefinition def: this.storeDefs) {
if(storesNamesToBeUpdated.contains(def.getName())) {
originalStoreDefinitionsList.add(def);
}
}
// Update the definitions on all the nodes
AdminClient adminClient = getAdminClient();
adminClient.metadataMgmtOps.updateRemoteStoreDefList(storesToBeUpdatedList);
// Retrieve stores list and check that other definitions are unchanged
String allStoresDefStr = bootstrapMetadata(MetadataStore.STORES_KEY);
StoreDefinitionsMapper mapper = new StoreDefinitionsMapper();
List<StoreDefinition> storeDefList = mapper.readStoreList(new StringReader(allStoresDefStr));
assertEquals(storeDefList.size(), this.storeDefs.size());
// Insert original stores in the map
Map<String, StoreDefinition> storeNameToDefMap = new HashMap<String, StoreDefinition>();
for(StoreDefinition def: this.storeDefs) {
storeNameToDefMap.put(def.getName(), def);
}
// Now validate the received definitions. Only the updated store
// definition should be different. Everything else should be as is
for(StoreDefinition def: storeDefList) {
if(!storesNamesToBeUpdated.contains(def.getName())) {
assertEquals(def, storeNameToDefMap.get(def.getName()));
}
}
// Reset the store definition back to original
adminClient.metadataMgmtOps.updateRemoteStoreDefList(originalStoreDefinitionsList,
this.cluster.getNodeIds());
}
@Test
public void testFetchSingleStoreFromAdminClient() {
String storeName = "test-replication-memory";
StoreDefinitionsMapper mapper = new StoreDefinitionsMapper();
for(int nodeId: this.cluster.getNodeIds()) {
Versioned<String> storeDef = adminClient.metadataMgmtOps.getRemoteMetadata(nodeId,
storeName);
List<StoreDefinition> def = mapper.readStoreList(new StringReader(storeDef.getValue()));
assertEquals(def.get(0).getName(), storeName);
}
}
@Test
public void testUpdateSingleStore() {
doClientOperation();
// Create a store definition for an existing store with a different
// replication factor
List<StoreDefinition> storesToBeUpdatedList = new ArrayList<StoreDefinition>();
String storeName = "test-replication-memory";
StoreDefinition definitionNew = new StoreDefinitionBuilder().setName(storeName)
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(2)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
storesToBeUpdatedList.add(definitionNew);
updateAndResetStoreDefinitions(storesToBeUpdatedList);
doClientOperation();
}
@Test
public void testUpdateMultipleStores() {
doClientOperation();
// Create store definitions for existing stores with a different
// replication factor
List<StoreDefinition> storesToBeUpdatedList = new ArrayList<StoreDefinition>();
StoreDefinition definition1 = new StoreDefinitionBuilder().setName("test-replication-memory")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(2)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
StoreDefinition definition2 = new StoreDefinitionBuilder().setName("test-recovery-data")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(1)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
StoreDefinition definition3 = new StoreDefinitionBuilder().setName("test-basic-replication-memory")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(2)
.setPreferredReads(2)
.setRequiredReads(2)
.setPreferredWrites(2)
.setRequiredWrites(2)
.build();
storesToBeUpdatedList.add(definition1);
storesToBeUpdatedList.add(definition2);
storesToBeUpdatedList.add(definition3);
updateAndResetStoreDefinitions(storesToBeUpdatedList);
doClientOperation();
}
@Test
public void testFetchAndUpdateStoresMetadata() {
AdminClient client = getAdminClient();
int nodeId = 0;
String storeNameToBeUpdated = "users";
doClientOperation();
// Fetch the original list of stores
Versioned<List<StoreDefinition>> originalStoreDefinitions = client.metadataMgmtOps.getRemoteStoreDefList(nodeId);
List<StoreDefinition> updatedStoreDefList = new ArrayList<StoreDefinition>();
// Create an updated store definition for store: 'users'
StoreDefinition newDefinition = new StoreDefinitionBuilder().setName(storeNameToBeUpdated)
.setType(BdbStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(2)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(2)
.setRequiredWrites(2)
.build();
updatedStoreDefList.add(newDefinition);
// Update the 'users' store
client.metadataMgmtOps.fetchAndUpdateRemoteStore(nodeId, updatedStoreDefList);
// Fetch the stores list again and check that the 'users' store has been
// updated
Versioned<List<StoreDefinition>> newStoreDefinitions = client.metadataMgmtOps.getRemoteStoreDefList(nodeId);
assertFalse(originalStoreDefinitions.getValue().equals(newStoreDefinitions.getValue()));
for(StoreDefinition def: newStoreDefinitions.getValue()) {
if(def.getName().equalsIgnoreCase(storeNameToBeUpdated)) {
assertTrue(def.equals(newDefinition));
}
}
// Restore the old set of store definitions
client.metadataMgmtOps.updateRemoteStoreDefList(nodeId, originalStoreDefinitions.getValue());
doClientOperation();
}
@Test
public void testAddStore() throws Exception {
AdminClient adminClient = getAdminClient();
doClientOperation();
// Try to add a store whose replication factor is greater than the
// number of nodes
StoreDefinition definition = new StoreDefinitionBuilder().setName("updateTest")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(3)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
try {
adminClient.storeMgmtOps.addStore(definition);
fail("Should have thrown an exception because we cannot add a store with a replication factor greater than number of nodes");
} catch(Exception e) {}
// Try adding a legit store using inmemorystorage engine
definition = new StoreDefinitionBuilder().setName("updateTest")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(1)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
adminClient.storeMgmtOps.addStore(definition);
// now test the store
StoreClientFactory factory = new SocketStoreClientFactory(new ClientConfig().setBootstrapUrls(cluster.getNodeById(0)
.getSocketUrl()
.toString()));
StoreClient<Object, Object> client = factory.getStoreClient("updateTest");
client.put("abc", "123");
String s = (String) client.get("abc").getValue();
assertEquals(s, "123");
// test again with a unknown store
try {
client = factory.getStoreClient("updateTest2");
client.put("abc", "123");
s = (String) client.get("abc").getValue();
assertEquals(s, "123");
fail("Should have received bootstrap failure exception");
} catch(Exception e) {
if(!(e instanceof BootstrapFailureException))
throw e;
}
// make sure that the store list we get back from AdminClient
Versioned<List<StoreDefinition>> list = adminClient.metadataMgmtOps.getRemoteStoreDefList(0);
assertTrue(list.getValue().contains(definition));
// Now add a RO store
definition = new StoreDefinitionBuilder().setName("addStoreROFormatTest")
.setType(ReadOnlyStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(1)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
adminClient.storeMgmtOps.addStore(definition);
// Retrieve list of read-only stores
List<String> storeNames = Lists.newArrayList();
for(StoreDefinition storeDef: adminClient.metadataMgmtOps.getRemoteStoreDefList(0)
.getValue()) {
if(storeDef.getType().compareTo(ReadOnlyStorageConfiguration.TYPE_NAME) == 0) {
storeNames.add(storeDef.getName());
}
}
Map<String, String> storeToStorageFormat = adminClient.readonlyOps.getROStorageFormat(0,
storeNames);
for(String storeName: storeToStorageFormat.keySet()) {
assertEquals(storeToStorageFormat.get(storeName), "ro2");
}
doClientOperation();
}
@Test
public void testReplicationMapping() {
List<Zone> zones = ServerTestUtils.getZones(2);
List<Node> nodes = Lists.newArrayList();
nodes.add(new Node(0, "localhost", 1, 2, 3, 0, Lists.newArrayList(0, 4, 8)));
nodes.add(new Node(1, "localhost", 1, 2, 3, 0, Lists.newArrayList(1, 5, 9)));
nodes.add(new Node(2, "localhost", 1, 2, 3, 1, Lists.newArrayList(2, 6, 10)));
nodes.add(new Node(3, "localhost", 1, 2, 3, 1, Lists.newArrayList(3, 7, 11)));
// Test 0 - With rep-factor 1
StoreDefinition storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
Cluster newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(0, newCluster, storeDef);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// Test 1 - With consistent routing strategy
storeDef = ServerTestUtils.getStoreDef("consistent",
2,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
// On node 0
Map<Integer, List<Integer>> replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(1, Lists.newArrayList(0, 4, 8));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 1
replicationMapping = adminClient.replicaOps.getReplicationMapping(1,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8));
expectedMapping.put(2, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 2
replicationMapping = adminClient.replicaOps.getReplicationMapping(2,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
expectedMapping.put(3, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(3, 7, 11));
expectedMapping.put(2, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
// Test 2 - With zone routing strategy
HashMap<Integer, Integer> zoneReplicationFactors = Maps.newHashMap();
for(int zoneIds = 0; zoneIds < 2; zoneIds++) {
zoneReplicationFactors.put(zoneIds, 1);
}
storeDef = ServerTestUtils.getStoreDef("zone",
2,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
// On node 0
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(2, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 1
replicationMapping = adminClient.replicaOps.getReplicationMapping(1,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(2, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 2
replicationMapping = adminClient.replicaOps.getReplicationMapping(2,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// Test 3 - Consistent with rep factor 3
storeDef = ServerTestUtils.getStoreDef("consistent",
3,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
newCluster = new Cluster("single_zone_cluster", nodes, zones);
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(1, Lists.newArrayList(0, 4, 8));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
expectedMapping.put(2, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(1,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
expectedMapping.put(2, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(2,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8));
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
expectedMapping.put(3, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(3, 7, 11));
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
expectedMapping.put(2, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
zoneReplicationFactors = Maps.newHashMap();
for(int zoneIds = 0; zoneIds < 2; zoneIds++) {
zoneReplicationFactors.put(zoneIds, 2);
}
storeDef = ServerTestUtils.getStoreDef("zone",
1,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(1, Lists.newArrayList(0, 4, 8, 1, 5, 9));
expectedMapping.put(2, Lists.newArrayList(2, 6, 10));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(1,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8));
expectedMapping.put(2, Lists.newArrayList(1, 5, 9, 2, 6, 10));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(2,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8));
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
expectedMapping.put(3, Lists.newArrayList(2, 6, 10, 3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(0, Lists.newArrayList(0, 4, 8, 3, 7, 11));
expectedMapping.put(1, Lists.newArrayList(1, 5, 9));
expectedMapping.put(2, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
}
@Test
public void testReplicationMappingWithNonContiguousZones() {
int[] zoneIds = { 1, 3 };
List<Zone> zones = ServerTestUtils.getZonesFromZoneIds(zoneIds);
List<Node> nodes = Lists.newArrayList();
nodes.add(new Node(3, "localhost", 1, 2, 3, 1, Lists.newArrayList(0, 4, 8)));
nodes.add(new Node(4, "localhost", 1, 2, 3, 1, Lists.newArrayList(1, 5, 9)));
nodes.add(new Node(5, "localhost", 1, 2, 3, 3, Lists.newArrayList(2, 6, 10)));
nodes.add(new Node(6, "localhost", 1, 2, 3, 3, Lists.newArrayList(3, 7, 11)));
// Node 3 - With rep-factor 1
StoreDefinition storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
Cluster newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(3, newCluster, storeDef);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// Test 1 - With consistent routing strategy
storeDef = ServerTestUtils.getStoreDef("consistent",
2,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
// On node 3
Map<Integer, List<Integer>> replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(0, 4, 8));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 4
replicationMapping = adminClient.replicaOps.getReplicationMapping(4,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8));
expectedMapping.put(5, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 5
replicationMapping = adminClient.replicaOps.getReplicationMapping(5,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
expectedMapping.put(6, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 6
replicationMapping = adminClient.replicaOps.getReplicationMapping(6,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
expectedMapping.put(5, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
// Test 2 - With zone routing strategy
HashMap<Integer, Integer> zoneReplicationFactors = Maps.newHashMap();
for(int index = 0; index < zoneIds.length; index++) {
zoneReplicationFactors.put(zoneIds[index], 1);
}
storeDef = ServerTestUtils.getStoreDef("zone",
2,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
// On node 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(5, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 4
replicationMapping = adminClient.replicaOps.getReplicationMapping(4,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(5, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 5
replicationMapping = adminClient.replicaOps.getReplicationMapping(5,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 6
replicationMapping = adminClient.replicaOps.getReplicationMapping(6,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// Test 3 - Consistent with rep factor 3
storeDef = ServerTestUtils.getStoreDef("consistent",
3,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
newCluster = new Cluster("single_zone_cluster", nodes, zones);
{
// On node 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(0, 4, 8));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
expectedMapping.put(5, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 4
replicationMapping = adminClient.replicaOps.getReplicationMapping(4,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
expectedMapping.put(5, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 5
replicationMapping = adminClient.replicaOps.getReplicationMapping(5,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8));
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
expectedMapping.put(6, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 6
replicationMapping = adminClient.replicaOps.getReplicationMapping(6,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
expectedMapping.put(5, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
zoneReplicationFactors = Maps.newHashMap();
for(int index = 0; index < zoneIds.length; index++) {
zoneReplicationFactors.put(zoneIds[index], 2);
}
storeDef = ServerTestUtils.getStoreDef("zone",
1,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
// On node 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(0, 4, 8, 1, 5, 9));
expectedMapping.put(5, Lists.newArrayList(2, 6, 10));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 4
replicationMapping = adminClient.replicaOps.getReplicationMapping(4,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8));
expectedMapping.put(5, Lists.newArrayList(1, 5, 9, 2, 6, 10));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 5
replicationMapping = adminClient.replicaOps.getReplicationMapping(5,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8));
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
expectedMapping.put(6, Lists.newArrayList(2, 6, 10, 3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 6
replicationMapping = adminClient.replicaOps.getReplicationMapping(6,
newCluster,
storeDef);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(3, Lists.newArrayList(0, 4, 8, 3, 7, 11));
expectedMapping.put(4, Lists.newArrayList(1, 5, 9));
expectedMapping.put(5, Lists.newArrayList(2, 6, 10));
assertEquals(expectedMapping, replicationMapping);
}
}
@Test
public void testReplicationMappingWithZonePreference() {
List<Zone> zones = ServerTestUtils.getZones(2);
List<Node> nodes = Lists.newArrayList();
nodes.add(new Node(0, "localhost", 1, 2, 3, 0, Lists.newArrayList(0, 4, 8)));
nodes.add(new Node(1, "localhost", 1, 2, 3, 0, Lists.newArrayList(1, 5, 9)));
nodes.add(new Node(2, "localhost", 1, 2, 3, 1, Lists.newArrayList(2, 6, 10)));
nodes.add(new Node(3, "localhost", 1, 2, 3, 1, Lists.newArrayList(3, 7, 11)));
// Test 0 - With rep-factor 1; zone 1
StoreDefinition storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
Cluster newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(0, newCluster, storeDef, 1);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// With rep-factor 1; zone 0
storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(0, newCluster, storeDef, 0);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// Test 1 - With consistent routing strategy
storeDef = ServerTestUtils.getStoreDef("consistent",
4,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
// On node 0; zone id 1
Map<Integer, List<Integer>> replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef,
1);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(2, Lists.newArrayList(0, 4, 8, 1, 5, 9, 2, 6, 10));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// On node 0; zone id 0
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef,
0);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
// partitionTuple.put(1, Lists.newArrayList(0, 4, 8));
// partitionTuple.put(2, Lists.newArrayList(3, 7, 11));
// partitionTuple.put(3, Lists.newArrayList(2, 6, 10));
expectedMapping.put(1, Lists.newArrayList(0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// Test 2 - With zone routing strategy, and zone replication factor 1
HashMap<Integer, Integer> zoneReplicationFactors = Maps.newHashMap();
for(int zoneIds = 0; zoneIds < 2; zoneIds++) {
zoneReplicationFactors.put(zoneIds, 1);
}
storeDef = ServerTestUtils.getStoreDef("zone",
2,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
// On node 0, zone 0 - failure case since zoneReplicationFactor is 1
try {
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef,
0);
fail("Should have thrown an exception since zoneReplicationFactor is 1");
} catch(VoldemortException e) {}
}
{
// On node 0, zone 1
replicationMapping = adminClient.replicaOps.getReplicationMapping(0,
newCluster,
storeDef,
1);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(2, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(3, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 1, zone 1
replicationMapping = adminClient.replicaOps.getReplicationMapping(1,
newCluster,
storeDef,
1);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(2, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
}
@Test
public void testReplicationMappingWithZonePreferenceWithNonContiguousZones() {
int[] zoneIds = { 1, 3 };
List<Zone> zones = ServerTestUtils.getZonesFromZoneIds(zoneIds);
List<Node> nodes = Lists.newArrayList();
nodes.add(new Node(3, "localhost", 1, 2, 3, 1, Lists.newArrayList(0, 4, 8)));
nodes.add(new Node(4, "localhost", 1, 2, 3, 1, Lists.newArrayList(1, 5, 9)));
nodes.add(new Node(5, "localhost", 1, 2, 3, 3, Lists.newArrayList(2, 6, 10)));
nodes.add(new Node(6, "localhost", 1, 2, 3, 3, Lists.newArrayList(3, 7, 11)));
// Node 3 - With rep-factor 1; zone 1
StoreDefinition storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
Cluster newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(3, newCluster, storeDef, 1);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// With rep-factor 1; zone 1
storeDef = ServerTestUtils.getStoreDef("consistent",
1,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
newCluster = new Cluster("single_zone_cluster", nodes, zones);
try {
adminClient.replicaOps.getReplicationMapping(3, newCluster, storeDef, 1);
fail("Should have thrown an exception since rep-factor = 1");
} catch(VoldemortException e) {}
// Node 1 - With consistent routing strategy
storeDef = ServerTestUtils.getStoreDef("consistent",
4,
1,
1,
1,
1,
RoutingStrategyType.CONSISTENT_STRATEGY);
// On node 3; zone id 1
Map<Integer, List<Integer>> replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef,
1);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// On node 3; zone id 1
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef,
1);
{
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(4, Lists.newArrayList(0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
// Test 2 - With zone routing strategy, and zone replication factor 1
HashMap<Integer, Integer> zoneReplicationFactors = Maps.newHashMap();
for(int index = 0; index < zoneIds.length; index++) {
zoneReplicationFactors.put(zoneIds[index], 1);
}
storeDef = ServerTestUtils.getStoreDef("zone",
2,
1,
1,
1,
0,
0,
zoneReplicationFactors,
HintedHandoffStrategyType.PROXIMITY_STRATEGY,
RoutingStrategyType.ZONE_STRATEGY);
newCluster = new Cluster("multi_zone_cluster", nodes, zones);
{
// On node 3, zone 1 - failure case since zoneReplicationFactor is 1
try {
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef,
1);
fail("Should have thrown an exception since zoneReplicationFactor is 1");
} catch(VoldemortException e) {}
}
{
// On node 3, zone 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(3,
newCluster,
storeDef,
3);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(5, Lists.newArrayList(0, 4, 8, 2, 6, 10));
expectedMapping.put(6, Lists.newArrayList(3, 7, 11));
assertEquals(expectedMapping, replicationMapping);
}
{
// On node 4, zone 3
replicationMapping = adminClient.replicaOps.getReplicationMapping(4,
newCluster,
storeDef,
3);
HashMap<Integer, List<Integer>> expectedMapping = Maps.newHashMap();
expectedMapping.put(5, Lists.newArrayList(1, 5, 9));
assertEquals(expectedMapping, replicationMapping);
}
}
@Test
public void testDeleteStore() throws Exception {
AdminClient adminClient = getAdminClient();
doClientOperation();
StoreDefinition definition = new StoreDefinitionBuilder().setName("deleteTest")
.setType(InMemoryStorageConfiguration.TYPE_NAME)
.setKeySerializer(new SerializerDefinition("string"))
.setValueSerializer(new SerializerDefinition("string"))
.setRoutingPolicy(RoutingTier.CLIENT)
.setRoutingStrategyType(RoutingStrategyType.CONSISTENT_STRATEGY)
.setReplicationFactor(1)
.setPreferredReads(1)
.setRequiredReads(1)
.setPreferredWrites(1)
.setRequiredWrites(1)
.build();
adminClient.storeMgmtOps.addStore(definition);
// now test the store
StoreClientFactory factory = new SocketStoreClientFactory(new ClientConfig().setCacheStoreClients(false)
.setBootstrapUrls(cluster.getNodeById(0)
.getSocketUrl()
.toString()));
StoreClient<Object, Object> client = factory.getStoreClient("deleteTest");
int numStores = adminClient.metadataMgmtOps.getRemoteStoreDefList(0).getValue().size();
// delete the store
assertEquals(adminClient.metadataMgmtOps.getRemoteStoreDefList(0)
.getValue()
.contains(definition),
true);
adminClient.storeMgmtOps.deleteStore("deleteTest");
assertEquals(adminClient.metadataMgmtOps.getRemoteStoreDefList(0).getValue().size(),
numStores - 1);
assertEquals(adminClient.metadataMgmtOps.getRemoteStoreDefList(0)
.getValue()
.contains(definition),
false);
// test with deleted store
// (Turning off store client caching above will ensures the new client
// will attempt to bootstrap)
try {
client = factory.getStoreClient("deleteTest");
client.put("abc", "123");
String s = (String) client.get("abc").getValue();
assertEquals(s, "123");
fail("Should have received bootstrap failure exception");
} catch(Exception e) {
if(!(e instanceof BootstrapFailureException))
throw e;
}
doClientOperation();
// try adding the store again
adminClient.storeMgmtOps.addStore(definition);
client = factory.getStoreClient("deleteTest");
client.put("abc", "123");
String s = (String) client.get("abc").getValue();
assertEquals(s, "123");
doClientOperation();
}
/**
* Update the server state (
* {@link voldemort.store.metadata.MetadataStore.VoldemortState}) on a
* remote node.
*
* @param nodeId The node id on which we want to update the state
* @param state The state to update to
* @param clock The vector clock
*/
private void updateRemoteServerState(AdminClient client,
int nodeId,
MetadataStore.VoldemortState state,
Version clock) {
client.metadataMgmtOps.updateRemoteMetadata(nodeId,
MetadataStore.SERVER_STATE_KEY,
new Versioned<String>(state.toString(), clock));
}
@Test
public void testStateTransitions() {
// change to REBALANCING STATE
AdminClient client = getAdminClient();
updateRemoteServerState(client,
getVoldemortServer(0).getIdentityNode().getId(),
MetadataStore.VoldemortState.REBALANCING_MASTER_SERVER,
((VectorClock) client.rebalanceOps.getRemoteServerState(0)
.getVersion()).incremented(0,
System.currentTimeMillis()));
MetadataStore.VoldemortState state = getVoldemortServer(0).getMetadataStore()
.getServerStateUnlocked();
assertEquals("State should be changed correctly to rebalancing state",
MetadataStore.VoldemortState.REBALANCING_MASTER_SERVER,
state);
// change back to NORMAL state
updateRemoteServerState(client,
getVoldemortServer(0).getIdentityNode().getId(),
MetadataStore.VoldemortState.NORMAL_SERVER,
((VectorClock) client.rebalanceOps.getRemoteServerState(0)
.getVersion()).incremented(0,
System.currentTimeMillis()));
state = getVoldemortServer(0).getMetadataStore().getServerStateUnlocked();
assertEquals("State should be changed correctly to rebalancing state",
MetadataStore.VoldemortState.NORMAL_SERVER,
state);
// lets revert back to REBALANCING STATE AND CHECK
updateRemoteServerState(client,
getVoldemortServer(0).getIdentityNode().getId(),
MetadataStore.VoldemortState.REBALANCING_MASTER_SERVER,
((VectorClock) client.rebalanceOps.getRemoteServerState(0)
.getVersion()).incremented(0,
System.currentTimeMillis()));
state = getVoldemortServer(0).getMetadataStore().getServerStateUnlocked();
assertEquals("State should be changed correctly to rebalancing state",
MetadataStore.VoldemortState.REBALANCING_MASTER_SERVER,
state);
// change back to NORMAL_SERVER
updateRemoteServerState(client,
getVoldemortServer(0).getIdentityNode().getId(),
MetadataStore.VoldemortState.NORMAL_SERVER,
((VectorClock) client.rebalanceOps.getRemoteServerState(0)
.getVersion()).incremented(0,
System.currentTimeMillis()));
state = getVoldemortServer(0).getMetadataStore().getServerStateUnlocked();
assertEquals("State should be changed correctly to normal state",
MetadataStore.VoldemortState.NORMAL_SERVER,
state);
// change to OFFLINE_SERVER
client.metadataMgmtOps.setRemoteOfflineState(getVoldemortServer(0).getIdentityNode()
.getId(), true);
state = getVoldemortServer(0).getMetadataStore().getServerStateUnlocked();
assertEquals("State should be changed correctly to offline state",
MetadataStore.VoldemortState.OFFLINE_SERVER,
state);
// change back to NORMAL_SERVER
client.metadataMgmtOps.setRemoteOfflineState(getVoldemortServer(0).getIdentityNode()
.getId(), false);
state = getVoldemortServer(0).getMetadataStore().getServerStateUnlocked();
assertEquals("State should be changed correctly to normal state",
MetadataStore.VoldemortState.NORMAL_SERVER,
state);
}
@Test
public void testDeletePartitionEntries() {
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
// insert it into server-0 store
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
}
List<Integer> deletePartitionsList = Arrays.asList(0, 2);
// do delete partitions request
getAdminClient().storeMntOps.deletePartitions(0, testStoreName, deletePartitionsList, null);
store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
if(isKeyPartition(entry.getKey(), 0, testStoreName, deletePartitionsList)) {
assertEquals("deleted partitions should be missing.",
0,
store.get(entry.getKey(), null).size());
}
}
}
@Test
public void testFetchPartitionKeys() {
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
List<Integer> fetchPartitionsList = Arrays.asList(0, 2);
// insert it into server-0 store
int fetchPartitionKeyCount = 0;
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
if(isKeyPartition(entry.getKey(), 0, testStoreName, fetchPartitionsList)) {
fetchPartitionKeyCount++;
}
}
Iterator<ByteArray> fetchIt = getAdminClient().bulkFetchOps.fetchKeys(0,
testStoreName,
fetchPartitionsList,
null,
false);
// check values
int count = 0;
while(fetchIt.hasNext()) {
assertEquals("Fetched key should belong to asked partitions",
true,
isKeyPartition(fetchIt.next(), 0, testStoreName, fetchPartitionsList));
count++;
}
// assert all keys for asked partitions are returned.
assertEquals("All keys for asked partitions should be received",
fetchPartitionKeyCount,
count);
}
@Test
public void testFetchPartitionFiles() throws IOException {
generateAndFetchFiles(2, 1, 1200, 1000);
}
private void generateROFiles(int numChunks,
long indexSize,
long dataSize,
HashMap<Integer, List<Integer>> buckets,
File versionDir) throws IOException {
ReadOnlyStorageMetadata metadata = new ReadOnlyStorageMetadata();
metadata.add(ReadOnlyStorageMetadata.FORMAT, ReadOnlyStorageFormat.READONLY_V2.getCode());
File metadataFile = new File(versionDir, ".metadata");
BufferedWriter writer = new BufferedWriter(new FileWriter(metadataFile));
writer.write(metadata.toJsonString());
writer.close();
for(Entry<Integer, List<Integer>> entry: buckets.entrySet()) {
int replicaType = entry.getKey();
for(int partitionId: entry.getValue()) {
for(int chunkId = 0; chunkId < numChunks; chunkId++) {
File index = new File(versionDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".index");
File data = new File(versionDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".data");
// write some random crap for index and data
FileOutputStream dataOs = new FileOutputStream(data);
for(int i = 0; i < dataSize; i++)
dataOs.write(i);
dataOs.close();
FileOutputStream indexOs = new FileOutputStream(index);
for(int i = 0; i < indexSize; i++)
indexOs.write(i);
indexOs.close();
}
}
}
}
@SuppressWarnings("unchecked")
private void generateAndFetchFiles(int numChunks, long versionId, long indexSize, long dataSize)
throws IOException {
Map<Integer, Set<Pair<Integer, Integer>>> buckets = ROTestUtils.getNodeIdToAllPartitions(cluster,
StoreDefinitionUtils.getStoreDefinitionWithName(storeDefs,
"test-readonly-fetchfiles"),
true);
for(Node node: cluster.getNodes()) {
ReadOnlyStorageEngine store = (ReadOnlyStorageEngine) getStore(node.getId(),
"test-readonly-fetchfiles");
// Create list of buckets ( replica to partition )
Set<Pair<Integer, Integer>> nodeBucketsSet = buckets.get(node.getId());
HashMap<Integer, List<Integer>> nodeBuckets = ROTestUtils.flattenPartitionTuples(nodeBucketsSet);
// Split the buckets into primary and replica buckets
HashMap<Integer, List<Integer>> primaryNodeBuckets = Maps.newHashMap();
primaryNodeBuckets.put(0, nodeBuckets.get(0));
int primaryPartitions = nodeBuckets.get(0).size();
HashMap<Integer, List<Integer>> replicaNodeBuckets = Maps.newHashMap(nodeBuckets);
replicaNodeBuckets.remove(0);
int replicaPartitions = 0;
for(List<Integer> partitions: replicaNodeBuckets.values()) {
replicaPartitions += partitions.size();
}
// Generate data...
File newVersionDir = new File(store.getStoreDirPath(), "version-"
+ Long.toString(versionId));
Utils.mkdirs(newVersionDir);
generateROFiles(numChunks, indexSize, dataSize, nodeBuckets, newVersionDir);
// Swap it...
store.swapFiles(newVersionDir.getAbsolutePath());
// Check if everything got mmap-ed correctly...
HashMap<Object, Integer> chunkIdToNumChunks = store.getChunkedFileSet()
.getChunkIdToNumChunks();
for(Object bucket: chunkIdToNumChunks.keySet()) {
Pair<Integer, Integer> partitionToReplicaBucket = (Pair<Integer, Integer>) bucket;
Pair<Integer, Integer> replicaToPartitionBucket = Pair.create(partitionToReplicaBucket.getSecond(),
partitionToReplicaBucket.getFirst());
assertTrue(nodeBucketsSet.contains(replicaToPartitionBucket));
}
// Test 0) Try to fetch a partition which doesn't exist
File tempDir = TestUtils.createTempDir();
HashMap<Integer, List<Integer>> dumbMap = Maps.newHashMap();
dumbMap.put(0, Lists.newArrayList(100));
try {
getAdminClient().readonlyOps.fetchPartitionFiles(node.getId(),
"test-readonly-fetchfiles",
Lists.newArrayList(100),
tempDir.getAbsolutePath(),
null,
running);
fail("Should throw exception since partition map passed is bad");
} catch(VoldemortException e) {}
// Test 1) Fetch all the primary partitions...
tempDir = TestUtils.createTempDir();
getAdminClient().readonlyOps.fetchPartitionFiles(node.getId(),
"test-readonly-fetchfiles",
primaryNodeBuckets.get(0),
tempDir.getAbsolutePath(),
null,
running);
// Check it...
assertEquals(tempDir.list().length, 2 * primaryPartitions * numChunks + 1);
for(Entry<Integer, List<Integer>> entry: primaryNodeBuckets.entrySet()) {
int replicaType = entry.getKey();
for(int partitionId: entry.getValue()) {
for(int chunkId = 0; chunkId < numChunks; chunkId++) {
File indexFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".index");
File dataFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".data");
assertTrue(indexFile.exists());
assertTrue(dataFile.exists());
assertEquals(indexFile.length(), indexSize);
assertEquals(dataFile.length(), dataSize);
}
}
}
// Check if metadata file exists
File metadataFile = new File(tempDir, ".metadata");
assertEquals(metadataFile.exists(), true);
// Test 2) Fetch all the replica partitions...
tempDir = TestUtils.createTempDir();
for(Entry<Integer, List<Integer>> entry: replicaNodeBuckets.entrySet()) {
getAdminClient().readonlyOps.fetchPartitionFiles(node.getId(),
"test-readonly-fetchfiles",
entry.getValue(),
tempDir.getAbsolutePath(),
null,
running);
}
// Check it...
assertEquals(tempDir.list().length, 2 * replicaPartitions * numChunks + 1);
for(Entry<Integer, List<Integer>> entry: replicaNodeBuckets.entrySet()) {
int replicaType = entry.getKey();
for(int partitionId: entry.getValue()) {
for(int chunkId = 0; chunkId < numChunks; chunkId++) {
File indexFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".index");
File dataFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".data");
assertTrue(indexFile.exists());
assertTrue(dataFile.exists());
assertEquals(indexFile.length(), indexSize);
assertEquals(dataFile.length(), dataSize);
}
}
}
// Check if metadata file exists
metadataFile = new File(tempDir, ".metadata");
assertEquals(metadataFile.exists(), true);
// Test 3) Fetch all the partitions...
tempDir = TestUtils.createTempDir();
for(Entry<Integer, List<Integer>> entry: nodeBuckets.entrySet()) {
getAdminClient().readonlyOps.fetchPartitionFiles(node.getId(),
"test-readonly-fetchfiles",
entry.getValue(),
tempDir.getAbsolutePath(),
null,
running);
}
// Check it...
assertEquals(tempDir.list().length, 2 * (primaryPartitions + replicaPartitions)
* numChunks + 1);
for(Entry<Integer, List<Integer>> entry: nodeBuckets.entrySet()) {
int replicaType = entry.getKey();
for(int partitionId: entry.getValue()) {
for(int chunkId = 0; chunkId < numChunks; chunkId++) {
File indexFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".index");
File dataFile = new File(tempDir, Integer.toString(partitionId) + "_"
+ Integer.toString(replicaType) + "_"
+ Integer.toString(chunkId) + ".data");
assertTrue(indexFile.exists());
assertTrue(dataFile.exists());
assertEquals(indexFile.length(), indexSize);
assertEquals(dataFile.length(), dataSize);
}
}
}
// Check if metadata file exists
metadataFile = new File(tempDir, ".metadata");
assertEquals(metadataFile.exists(), true);
}
}
@Test
public void testGetROStorageFormat() {
Map<String, String> storesToStorageFormat = getAdminClient().readonlyOps.getROStorageFormat(0,
Lists.newArrayList("test-readonly-fetchfiles",
"test-readonly-versions"));
assertEquals(storesToStorageFormat.size(), 2);
assertEquals(storesToStorageFormat.get("test-readonly-fetchfiles"),
ReadOnlyStorageFormat.READONLY_V2.getCode());
assertEquals(storesToStorageFormat.get("test-readonly-versions"),
ReadOnlyStorageFormat.READONLY_V2.getCode());
}
@Test
public void testGetROVersions() {
// Tests get current version
Map<String, Long> storesToVersions = getAdminClient().readonlyOps.getROCurrentVersion(0,
Lists.newArrayList("test-readonly-fetchfiles",
"test-readonly-versions"));
assertEquals(storesToVersions.size(), 2);
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 0);
assertEquals(storesToVersions.get("test-readonly-versions").longValue(), 0);
// Tests get maximum version
storesToVersions = getAdminClient().readonlyOps.getROMaxVersion(0,
Lists.newArrayList("test-readonly-fetchfiles",
"test-readonly-versions"));
assertEquals(storesToVersions.size(), 2);
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 0);
assertEquals(storesToVersions.get("test-readonly-versions").longValue(), 0);
// Tests global get maximum versions
storesToVersions = getAdminClient().readonlyOps.getROMaxVersion(Lists.newArrayList("test-readonly-fetchfiles",
"test-readonly-versions"));
assertEquals(storesToVersions.size(), 2);
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 0);
assertEquals(storesToVersions.get("test-readonly-versions").longValue(), 0);
ReadOnlyStorageEngine storeNode0 = (ReadOnlyStorageEngine) getStore(0,
"test-readonly-fetchfiles");
ReadOnlyStorageEngine storeNode1 = (ReadOnlyStorageEngine) getStore(1,
"test-readonly-fetchfiles");
Utils.mkdirs(new File(storeNode0.getStoreDirPath(), "version-10"));
File newVersionNode1 = new File(storeNode1.getStoreDirPath(), "version-11");
Utils.mkdirs(newVersionNode1);
storeNode1.swapFiles(newVersionNode1.getAbsolutePath());
// Node 0
// Test current version
storesToVersions = getAdminClient().readonlyOps.getROCurrentVersion(0,
Lists.newArrayList("test-readonly-fetchfiles"));
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 0);
// Test max version
storesToVersions = getAdminClient().readonlyOps.getROMaxVersion(0,
Lists.newArrayList("test-readonly-fetchfiles"));
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 10);
// Node 1
// Test current version
storesToVersions = getAdminClient().readonlyOps.getROCurrentVersion(1,
Lists.newArrayList("test-readonly-fetchfiles"));
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 11);
// Test max version
storesToVersions = getAdminClient().readonlyOps.getROMaxVersion(1,
Lists.newArrayList("test-readonly-fetchfiles"));
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 11);
// Test global max
storesToVersions = getAdminClient().readonlyOps.getROMaxVersion(Lists.newArrayList("test-readonly-fetchfiles",
"test-readonly-versions"));
assertEquals(storesToVersions.get("test-readonly-fetchfiles").longValue(), 11);
assertEquals(storesToVersions.get("test-readonly-versions").longValue(), 0);
}
@Test
public void testTruncate() throws Exception {
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
// insert it into server-0 store
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
}
// do truncate request
getAdminClient().storeMntOps.truncate(0, testStoreName);
store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
assertEquals("Deleted key should be missing.", 0, store.get(entry.getKey(), null)
.size());
}
}
@Test
public void testFetch() {
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
List<Integer> fetchPartitionsList = Arrays.asList(0, 2);
// insert it into server-0 store
int fetchPartitionKeyCount = 0;
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
if(isKeyPartition(entry.getKey(), 0, testStoreName, fetchPartitionsList)) {
fetchPartitionKeyCount++;
}
}
Iterator<Pair<ByteArray, Versioned<byte[]>>> fetchIt = getAdminClient().bulkFetchOps.fetchEntries(0,
testStoreName,
fetchPartitionsList,
null,
false);
// check values
int count = 0;
while(fetchIt.hasNext()) {
Pair<ByteArray, Versioned<byte[]>> entry = fetchIt.next();
assertEquals("Fetched entries should belong to asked partitions",
true,
isKeyPartition(entry.getFirst(), 0, testStoreName, fetchPartitionsList));
assertEquals("entry value should match",
new String(entry.getSecond().getValue()),
new String(entrySet.get(entry.getFirst())));
count++;
}
// assert all keys for asked partitions are returned.
assertEquals("All entries for asked partitions should be received",
fetchPartitionKeyCount,
count);
}
@Test
public void testQuery() {
HashMap<ByteArray, byte[]> belongToAndInsideServer0 = new HashMap<ByteArray, byte[]>();
HashMap<ByteArray, byte[]> belongToAndInsideServer1 = new HashMap<ByteArray, byte[]>();
HashMap<ByteArray, byte[]> notBelongServer0ButInsideServer0 = new HashMap<ByteArray, byte[]>();
HashMap<ByteArray, byte[]> belongToServer0ButOutsideBoth = new HashMap<ByteArray, byte[]>();
HashMap<ByteArray, byte[]> notBelongToServer0AndOutsideBoth = new HashMap<ByteArray, byte[]>();
Store<ByteArray, byte[], byte[]> store0 = getStore(0, testStoreName);
Store<ByteArray, byte[], byte[]> store1 = getStore(1, testStoreName);
HashMap<ByteArray, byte[]> entrySet = null;
Iterator<ByteArray> keys = null;
RoutingStrategy strategy = servers[0].getMetadataStore().getRoutingStrategy(testStoreName);
while(true) {
ByteArray key;
byte[] value;
if(keys == null || !keys.hasNext()) {
entrySet = ServerTestUtils.createRandomKeyValuePairs(100);
keys = entrySet.keySet().iterator();
}
key = keys.next();
value = entrySet.get(key);
List<Node> routedNodes = strategy.routeRequest(key.get());
boolean keyShouldBeInNode0 = false;
boolean keyShouldBeInNode1 = false;
for(Node node: routedNodes) {
keyShouldBeInNode0 = keyShouldBeInNode0 || (node.getId() == 0);
keyShouldBeInNode1 = keyShouldBeInNode1 || (node.getId() == 1);
}
if(belongToAndInsideServer0.size() < 10) {
if(keyShouldBeInNode0) {
belongToAndInsideServer0.put(key, value);
store0.put(key, new Versioned<byte[]>(value), null);
}
} else if(belongToAndInsideServer1.size() < 10) {
if(keyShouldBeInNode1) {
belongToAndInsideServer1.put(key, value);
store1.put(key, new Versioned<byte[]>(value), null);
}
} else if(notBelongServer0ButInsideServer0.size() < 5) {
if(!keyShouldBeInNode0) {
notBelongServer0ButInsideServer0.put(key, value);
store0.put(key, new Versioned<byte[]>(value), null);
}
} else if(belongToServer0ButOutsideBoth.size() < 5) {
if(keyShouldBeInNode0) {
belongToServer0ButOutsideBoth.put(key, value);
}
} else if(notBelongToServer0AndOutsideBoth.size() < 5) {
if(!keyShouldBeInNode0) {
notBelongToServer0AndOutsideBoth.put(key, value);
}
} else {
break;
}
}
ArrayList<ByteArray> belongToAndInsideServer0Keys = new ArrayList<ByteArray>(belongToAndInsideServer0.keySet());
ArrayList<ByteArray> belongToAndInsideServer1Keys = new ArrayList<ByteArray>(belongToAndInsideServer1.keySet());
ArrayList<ByteArray> notBelongServer0ButInsideServer0Keys = new ArrayList<ByteArray>(notBelongServer0ButInsideServer0.keySet());
ArrayList<ByteArray> belongToServer0ButOutsideBothKeys = new ArrayList<ByteArray>(belongToServer0ButOutsideBoth.keySet());
ArrayList<ByteArray> notBelongToServer0AndOutsideBothKeys = new ArrayList<ByteArray>(notBelongToServer0AndOutsideBoth.keySet());
List<ByteArray> queryKeys;
Iterator<QueryKeyResult> results;
QueryKeyResult entry;
// test one key on store 0
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(belongToAndInsideServer0Keys.get(0));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertEquals(queryKeys.get(0), entry.getKey());
assertNull("There should not be exception in response", entry.getException());
assertEquals("There should be only 1 value in versioned list", 1, entry.getValues().size());
assertEquals("Two byte[] should be equal",
0,
ByteUtils.compare(belongToAndInsideServer0.get(queryKeys.get(0)),
entry.getValues().get(0).getValue()));
assertFalse("There should be only one result", results.hasNext());
// test one key belongs to but not exists in server 0
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(belongToServer0ButOutsideBothKeys.get(0));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertFalse("There should not be more results", results.hasNext());
assertEquals("Not the right key", queryKeys.get(0), entry.getKey());
assertFalse("There should not be exception", entry.hasException());
assertTrue("There should be values", entry.hasValues());
assertNotNull("Response should be non-null", entry.getValues());
assertEquals("Value should be empty list", 0, entry.getValues().size());
assertNull("There should not be exception", entry.getException());
// test one key not exist and does not belong to server 0
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(notBelongToServer0AndOutsideBothKeys.get(0));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertFalse("There should not be more results", results.hasNext());
assertEquals("Not the right key", queryKeys.get(0), entry.getKey());
assertTrue("There should be exception", entry.hasException());
assertFalse("There should not be values", entry.hasValues());
assertNull("Value should be null", entry.getValues());
assertTrue("There should be InvalidMetadataException exception",
entry.getException() instanceof InvalidMetadataException);
// test one key that exists on server 0 but does not belong to server 0
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(notBelongServer0ButInsideServer0Keys.get(0));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertFalse("There should not be more results", results.hasNext());
assertEquals("Not the right key", queryKeys.get(0), entry.getKey());
assertTrue("There should be exception", entry.hasException());
assertFalse("There should not be values", entry.hasValues());
assertNull("Value should be null", entry.getValues());
assertTrue("There should be InvalidMetadataException exception",
entry.getException() instanceof InvalidMetadataException);
// test one key deleted
store0.delete(belongToAndInsideServer0Keys.get(4), null);
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(belongToAndInsideServer0Keys.get(4));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertFalse("There should not be more results", results.hasNext());
assertFalse("There should not be exception", entry.hasException());
assertTrue("There should be values", entry.hasValues());
assertEquals("Not the right key", queryKeys.get(0), entry.getKey());
assertEquals("Value should be empty list", 0, entry.getValues().size());
// test empty request
queryKeys = new ArrayList<ByteArray>();
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertFalse("Results should be empty", results.hasNext());
// test null key
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(null);
assertEquals(1, queryKeys.size());
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
entry = results.next();
assertFalse("There should not be more results", results.hasNext());
assertTrue("There should be exception", entry.hasException());
assertFalse("There should not be values", entry.hasValues());
assertNull("Value should be null", entry.getValues());
assertTrue("There should be IllegalArgumentException exception",
entry.getException() instanceof IllegalArgumentException);
// test multiple keys (3) on store 1
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(belongToAndInsideServer1Keys.get(0));
queryKeys.add(belongToAndInsideServer1Keys.get(1));
queryKeys.add(belongToAndInsideServer1Keys.get(2));
results = getAdminClient().streamingOps.queryKeys(1, testStoreName, queryKeys.iterator());
assertTrue("Results should not be empty", results.hasNext());
Map<ByteArray, List<Versioned<byte[]>>> entries = new HashMap<ByteArray, List<Versioned<byte[]>>>();
int resultCount = 0;
while(results.hasNext()) {
resultCount++;
entry = results.next();
assertNull("There should not be exception in response", entry.getException());
assertNotNull("Value should not be null for Key: ", entry.getValues());
entries.put(entry.getKey(), entry.getValues());
}
assertEquals("There should 3 and only 3 results", 3, resultCount);
for(ByteArray key: queryKeys) {
// this loop and the count ensure one-to-one mapping
assertNotNull("This key should exist in the results: " + key, entries.get(key));
assertEquals("Two byte[] should be equal for key: " + key,
0,
ByteUtils.compare(belongToAndInsideServer1.get(key),
entries.get(key).get(0).getValue()));
}
// test multiple keys, mixed situation
// key 0: Exists and belongs to
// key 1: Exists but does not belong to
// key 2: Does not exist but belongs to
// key 3: Does not belong and not exist
// key 4: Same situation with key0
// key 5: Deleted
// key 6: Same situation with key2
store0.delete(belongToAndInsideServer0Keys.get(5), null);
queryKeys = new ArrayList<ByteArray>();
queryKeys.add(belongToAndInsideServer0Keys.get(2));
queryKeys.add(notBelongServer0ButInsideServer0Keys.get(1));
queryKeys.add(belongToServer0ButOutsideBothKeys.get(1));
queryKeys.add(notBelongToServer0AndOutsideBothKeys.get(1));
queryKeys.add(belongToAndInsideServer0Keys.get(3));
queryKeys.add(belongToAndInsideServer0Keys.get(5));
queryKeys.add(notBelongServer0ButInsideServer0Keys.get(2));
results = getAdminClient().streamingOps.queryKeys(0, testStoreName, queryKeys.iterator());
// key 0
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(0).get(), entry.getKey().get()));
assertEquals(0, ByteUtils.compare(belongToAndInsideServer0.get(queryKeys.get(0)),
entry.getValues().get(0).getValue()));
assertNull(entry.getException());
// key 1
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(1).get(), entry.getKey().get()));
assertTrue("There should be InvalidMetadataException exception",
entry.getException() instanceof InvalidMetadataException);
// key 2
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(2).get(), entry.getKey().get()));
assertEquals(0, entry.getValues().size());
assertNull(entry.getException());
// key 3
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(3).get(), entry.getKey().get()));
assertTrue("There should be InvalidMetadataException exception",
entry.getException() instanceof InvalidMetadataException);
// key 4
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(4).get(), entry.getKey().get()));
assertEquals(0, ByteUtils.compare(belongToAndInsideServer0.get(queryKeys.get(4)),
entry.getValues().get(0).getValue()));
assertNull(entry.getException());
// key 5
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(5).get(), entry.getKey().get()));
assertEquals(0, entry.getValues().size());
assertNull(entry.getException());
// key 6
entry = results.next();
assertEquals(0, ByteUtils.compare(queryKeys.get(6).get(), entry.getKey().get()));
assertTrue("There should be InvalidMetadataException exception",
entry.getException() instanceof InvalidMetadataException);
// no more keys
assertFalse(results.hasNext());
}
@Test
public void testUpdate() {
// TODO what guarantees these randomly generated keys don't collide
// between test cases??
final HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
Iterator<Pair<ByteArray, Versioned<byte[]>>> iterator = new AbstractIterator<Pair<ByteArray, Versioned<byte[]>>>() {
final Iterator<Entry<ByteArray, byte[]>> entrySetItr = entrySet.entrySet().iterator();
@Override
protected Pair<ByteArray, Versioned<byte[]>> computeNext() {
while(entrySetItr.hasNext()) {
Entry<ByteArray, byte[]> entry = entrySetItr.next();
return new Pair<ByteArray, Versioned<byte[]>>(entry.getKey(),
new Versioned<byte[]>(entry.getValue()));
}
return endOfData();
}
};
getAdminClient().streamingOps.updateEntries(0, testStoreName, iterator, null);
// check updated values
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
assertNotSame("entry should be present at store", 0, store.get(entry.getKey(), null)
.size());
assertEquals("entry value should match",
new String(entry.getValue()),
new String(store.get(entry.getKey(), null).get(0).getValue()));
}
}
@Test
public void testUpdateTimeBased() {
final long baseTimeMs = System.currentTimeMillis();
String storeName = "test-replication-persistent";
final HashMap<ByteArray, byte[]> entries = ServerTestUtils.createRandomKeyValuePairs(5);
final List<ByteArray> keys = new ArrayList<ByteArray>(entries.keySet());
// Insert some data for the keys
Store<ByteArray, byte[], byte[]> nodeStore = getStore(0, storeName);
for(int i = 0; i < keys.size(); i++) {
ByteArray key = keys.get(i);
byte[] val = entries.get(key);
long ts = 0;
if(i == 0) {
// have multiple conflicting versions.. one lower ts and one
// higher ts, than the streaming version
Versioned<byte[]> v1 = new Versioned<byte[]>(val,
TestUtils.getClockWithTs(baseTimeMs - 1,
1));
nodeStore.put(key, v1, null);
Versioned<byte[]> v2 = new Versioned<byte[]>(val,
TestUtils.getClockWithTs(baseTimeMs + 1,
2));
nodeStore.put(key, v2, null);
} else {
if(i % 2 == 0) {
// even keys : streaming write wins
ts = baseTimeMs + i;
} else {
// odd keys : storage version wins
ts = baseTimeMs - i;
}
nodeStore.put(key, new Versioned<byte[]>(val, new VectorClock(ts)), null);
}
}
Iterator<Pair<ByteArray, Versioned<byte[]>>> iterator = new AbstractIterator<Pair<ByteArray, Versioned<byte[]>>>() {
final Iterator<ByteArray> keysItr = keys.iterator();
int keyCount = 0;
@Override
protected Pair<ByteArray, Versioned<byte[]>> computeNext() {
while(keysItr.hasNext()) {
ByteArray key = keysItr.next();
byte[] val = entries.get(key);
long ts = 0;
if(keyCount == 0) {
// streaming put will be in the middle of two version on
// storage
keyCount++;
return new Pair<ByteArray, Versioned<byte[]>>(key,
new Versioned<byte[]>(val,
new VectorClock(baseTimeMs)));
} else {
if(keyCount % 2 == 0) {
// even keys : streaming write wins
ts = baseTimeMs - keyCount;
} else {
// odd keys : storage version wins
ts = baseTimeMs + keyCount;
}
keyCount++;
return new Pair<ByteArray, Versioned<byte[]>>(key,
new Versioned<byte[]>(val,
new VectorClock(ts)));
}
}
return endOfData();
}
};
getAdminClient().streamingOps.updateEntriesTimeBased(0, storeName, iterator, null);
// check updated values
for(int i = 0; i < keys.size(); i++) {
ByteArray key = keys.get(i);
List<Versioned<byte[]>> vals = nodeStore.get(key, null);
if(i == 0) {
assertEquals("Must contain exactly two versions", 2, vals.size());
Set<Long> storageTimeSet = new HashSet<Long>();
storageTimeSet.add(((VectorClock) vals.get(0).getVersion()).getTimestamp());
storageTimeSet.add(((VectorClock) vals.get(1).getVersion()).getTimestamp());
Set<Long> expectedTimeSet = new HashSet<Long>();
expectedTimeSet.add(baseTimeMs - 1);
expectedTimeSet.add(baseTimeMs + 1);
assertEquals("Streaming put should have backed off since atleast one version has greater timestamp",
expectedTimeSet,
storageTimeSet);
} else {
assertEquals("Must contain exactly one version", 1, vals.size());
assertEquals("Must contain the version the the maximum timestamp",
baseTimeMs + i,
((VectorClock) vals.get(0).getVersion()).getTimestamp());
}
}
}
@Test
public void testUpdateSlops() {
final List<Versioned<Slop>> entrySet = ServerTestUtils.createRandomSlops(0,
10000,
testStoreName,
"users",
"test-replication-persistent",
"test-readrepair-memory",
"test-consistent",
"test-consistent-with-pref-list");
Iterator<Versioned<Slop>> slopIterator = entrySet.iterator();
getAdminClient().streamingOps.updateSlopEntries(0, slopIterator);
// check updated values
Iterator<Versioned<Slop>> entrysetItr = entrySet.iterator();
while(entrysetItr.hasNext()) {
Versioned<Slop> versioned = entrysetItr.next();
Slop nextSlop = versioned.getValue();
Store<ByteArray, byte[], byte[]> store = getStore(0, nextSlop.getStoreName());
if(nextSlop.getOperation().equals(Slop.Operation.PUT)) {
assertNotSame("entry should be present at store",
0,
store.get(nextSlop.getKey(), null).size());
assertEquals("entry value should match",
new String(nextSlop.getValue()),
new String(store.get(nextSlop.getKey(), null).get(0).getValue()));
} else if(nextSlop.getOperation().equals(Slop.Operation.DELETE)) {
assertEquals("entry value should match", 0, store.get(nextSlop.getKey(), null)
.size());
}
}
}
@Test
public void testRecoverData() {
// use store with replication 2, required write 2 for this test.
String testStoreName = "test-recovery-data";
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(5);
// insert it into server-0 store
Store<ByteArray, byte[], byte[]> store = getStore(0, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
}
// assert server 1 is empty
store = getStore(1, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
assertSame("entry should NOT be present at store", 0, store.get(entry.getKey(), null)
.size());
}
// recover all data
adminClient.restoreOps.restoreDataFromReplications(1, 2);
// assert server 1 has all entries for its partitions
store = getStore(1, testStoreName);
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
ByteArray key = entry.getKey();
assertSame("entry should be present for key " + key, 1, store.get(entry.getKey(), null)
.size());
assertEquals("entry value should match",
new String(entry.getValue()),
new String(store.get(entry.getKey(), null).get(0).getValue()));
}
}
/**
* Tests the basic RW fetch and update
*/
@Test
public void testFetchAndUpdateRW() {
HashMap<ByteArray, byte[]> entrySet = ServerTestUtils.createRandomKeyValuePairs(TEST_STREAM_KEYS_SIZE);
List<Integer> primaryMoved = Arrays.asList(0, 2);
List<Integer> secondaryMoved = Arrays.asList(1, 4);
List<Integer> combinedLists = Arrays.asList(0, 1, 2, 4);
Cluster targetCluster = UpdateClusterUtils.createUpdatedCluster(cluster, 1, primaryMoved);
HashMap<ByteArray, byte[]> keysMovedWith0AsSecondary = Maps.newHashMap();
// insert it into server-0 store
RoutingStrategy strategy = new RoutingStrategyFactory().updateRoutingStrategy(StoreDefinitionUtils.getStoreDefinitionWithName(storeDefs,
"test-recovery-data"),
cluster);
Store<ByteArray, byte[], byte[]> store0 = getStore(0, "test-recovery-data");
Store<ByteArray, byte[], byte[]> store1 = getStore(1, "test-recovery-data");
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet()) {
store0.put(entry.getKey(), new Versioned<byte[]>(entry.getValue()), null);
List<Integer> partitions = strategy.getPartitionList(entry.getKey().get());
if(primaryMoved.contains(partitions.get(0))
|| (secondaryMoved.contains(partitions.get(0)) && cluster.getNodeById(0)
.getPartitionIds()
.contains(partitions.get(1)))) {
keysMovedWith0AsSecondary.put(entry.getKey(), entry.getValue());
}
}
// Assert that server1 is empty.
for(Entry<ByteArray, byte[]> entry: entrySet.entrySet())
assertEquals("server1 should be empty at start.", 0, store1.get(entry.getKey(), null)
.size());
// Set some other metadata, so as to pick the right up later
getServer(0).getMetadataStore().put(MetadataStore.CLUSTER_KEY, targetCluster);
// Migrate the partition
AdminClient client = getAdminClient();
int id = client.storeMntOps.migratePartitions(0,
1,
"test-recovery-data",
combinedLists,
null,
cluster);
client.rpcOps.waitForCompletion(1, id, 120, TimeUnit.SECONDS);
// Check the values
for(Entry<ByteArray, byte[]> entry: keysMovedWith0AsSecondary.entrySet()) {
assertEquals("server1 store should contain fetchAndupdated partitions.",
1,
store1.get(entry.getKey(), null).size());
assertEquals("entry value should match",
new String(entry.getValue()),
new String(store1.get(entry.getKey(), null).get(0).getValue()));
}
}
}