/*************************************************************************** * Copyright (C) 2010 by H-Store Project * * Brown University * * Massachusetts Institute of Technology * * Yale University * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * ***************************************************************************/ package edu.brown.benchmark.locality; import java.io.IOException; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import org.voltdb.TheHashinator; import org.voltdb.catalog.Catalog; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Site; import org.voltdb.client.ClientResponse; import org.voltdb.client.NoConnectionsException; import org.voltdb.client.ProcedureCallback; import edu.brown.api.BenchmarkComponent; import edu.brown.benchmark.locality.LocalityConstants.ExecutionType; import edu.brown.catalog.CatalogUtil; import edu.brown.hstore.conf.HStoreConf; import edu.brown.rand.AbstractRandomGenerator; public class LocalityClient extends BenchmarkComponent { // -------------------------------------------------------------------- // DATA MEMBERS // -------------------------------------------------------------------- private ExecutionType m_type = LocalityConstants.ExecutionType.SAME_SITE; protected final AbstractRandomGenerator m_rng; /** * Number of Records Per Table */ protected final Map<String, Long> table_sizes = new HashMap<String, Long>(); // -------------------------------------------------------------------- // TXN PARAMETER GENERATOR // -------------------------------------------------------------------- public interface LocalityParamGenerator { public Object[] generate(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes); } // -------------------------------------------------------------------- // BENCHMARK CONTROLLER REQUIREMENTS // -------------------------------------------------------------------- public enum Transaction { GetLocal(LocalityConstants.FREQUENCY_GET_LOCAL, new LocalityParamGenerator() { @Override public Object[] generate(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes) { int aid = rng.nextInt(table_sizes.get(LocalityConstants.TABLENAME_TABLEA).intValue()); Object params[] = new Object[] { getDataId(Long.valueOf(String.valueOf(aid)), rng, mtype, catalog, table_sizes) }; return (params); } }), SetLocal(LocalityConstants.FREQUENCY_SET_LOCAL, new LocalityParamGenerator() { @Override public Object[] generate(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes) { Object params[] = new Object[] { rng.nextInt(table_sizes.get(LocalityConstants.TABLENAME_TABLEA).intValue()), rng.astring(5, 50), rng.nextInt((int) LocalityConstants.TABLESIZE_TABLEB_MULTIPLIER), rng.astring(5, 50) }; return (params); } }), GetRemote(LocalityConstants.FREQUENCY_GET_REMOTE, new LocalityParamGenerator() { @Override public Object[] generate(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes) { // pass the same local aid as the aid Integer aid = rng.nextInt(table_sizes.get(LocalityConstants.TABLENAME_TABLEA).intValue()); Object params[] = new Object[] { aid, getDataId(Long.valueOf(String.valueOf(aid)), rng, mtype, catalog, table_sizes) }; return (params); } }), SetRemote(LocalityConstants.FREQUENCY_SET_REMOTE, new LocalityParamGenerator() { @Override public Object[] generate(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes) { /** * long local_a_id, long a_id, String a_value, long b_id, String * b_value */ Integer aid = rng.nextInt(table_sizes.get(LocalityConstants.TABLENAME_TABLEA).intValue()); Object params[] = new Object[] { aid, getDataId(Long.valueOf(String.valueOf(aid)), rng, mtype, catalog, table_sizes), rng.astring(5, 50), rng.nextInt((int) LocalityConstants.TABLESIZE_TABLEB_MULTIPLIER), rng.astring(5, 50) }; return (params); } }); private Transaction(int weight, LocalityParamGenerator generator) { this.weight = weight; this.generator = generator; LocalityClient.TOTAL_WEIGHT += this.weight; } protected static final Map<Integer, Transaction> idx_lookup = new HashMap<Integer, Transaction>(); static { for (Transaction vt : EnumSet.allOf(Transaction.class)) { Transaction.idx_lookup.put(vt.ordinal(), vt); } } public static Transaction get(int idx) { assert (idx >= 0); Transaction ret = Transaction.idx_lookup.get(idx); return (ret); } public Object[] params(AbstractRandomGenerator rng, ExecutionType mtype, Catalog catalog, Map<String, Long> table_sizes) { return (this.generator.generate(rng, mtype, catalog, table_sizes)); } public int getWeight() { return weight; } private final LocalityParamGenerator generator; private final int weight; }; private static int TOTAL_WEIGHT; /** * For a given a_id, return a new a_id that follows the given scheme of the * current ExecutionType for the client. * * @param a_id * @return */ protected static long getDataId(long a_id, AbstractRandomGenerator rng, ExecutionType type, Catalog catalog, Map<String, Long> table_sizes) { long a_id2 = -1; Cluster catalog_clus = CatalogUtil.getCluster(catalog); long temp = LocalityConstants.TABLESIZE_TABLEA / catalog_clus.getNum_partitions(); int num_aids_per_partition = (int) (Math.floor(temp)); int random_int = rng.nextInt(num_aids_per_partition); switch (type) { case SAME_PARTITION: { int partition_num = TheHashinator.hashToPartition(a_id, catalog_clus.getNum_partitions()); System.out.println("Total number of partitions: " + catalog_clus.getNum_partitions()); a_id2 = random_int * catalog_clus.getNum_partitions() + partition_num; break; } case SAME_SITE: { int partition_num = TheHashinator.hashToPartition(a_id, catalog_clus.getNum_partitions()); Site site = CatalogUtil.getPartitionById(catalog, partition_num).getParent(); int num_sites_per_host = CatalogUtil.getSitesPerHost(site).get(site.getHost()).size(); int num_partitions_per_site = site.getPartitions().size(); double a_id_site_num = Math.floor((double) partition_num / (double) num_partitions_per_site); double a_id_host_num = Math.floor((double) a_id_site_num / (double) num_sites_per_host); // determine the partition range for the cluster (with a random // host and random sites) // and pick partition randomly from this range int lowerbound = (int) a_id_host_num * num_sites_per_host * num_partitions_per_site + (int) a_id_site_num * num_partitions_per_site; int upperbound = (int) a_id_host_num * num_sites_per_host * num_partitions_per_site + (int) a_id_site_num * num_partitions_per_site + (num_partitions_per_site - 1); int a_id2_partition_num = rng.numberExcluding(lowerbound, upperbound, partition_num); // get a random partition a_id2 = random_int * catalog_clus.getNum_partitions() + a_id2_partition_num; break; } case SAME_HOST: { int partition_num = TheHashinator.hashToPartition(a_id, catalog_clus.getNum_partitions()); Site site = CatalogUtil.getPartitionById(catalog, partition_num).getParent(); int num_sites_per_host = CatalogUtil.getSitesPerHost(site).get(site.getHost()).size(); int num_partitions_per_site = site.getPartitions().size(); double a_id_site_num = Math.floor((double) partition_num / (double) num_partitions_per_site); double a_id_host_num = Math.floor((double) a_id_site_num / (double) num_sites_per_host); int lowerboundsite = (int) a_id_host_num * num_sites_per_host; int upperboundsite = (int) a_id_host_num * num_sites_per_host + (num_sites_per_host - 1); int new_site = rng.numberExcluding(lowerboundsite, upperboundsite, (int) a_id_site_num); int lowerbound = new_site * num_partitions_per_site; int upperbound = new_site * num_partitions_per_site + (num_partitions_per_site - 1); int a_id2_partition_num = rng.number(lowerbound, upperbound); // get a random partition a_id2 = random_int * catalog_clus.getNum_partitions() + a_id2_partition_num; break; } case REMOTE_HOST: { int total_number_of_hosts = catalog_clus.getHosts().size(); int partition_num = TheHashinator.hashToPartition(a_id, catalog_clus.getNum_partitions()); Site site = CatalogUtil.getPartitionById(catalog, partition_num).getParent(); int num_sites_per_host = CatalogUtil.getSitesPerHost(site).get(site.getHost()).size(); int num_partitions_per_site = site.getPartitions().size(); // get the site number the partition exists on double a_id_site_num = Math.floor((double) partition_num / (double) num_partitions_per_site); double a_id_host_num = Math.floor((double) a_id_site_num / (double) num_sites_per_host); int new_host = (int) a_id_host_num; if (total_number_of_hosts > 1) { new_host = rng.numberExcluding(0, total_number_of_hosts - 1, (int) a_id_host_num); } int new_site = rng.number(0, num_sites_per_host - 1); // determine the partition range for the cluster (with a random // host and random sites) // and pick partition randomly from this range int lowerbound = new_host * num_sites_per_host * num_partitions_per_site + new_site * num_partitions_per_site; int upperbound = new_host * num_sites_per_host * num_partitions_per_site + new_site * num_partitions_per_site + (num_partitions_per_site - 1); int a_id2_partition_num = rng.number(lowerbound, upperbound); a_id2 = random_int * catalog_clus.getNum_partitions() + a_id2_partition_num; break; } case RANDOM: { a_id2 = rng.nextInt(table_sizes.get(LocalityConstants.TABLENAME_TABLEA).intValue()); break; } default: assert (false) : "Unexpected ExecutionType " + type; } // SWITCH assert (a_id2 != -1); return (a_id2); } /** * Transaction Execution Weights */ private static final LocalityClient.Transaction XACT_WEIGHTS[] = new LocalityClient.Transaction[100]; static { int i = 0; int sum = 0; for (Transaction t : LocalityClient.Transaction.values()) { for (int j = 0; j < t.weight; j++, i++) { XACT_WEIGHTS[i] = t; } // FOR sum += t.weight; } // FOR assert (100 == sum); } public static void main(String args[]) { edu.brown.api.BenchmarkComponent.main(LocalityClient.class, args, false); } /** * Constructor * * @param args */ public LocalityClient(String[] args) { super(args); assert (this.getCatalogContext() != null); // Sanity check assert (LocalityClient.TOTAL_WEIGHT == 100); int seed = 0; String randGenClassName = RandomGenerator.class.getName(); String randGenProfilePath = null; for (String key : m_extraParams.keySet()) { String value = m_extraParams.get(key); // Execution Type if (key.equalsIgnoreCase("TYPE")) { m_type = ExecutionType.valueOf(value); } } // FOR AbstractRandomGenerator rng = null; try { rng = AbstractRandomGenerator.factory(randGenClassName, seed); if (randGenProfilePath != null) rng.loadProfile(randGenProfilePath); } catch (Exception ex) { ex.printStackTrace(); System.exit(1); } m_rng = rng; System.err.println("ExecutionType: " + m_type); HStoreConf hstore_conf = this.getHStoreConf(); // Number of Records Per Table this.table_sizes.put(LocalityConstants.TABLENAME_TABLEA, Math.round(LocalityConstants.TABLESIZE_TABLEA * hstore_conf.client.scalefactor)); this.table_sizes.put(LocalityConstants.TABLENAME_TABLEB, Math.round(LocalityConstants.TABLESIZE_TABLEB * hstore_conf.client.scalefactor)); for (String tableName : LocalityConstants.TABLENAMES) { assert (this.table_sizes.containsKey(tableName)) : "Missing table size entry for " + tableName; } // FOR } @Override public String[] getTransactionDisplayNames() { String names[] = new String[Transaction.values().length]; for (int i = 0; i < names.length; i++) { names[i] = Transaction.values()[i].name(); } return names; } @Override public void runLoop() { try { while (true) { runOnce(); this.getClientHandle().backpressureBarrier(); } // WHILE } catch (NoConnectionsException e) { /* * Client has no clean mechanism for terminating with the DB. */ return; } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { /* * At shutdown an IOException is thrown for every connection to the * DB that is lost Ignore the exception here in order to not get * spammed, but will miss lost connections at runtime */ } } @Override protected boolean runOnce() throws IOException { LocalityClient.Transaction txn_type = XACT_WEIGHTS[m_rng.number(0, 99)]; assert (txn_type != null); Object params[] = txn_type.params(m_rng, m_type, this.getCatalogContext().catalog, this.table_sizes); boolean ret = this.getClientHandle().callProcedure(new LocalityCallback(txn_type), txn_type.name(), params); return (ret); } /** * Basic Callback Class */ protected class LocalityCallback implements ProcedureCallback { private final Transaction txn; public LocalityCallback(Transaction txn) { super(); this.txn = txn; } @Override public void clientCallback(ClientResponse clientResponse) { incrementTransactionCounter(clientResponse, this.txn.ordinal()); } } // END CLASS }