/***************************************************************************
* Copyright (C) 2012 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* Coded By: Justin A. DeBrabant (http://www.cs.brown.edu/~debrabant/) *
* *
* *
* 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.ycsb;
import java.io.IOException;
import java.util.Random;
import org.apache.log4j.Logger;
import org.voltdb.CatalogContext;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ProcedureCallback;
import edu.brown.api.BenchmarkComponent;
import edu.brown.benchmark.ycsb.distributions.ZipfianGenerator;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.rand.RandomDistribution.FlatHistogram;
import edu.brown.statistics.Histogram;
import edu.brown.statistics.ObjectHistogram;
/**
* YCSB Client
* @author jdebrabant
* @author pavlo
*/
public class YCSBClient extends BenchmarkComponent {
private static final Logger LOG = Logger.getLogger(YCSBClient.class);
private static final LoggerBoolean debug = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug);
}
public static enum Transaction {
INSERT_RECORD("Insert Record", YCSBConstants.FREQUENCY_INSERT_RECORD),
DELETE_RECORD("Delete Record", YCSBConstants.FREQUENCY_DELETE_RECORD),
READ_RECORD("Read Record", YCSBConstants.FREQUENCY_READ_RECORD),
SCAN_RECORD("Scan Record", YCSBConstants.FREQUENCY_SCAN_RECORD),
UPDATE_RECORD("Update Record", YCSBConstants.FREQUENCY_UPDATE_RECORD);
/**
* Constructor
*/
private Transaction(String displayName, int weight) {
this.displayName = displayName;
this.callName = displayName.replace(" ", "");
this.weight = weight;
}
public final String displayName;
public final String callName;
public final int weight; // probability (in terms of percentage) the transaction gets executed
} // TRANSCTION ENUM
private final long init_record_count;
// private final CustomSkewGenerator readRecord;
private final ZipfianGenerator readRecord;
@SuppressWarnings("unused")
private final ZipfianGenerator insertRecord;
// private final CustomSkewGenerator insertRecord;
private final ZipfianGenerator randScan;
private final FlatHistogram<Transaction> txnWeights;
private final Random rand_gen;
private double skewFactor = YCSBConstants.ZIPFIAN_CONSTANT;
int run_count = 0;
// private ZipfianGenerator readRecord;
public static void main(String args[]) {
BenchmarkComponent.main(YCSBClient.class, args, false);
}
public YCSBClient(String args[]) {
super(args);
boolean useFixedSize = false;
long fixedSize = -1;
for (String key : m_extraParams.keySet()) {
String value = m_extraParams.get(key);
// Used Fixed-size Database
// Parameter that points to where we can find the initial data files
if (key.equalsIgnoreCase("fixed_size")) {
useFixedSize = Boolean.valueOf(value);
}
// Fixed Database Size
else if (key.equalsIgnoreCase("num_records")) {
fixedSize = Long.valueOf(value);
}
// Zipfian Skew Factor
else if (key.equalsIgnoreCase("skew_factor")) {
this.skewFactor = Double.valueOf(value);
}
} // FOR
// Figure out the # of records that we need
if (useFixedSize && fixedSize > 0) {
this.init_record_count = fixedSize;
}
else {
this.init_record_count = (long)Math.round(YCSBConstants.NUM_RECORDS * this.getScaleFactor());
}
this.rand_gen = new Random();
this.randScan = new ZipfianGenerator(YCSBConstants.MAX_SCAN);
// // initialize distribution generators
// // We must know where to start inserting
// this.insertRecord = new CustomSkewGenerator(this.rand_gen, this.init_record_count,
// YCSBConstants.HOT_DATA_WORKLOAD_SKEW, YCSBConstants.HOT_DATA_SIZE,
// YCSBConstants.WARM_DATA_WORKLOAD_SKEW, YCSBConstants.WARM_DATA_SIZE);
//
// this.readRecord = new CustomSkewGenerator(this.rand_gen, this.init_record_count,
// YCSBConstants.HOT_DATA_WORKLOAD_SKEW, YCSBConstants.HOT_DATA_SIZE,
// YCSBConstants.WARM_DATA_WORKLOAD_SKEW, YCSBConstants.WARM_DATA_SIZE);
this.insertRecord = new ZipfianGenerator(this.init_record_count, this.skewFactor);
this.readRecord = new ZipfianGenerator(this.init_record_count, this.skewFactor);
// Initialize the sampling table
Histogram<Transaction> txns = new ObjectHistogram<Transaction>();
for (Transaction t : Transaction.values()) {
Integer weight = this.getTransactionWeight(t.callName);
if (weight == null) weight = t.weight;
txns.put(t, weight);
} // FOR
assert(txns.getSampleCount() == 100) : txns;
this.txnWeights = new FlatHistogram<Transaction>(this.rand_gen, txns);
}
@SuppressWarnings("unused")
@Deprecated
@Override
public void runLoop() {
try {
Client client = this.getClientHandle();
Random rand = new Random();
int key = -1;
int scan_count;
while (true) {
runOnce();
this.run_count++;
}
}
catch (IOException e) {
}
}
@Override
public boolean runOnce() throws IOException {
// pick random transaction to call, weighted by txnWeights
final Transaction target = this.txnWeights.nextValue();
Object params[];
switch (target) {
case DELETE_RECORD:
case READ_RECORD: {
params = new Object[]{ this.readRecord.nextInt() };
break;
}
case UPDATE_RECORD:
case INSERT_RECORD: {
int key = this.insertRecord.nextInt();
String fields[] = new String[YCSBConstants.NUM_COLUMNS];
for (int i = 0; i < fields.length; i++) {
fields[i] = YCSBUtil.astring(YCSBConstants.COLUMN_LENGTH, YCSBConstants.COLUMN_LENGTH);
} // FOR
params = new Object[]{ key, fields };
break;
}
case SCAN_RECORD:
params = new Object[]{ this.readRecord.nextInt(), this.randScan.nextInt() };
break;
default:
throw new RuntimeException("Unexpected txn '" + target + "'");
} // SWITCH
assert(params != null);
Callback callback = new Callback(target.ordinal());
return this.getClientHandle().callProcedure(callback, target.callName, params);
}
private class Callback implements ProcedureCallback {
private final int idx;
public Callback(int idx) {
this.idx = idx;
}
@Override
public void clientCallback(ClientResponse clientResponse) {
// Increment the BenchmarkComponent's internal counter on the
// number of transactions that have been completed
incrementTransactionCounter(clientResponse, this.idx);
}
} // END CLASS
@Override
public String[] getTransactionDisplayNames() {
// Return an array of transaction names
String procNames[] = new String[YCSBProjectBuilder.PROCEDURES.length];
for (int i = 0; i < procNames.length; i++) {
procNames[i] = YCSBProjectBuilder.PROCEDURES[i].getSimpleName();
}
return (procNames);
}
}