/*
* Copyright 2010-2012 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.performance;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import voldemort.ServerTestUtils;
import voldemort.VoldemortException;
import voldemort.client.ClientConfig;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.cluster.failuredetector.BannagePeriodFailureDetector;
import voldemort.cluster.failuredetector.FailureDetector;
import voldemort.cluster.failuredetector.FailureDetectorConfig;
import voldemort.cluster.failuredetector.FailureDetectorUtils;
import voldemort.cluster.failuredetector.MutableStoreVerifier;
import voldemort.server.StoreRepository;
import voldemort.server.VoldemortServer;
import voldemort.store.SleepyStore;
import voldemort.store.Store;
import voldemort.store.StoreDefinition;
import voldemort.store.memory.InMemoryStorageEngine;
import voldemort.store.routed.RoutedStore;
import voldemort.store.routed.RoutedStoreConfig;
import voldemort.store.routed.RoutedStoreFactory;
import voldemort.store.socket.SocketStoreFactory;
import voldemort.store.socket.clientrequest.ClientRequestExecutorPool;
import voldemort.utils.ByteArray;
import voldemort.utils.CmdUtils;
import voldemort.utils.Time;
import voldemort.xml.StoreDefinitionsMapper;
public class RoutedStoreParallelismTest {
private final static String THREAD_POOL_ROUTED_STORE = "threaded";
private final static String PIPELINE_ROUTED_STORE = "pipeline";
private final static int DEFAULT_NUM_KEYS = 50;
private final static int DEFAULT_MAX_CONNECTIONS = new ClientConfig().getMaxConnectionsPerNode();
private final static int DEFAULT_MAX_THREADS = new ClientConfig().getMaxThreads();
private final static int DEFAULT_NUM_NODES = 2;
private final static int DEFAULT_NUM_SLOW_NODES = 1;
private final static int DEFAULT_DELAY = 500;
private final static int DEFAULT_NUM_CLIENTS = 20;
private final static String DEFAULT_ROUTED_STORE_TYPE = PIPELINE_ROUTED_STORE;
public static void main(String[] args) throws Throwable {
OptionParser parser = new OptionParser();
parser.accepts("num-keys",
"The number of keys to submit for retrieval Default = " + DEFAULT_NUM_KEYS)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("max-connections",
"The maximum number of connections (sockets) per node; same value as client configuration parameter \""
+ ClientConfig.MAX_CONNECTIONS_PER_NODE_PROPERTY
+ "\" Default = "
+ DEFAULT_MAX_CONNECTIONS)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("max-threads",
"The maximum number of threads used by the threaded RoutedStore implementation; same value as client configuration parameter \""
+ ClientConfig.MAX_THREADS_PROPERTY
+ "\" Default = "
+ DEFAULT_MAX_THREADS)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("num-nodes", "The number of nodes Default = " + DEFAULT_NUM_NODES)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("num-slow-nodes",
"The number of nodes that exhibit delay Default = " + DEFAULT_NUM_SLOW_NODES)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("delay",
"The millisecond delay shown by slow nodes Default = " + DEFAULT_DELAY)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("num-clients",
"The number of threads to make requests concurrently Default = "
+ DEFAULT_NUM_CLIENTS)
.withRequiredArg()
.ofType(Integer.class);
parser.accepts("routed-store-type",
"Type of routed store, either \"" + THREAD_POOL_ROUTED_STORE + "\" or \""
+ PIPELINE_ROUTED_STORE + "\" Default = "
+ DEFAULT_ROUTED_STORE_TYPE).withRequiredArg();
parser.accepts("help", "This help");
OptionSet options = parser.parse(args);
if(options.has("help")) {
printUsage(System.out, parser);
}
final int numKeys = CmdUtils.valueOf(options, "num-keys", DEFAULT_NUM_KEYS);
int maxConnectionsPerNode = CmdUtils.valueOf(options,
"max-connections",
DEFAULT_MAX_CONNECTIONS);
int maxThreads = CmdUtils.valueOf(options, "max-threads", DEFAULT_MAX_THREADS);
int numNodes = CmdUtils.valueOf(options, "num-nodes", DEFAULT_NUM_NODES);
int numSlowNodes = CmdUtils.valueOf(options, "num-slow-nodes", DEFAULT_NUM_SLOW_NODES);
int delay = CmdUtils.valueOf(options, "delay", DEFAULT_DELAY);
int numClients = CmdUtils.valueOf(options, "num-clients", DEFAULT_NUM_CLIENTS);
System.err.println("num-keys : " + numKeys);
System.err.println("max-connections : " + maxConnectionsPerNode);
System.err.println("max-threads : " + maxThreads);
System.err.println("num-nodes : " + numNodes);
System.err.println("num-slow-nodes : " + numSlowNodes);
System.err.println("delay : " + delay);
System.err.println("num-clients : " + numClients);
ClientConfig clientConfig = new ClientConfig().setMaxConnectionsPerNode(maxConnectionsPerNode)
.setMaxThreads(maxThreads);
String storeDefinitionFile = "test/common/voldemort/config/single-store.xml";
StoreDefinition storeDefinition = new StoreDefinitionsMapper().readStoreList(new File(storeDefinitionFile))
.get(0);
SocketStoreFactory socketStoreFactory = new ClientRequestExecutorPool(clientConfig.getSelectors(),
clientConfig.getMaxConnectionsPerNode(),
clientConfig.getConnectionTimeout(TimeUnit.MILLISECONDS),
clientConfig.getSocketTimeout(TimeUnit.MILLISECONDS),
clientConfig.getSocketBufferSize(),
clientConfig.getSocketKeepAlive());
VoldemortServer[] servers = new VoldemortServer[numNodes];
int[][] partitionMap = new int[numNodes][1];
for(int i = 0; i < numNodes; i++) {
partitionMap[i][0] = i;
}
Cluster cluster = ServerTestUtils.startVoldemortCluster(numNodes,
servers,
partitionMap,
socketStoreFactory,
true,
null,
storeDefinitionFile,
new Properties());
Map<Integer, VoldemortServer> serverMap = new HashMap<Integer, VoldemortServer>();
for(int i = 0; i < cluster.getNumberOfNodes(); i++) {
serverMap.put(i, servers[i]);
Store<ByteArray, byte[], byte[]> store = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("test-sleepy");
if(i < numSlowNodes)
store = new SleepyStore<ByteArray, byte[], byte[]>(delay, store);
StoreRepository storeRepository = servers[i].getStoreRepository();
storeRepository.addLocalStore(store);
}
Map<Integer, Store<ByteArray, byte[], byte[]>> stores = new HashMap<Integer, Store<ByteArray, byte[], byte[]>>();
for(Node node: cluster.getNodes()) {
Store<ByteArray, byte[], byte[]> socketStore = ServerTestUtils.getSocketStore(socketStoreFactory,
"test-sleepy",
node.getSocketPort(),
clientConfig.getRequestFormatType());
stores.put(node.getId(), socketStore);
}
FailureDetectorConfig failureDetectorConfig = new FailureDetectorConfig().setImplementationClassName(BannagePeriodFailureDetector.class.getName())
.setCluster(cluster)
.setStoreVerifier(MutableStoreVerifier.create(stores));
FailureDetector failureDetector = FailureDetectorUtils.create(failureDetectorConfig, false);
ExecutorService routedStoreThreadPool = Executors.newFixedThreadPool(clientConfig.getMaxThreads());
RoutedStoreFactory routedStoreFactory = new RoutedStoreFactory(routedStoreThreadPool);
RoutedStoreConfig routedStoreConfig = new RoutedStoreConfig(clientConfig);
final RoutedStore routedStore = routedStoreFactory.create(cluster,
storeDefinition,
stores,
failureDetector,
routedStoreConfig);
ExecutorService runner = Executors.newFixedThreadPool(numClients);
long start = System.nanoTime();
try {
for(int i = 0; i < numClients; i++) {
runner.submit(new Runnable() {
public void run() {
for(int i = 0; i < numKeys; i++) {
ByteArray key = new ByteArray(("test-key-" + i).getBytes());
try {
routedStore.get(key, null);
} catch(VoldemortException e) {
//
}
}
}
});
}
runner.shutdown();
runner.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long time = (System.nanoTime() - start) / Time.NS_PER_MS;
System.err.println("Time: " + time + " ms.");
} finally {
runner.shutdown();
}
if(failureDetector != null)
failureDetector.destroy();
for(VoldemortServer server: serverMap.values())
server.stop();
if(routedStoreThreadPool != null)
routedStoreThreadPool.shutdown();
System.exit(0);
}
private static void printUsage(PrintStream out, OptionParser parser) throws IOException {
out.println("Usage: $VOLDEMORT_HOME/bin/run-class.sh "
+ RoutedStoreParallelismTest.class.getName() + " [options]\n");
parser.printHelpOn(out);
System.exit(1);
}
}