Added additional parameter to turn on deterministic creation of values for

specified keys. When running a workload, read data is checked against the
deterministic value and inconsistencies exported to the measurements object.

Discovered flaws in multithreaded YCSB 'latest' workload distribution as
data may be read by one thread before it is written by another.
This commit is contained in:
nixon 2012-08-15 15:57:46 -07:00
Родитель 0caa77fe44
Коммит 5134d46db6
1 изменённых файлов: 150 добавлений и 31 удалений

Просмотреть файл

@ -37,6 +37,9 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Vector; import java.util.Vector;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
/** /**
* The core benchmark scenario. Represents a set of clients doing simple CRUD operations. The relative * The core benchmark scenario. Represents a set of clients doing simple CRUD operations. The relative
@ -88,6 +91,8 @@ public class CoreWorkload extends Workload
int fieldcount; int fieldcount;
private List<String> fieldnames;
/** /**
* The name of the property for the field length distribution. Options are "uniform", "zipfian" (favoring short records), "constant", and "histogram". * The name of the property for the field length distribution. Options are "uniform", "zipfian" (favoring short records), "constant", and "histogram".
* *
@ -148,6 +153,33 @@ public class CoreWorkload extends Workload
boolean writeallfields; boolean writeallfields;
/**
* The name of the property for deciding whether to check all returned
* data against the formation template to ensure data integrity.
*/
public static final String DATA_INTEGRITY_PROPERTY = "dataintegrity";
/**
* The default value for the dataintegrity property.
*/
public static final String DATA_INTEGRITY_PROPERTY_DEFAULT = "false";
/**
* Set to true if want to check correctness of reads. Must also
* be set to true during loading phase to function.
*/
private boolean dataintegrity;
/**
* Response values for data integrity checks.
* Need to be multiples of 1000 to match bucket offsets of
* measurements/OneMeasurementHistogram.java.
*/
private final int DATA_INT_MATCH = 0;
private final int DATA_INT_DEVIATE = 1000;
private final int DATA_INT_UNEXPECTED_NULL = 2000;
/** /**
* The name of the property for the proportion of transactions that are reads. * The name of the property for the proportion of transactions that are reads.
*/ */
@ -306,6 +338,10 @@ public class CoreWorkload extends Workload
table = p.getProperty(TABLENAME_PROPERTY,TABLENAME_PROPERTY_DEFAULT); table = p.getProperty(TABLENAME_PROPERTY,TABLENAME_PROPERTY_DEFAULT);
fieldcount=Integer.parseInt(p.getProperty(FIELD_COUNT_PROPERTY,FIELD_COUNT_PROPERTY_DEFAULT)); fieldcount=Integer.parseInt(p.getProperty(FIELD_COUNT_PROPERTY,FIELD_COUNT_PROPERTY_DEFAULT));
fieldnames = new ArrayList<String>();
for (int i = 0; i < fieldcount; i++) {
fieldnames.add("field" + i);
}
fieldlengthgenerator = CoreWorkload.getFieldLengthGenerator(p); fieldlengthgenerator = CoreWorkload.getFieldLengthGenerator(p);
double readproportion=Double.parseDouble(p.getProperty(READ_PROPORTION_PROPERTY,READ_PROPORTION_PROPERTY_DEFAULT)); double readproportion=Double.parseDouble(p.getProperty(READ_PROPORTION_PROPERTY,READ_PROPORTION_PROPERTY_DEFAULT));
@ -323,6 +359,15 @@ public class CoreWorkload extends Workload
readallfields=Boolean.parseBoolean(p.getProperty(READ_ALL_FIELDS_PROPERTY,READ_ALL_FIELDS_PROPERTY_DEFAULT)); readallfields=Boolean.parseBoolean(p.getProperty(READ_ALL_FIELDS_PROPERTY,READ_ALL_FIELDS_PROPERTY_DEFAULT));
writeallfields=Boolean.parseBoolean(p.getProperty(WRITE_ALL_FIELDS_PROPERTY,WRITE_ALL_FIELDS_PROPERTY_DEFAULT)); writeallfields=Boolean.parseBoolean(p.getProperty(WRITE_ALL_FIELDS_PROPERTY,WRITE_ALL_FIELDS_PROPERTY_DEFAULT));
dataintegrity = Boolean.parseBoolean(p.getProperty(DATA_INTEGRITY_PROPERTY, DATA_INTEGRITY_PROPERTY_DEFAULT));
//Confirm that fieldlengthgenerator returns a constant if data
//integrity check requested.
if (dataintegrity && !(p.getProperty(FIELD_LENGTH_DISTRIBUTION_PROPERTY, FIELD_LENGTH_DISTRIBUTION_PROPERTY_DEFAULT)).equals("constant"))
{
System.err.println("Must have constant field size to check data integrity.");
System.exit(-1);
}
if (p.getProperty(INSERT_ORDER_PROPERTY,INSERT_ORDER_PROPERTY_DEFAULT).compareTo("hashed")==0) if (p.getProperty(INSERT_ORDER_PROPERTY,INSERT_ORDER_PROPERTY_DEFAULT).compareTo("hashed")==0)
{ {
orderedinserts=false; orderedinserts=false;
@ -427,24 +472,61 @@ public class CoreWorkload extends Workload
} }
return "user"+keynum; return "user"+keynum;
} }
HashMap<String, ByteIterator> buildValues() {
/**
* Builds a value for a randomly chosen field.
*/
private HashMap<String, ByteIterator> buildSingleValue(String key) {
HashMap<String,ByteIterator> value = new HashMap<String,ByteIterator>();
String fieldkey = fieldnames.get(Integer.parseInt(fieldchooser.nextString()));
ByteIterator data;
if (dataintegrity) {
data = new StringByteIterator(buildDeterministicValue(key, fieldkey));
} else {
//fill with random data
data = new RandomByteIterator(fieldlengthgenerator.nextInt());
}
value.put(fieldkey,data);
return value;
}
/**
* Builds values for all fields.
*/
private HashMap<String, ByteIterator> buildValues(String key) {
HashMap<String,ByteIterator> values = new HashMap<String,ByteIterator>(); HashMap<String,ByteIterator> values = new HashMap<String,ByteIterator>();
for (int i=0; i<fieldcount; i++) for (String fieldkey : fieldnames) {
{ ByteIterator data;
String fieldkey="field"+i; if (dataintegrity) {
ByteIterator data= new RandomByteIterator(fieldlengthgenerator.nextInt()); data = new StringByteIterator(buildDeterministicValue(key, fieldkey));
} else {
//fill with random data
data = new RandomByteIterator(fieldlengthgenerator.nextInt());
}
values.put(fieldkey,data); values.put(fieldkey,data);
} }
return values; return values;
} }
HashMap<String, ByteIterator> buildUpdate() {
//update a random field /**
HashMap<String, ByteIterator> values=new HashMap<String,ByteIterator>(); * Build a deterministic value given the key information.
String fieldname="field"+fieldchooser.nextString(); */
ByteIterator data = new RandomByteIterator(fieldlengthgenerator.nextInt()); private String buildDeterministicValue(String key, String fieldkey) {
values.put(fieldname,data); int size = fieldlengthgenerator.nextInt();
return values; StringBuilder sb = new StringBuilder(size);
sb.append(key);
sb.append(':');
sb.append(fieldkey);
while (sb.length() < size) {
sb.append(':');
sb.append(sb.toString().hashCode());
}
sb.setLength(size);
return sb.toString();
} }
/** /**
@ -457,7 +539,7 @@ public class CoreWorkload extends Workload
{ {
int keynum=keysequence.nextInt(); int keynum=keysequence.nextInt();
String dbkey = buildKeyName(keynum); String dbkey = buildKeyName(keynum);
HashMap<String, ByteIterator> values = buildValues(); HashMap<String, ByteIterator> values = buildValues(dbkey);
if (db.insert(table,dbkey,values) == 0) if (db.insert(table,dbkey,values) == 0)
return true; return true;
else else
@ -498,6 +580,30 @@ public class CoreWorkload extends Workload
return true; return true;
} }
/**
* Results are reported in the first three buckets of the histogram under
* the label "VERIFY".
* Bucket 0 means the expected data was returned.
* Bucket 1 means incorrect data was returned.
* Bucket 2 means null data was returned when some data was expected.
*/
protected void verifyRow(String key, HashMap<String,ByteIterator> cells) {
int matchType = DATA_INT_MATCH;
if (!cells.isEmpty()) {
for (Map.Entry<String, ByteIterator> entry : cells.entrySet()) {
if (!entry.getValue().toString().equals(
buildDeterministicValue(key, entry.getKey()))) {
matchType = DATA_INT_DEVIATE;
break;
}
}
} else {
//This assumes that null data is never valid
matchType = DATA_INT_UNEXPECTED_NULL;
}
Measurements.getMeasurements().measure("VERIFY", matchType);
}
int nextKeynum() { int nextKeynum() {
int keynum; int keynum;
if(keychooser instanceof ExponentialGenerator) { if(keychooser instanceof ExponentialGenerator) {
@ -528,13 +634,19 @@ public class CoreWorkload extends Workload
if (!readallfields) if (!readallfields)
{ {
//read a random field //read a random field
String fieldname="field"+fieldchooser.nextString(); String fieldname=fieldnames.get(Integer.parseInt(fieldchooser.nextString()));
fields=new HashSet<String>(); fields=new HashSet<String>();
fields.add(fieldname); fields.add(fieldname);
} }
db.read(table,keyname,fields,new HashMap<String,ByteIterator>()); HashMap<String,ByteIterator> cells =
new HashMap<String,ByteIterator>();
db.read(table,keyname,fields,cells);
if (dataintegrity) {
verifyRow(keyname, cells);
}
} }
public void doTransactionReadModifyWrite(DB db) public void doTransactionReadModifyWrite(DB db)
@ -549,7 +661,7 @@ public class CoreWorkload extends Workload
if (!readallfields) if (!readallfields)
{ {
//read a random field //read a random field
String fieldname="field"+fieldchooser.nextString(); String fieldname=fieldnames.get(Integer.parseInt(fieldchooser.nextString()));
fields=new HashSet<String>(); fields=new HashSet<String>();
fields.add(fieldname); fields.add(fieldname);
@ -560,24 +672,31 @@ public class CoreWorkload extends Workload
if (writeallfields) if (writeallfields)
{ {
//new data for all the fields //new data for all the fields
values = buildValues(); values = buildValues(keyname);
} }
else else
{ {
//update a random field //update a random field
values = buildUpdate(); values = buildSingleValue(keyname);
} }
//do the transaction //do the transaction
HashMap<String,ByteIterator> cells =
new HashMap<String,ByteIterator>();
long st=System.nanoTime(); long st=System.nanoTime();
db.read(table,keyname,fields,new HashMap<String,ByteIterator>()); db.read(table,keyname,fields,cells);
db.update(table,keyname,values); db.update(table,keyname,values);
long en=System.nanoTime(); long en=System.nanoTime();
if (dataintegrity) {
verifyRow(keyname, cells);
}
Measurements.getMeasurements().measure("READ-MODIFY-WRITE", (int)((en-st)/1000)); Measurements.getMeasurements().measure("READ-MODIFY-WRITE", (int)((en-st)/1000));
} }
@ -596,7 +715,7 @@ public class CoreWorkload extends Workload
if (!readallfields) if (!readallfields)
{ {
//read a random field //read a random field
String fieldname="field"+fieldchooser.nextString(); String fieldname=fieldnames.get(Integer.parseInt(fieldchooser.nextString()));
fields=new HashSet<String>(); fields=new HashSet<String>();
fields.add(fieldname); fields.add(fieldname);
@ -617,12 +736,12 @@ public class CoreWorkload extends Workload
if (writeallfields) if (writeallfields)
{ {
//new data for all the fields //new data for all the fields
values = buildValues(); values = buildValues(keyname);
} }
else else
{ {
//update a random field //update a random field
values = buildUpdate(); values = buildSingleValue(keyname);
} }
db.update(table,keyname,values); db.update(table,keyname,values);
@ -635,7 +754,7 @@ public class CoreWorkload extends Workload
String dbkey = buildKeyName(keynum); String dbkey = buildKeyName(keynum);
HashMap<String, ByteIterator> values = buildValues(); HashMap<String, ByteIterator> values = buildValues(dbkey);
db.insert(table,dbkey,values); db.insert(table,dbkey,values);
} }
} }