package com.bazaarvoice.ostrich.perftest.utils; import com.bazaarvoice.ostrich.MultiThreadedServiceFactory; import com.bazaarvoice.ostrich.perftest.core.Service; import com.bazaarvoice.ostrich.perftest.core.SimpleServiceFactory; import com.bazaarvoice.ostrich.pool.ServiceRunner; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Snapshot; import com.codahale.metrics.Timer; import com.google.common.collect.Lists; import java.io.PrintStream; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; /** * This class creates the service runner and runs the requested load as parsed via Arguments * This also prints/writes the report log and/or the statistics as requested in the Arguments */ public class LoadRunner { private final PrintStream _out; private final boolean _doPrintStats; private final long _totalRuntime; private final List<Thread> _workers; private final long _startTime; private final int _reportingIntervalSeconds; private final Arguments _arguments; private final Meter _serviceCreated; private final Meter _serviceDestroyed; private final Meter _serviceCalled; private final Meter _cacheMissed; private final Meter _serviceFailed; private final Meter _chaosCreated; private final Meter _stableCreated; private final Timer _checkoutTimer; private final Timer _checkinTimer; private final Timer _serviceTimer; private final Timer _totalTimer; private final Timer _evictionTimer; private final Timer _registerTimer; private final Timer _loadTimer; private long _counter = 0; public LoadRunner(Arguments arguments) { MetricRegistry metricRegistry = new MetricRegistry(); MultiThreadedServiceFactory<Service<String, String>> serviceFactory = SimpleServiceFactory.newInstance(metricRegistry); ServiceRunner serviceRunner = new ServiceRunner(serviceFactory, metricRegistry, arguments); ChaosRunner chaosRunner = new ChaosRunner(serviceRunner.getServiceCache(), arguments, metricRegistry); _arguments = arguments; _out = arguments.getOutput(); _doPrintStats = arguments.doPrintStats(); _totalRuntime = arguments.getRunTimeSecond(); _reportingIntervalSeconds = arguments.getReportingIntervalSeconds(); _workers = Lists.newArrayList(); _workers.addAll(serviceRunner.generateWorkers()); _workers.addAll(chaosRunner.generateChaosWorkers()); for (Thread thread : _workers) { thread.start(); } _startTime = currentTimeSeconds(); _serviceCreated = metricRegistry.meter("com.bazaarvoice.ostrich.perftest.core.SimpleServiceFactory.ServiceFactory.Created"); _serviceDestroyed = metricRegistry.meter("com.bazaarvoice.ostrich.perftest.core.SimpleServiceFactory.ServiceFactory.Destroyed"); _serviceCalled = metricRegistry.meter("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Executed"); _cacheMissed = metricRegistry.meter("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Cache-Miss"); _serviceFailed = metricRegistry.meter("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Failure"); _chaosCreated = metricRegistry.meter("com.bazaarvoice.ostrich.perftest.utils.ChaosRunner.ChaosRunner.Chaos"); _stableCreated = metricRegistry.meter("com.bazaarvoice.ostrich.perftest.utils.ChaosRunner.ChaosRunner.Stable"); _checkoutTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Checkout"); _checkinTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Checkin"); _serviceTimer = metricRegistry.timer("com.bazaarvoice.ostrich.perftest.core.SimpleServiceFactory.ServiceFactory.Timer"); _totalTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.ServiceRunner.ServiceRunner.Total"); if(arguments.isRunSingletonMode()) { _evictionTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.MultiThreadedClientServiceCache.SimpleService.eviction-time"); _registerTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.MultiThreadedClientServiceCache.SimpleService.register-time"); _loadTimer = metricRegistry.timer("dummy"); } else { _loadTimer = metricRegistry.timer("com.bazaarvoice.ostrich.pool.SingleThreadedClientServiceCache.SimpleService.load-time"); _evictionTimer = _registerTimer = metricRegistry.timer("dummy"); } } public void printLog() { long currentRuntime = currentTimeSeconds() - _startTime; _out.print(String.format("%d,", _counter++)); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,", _serviceCreated.getCount(), _serviceCreated.getMeanRate(), _serviceCreated.getOneMinuteRate(), _serviceCreated.getFiveMinuteRate(), _serviceCreated.getFifteenMinuteRate())); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,", _serviceDestroyed.getCount(), _serviceDestroyed.getMeanRate(), _serviceDestroyed.getOneMinuteRate(), _serviceDestroyed.getFiveMinuteRate(), _serviceDestroyed.getFifteenMinuteRate())); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,", _serviceCalled.getCount(), _serviceCalled.getMeanRate(), _serviceCalled.getOneMinuteRate(), _serviceCalled.getFiveMinuteRate(), _serviceCalled.getFifteenMinuteRate())); Snapshot checkoutTimerSnapshot = _checkoutTimer.getSnapshot(); Snapshot checkinTimerSnapshot = _checkinTimer.getSnapshot(); Snapshot serviceTimerSnapshot = _serviceTimer.getSnapshot(); Snapshot totalTimerSnapshot = _totalTimer.getSnapshot(); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", _checkoutTimer.getCount(), nsToMs(checkoutTimerSnapshot.getMin()), nsToMs(checkoutTimerSnapshot.getMax()), nsToMs(checkoutTimerSnapshot.getMean()), _checkoutTimer.getOneMinuteRate(), _checkoutTimer.getFiveMinuteRate(), _checkoutTimer.getFifteenMinuteRate())); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", _checkinTimer.getCount(), nsToMs(checkinTimerSnapshot.getMin()), nsToMs(checkinTimerSnapshot.getMax()), nsToMs(checkinTimerSnapshot.getMean()), _checkinTimer.getOneMinuteRate(), _checkinTimer.getFiveMinuteRate(), _checkinTimer.getFifteenMinuteRate())); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", _serviceTimer.getCount(), nsToMs(serviceTimerSnapshot.getMin()), nsToMs(serviceTimerSnapshot.getMax()), nsToMs(serviceTimerSnapshot.getMean()), _serviceTimer.getOneMinuteRate(), _serviceTimer.getFiveMinuteRate(), _serviceTimer.getFifteenMinuteRate())); _out.print(String.format("%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", _totalTimer.getCount(), nsToMs(totalTimerSnapshot.getMin()), nsToMs(totalTimerSnapshot.getMax()), nsToMs(totalTimerSnapshot.getMean()), _totalTimer.getOneMinuteRate(), _totalTimer.getFiveMinuteRate(), _totalTimer.getFifteenMinuteRate())); _out.println(); _out.flush(); if (_doPrintStats) { System.out.print("\u001b[2J"); System.out.println(new Date()); System.out.println(String.format("Running %d seconds of %s with threads: %d, work size: %d, idle time: %d, " + "max instance: %d, exhaust action: %s, singleton-mode: %s, chaos-worker: %d, chaos-interval: %d", currentRuntime, _arguments.getRunTimeSecond(), _arguments.getThreadSize(), _arguments.getWorkSize(), _arguments.getIdleTimeSecond(), _arguments.getMaxInstance(), _arguments.getExhaustionAction().name(), _arguments.isRunSingletonMode(), _arguments.getChaosWorkers(), _arguments.getChaosInterval())); System.out.println(String.format("Called count: %d\tCache Miss: %d\tFailed Count: %d\tService Created: %d" + "\tService Destroyed: %d\tChaos: %d\tStable: %d\tRegister: %d\tEvict: %d\tLoad: %d", _serviceCalled.getCount(), _cacheMissed.getCount(), _serviceFailed.getCount(), _serviceCreated.getCount(), _serviceDestroyed.getCount(), _chaosCreated.getCount(), _stableCreated.getCount(), _registerTimer.getCount(), _evictionTimer.getCount(), _loadTimer.getCount())); System.out.println(); System.out.println(String.format("\tcreated / destroyed\t-- 1-min: %3.2f/s / %3.2f/s" + "\t5-min: %3.2f/s / %3.2f/s \t15-min: %3.2f/s / %3.2f/s\tmean: %3.2f/s / %3.2f/s", _serviceCreated.getOneMinuteRate(), _serviceDestroyed.getOneMinuteRate(), _serviceCreated.getFiveMinuteRate(), _serviceDestroyed.getFiveMinuteRate(), _serviceCreated.getFifteenMinuteRate(), _serviceDestroyed.getFifteenMinuteRate(), _serviceCreated.getMeanRate(), _serviceDestroyed.getMeanRate())); System.out.println(String.format("\tchaos / stable\t\t-- 1-min: %3.2f/s / %3.2f/s" + "\t5-min: %3.2f/s / %3.2f/s \t15-min: %3.2f/s / %3.2f/s\tmean: %3.2f/s / %3.2f/s", _chaosCreated.getOneMinuteRate(), _stableCreated.getOneMinuteRate(), _chaosCreated.getFiveMinuteRate(), _stableCreated.getFiveMinuteRate(), _chaosCreated.getFifteenMinuteRate(), _stableCreated.getFifteenMinuteRate(), _chaosCreated.getMeanRate(), _stableCreated.getMeanRate())); System.out.println(String.format("\texecuted / failure\t-- 1-min: %3.2f/s / %3.2f/s" + "\t5-min: %3.2f/s / %3.2f/s \t15-min: %3.2f/s / %3.2f/s\tmean: %3.2f/s / %3.2f/s", _serviceCalled.getOneMinuteRate(), _serviceFailed.getOneMinuteRate(), _serviceCalled.getFiveMinuteRate(), _serviceFailed.getFiveMinuteRate(), _serviceCalled.getFifteenMinuteRate(), _serviceFailed.getFifteenMinuteRate(), _serviceCalled.getMeanRate(), _serviceFailed.getMeanRate())); System.out.println(String.format("\tservice / total\t\t-- 1-min: %3.2f/s / %3.2f/s" + "\t5-min: %3.2f/s / %3.2f/s \t15-min: %3.2f/s / %3.2f/s\tmean: %3.2f/s / %3.2f/s", _serviceTimer.getOneMinuteRate(), _totalTimer.getOneMinuteRate(), _serviceTimer.getFiveMinuteRate(), _totalTimer.getFiveMinuteRate(), _serviceTimer.getFifteenMinuteRate(), _totalTimer.getFifteenMinuteRate(), _serviceTimer.getMeanRate(), _totalTimer.getMeanRate())); System.out.println(String.format("\tcheckout / checkin\t-- 1-min: %3.2f/s / %3.2f/s" + "\t5-min: %3.2f/s / %3.2f/s \t15-min: %3.2f/s / %3.2f/s\tmean: %3.2f/s / %3.2f/s", _checkoutTimer.getOneMinuteRate(), _checkinTimer.getOneMinuteRate(), _checkoutTimer.getFiveMinuteRate(), _checkinTimer.getFiveMinuteRate(), _checkoutTimer.getFifteenMinuteRate(), _checkinTimer.getFifteenMinuteRate(), _checkoutTimer.getMeanRate(), _checkinTimer.getMeanRate())); System.out.println(); System.out.println(String.format("\tService\t -- mean: %3.2fms\tmin: %3.2fms\tmax: %3.2fms\t75th: %3.2fms" + "\t95th: %3.2fms\t98th: %3.2fms\t99th: %3.2fms\t999th: %3.2fms", nsToMs(serviceTimerSnapshot.getMean()), nsToMs(serviceTimerSnapshot.getMin()), nsToMs(serviceTimerSnapshot.getMax()), nsToMs(serviceTimerSnapshot.get75thPercentile()), nsToMs(serviceTimerSnapshot.get95thPercentile()), nsToMs(serviceTimerSnapshot.get98thPercentile()), nsToMs(serviceTimerSnapshot.get99thPercentile()), nsToMs(serviceTimerSnapshot.get999thPercentile()) )); System.out.println(String.format("\tCheckout -- mean: %3.2fms\tmin: %3.2fms\tmax: %3.2fms\t75th: %3.2fms" + "\t95th: %3.2fms\t98th: %3.2fms\t99th: %3.2fms\t999th: %3.2fms", nsToMs(checkoutTimerSnapshot.getMean()), nsToMs(checkoutTimerSnapshot.getMin()), nsToMs(checkoutTimerSnapshot.getMax()), nsToMs(checkoutTimerSnapshot.get75thPercentile()), nsToMs(checkoutTimerSnapshot.get95thPercentile()), nsToMs(checkoutTimerSnapshot.get98thPercentile()), nsToMs(checkoutTimerSnapshot.get99thPercentile()), nsToMs(checkoutTimerSnapshot.get999thPercentile()) )); System.out.println(String.format("\tCheckin\t -- mean: %3.2fms\tmin: %3.2fms\tmax: %3.2fms\t75th: %3.2fms" + "\t95th: %3.2fms\t98th: %3.2fms\t99th: %3.2fms\t999th: %3.2fms", nsToMs(checkinTimerSnapshot.getMean()), nsToMs(checkinTimerSnapshot.getMin()), nsToMs(checkinTimerSnapshot.getMax()), nsToMs(checkinTimerSnapshot.get75thPercentile()), nsToMs(checkinTimerSnapshot.get95thPercentile()), nsToMs(checkinTimerSnapshot.get98thPercentile()), nsToMs(checkinTimerSnapshot.get99thPercentile()), nsToMs(checkinTimerSnapshot.get999thPercentile()) )); System.out.println(String.format("\tTotal\t -- mean: %3.2fms\tmin: %3.2fms\tmax: %3.2fms\t75th: %3.2fms" + "\t95th: %3.2fms\t98th: %3.2fms\t99th: %3.2fms\t999th: %3.2fms", nsToMs(totalTimerSnapshot.getMean()), nsToMs(totalTimerSnapshot.getMin()), nsToMs(totalTimerSnapshot.getMax()), nsToMs(totalTimerSnapshot.get75thPercentile()), nsToMs(totalTimerSnapshot.get95thPercentile()), nsToMs(totalTimerSnapshot.get98thPercentile()), nsToMs(totalTimerSnapshot.get99thPercentile()), nsToMs(totalTimerSnapshot.get999thPercentile()) )); System.out.flush(); } try { Thread.sleep(TimeUnit.SECONDS.toMillis(_reportingIntervalSeconds)); } catch (InterruptedException ignored) { } } public void printHeaders() { _out.print("counter,"); _out.print(String.format("%s,%s,%s,%s,%s,", "cr-totl", "cr-mnrt", "cr-1mrt", "cr-5mrt", "cr-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,", "dt-totl", "dt-mnrt", "dt-1mrt", "dt-5mrt", "dt-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,", "sc-totl", "sc-mnrt", "sc-1mrt", "sc-5mrt", "sc-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,%s,%s,", "co-totl", "co-min", "co-max", "co-mean", "co-1mrt", "co-5mrt", "co-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,%s,%s,", "ci-totl", "ci-min", "ci-max", "ci-mean", "ci-1mrt", "ci-5mrt", "ci-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,%s,%s,", "st-totl", "st-min", "st-max", "st-mean", "st-1mrt", "st-5mrt", "st-15rt")); _out.print(String.format("%s,%s,%s,%s,%s,%s,%s,", "tt-totl", "tt-min", "tt-max", "tt-mean", "tt-1mrt", "tt-5mrt", "tt-15rt")); _out.println(); } public boolean shouldContinue() { long spent = currentTimeSeconds() - _startTime; boolean shouldContinue; if (spent >= _totalRuntime) { for (Thread t : _workers) { if (t.isAlive()) { try { t.interrupt(); t.join(); } catch (InterruptedException ignored) { } } } shouldContinue = false; } else { int total = _workers.size(); int done = 0; for (Thread t : _workers) { if (!t.isAlive()) { done++; } } shouldContinue = (done != total); } if (!shouldContinue) { _out.close(); } return shouldContinue; } public long currentTimeSeconds() { return TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); } // Snapshot returns times in NS, this converts them to ms as we need private double nsToMs(double ns) { return ns / 1000000; } }