/**
* Copyright 2014 LinkedIn Corp. 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.
*/
package com.linkedin.multitenant.workload;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import com.linkedin.multitenant.common.Query;
import com.linkedin.multitenant.common.Query.QueryType;
import com.linkedin.multitenant.main.RunExperiment;
import com.yahoo.ycsb.Utils;
import com.yahoo.ycsb.generator.ConstantIntegerGenerator;
import com.yahoo.ycsb.generator.CounterGenerator;
import com.yahoo.ycsb.generator.DiscreteGenerator;
import com.yahoo.ycsb.generator.HotspotIntegerGenerator;
import com.yahoo.ycsb.generator.IntegerGenerator;
import com.yahoo.ycsb.generator.ScrambledZipfianGenerator;
import com.yahoo.ycsb.generator.SkewedLatestGenerator;
import com.yahoo.ycsb.generator.UniformIntegerGenerator;
import com.yahoo.ycsb.generator.UpdatedUniformIntegerGenerator;
public class CoreWorkload implements Workload
{
public static final String FLAG_JOB_INSERT_RATE = "job.insertRate";
public static final String FLAG_JOB_READ_RATE = "job.readRate";
public static final String FLAG_JOB_DELETE_RATE = "job.deleteRate";
public static final String FLAG_JOB_UPDATE_RATE = "job.updateRate";
public static final String FLAG_JOB_QUERY_DISTRIBUTION = "job.queryDist";
public static final String FLAG_JOB_HOTSPOT_SETFRAC = "job.queryDist.hotSpot.setFrac";
public static final String FLAG_JOB_HOTSPOT_OPNFRAC = "job.queryDist.hotSpot.optFrac";
public static final String FLAG_JOB_VALUE_SIZE_DISTRIBUTION = "job.valueSize.dist";
public static final String FLAG_JOB_VALUE_SIZE = "job.valueSize";
public static final String FLAG_JOB_VALUE_SIZE_MIN = "job.valueSize.min";
public static final String CONST_QUERY_INSERT = "in";
public static final String CONST_QUERY_READ = "re";
public static final String CONST_QUERY_DELETE = "de";
public static final String CONST_QUERY_UPDATE = "up";
public static final String CONST_DIST_UNIFORM = "uniform";
public static final String CONST_DIST_ZIPFIAN = "zipfian";
public static final String CONST_DIST_LATEST = "latest";
public static final String CONST_DIST_HOTSPOT = "hotspot";
public static final String CONST_DIST_CONSTANT = "constant";
private static final Logger _LOG = Logger.getLogger(CoreWorkload.class);
protected int _id;
protected DiscreteGenerator _operationGen;
protected IntegerGenerator _loadInsertKeyGen;
protected IntegerGenerator _transactionKeyGen;
protected IntegerGenerator _transactionInsertKeyGen;
protected IntegerGenerator _valueGen;
protected Random _ranGen;
protected int _rowsResponsible;
public int getRowsResponsible()
{
return _rowsResponsible;
}
@Override
public WorkloadResult init(int myId, int numberOfWorkers, Map<String, String> workPlanProperties, Map<String, String> jobProperties)
{
//get job name
String jobName;
String temp = jobProperties.get(RunExperiment.FLAG_JOB_NAME);
if(temp == null)
{
_LOG.error("Job name is missing");
return WorkloadResult.FAIL;
}
else
{
jobName = temp;
}
//set id
_id = myId;
//set key generator for insert operations in Load phase
_loadInsertKeyGen = new CounterGenerator(0);
//set key generator for insert operations in Run phase
temp = jobProperties.get(RunExperiment.FLAG_JOB_ROW);
if(temp != null)
{
int rowCount = Integer.parseInt(temp);
_LOG.debug("Number of rows for job " + jobName + " is " + rowCount);
_rowsResponsible = rowCount / numberOfWorkers;
_LOG.debug("Thread " + _id + " for job " + jobName + " is responsible for " + _rowsResponsible + " rows");
_transactionInsertKeyGen = new CounterGenerator(_rowsResponsible);
}
else
{
_LOG.error("Number of rows for job " + jobName + " is not specified.");
return WorkloadResult.FAIL;
}
//set operation distributions
_operationGen = new DiscreteGenerator();
float insertRate;
float readRate;
float deleteRate;
float updateRate;
temp = jobProperties.get(FLAG_JOB_INSERT_RATE);
if(temp == null)
{
insertRate = 0;
_LOG.debug("Insert rate for job " + jobName + " is not given. Assigned 0 by default");
}
else
{
insertRate = Float.parseFloat(temp);
_LOG.debug("Insert rate for job " + jobName + " is set to " + insertRate);
}
temp = jobProperties.get(FLAG_JOB_READ_RATE);
if(temp == null)
{
readRate = 0;
_LOG.debug("Read rate for job " + jobName + " is not given. Assigned 0 by default");
}
else
{
readRate = Float.parseFloat(temp);
_LOG.debug("Read rate for job " + jobName + " is set to " + readRate);
}
temp = jobProperties.get(FLAG_JOB_DELETE_RATE);
if(temp == null)
{
deleteRate = 0;
_LOG.debug("Delete rate for job " + jobName + " is not given. Assigned 0 by default");
}
else
{
deleteRate = Float.parseFloat(temp);
_LOG.debug("Delete rate for job " + jobName + " is set to " + deleteRate);
}
temp = jobProperties.get(FLAG_JOB_UPDATE_RATE);
if(temp == null)
{
updateRate = 0;
_LOG.debug("Update rate for job " + jobName + " is not given. Assigned 0 by default");
}
else
{
updateRate = Float.parseFloat(temp);
_LOG.debug("Update rate for job " + jobName + " is set to " + updateRate);
}
//normalize operation rate values
float sum = 0;
if(readRate > 0)
sum += readRate;
if(insertRate > 0)
sum += insertRate;
if(deleteRate > 0)
sum += deleteRate;
if(updateRate > 0)
sum += updateRate;
if(readRate == 0 && insertRate == 0 && deleteRate == 0 && updateRate == 0)
{
_operationGen.addValue(0.25, CONST_QUERY_INSERT);
_operationGen.addValue(0.25, CONST_QUERY_READ);
_operationGen.addValue(0.25, CONST_QUERY_DELETE);
_operationGen.addValue(0.25, CONST_QUERY_UPDATE);
}
else
{
if(readRate > 0)
_operationGen.addValue(readRate/sum, CONST_QUERY_READ);
if(insertRate > 0)
_operationGen.addValue(insertRate/sum, CONST_QUERY_INSERT);
if(deleteRate > 0)
_operationGen.addValue(deleteRate/sum, CONST_QUERY_DELETE);
if(updateRate > 0)
_operationGen.addValue(updateRate/sum, CONST_QUERY_UPDATE);
}
//set query distribution. possible options are uniform, zipfian, latest, hotspot
temp = jobProperties.get(FLAG_JOB_QUERY_DISTRIBUTION);
if(temp == null)
{
temp = CONST_DIST_UNIFORM;
_transactionKeyGen = new UpdatedUniformIntegerGenerator(0, (CounterGenerator) _transactionInsertKeyGen);
}
else
{
temp = temp.toLowerCase();
if(temp.equals(CONST_DIST_UNIFORM))
{
_transactionKeyGen = new UpdatedUniformIntegerGenerator(0, (CounterGenerator) _transactionInsertKeyGen);
}
else if(temp.equals(CONST_DIST_ZIPFIAN))
{
_transactionKeyGen = new ScrambledZipfianGenerator(_rowsResponsible);
}
else if(temp.equals(CONST_DIST_LATEST))
{
_transactionKeyGen = new SkewedLatestGenerator((CounterGenerator) _transactionInsertKeyGen);
}
else if(temp.equals(CONST_DIST_HOTSPOT))
{
double setFrac;
double opnFrac;
String in = jobProperties.get(FLAG_JOB_HOTSPOT_SETFRAC);
if(in == null)
{
setFrac = 0.3;
_LOG.debug("Set fraction for hotspot is missing. It is set to 0.3 by defult.");
}
else
{
setFrac = Double.parseDouble(in);
_LOG.debug("Set fraction for hotspot is set to " + setFrac);
}
in = jobProperties.get(FLAG_JOB_HOTSPOT_OPNFRAC);
if(in == null)
{
opnFrac = 0.3;
_LOG.debug("Operation fraction for hotspot is missing. It is set to 0.3 by default");
}
else
{
opnFrac = Double.parseDouble(in);
_LOG.debug("Operation fraction for hotspot is set to " + opnFrac);
}
_transactionKeyGen = new HotspotIntegerGenerator(0, _rowsResponsible-1, setFrac, opnFrac);
}
else
{
_LOG.error("Unkown query distribution for job " + jobName + ": " + temp);
return WorkloadResult.FAIL;
}
}
_LOG.debug("Query distribution is set to " + temp);
//set value size distribution
int valSize;
int valSizeMin;
temp = jobProperties.get(FLAG_JOB_VALUE_SIZE);
if(temp == null)
{
_LOG.error("Value size for job " + jobName + " is not specified.");
return WorkloadResult.FAIL;
}
else
{
valSize = Integer.parseInt(temp);
}
temp = jobProperties.get(FLAG_JOB_VALUE_SIZE_MIN);
if(temp == null)
{
valSizeMin = 1;
}
else
{
valSizeMin = Integer.parseInt(temp);
}
temp = jobProperties.get(FLAG_JOB_VALUE_SIZE_DISTRIBUTION);
if(temp == null)
{
_valueGen = new ConstantIntegerGenerator(valSize);
temp = CONST_DIST_UNIFORM;
}
else
{
temp = temp.toLowerCase();
if(temp.equals(CONST_DIST_CONSTANT))
{
_valueGen = new ConstantIntegerGenerator(valSize);
}
else if(temp.equals(CONST_DIST_UNIFORM))
{
_valueGen = new UniformIntegerGenerator(valSizeMin, valSize);
}
else if(temp.equals(CONST_DIST_ZIPFIAN))
{
_valueGen = new ScrambledZipfianGenerator(valSizeMin, valSize);
}
else
{
_LOG.error("Unkown value size distribution for job " + jobName);
return WorkloadResult.FAIL;
}
}
_LOG.debug("Value size distibution for job " + jobName + " is set to " + temp);
//init random generator
_ranGen = new Random(_id);
return WorkloadResult.OK;
}
@Override
public Query generateInsertLoad()
{
Query result = new Query();
//create key part
long keyNum = _loadInsertKeyGen.nextInt();
result.setKey(createKeyString(keyNum));
//create value part
int valueSize = _valueGen.nextInt();
byte[] val = new byte[valueSize];
_ranGen.nextBytes(val);
result.setValue(val);
//set query type
result.setType(QueryType.INSERT);
return result;
}
@Override
public Query generateTransaction()
{
String nextOp = _operationGen.nextString();
if(nextOp.equals(CONST_QUERY_INSERT))
{
return generateInsertTransaction();
}
else if(nextOp.equals(CONST_QUERY_READ))
{
return generateReadTransaction();
}
else if(nextOp.equals(CONST_QUERY_DELETE))
{
return generateDeleteTransaction();
}
else
{
return generateUpdateTransaction();
}
}
@Override
public WorkloadResult close()
{
return WorkloadResult.OK;
}
private Query generateInsertTransaction()
{
Query result = new Query();
//create key part
long keyNum = _transactionInsertKeyGen.nextInt();
result.setKey(createKeyString(keyNum));
//create value part
int valueSize = _valueGen.nextInt();
byte[] val = new byte[valueSize];
_ranGen.nextBytes(val);
result.setValue(val);
//set query type
result.setType(QueryType.INSERT);
return result;
}
private Query generateUpdateTransaction()
{
Query result = new Query();
//create key part
long keyNum = _transactionKeyGen.nextInt();
result.setKey(createKeyString(keyNum));
//create value part
int valueSize = _valueGen.nextInt();
byte[] val = new byte[valueSize];
_ranGen.nextBytes(val);
result.setValue(val);
//set query type
result.setType(QueryType.UPDATE);
return result;
}
private Query generateReadTransaction()
{
Query result = new Query();
//create key part
long keyNum = _transactionKeyGen.nextInt();
result.setKey(createKeyString(keyNum));
//set query type
result.setType(QueryType.READ);
return result;
}
private Query generateDeleteTransaction()
{
Query result = new Query();
//create key part
long keyNum = _transactionKeyGen.nextInt();
result.setKey(createKeyString(keyNum));
//set query type
result.setType(QueryType.DELETE);
return result;
}
private String createKeyString(long keyNum)
{
StringBuilder sb = new StringBuilder();
sb.append(Utils.hash(keyNum));
sb.append("-");
sb.append(_id);
return sb.toString();
}
}