diff --git a/core/src/main/java/com/yahoo/ycsb/Client.java b/core/src/main/java/com/yahoo/ycsb/Client.java index d6477256..07c46034 100644 --- a/core/src/main/java/com/yahoo/ycsb/Client.java +++ b/core/src/main/java/com/yahoo/ycsb/Client.java @@ -1,979 +1,1109 @@ /** * Copyright (c) 2010 - 2016 Yahoo! Inc. All rights reserved. * * 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. See accompanying * LICENSE file. */ package com.yahoo.ycsb; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import com.yahoo.ycsb.measurements.Measurements; import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; import com.yahoo.ycsb.measurements.exporter.TextMeasurementsExporter; /** * A thread to periodically show the status of the experiment, to reassure you that progress is being made. * * @author cooperb */ class StatusThread extends Thread { /** Counts down each of the clients completing. */ private final CountDownLatch _completeLatch; + /** Stores the measurements for the run. */ + private final Measurements _measurements; + + /** Whether or not to track the JVM stats per run */ + private final boolean _trackJVMStats; + /** The clients that are running. */ private final List _clients; private final String _label; private final boolean _standardstatus; /** The interval for reporting status. */ private long _sleeptimeNs; + /** JVM max/mins */ + private int _maxThreads; + private int _minThreads = Integer.MAX_VALUE; + private long _maxUsedMem; + private long _minUsedMem = Long.MAX_VALUE; + private double _maxLoadAvg; + private double _minLoadAvg = Double.MAX_VALUE; + private long lastGCCount = 0; + /** - * Creates a new StatusThread. + * Creates a new StatusThread without JVM stat tracking. * * @param completeLatch The latch that each client thread will {@link CountDownLatch#countDown()} as they complete. * @param clients The clients to collect metrics from. * @param label The label for the status. * @param standardstatus If true the status is printed to stdout in addition to stderr. * @param statusIntervalSeconds The number of seconds between status updates. */ public StatusThread(CountDownLatch completeLatch, List clients, String label, boolean standardstatus, int statusIntervalSeconds) + { + this(completeLatch, clients, label, standardstatus, statusIntervalSeconds, false); + } + + /** + * Creates a new StatusThread. + * + * @param completeLatch The latch that each client thread will {@link CountDownLatch#countDown()} as they complete. + * @param clients The clients to collect metrics from. + * @param label The label for the status. + * @param standardstatus If true the status is printed to stdout in addition to stderr. + * @param statusIntervalSeconds The number of seconds between status updates. + * @param trackJVMStats Whether or not to track JVM stats. + */ + public StatusThread(CountDownLatch completeLatch, List clients, + String label, boolean standardstatus, int statusIntervalSeconds, + boolean trackJVMStats) { _completeLatch=completeLatch; _clients=clients; _label=label; _standardstatus=standardstatus; _sleeptimeNs=TimeUnit.SECONDS.toNanos(statusIntervalSeconds); + _measurements = Measurements.getMeasurements(); + _trackJVMStats = trackJVMStats; } - + /** * Run and periodically report status. */ @Override public void run() { final long startTimeMs=System.currentTimeMillis(); final long startTimeNanos = System.nanoTime(); long deadline = startTimeNanos + _sleeptimeNs; long startIntervalMs=startTimeMs; long lastTotalOps=0; boolean alldone; do { long nowMs=System.currentTimeMillis(); lastTotalOps = computeStats(startTimeMs, startIntervalMs, nowMs, lastTotalOps); + + if (_trackJVMStats) { + measureJVM(); + } alldone = waitForClientsUntil(deadline); startIntervalMs=nowMs; deadline+=_sleeptimeNs; } while (!alldone); + if (_trackJVMStats) { + measureJVM(); + } // Print the final stats. computeStats(startTimeMs, startIntervalMs, System.currentTimeMillis(), lastTotalOps); } /** * Computes and prints the stats. * * @param startTimeMs The start time of the test. * @param startIntervalMs The start time of this interval. * @param endIntervalMs The end time (now) for the interval. * @param lastTotalOps The last total operations count. * * @return The current operation count. */ private long computeStats(final long startTimeMs, long startIntervalMs, long endIntervalMs, long lastTotalOps) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); long totalops=0; long todoops=0; // Calculate the total number of operations completed. for (ClientThread t : _clients) { totalops+=t.getOpsDone(); todoops+=t.getOpsTodo(); } long interval=endIntervalMs-startTimeMs; double throughput=1000.0*(((double)totalops)/(double)interval); double curthroughput=1000.0*(((double)(totalops-lastTotalOps))/((double)(endIntervalMs-startIntervalMs))); long estremaining = (long) Math.ceil(todoops / throughput); DecimalFormat d = new DecimalFormat("#.##"); String label = _label + format.format(new Date()); StringBuilder msg = new StringBuilder(label).append(" ").append(interval/1000).append(" sec: "); msg.append(totalops).append(" operations; "); if (totalops != 0) { msg.append(d.format(curthroughput)).append(" current ops/sec; "); } if (todoops != 0) { msg.append("est completion in ").append(RemainingFormatter.format(estremaining)); } msg.append(Measurements.getMeasurements().getSummary()); System.err.println(msg); if (_standardstatus) { System.out.println(msg); } return totalops; } /** * Waits for all of the client to finish or the deadline to expire. * * @param deadline The current deadline. * * @return True if all of the clients completed. */ private boolean waitForClientsUntil(long deadline) { boolean alldone=false; long now=System.nanoTime(); while( !alldone && now < deadline ) { try { alldone = _completeLatch.await(deadline-now, TimeUnit.NANOSECONDS); } catch( InterruptedException ie) { // If we are interrupted the thread is being asked to shutdown. // Return true to indicate that and reset the interrupt state // of the thread. Thread.currentThread().interrupt(); alldone=true; } now=System.nanoTime(); } return alldone; } + + /** Executes the JVM measurements. */ + private void measureJVM() { + final int threads = Utils.getActiveThreadCount(); + if (threads < _minThreads) { + _minThreads = threads; + } + if (threads > _maxThreads) { + _maxThreads = threads; + } + _measurements.measure("THREAD_COUNT", threads); + + // TODO - once measurements allow for other number types, switch to using + // the raw bytes. Otherwise we can track in MB to avoid negative values + // when faced with huge heaps. + final int usedMem = Utils.getUsedMemoryMegaBytes(); + if (usedMem < _minUsedMem) { + _minUsedMem = usedMem; + } + if (usedMem > _maxUsedMem) { + _maxUsedMem = usedMem; + } + _measurements.measure("USED_MEM_MB", usedMem); + + // Some JVMs may not implement this feature so if the value is less than + // zero, just ommit it. + final double systemLoad = Utils.getSystemLoadAverage(); + if (systemLoad >= 0) { + // TODO - store the double if measurements allows for them + _measurements.measure("SYS_LOAD_AVG", (int)systemLoad); + if (systemLoad > _maxLoadAvg) { + _maxLoadAvg = systemLoad; + } + if (systemLoad < _minLoadAvg) { + _minLoadAvg = systemLoad; + } + } + + final long gcs = Utils.getGCTotalCollectionCount(); + _measurements.measure("GCS", (int)(gcs - lastGCCount)); + lastGCCount = gcs; + } + + /** @return The maximum threads running during the test. */ + public int getMaxThreads() { + return _maxThreads; + } + + /** @return The minimum threads running during the test. */ + public int getMinThreads() { + return _minThreads; + } + + /** @return The maximum memory used during the test. */ + public long getMaxUsedMem() { + return _maxUsedMem; + } + + /** @return The minimum memory used during the test. */ + public long getMinUsedMem() { + return _minUsedMem; + } + + /** @return The maximum load average during the test. */ + public double getMaxLoadAvg() { + return _maxLoadAvg; + } + + /** @return The minimum load average during the test. */ + public double getMinLoadAvg() { + return _minLoadAvg; + } + + /** @return Whether or not the thread is tracking JVM stats. */ + public boolean trackJVMStats() { + return _trackJVMStats; + } } /** * Turn seconds remaining into more useful units. * i.e. if there are hours or days worth of seconds, use them. */ class RemainingFormatter { public static StringBuilder format(long seconds) { StringBuilder time = new StringBuilder(); long days = TimeUnit.SECONDS.toDays(seconds); if (days > 0) { time.append(days).append(" days "); seconds -= TimeUnit.DAYS.toSeconds(days); } long hours = TimeUnit.SECONDS.toHours(seconds); if (hours > 0) { time.append(hours).append(" hours "); seconds -= TimeUnit.HOURS.toSeconds(hours); } /* Only include minute granularity if we're < 1 day. */ if (days < 1) { long minutes = TimeUnit.SECONDS.toMinutes(seconds); if (minutes > 0) { time.append(minutes).append(" minutes "); seconds -= TimeUnit.MINUTES.toSeconds(seconds); } } /* Only bother to include seconds if we're < 1 minute */ if (time.length() == 0) { time.append(seconds).append(" seconds "); } return time; } } /** * A thread for executing transactions or data inserts to the database. * * @author cooperb * */ class ClientThread extends Thread { /** Counts down each of the clients completing. */ private final CountDownLatch _completeLatch; private static boolean _spinSleep; DB _db; boolean _dotransactions; Workload _workload; int _opcount; double _targetOpsPerMs; int _opsdone; int _threadid; int _threadcount; Object _workloadstate; Properties _props; long _targetOpsTickNs; final Measurements _measurements; /** * Constructor. * * @param db the DB implementation to use * @param dotransactions true to do transactions, false to insert data * @param workload the workload to use * @param props the properties defining the experiment * @param opcount the number of operations (transactions or inserts) to do * @param targetperthreadperms target number of operations per thread per ms * @param completeLatch The latch tracking the completion of all clients. */ public ClientThread(DB db, boolean dotransactions, Workload workload, Properties props, int opcount, double targetperthreadperms, CountDownLatch completeLatch) { _db=db; _dotransactions=dotransactions; _workload=workload; _opcount=opcount; _opsdone=0; if(targetperthreadperms > 0){ _targetOpsPerMs=targetperthreadperms; _targetOpsTickNs=(long)(1000000/_targetOpsPerMs); } _props=props; _measurements = Measurements.getMeasurements(); _spinSleep = Boolean.valueOf(_props.getProperty("spin.sleep", "false")); _completeLatch=completeLatch; } public int getOpsDone() { return _opsdone; } @Override public void run() { try { _db.init(); } catch (DBException e) { e.printStackTrace(); e.printStackTrace(System.out); return; } try { _workloadstate=_workload.initThread(_props,_threadid,_threadcount); } catch (WorkloadException e) { e.printStackTrace(); e.printStackTrace(System.out); return; } //NOTE: Switching to using nanoTime and parkNanos for time management here such that the measurements // and the client thread have the same view on time. //spread the thread operations out so they don't all hit the DB at the same time // GH issue 4 - throws exception if _target>1 because random.nextInt argument must be >0 // and the sleep() doesn't make sense for granularities < 1 ms anyway if ((_targetOpsPerMs > 0) && (_targetOpsPerMs <= 1.0)) { long randomMinorDelay = Utils.random().nextInt((int) _targetOpsTickNs); sleepUntil(System.nanoTime() + randomMinorDelay); } try { if (_dotransactions) { long startTimeNanos = System.nanoTime(); while (((_opcount == 0) || (_opsdone < _opcount)) && !_workload.isStopRequested()) { if (!_workload.doTransaction(_db,_workloadstate)) { break; } _opsdone++; throttleNanos(startTimeNanos); } } else { long startTimeNanos = System.nanoTime(); while (((_opcount == 0) || (_opsdone < _opcount)) && !_workload.isStopRequested()) { if (!_workload.doInsert(_db,_workloadstate)) { break; } _opsdone++; throttleNanos(startTimeNanos); } } } catch (Exception e) { e.printStackTrace(); e.printStackTrace(System.out); System.exit(0); } try { _measurements.setIntendedStartTimeNs(0); _db.cleanup(); } catch (DBException e) { e.printStackTrace(); e.printStackTrace(System.out); return; } finally { _completeLatch.countDown(); } } static void sleepUntil(long deadline) { long now = System.nanoTime(); while((now = System.nanoTime()) < deadline) { if (!_spinSleep) { LockSupport.parkNanos(deadline - now); } } } private void throttleNanos(long startTimeNanos) { //throttle the operations if (_targetOpsPerMs > 0) { // delay until next tick long deadline = startTimeNanos + _opsdone*_targetOpsTickNs; sleepUntil(deadline); _measurements.setIntendedStartTimeNs(deadline); } } /** * the total amount of work this thread is still expected to do */ public int getOpsTodo() { int todo = _opcount - _opsdone; return todo < 0 ? 0 : todo; } } /** * Main class for executing YCSB. */ public class Client { public static final String DEFAULT_RECORD_COUNT = "0"; /** * The target number of operations to perform. */ public static final String OPERATION_COUNT_PROPERTY="operationcount"; /** * The number of records to load into the database initially. */ public static final String RECORD_COUNT_PROPERTY="recordcount"; /** * The workload class to be loaded. */ public static final String WORKLOAD_PROPERTY="workload"; /** * The database class to be used. */ public static final String DB_PROPERTY="db"; /** * The exporter class to be used. The default is * com.yahoo.ycsb.measurements.exporter.TextMeasurementsExporter. */ public static final String EXPORTER_PROPERTY="exporter"; /** * If set to the path of a file, YCSB will write all output to this file * instead of STDOUT. */ public static final String EXPORT_FILE_PROPERTY="exportfile"; /** * The number of YCSB client threads to run. */ public static final String THREAD_COUNT_PROPERTY="threadcount"; /** * Indicates how many inserts to do, if less than recordcount. Useful for partitioning * the load among multiple servers, if the client is the bottleneck. Additionally, workloads * should support the "insertstart" property, which tells them which record to start at. */ public static final String INSERT_COUNT_PROPERTY="insertcount"; /** * Target number of operations per second */ public static final String TARGET_PROPERTY="target"; /** * The maximum amount of time (in seconds) for which the benchmark will be run. */ public static final String MAX_EXECUTION_TIME = "maxexecutiontime"; /** * Whether or not this is the transaction phase (run) or not (load). */ public static final String DO_TRANSACTIONS_PROPERTY = "dotransactions"; + /** An optional thread used to track progress and measure JVM stats. */ + private static StatusThread statusthread = null; public static void usageMessage() { System.out.println("Usage: java com.yahoo.ycsb.Client [options]"); System.out.println("Options:"); System.out.println(" -threads n: execute using n threads (default: 1) - can also be specified as the \n" + " \"threadcount\" property using -p"); System.out.println(" -target n: attempt to do n operations per second (default: unlimited) - can also\n" + " be specified as the \"target\" property using -p"); System.out.println(" -load: run the loading phase of the workload"); System.out.println(" -t: run the transactions phase of the workload (default)"); System.out.println(" -db dbname: specify the name of the DB to use (default: com.yahoo.ycsb.BasicDB) - \n" + " can also be specified as the \"db\" property using -p"); System.out.println(" -P propertyfile: load properties from the given file. Multiple files can"); System.out.println(" be specified, and will be processed in the order specified"); System.out.println(" -p name=value: specify a property to be passed to the DB and workloads;"); System.out.println(" multiple properties can be specified, and override any"); System.out.println(" values in the propertyfile"); System.out.println(" -s: show status during run (default: no status)"); System.out.println(" -l label: use label for status (e.g. to label one experiment out of a whole batch)"); System.out.println(""); System.out.println("Required properties:"); System.out.println(" "+WORKLOAD_PROPERTY+": the name of the workload class to use (e.g. com.yahoo.ycsb.workloads.CoreWorkload)"); System.out.println(""); System.out.println("To run the transaction phase from multiple servers, start a separate client on each."); System.out.println("To run the load phase from multiple servers, start a separate client on each; additionally,"); System.out.println("use the \"insertcount\" and \"insertstart\" properties to divide up the records to be inserted"); } public static boolean checkRequiredProperties(Properties props) { if (props.getProperty(WORKLOAD_PROPERTY)==null) { System.out.println("Missing property: "+WORKLOAD_PROPERTY); return false; } return true; } /** * Exports the measurements to either sysout or a file using the exporter * loaded from conf. * @throws IOException Either failed to write to output stream or failed to close it. */ private static void exportMeasurements(Properties props, int opcount, long runtime) throws IOException { MeasurementsExporter exporter = null; try { // if no destination file is provided the results will be written to stdout OutputStream out; String exportFile = props.getProperty(EXPORT_FILE_PROPERTY); if (exportFile == null) { out = System.out; } else { out = new FileOutputStream(exportFile); } // if no exporter is provided the default text one will be used String exporterStr = props.getProperty(EXPORTER_PROPERTY, "com.yahoo.ycsb.measurements.exporter.TextMeasurementsExporter"); try { exporter = (MeasurementsExporter) Class.forName(exporterStr).getConstructor(OutputStream.class).newInstance(out); } catch (Exception e) { System.err.println("Could not find exporter " + exporterStr + ", will use default text reporter."); e.printStackTrace(); exporter = new TextMeasurementsExporter(out); } exporter.write("OVERALL", "RunTime(ms)", runtime); double throughput = 1000.0 * (opcount) / (runtime); exporter.write("OVERALL", "Throughput(ops/sec)", throughput); + + exporter.write("TOTAL_GCs", "Count", Utils.getGCTotalCollectionCount()); + if (statusthread != null && statusthread.trackJVMStats()) { + exporter.write("MAX_MEM_USED", "MBs", statusthread.getMaxUsedMem()); + exporter.write("MIN_MEM_USED", "MBs", statusthread.getMinUsedMem()); + exporter.write("MAX_THREADS", "Count", statusthread.getMaxThreads()); + exporter.write("MIN_THREADS", "Count", statusthread.getMinThreads()); + exporter.write("MAX_SYS_LOAD_AVG", "Load", statusthread.getMaxLoadAvg()); + exporter.write("MIN_SYS_LOAD_AVG", "Load", statusthread.getMinLoadAvg()); + } Measurements.getMeasurements().exportMeasurements(exporter); } finally { if (exporter != null) { exporter.close(); } } } @SuppressWarnings("unchecked") public static void main(String[] args) { String dbname; Properties props=new Properties(); Properties fileprops=new Properties(); boolean dotransactions=true; int threadcount=1; int target=0; boolean status=false; String label=""; //parse arguments int argindex=0; if (args.length==0) { usageMessage(); System.out.println("At least one argument specifying a workload is required."); System.exit(0); } while (args[argindex].startsWith("-")) { if (args[argindex].compareTo("-threads")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -threads."); System.exit(0); } int tcount=Integer.parseInt(args[argindex]); props.setProperty(THREAD_COUNT_PROPERTY, String.valueOf(tcount)); argindex++; } else if (args[argindex].compareTo("-target")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -target."); System.exit(0); } int ttarget=Integer.parseInt(args[argindex]); props.setProperty(TARGET_PROPERTY, String.valueOf(ttarget)); argindex++; } else if (args[argindex].compareTo("-load")==0) { dotransactions=false; argindex++; } else if (args[argindex].compareTo("-t")==0) { dotransactions=true; argindex++; } else if (args[argindex].compareTo("-s")==0) { status=true; argindex++; } else if (args[argindex].compareTo("-db")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -db."); System.exit(0); } props.setProperty(DB_PROPERTY,args[argindex]); argindex++; } else if (args[argindex].compareTo("-l")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -l."); System.exit(0); } label=args[argindex]; argindex++; } else if (args[argindex].compareTo("-P")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -P."); System.exit(0); } String propfile=args[argindex]; argindex++; Properties myfileprops=new Properties(); try { myfileprops.load(new FileInputStream(propfile)); } catch (IOException e) { System.out.println("Unable to open the properties file " + propfile); System.out.println(e.getMessage()); System.exit(0); } //Issue #5 - remove call to stringPropertyNames to make compilable under Java 1.5 for (Enumeration e=myfileprops.propertyNames(); e.hasMoreElements(); ) { String prop=(String)e.nextElement(); fileprops.setProperty(prop,myfileprops.getProperty(prop)); } } else if (args[argindex].compareTo("-p")==0) { argindex++; if (argindex>=args.length) { usageMessage(); System.out.println("Missing argument value for -p"); System.exit(0); } int eq=args[argindex].indexOf('='); if (eq<0) { usageMessage(); System.out.println("Argument '-p' expected to be in key=value format (e.g., -p operationcount=99999)"); System.exit(0); } String name=args[argindex].substring(0,eq); String value=args[argindex].substring(eq+1); props.put(name,value); //System.out.println("["+name+"]=["+value+"]"); argindex++; } else { usageMessage(); System.out.println("Unknown option " + args[argindex]); System.exit(0); } if (argindex>=args.length) { break; } } if (argindex != args.length) { usageMessage(); if (argindex < args.length) { System.out.println("An argument value without corresponding argument specifier (e.g., -p, -s) was found. " + "We expected an argument specifier and instead found " + args[argindex]); } else { System.out.println("An argument specifier without corresponding value was found at the end of the supplied command line arguments."); } System.exit(0); } //set up logging //BasicConfigurator.configure(); //overwrite file properties with properties from the command line //Issue #5 - remove call to stringPropertyNames to make compilable under Java 1.5 for (Enumeration e=props.propertyNames(); e.hasMoreElements(); ) { String prop=(String)e.nextElement(); fileprops.setProperty(prop,props.getProperty(prop)); } props=fileprops; if (!checkRequiredProperties(props)) { System.out.println("Failed check required properties."); System.exit(0); } props.setProperty(DO_TRANSACTIONS_PROPERTY, String.valueOf(dotransactions)); long maxExecutionTime = Integer.parseInt(props.getProperty(MAX_EXECUTION_TIME, "0")); //get number of threads, target and db threadcount=Integer.parseInt(props.getProperty(THREAD_COUNT_PROPERTY,"1")); dbname=props.getProperty(DB_PROPERTY,"com.yahoo.ycsb.BasicDB"); target=Integer.parseInt(props.getProperty(TARGET_PROPERTY,"0")); //compute the target throughput double targetperthreadperms=-1; if (target>0) { double targetperthread=((double)target)/((double)threadcount); targetperthreadperms=targetperthread/1000.0; } //show a warning message that creating the workload is taking a while //but only do so if it is taking longer than 2 seconds //(showing the message right away if the setup wasn't taking very long was confusing people) Thread warningthread=new Thread() { @Override public void run() { try { sleep(2000); } catch (InterruptedException e) { return; } System.err.println(" (might take a few minutes for large data sets)"); } }; warningthread.start(); //set up measurements Measurements.setProperties(props); //load the workload ClassLoader classLoader = Client.class.getClassLoader(); try { Properties projectProp = new Properties(); projectProp.load(classLoader.getResourceAsStream("project.properties")); System.err.println("YCSB Client " + projectProp.getProperty("version")); } catch (IOException e) { System.err.println("Unable to retrieve client version."); } System.err.print("Command line:"); for (int i=0; i clients=new ArrayList(threadcount); for (int threadid=0; threadid 0) { terminator = new TerminatorThread(maxExecutionTime, clients, workload); terminator.start(); } int opsDone = 0; for (Thread t : clients) { try { t.join(); opsDone += ((ClientThread)t).getOpsDone(); } catch (InterruptedException e) { } } long en=System.currentTimeMillis(); if (terminator != null && !terminator.isInterrupted()) { terminator.interrupt(); } if (status) { // wake up status thread if it's asleep statusthread.interrupt(); // at this point we assume all the monitored threads are already gone as per above join loop. try { statusthread.join(); } catch (InterruptedException e) { } } try { workload.cleanup(); } catch (WorkloadException e) { e.printStackTrace(); e.printStackTrace(System.out); System.exit(0); } try { exportMeasurements(props, opsDone, en - st); } catch (IOException e) { System.err.println("Could not export measurements, error: " + e.getMessage()); e.printStackTrace(); System.exit(-1); } System.exit(0); } } diff --git a/core/src/main/java/com/yahoo/ycsb/Utils.java b/core/src/main/java/com/yahoo/ycsb/Utils.java index 5fe699af..21990677 100644 --- a/core/src/main/java/com/yahoo/ycsb/Utils.java +++ b/core/src/main/java/com/yahoo/ycsb/Utils.java @@ -1,177 +1,226 @@ /** * Copyright (c) 2010 Yahoo! Inc., 2016 YCSB contributors. All rights reserved. * * 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. See accompanying * LICENSE file. */ package com.yahoo.ycsb; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.util.List; import java.util.Random; /** * Utility functions. */ public class Utils { private static final Random rand = new Random(); private static final ThreadLocal rng = new ThreadLocal(); public static Random random() { Random ret = rng.get(); if(ret == null) { ret = new Random(rand.nextLong()); rng.set(ret); } return ret; } /** * Generate a random ASCII string of a given length. */ public static String ASCIIString(int length) { int interval='~'-' '+1; byte []buf = new byte[length]; random().nextBytes(buf); for (int i = 0; i < length; i++) { if (buf[i] < 0) { buf[i] = (byte)((-buf[i] % interval) + ' '); } else { buf[i] = (byte)((buf[i] % interval) + ' '); } } return new String(buf); } /** * Hash an integer value. */ public static long hash(long val) { return FNVhash64(val); } public static final int FNV_offset_basis_32=0x811c9dc5; public static final int FNV_prime_32=16777619; /** * 32 bit FNV hash. Produces more "random" hashes than (say) String.hashCode(). * * @param val The value to hash. * @return The hash value */ public static int FNVhash32(int val) { //from http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash int hashval = FNV_offset_basis_32; for (int i=0; i<4; i++) { int octet=val&0x00ff; val=val>>8; hashval = hashval ^ octet; hashval = hashval * FNV_prime_32; //hashval = hashval ^ octet; } return Math.abs(hashval); } public static final long FNV_offset_basis_64=0xCBF29CE484222325L; public static final long FNV_prime_64=1099511628211L; /** * 64 bit FNV hash. Produces more "random" hashes than (say) String.hashCode(). * * @param val The value to hash. * @return The hash value */ public static long FNVhash64(long val) { //from http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash long hashval = FNV_offset_basis_64; for (int i=0; i<8; i++) { long octet=val&0x00ff; val=val>>8; hashval = hashval ^ octet; hashval = hashval * FNV_prime_64; //hashval = hashval ^ octet; } return Math.abs(hashval); } /** * Reads a big-endian 8-byte long from an offset in the given array. * @param bytes The array to read from. * @return A long integer. * @throws IndexOutOfBoundsException if the byte array is too small. * @throws NullPointerException if the byte array is null. */ public static long bytesToLong(final byte[] bytes) { return (bytes[0] & 0xFFL) << 56 | (bytes[1] & 0xFFL) << 48 | (bytes[2] & 0xFFL) << 40 | (bytes[3] & 0xFFL) << 32 | (bytes[4] & 0xFFL) << 24 | (bytes[5] & 0xFFL) << 16 | (bytes[6] & 0xFFL) << 8 | (bytes[7] & 0xFFL) << 0; } /** * Writes a big-endian 8-byte long at an offset in the given array. * @param val The value to encode. * @throws IndexOutOfBoundsException if the byte array is too small. */ public static byte[] longToBytes(final long val) { final byte[] bytes = new byte[8]; bytes[0] = (byte) (val >>> 56); bytes[1] = (byte) (val >>> 48); bytes[2] = (byte) (val >>> 40); bytes[3] = (byte) (val >>> 32); bytes[4] = (byte) (val >>> 24); bytes[5] = (byte) (val >>> 16); bytes[6] = (byte) (val >>> 8); bytes[7] = (byte) (val >>> 0); return bytes; } /** * Parses the byte array into a double. * The byte array must be at least 8 bytes long and have been encoded using * {@link #doubleToBytes}. If the array is longer than 8 bytes, only the * first 8 bytes are parsed. * @param bytes The byte array to parse, at least 8 bytes. * @return A double value read from the byte array. * @throws IllegalArgumentException if the byte array is not 8 bytes wide. */ public static double bytesToDouble(final byte[] bytes) { if (bytes.length < 8) { throw new IllegalArgumentException("Byte array must be 8 bytes wide."); } return Double.longBitsToDouble(bytesToLong(bytes)); } /** * Encodes the double value as an 8 byte array. * @param val The double value to encode. * @return A byte array of length 8. */ public static byte[] doubleToBytes(final double val) { return longToBytes(Double.doubleToRawLongBits(val)); } + + /** + * Measure the estimated active thread count in the current thread group. + * Since this calls {@link Thread.activeCount} it should be called from the + * main thread or one started by the main thread. Threads included in the + * count can be in any state. + * For a more accurate count we could use {@link Thread.getAllStackTraces().size()} + * but that freezes the JVM and incurs a high overhead. + * @return An estimated thread count, good for showing the thread count + * over time. + */ + public static int getActiveThreadCount() { + return Thread.activeCount(); + } + + /** @return The currently used memory in bytes */ + public static long getUsedMemoryBytes() { + final Runtime runtime = Runtime.getRuntime(); + return runtime.totalMemory() - runtime.freeMemory(); + } + + /** @return The currently used memory in megabytes. */ + public static int getUsedMemoryMegaBytes() { + return (int)(getUsedMemoryBytes() / 1024 / 1024); + } + + /** @return The current system load average if supported by the JDK. + * If it's not supported, the value will be negative. */ + public static double getSystemLoadAverage() { + final OperatingSystemMXBean osBean = + ManagementFactory.getOperatingSystemMXBean(); + return osBean.getSystemLoadAverage(); + } + + /** @return The total number of garbage collections executed for all + * memory pools. */ + public static long getGCTotalCollectionCount() { + final List gcBeans = + ManagementFactory.getGarbageCollectorMXBeans(); + long count = 0; + for (final GarbageCollectorMXBean bean : gcBeans) { + count += bean.getCollectionCount(); + } + return count; + } } diff --git a/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java b/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java index fe1e9cc1..26d340cc 100644 --- a/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java +++ b/core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java @@ -1,311 +1,314 @@ /** * Copyright (c) 2010 Yahoo! Inc. All rights reserved. * * 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. See accompanying * LICENSE file. */ package com.yahoo.ycsb.measurements; import com.yahoo.ycsb.Status; import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; import java.io.IOException; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; /** * Collects latency measurements, and reports them when requested. * * @author cooperb * */ public class Measurements { /** * All supported measurement types are defined in this enum. * */ public enum MeasurementType { HISTOGRAM, HDRHISTOGRAM, HDRHISTOGRAM_AND_HISTOGRAM, HDRHISTOGRAM_AND_RAW, TIMESERIES, RAW } public static final String MEASUREMENT_TYPE_PROPERTY = "measurementtype"; private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "hdrhistogram"; public static final String MEASUREMENT_INTERVAL = "measurement.interval"; private static final String MEASUREMENT_INTERVAL_DEFAULT = "op"; + + public static final String MEASUREMENT_TRACK_JVM_PROPERTY = "measurement.trackjvm"; + public static final String MEASUREMENT_TRACK_JVM_PROPERTY_DEFAULT = "false"; static Measurements singleton=null; static Properties measurementproperties=null; public static void setProperties(Properties props) { measurementproperties=props; } /** * Return the singleton Measurements object. */ public synchronized static Measurements getMeasurements() { if (singleton==null) { singleton=new Measurements(measurementproperties); } return singleton; } final ConcurrentHashMap _opToMesurementMap; final ConcurrentHashMap _opToIntendedMesurementMap; final MeasurementType _measurementType; final int _measurementInterval; private Properties _props; /** * Create a new object with the specified properties. */ public Measurements(Properties props) { _opToMesurementMap=new ConcurrentHashMap(); _opToIntendedMesurementMap=new ConcurrentHashMap(); _props=props; String mTypeString = _props.getProperty(MEASUREMENT_TYPE_PROPERTY, MEASUREMENT_TYPE_PROPERTY_DEFAULT); if (mTypeString.equals("histogram")) { _measurementType = MeasurementType.HISTOGRAM; } else if (mTypeString.equals("hdrhistogram")) { _measurementType = MeasurementType.HDRHISTOGRAM; } else if (mTypeString.equals("hdrhistogram+histogram")) { _measurementType = MeasurementType.HDRHISTOGRAM_AND_HISTOGRAM; } else if (mTypeString.equals("hdrhistogram+raw")) { _measurementType = MeasurementType.HDRHISTOGRAM_AND_RAW; } else if (mTypeString.equals("timeseries")) { _measurementType = MeasurementType.TIMESERIES; } else if (mTypeString.equals("raw")) { _measurementType = MeasurementType.RAW; } else { throw new IllegalArgumentException("unknown "+MEASUREMENT_TYPE_PROPERTY+"="+mTypeString); } String mIntervalString = _props.getProperty(MEASUREMENT_INTERVAL, MEASUREMENT_INTERVAL_DEFAULT); if (mIntervalString.equals("op")) { _measurementInterval = 0; } else if (mIntervalString.equals("intended")) { _measurementInterval = 1; } else if (mIntervalString.equals("both")) { _measurementInterval = 2; } else { throw new IllegalArgumentException("unknown "+MEASUREMENT_INTERVAL+"="+mIntervalString); } } OneMeasurement constructOneMeasurement(String name) { switch (_measurementType) { case HISTOGRAM: return new OneMeasurementHistogram(name, _props); case HDRHISTOGRAM: return new OneMeasurementHdrHistogram(name, _props); case HDRHISTOGRAM_AND_HISTOGRAM: return new TwoInOneMeasurement(name, new OneMeasurementHdrHistogram("Hdr"+name, _props), new OneMeasurementHistogram("Bucket"+name, _props)); case HDRHISTOGRAM_AND_RAW: return new TwoInOneMeasurement(name, new OneMeasurementHdrHistogram("Hdr"+name, _props), new OneMeasurementHistogram("Raw"+name, _props)); case TIMESERIES: return new OneMeasurementTimeSeries(name, _props); case RAW: return new OneMeasurementRaw(name, _props); default: throw new AssertionError("Impossible to be here. Dead code reached. Bugs?"); } } static class StartTimeHolder { long time; long startTime(){ if(time == 0) { return System.nanoTime(); } else { return time; } } } ThreadLocal tlIntendedStartTime = new ThreadLocal() { protected StartTimeHolder initialValue() { return new StartTimeHolder(); } }; public void setIntendedStartTimeNs(long time) { if(_measurementInterval==0) return; tlIntendedStartTime.get().time=time; } public long getIntendedtartTimeNs() { if(_measurementInterval==0) return 0L; return tlIntendedStartTime.get().startTime(); } /** * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured * value. */ public void measure(String operation, int latency) { if(_measurementInterval==1) return; try { OneMeasurement m = getOpMeasurement(operation); m.measure(latency); } // This seems like a terribly hacky way to cover up for a bug in the measurement code catch (java.lang.ArrayIndexOutOfBoundsException e) { System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); e.printStackTrace(); e.printStackTrace(System.out); } } /** * Report a single value of a single metric. E.g. for read latency, operation="READ" and latency is the measured * value. */ public void measureIntended(String operation, int latency) { if(_measurementInterval==0) return; try { OneMeasurement m = getOpIntendedMeasurement(operation); m.measure(latency); } // This seems like a terribly hacky way to cover up for a bug in the measurement code catch (java.lang.ArrayIndexOutOfBoundsException e) { System.out.println("ERROR: java.lang.ArrayIndexOutOfBoundsException - ignoring and continuing"); e.printStackTrace(); e.printStackTrace(System.out); } } private OneMeasurement getOpMeasurement(String operation) { OneMeasurement m = _opToMesurementMap.get(operation); if(m == null) { m = constructOneMeasurement(operation); OneMeasurement oldM = _opToMesurementMap.putIfAbsent(operation, m); if(oldM != null) { m = oldM; } } return m; } private OneMeasurement getOpIntendedMeasurement(String operation) { OneMeasurement m = _opToIntendedMesurementMap.get(operation); if(m == null) { final String name = _measurementInterval==1 ? operation : "Intended-" + operation; m = constructOneMeasurement(name); OneMeasurement oldM = _opToIntendedMesurementMap.putIfAbsent(operation, m); if(oldM != null) { m = oldM; } } return m; } /** * Report a return code for a single DB operation. */ public void reportStatus(final String operation, final Status status) { OneMeasurement m = _measurementInterval==1 ? getOpIntendedMeasurement(operation) : getOpMeasurement(operation); m.reportStatus(status); } /** * Export the current measurements to a suitable format. * * @param exporter Exporter representing the type of format to write to. * @throws IOException Thrown if the export failed. */ public void exportMeasurements(MeasurementsExporter exporter) throws IOException { for (OneMeasurement measurement : _opToMesurementMap.values()) { measurement.exportMeasurements(exporter); } for (OneMeasurement measurement : _opToIntendedMesurementMap.values()) { measurement.exportMeasurements(exporter); } } /** * Return a one line summary of the measurements. */ public synchronized String getSummary() { String ret=""; for (OneMeasurement m : _opToMesurementMap.values()) { ret += m.getSummary()+" "; } for (OneMeasurement m : _opToIntendedMesurementMap.values()) { ret += m.getSummary()+" "; } return ret; } } diff --git a/core/src/test/java/com/yahoo/ycsb/TestUtils.java b/core/src/test/java/com/yahoo/ycsb/TestUtils.java index cde51776..71213137 100644 --- a/core/src/test/java/com/yahoo/ycsb/TestUtils.java +++ b/core/src/test/java/com/yahoo/ycsb/TestUtils.java @@ -1,129 +1,142 @@ /** * Copyright (c) 2016 YCSB contributors. All rights reserved. * * 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. See accompanying * LICENSE file. */ package com.yahoo.ycsb; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.util.Arrays; import org.testng.annotations.Test; public class TestUtils { @Test public void bytesToFromLong() throws Exception { byte[] bytes = new byte[8]; assertEquals(Utils.bytesToLong(bytes), 0L); assertArrayEquals(Utils.longToBytes(0), bytes); bytes[7] = 1; assertEquals(Utils.bytesToLong(bytes), 1L); assertArrayEquals(Utils.longToBytes(1L), bytes); bytes = new byte[] { 127, -1, -1, -1, -1, -1, -1, -1 }; assertEquals(Utils.bytesToLong(bytes), Long.MAX_VALUE); assertArrayEquals(Utils.longToBytes(Long.MAX_VALUE), bytes); bytes = new byte[] { -128, 0, 0, 0, 0, 0, 0, 0 }; assertEquals(Utils.bytesToLong(bytes), Long.MIN_VALUE); assertArrayEquals(Utils.longToBytes(Long.MIN_VALUE), bytes); bytes = new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; assertEquals(Utils.bytesToLong(bytes), -1L); assertArrayEquals(Utils.longToBytes(-1L), bytes); // if the array is too long we just skip the remainder bytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 1, 42, 42, 42 }; assertEquals(Utils.bytesToLong(bytes), 1L); } @Test public void bytesToFromDouble() throws Exception { byte[] bytes = new byte[8]; assertEquals(Utils.bytesToDouble(bytes), 0, 0.0001); assertArrayEquals(Utils.doubleToBytes(0), bytes); bytes = new byte[] { 63, -16, 0, 0, 0, 0, 0, 0 }; assertEquals(Utils.bytesToDouble(bytes), 1, 0.0001); assertArrayEquals(Utils.doubleToBytes(1), bytes); bytes = new byte[] { -65, -16, 0, 0, 0, 0, 0, 0 }; assertEquals(Utils.bytesToDouble(bytes), -1, 0.0001); assertArrayEquals(Utils.doubleToBytes(-1), bytes); bytes = new byte[] { 127, -17, -1, -1, -1, -1, -1, -1 }; assertEquals(Utils.bytesToDouble(bytes), Double.MAX_VALUE, 0.0001); assertArrayEquals(Utils.doubleToBytes(Double.MAX_VALUE), bytes); bytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 1 }; assertEquals(Utils.bytesToDouble(bytes), Double.MIN_VALUE, 0.0001); assertArrayEquals(Utils.doubleToBytes(Double.MIN_VALUE), bytes); bytes = new byte[] { 127, -8, 0, 0, 0, 0, 0, 0 }; assertTrue(Double.isNaN(Utils.bytesToDouble(bytes))); assertArrayEquals(Utils.doubleToBytes(Double.NaN), bytes); bytes = new byte[] { 63, -16, 0, 0, 0, 0, 0, 0, 42, 42, 42 }; assertEquals(Utils.bytesToDouble(bytes), 1, 0.0001); } @Test (expectedExceptions = NullPointerException.class) public void bytesToLongNull() throws Exception { Utils.bytesToLong(null); } @Test (expectedExceptions = IndexOutOfBoundsException.class) public void bytesToLongTooShort() throws Exception { Utils.bytesToLong(new byte[] { 0, 0, 0, 0, 0, 0, 0 }); } @Test (expectedExceptions = IllegalArgumentException.class) public void bytesToDoubleTooShort() throws Exception { Utils.bytesToDouble(new byte[] { 0, 0, 0, 0, 0, 0, 0 }); } + @Test + public void jvmUtils() throws Exception { + // This should ALWAYS return at least one thread. + assertTrue(Utils.getActiveThreadCount() > 0); + // This should always be greater than 0 or something is goofed up in the JVM. + assertTrue(Utils.getUsedMemoryBytes() > 0); + // Some operating systems may not implement this so we don't have a good + // test. Just make sure it doesn't throw an exception. + Utils.getSystemLoadAverage(); + // This will probably be zero but should never be negative. + assertTrue(Utils.getGCTotalCollectionCount() >= 0); + } + /** * Since this version of TestNG doesn't appear to have an assertArrayEquals, * this will compare the two to make sure they're the same. * @param actual Actual array to validate * @param expected What the array should contain * @throws AssertionError if the test fails. */ public void assertArrayEquals(final byte[] actual, final byte[] expected) { if (actual == null && expected != null) { throw new AssertionError("Expected " + Arrays.toString(expected) + " but found [null]"); } if (actual != null && expected == null) { throw new AssertionError("Expected [null] but found " + Arrays.toString(actual)); } if (actual.length != expected.length) { throw new AssertionError("Expected length " + expected.length + " but found " + actual.length); } for (int i = 0; i < expected.length; i++) { if (actual[i] != expected[i]) { throw new AssertionError("Expected byte [" + expected[i] + "] at index " + i + " but found [" + actual[i] + "]"); } } } } \ No newline at end of file diff --git a/workloads/workload_template b/workloads/workload_template index f5e80c88..aff1e7c8 100644 --- a/workloads/workload_template +++ b/workloads/workload_template @@ -1,171 +1,181 @@ -# Copyright (c) 2012 YCSB contributors. All rights reserved. +# Copyright (c) 2012-2016 YCSB contributors. All rights reserved. # # 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. See accompanying # LICENSE file. # Yahoo! Cloud System Benchmark # Workload Template: Default Values # # File contains all properties that can be set to define a # YCSB session. All properties are set to their default # value if one exists. If not, the property is commented # out. When a property has a finite number of settings, # the default is enabled and the alternates are shown in # comments below it. # # Use of most explained through comments in Client.java or # CoreWorkload.java or on the YCSB wiki page: # https://github.com/brianfrankcooper/YCSB/wiki/Core-Properties # The name of the workload class to use workload=com.yahoo.ycsb.workloads.CoreWorkload # There is no default setting for recordcount but it is # required to be set. # The number of records in the table to be inserted in # the load phase or the number of records already in the # table before the run phase. recordcount=1000000 # There is no default setting for operationcount but it is # required to be set. # The number of operations to use during the run phase. operationcount=3000000 # The number of insertions to do, if different from recordcount. # Used with insertstart to grow an existing table. #insertcount= # The offset of the first insertion insertstart=0 # The number of fields in a record fieldcount=10 # The size of each field (in bytes) fieldlength=100 # Should read all fields readallfields=true # Should write all fields on update writeallfields=false # The distribution used to choose the length of a field fieldlengthdistribution=constant #fieldlengthdistribution=uniform #fieldlengthdistribution=zipfian # What proportion of operations are reads readproportion=0.95 # What proportion of operations are updates updateproportion=0.05 # What proportion of operations are inserts insertproportion=0 # What proportion of operations read then modify a record readmodifywriteproportion=0 # What proportion of operations are scans scanproportion=0 # On a single scan, the maximum number of records to access maxscanlength=1000 # The distribution used to choose the number of records to access on a scan scanlengthdistribution=uniform #scanlengthdistribution=zipfian # Should records be inserted in order or pseudo-randomly insertorder=hashed #insertorder=ordered # The distribution of requests across the keyspace requestdistribution=zipfian #requestdistribution=uniform #requestdistribution=latest # Percentage of data items that constitute the hot set hotspotdatafraction=0.2 # Percentage of operations that access the hot set hotspotopnfraction=0.8 # Maximum execution time in seconds #maxexecutiontime= # The name of the database table to run queries against table=usertable # The column family of fields (required by some databases) #columnfamily= # How the latency measurements are presented measurementtype=histogram #measurementtype=timeseries #measurementtype=raw # When measurementtype is set to raw, measurements will be output # as RAW datapoints in the following csv format: # "operation, timestamp of the measurement, latency in us" # # Raw datapoints are collected in-memory while the test is running. Each # data point consumes about 50 bytes (including java object overhead). # For a typical run of 1 million to 10 million operations, this should # fit into memory most of the time. If you plan to do 100s of millions of # operations per run, consider provisioning a machine with larger RAM when using # the RAW measurement type, or split the run into multiple runs. # # Optionally, you can specify an output file to save raw datapoints. # Otherwise, raw datapoints will be written to stdout. # The output file will be appended to if it already exists, otherwise # a new output file will be created. #measurement.raw.output_file = /tmp/your_output_file_for_this_run +# JVM Reporting. +# +# Measure JVM information over time including GC counts, max and min memory +# used, max and min thread counts, max and min system load and others. This +# setting must be enabled in conjunction with the "-s" flag to run the status +# thread. Every "status.interval", the status thread will capture JVM +# statistics and record the results. At the end of the run, max and mins will +# be recorded. +# measurement.trackjvm = false + # The range of latencies to track in the histogram (milliseconds) histogram.buckets=1000 # Granularity for time series (in milliseconds) timeseries.granularity=1000 # Latency reporting. # # YCSB records latency of failed operations separately from successful ones. # Latency of all OK operations will be reported under their operation name, # such as [READ], [UPDATE], etc. # # For failed operations: # By default we don't track latency numbers of specific error status. # We just report latency of all failed operation under one measurement name # such as [READ-FAILED]. But optionally, user can configure to have either: # 1. Record and report latency for each and every error status code by # setting reportLatencyForEachError to true, or # 2. Record and report latency for a select set of error status codes by # providing a CSV list of Status codes via the "latencytrackederrors" # property. # reportlatencyforeacherror=false # latencytrackederrors="" # Insertion error retry for the core workload. # # By default, the YCSB core workload does not retry any operations. # However, during the load process, if any insertion fails, the entire # load process is terminated. # If a user desires to have more robust behavior during this phase, they can # enable retry for insertion by setting the following property to a positive # number. # core_workload_insertion_retry_limit = 0 # # the following number controls the interval between retries (in seconds): -# core_workload_insertion_retry_interval = 3 +# core_workload_insertion_retry_interval = 3 \ No newline at end of file