blob: 5d9257bcf4ba4420602817205bcdbd8fcdf306ce [file] [log] [blame]
Jordan Halterman29718e62017-07-20 13:24:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Jordan Halterman29718e62017-07-20 13:24:33 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.primitiveperf;
17
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.Lists;
19import org.onosproject.cfg.ComponentConfigService;
20import org.onosproject.cluster.ClusterService;
21import org.onosproject.cluster.ControllerNode;
22import org.onosproject.cluster.NodeId;
23import org.onosproject.store.serializers.KryoNamespaces;
24import org.onosproject.store.service.AtomicValue;
25import org.onosproject.store.service.AtomicValueEventListener;
26import org.onosproject.store.service.ConsistentMap;
27import org.onosproject.store.service.Serializer;
28import org.onosproject.store.service.StorageService;
29import org.osgi.service.component.ComponentContext;
30import org.osgi.service.component.annotations.Activate;
31import org.osgi.service.component.annotations.Component;
32import org.osgi.service.component.annotations.Deactivate;
33import org.osgi.service.component.annotations.Modified;
34import org.osgi.service.component.annotations.Reference;
35import org.slf4j.Logger;
36
Jordan Halterman73296092018-03-08 16:06:28 -050037import java.util.ArrayList;
Jordan Halterman29718e62017-07-20 13:24:33 -070038import java.util.Dictionary;
39import java.util.List;
40import java.util.Map;
Jordan Halterman73296092018-03-08 16:06:28 -050041import java.util.Random;
Jordan Halterman29718e62017-07-20 13:24:33 -070042import java.util.Timer;
43import java.util.TimerTask;
44import java.util.TreeMap;
Jordan Halterman29718e62017-07-20 13:24:33 -070045import java.util.concurrent.ExecutorService;
46import java.util.concurrent.Executors;
47import java.util.concurrent.TimeUnit;
48import java.util.concurrent.atomic.AtomicLong;
Jordan Halterman29718e62017-07-20 13:24:33 -070049
Jordan Halterman29718e62017-07-20 13:24:33 -070050import static com.google.common.base.Strings.isNullOrEmpty;
51import static java.lang.System.currentTimeMillis;
Jordan Halterman29718e62017-07-20 13:24:33 -070052import static org.onlab.util.Tools.get;
53import static org.onlab.util.Tools.groupedThreads;
Ray Milkey88dd7e22018-10-24 10:04:03 -070054import static org.onosproject.primitiveperf.OsgiPropertyConstants.DETERMINISTIC;
55import static org.onosproject.primitiveperf.OsgiPropertyConstants.DETERMINISTIC_DEFAULT;
56import static org.onosproject.primitiveperf.OsgiPropertyConstants.INCLUDE_EVENTS;
57import static org.onosproject.primitiveperf.OsgiPropertyConstants.INCLUDE_EVENTS_DEFAULT;
58import static org.onosproject.primitiveperf.OsgiPropertyConstants.KEY_LENGTH;
59import static org.onosproject.primitiveperf.OsgiPropertyConstants.KEY_LENGTH_DEFAULT;
60import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_CLIENTS;
61import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_CLIENTS_DEFAULT;
62import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_KEYS;
63import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_KEYS_DEFAULT;
64import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_UNIQUE_VALUES;
65import static org.onosproject.primitiveperf.OsgiPropertyConstants.NUM_UNIQUE_VALUES_DEFAULT;
66import static org.onosproject.primitiveperf.OsgiPropertyConstants.VALUE_LENGTH;
67import static org.onosproject.primitiveperf.OsgiPropertyConstants.VALUE_LENGTH_DEFAULT;
68import static org.onosproject.primitiveperf.OsgiPropertyConstants.WRITE_PERCENTAGE;
69import static org.onosproject.primitiveperf.OsgiPropertyConstants.WRITE_PERCENTAGE_DEFAULT;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070import static org.osgi.service.component.annotations.ReferenceCardinality.MANDATORY;
Jordan Halterman29718e62017-07-20 13:24:33 -070071import static org.slf4j.LoggerFactory.getLogger;
72
73/**
74 * Application to test sustained primitive throughput.
75 */
Ray Milkey88dd7e22018-10-24 10:04:03 -070076@Component(
77 immediate = true,
78 service = PrimitivePerfApp.class,
79 property = {
80 NUM_CLIENTS + ":Integer=" + NUM_CLIENTS_DEFAULT,
81 WRITE_PERCENTAGE + ":Integer=" + WRITE_PERCENTAGE_DEFAULT,
82 NUM_KEYS + ":Integer=" + NUM_KEYS_DEFAULT,
83 KEY_LENGTH + ":Integer=" + KEY_LENGTH_DEFAULT,
84 NUM_UNIQUE_VALUES + ":Integer=" + NUM_UNIQUE_VALUES_DEFAULT,
85 VALUE_LENGTH + ":Integer=" + VALUE_LENGTH_DEFAULT,
86 INCLUDE_EVENTS + ":Boolean=" + INCLUDE_EVENTS_DEFAULT,
87 DETERMINISTIC + ":Boolean=" + DETERMINISTIC_DEFAULT,
88 }
89)
Jordan Halterman29718e62017-07-20 13:24:33 -070090public class PrimitivePerfApp {
91
92 private final Logger log = getLogger(getClass());
93
Jordan Halterman29718e62017-07-20 13:24:33 -070094 private static final int REPORT_PERIOD = 1_000; //ms
95
Jordan Halterman73296092018-03-08 16:06:28 -050096 private static final char[] CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
Jordan Halterman29718e62017-07-20 13:24:33 -070097
Ray Milkey88dd7e22018-10-24 10:04:03 -070098 /** Number of clients to use to submit writes. */
99 private int numClients = NUM_CLIENTS_DEFAULT;
Jordan Halterman29718e62017-07-20 13:24:33 -0700100
Ray Milkey88dd7e22018-10-24 10:04:03 -0700101 /** Percentage of operations to perform as writes. */
102 private int writePercentage = WRITE_PERCENTAGE_DEFAULT;
Jordan Halterman29718e62017-07-20 13:24:33 -0700103
Ray Milkey88dd7e22018-10-24 10:04:03 -0700104 /** Number of unique keys to write. */
105 private int numKeys = NUM_KEYS_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500106
Ray Milkey88dd7e22018-10-24 10:04:03 -0700107 /** Key length. */
108 private int keyLength = KEY_LENGTH_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500109
Ray Milkey88dd7e22018-10-24 10:04:03 -0700110 /** Number of unique values to write. */
111 private int numValues = NUM_UNIQUE_VALUES_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500112
Ray Milkey88dd7e22018-10-24 10:04:03 -0700113 /** Value length. */
114 private int valueLength = VALUE_LENGTH_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500115
Ray Milkey88dd7e22018-10-24 10:04:03 -0700116 /** Whether to include events in test. */
117 private boolean includeEvents = INCLUDE_EVENTS_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500118
Ray Milkey88dd7e22018-10-24 10:04:03 -0700119 /** Whether to deterministically populate entries. */
120 private boolean deterministic = DETERMINISTIC_DEFAULT;
Jordan Halterman73296092018-03-08 16:06:28 -0500121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = MANDATORY)
Jordan Halterman29718e62017-07-20 13:24:33 -0700123 protected ClusterService clusterService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = MANDATORY)
Jordan Halterman29718e62017-07-20 13:24:33 -0700126 protected StorageService storageService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = MANDATORY)
Jordan Halterman29718e62017-07-20 13:24:33 -0700129 protected ComponentConfigService configService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = MANDATORY)
Jordan Halterman29718e62017-07-20 13:24:33 -0700132 protected PrimitivePerfCollector sampleCollector;
133
Jordan Halterman29718e62017-07-20 13:24:33 -0700134 private ExecutorService messageHandlingExecutor;
135
136 private ExecutorService workers;
Jordan Halterman73296092018-03-08 16:06:28 -0500137
138 // Tracks whether the test has been started in the cluster.
139 private AtomicValue<Boolean> started;
140 private AtomicValueEventListener<Boolean> startedListener = event -> {
141 if (event.newValue()) {
142 startTestRun();
143 } else {
144 stopTestRun();
145 }
146 };
147
148 // Tracks whether local clients are running.
149 private volatile boolean running;
Jordan Halterman29718e62017-07-20 13:24:33 -0700150
151 private Timer reportTimer;
152
153 private NodeId nodeId;
154 private TimerTask reporterTask;
155
156 private long startTime;
157 private long currentStartTime;
158 private AtomicLong overallCounter;
159 private AtomicLong currentCounter;
160
161 @Activate
162 public void activate(ComponentContext context) {
163 configService.registerProperties(getClass());
164
165 nodeId = clusterService.getLocalNode().id();
Jordan Halterman73296092018-03-08 16:06:28 -0500166 started = storageService.<Boolean>atomicValueBuilder()
167 .withName("perf-test-started")
168 .withSerializer(Serializer.using(KryoNamespaces.BASIC))
169 .build()
170 .asAtomicValue();
171 started.addListener(startedListener);
Jordan Halterman29718e62017-07-20 13:24:33 -0700172
173 reportTimer = new Timer("onos-primitive-perf-reporter");
174
175 messageHandlingExecutor = Executors.newSingleThreadExecutor(
176 groupedThreads("onos/perf", "command-handler"));
177
Jordan Halterman29718e62017-07-20 13:24:33 -0700178 // TODO: investigate why this seems to be necessary for configs to get picked up on initial activation
179 modify(context);
180 }
181
182 @Deactivate
183 public void deactivate() {
184 stopTestRun();
185
186 configService.unregisterProperties(getClass(), false);
187 messageHandlingExecutor.shutdown();
Jordan Halterman73296092018-03-08 16:06:28 -0500188 started.removeListener(startedListener);
Jordan Halterman29718e62017-07-20 13:24:33 -0700189
190 if (reportTimer != null) {
191 reportTimer.cancel();
192 reportTimer = null;
193 }
194 }
195
Jordan Halterman73296092018-03-08 16:06:28 -0500196 private int parseInt(Dictionary<?, ?> properties, String name, int currentValue, int defaultValue) {
197 try {
198 String s = get(properties, name);
199 return isNullOrEmpty(s) ? currentValue : Integer.parseInt(s.trim());
200 } catch (NumberFormatException | ClassCastException e) {
201 log.warn("Malformed configuration detected; using defaults", e);
202 return defaultValue;
203 }
204 }
205
Jordan Halterman29718e62017-07-20 13:24:33 -0700206 @Modified
207 public void modify(ComponentContext context) {
208 if (context == null) {
209 logConfig("Reconfigured");
210 return;
211 }
212
213 Dictionary<?, ?> properties = context.getProperties();
Ray Milkey88dd7e22018-10-24 10:04:03 -0700214 int newNumClients = parseInt(properties, NUM_CLIENTS, numClients, NUM_CLIENTS_DEFAULT);
215 int newWritePercentage = parseInt(properties, WRITE_PERCENTAGE, writePercentage, WRITE_PERCENTAGE_DEFAULT);
216 int newNumKeys = parseInt(properties, NUM_KEYS, numKeys, NUM_KEYS_DEFAULT);
217 int newKeyLength = parseInt(properties, KEY_LENGTH, keyLength, KEY_LENGTH_DEFAULT);
218 int newNumValues = parseInt(properties, NUM_UNIQUE_VALUES, numValues, NUM_UNIQUE_VALUES_DEFAULT);
219 int newValueLength = parseInt(properties, VALUE_LENGTH, valueLength, VALUE_LENGTH_DEFAULT);
Jordan Halterman29718e62017-07-20 13:24:33 -0700220
Ray Milkey88dd7e22018-10-24 10:04:03 -0700221 String includeEventsString = get(properties, INCLUDE_EVENTS);
Jordan Halterman73296092018-03-08 16:06:28 -0500222 boolean newIncludeEvents = isNullOrEmpty(includeEventsString)
223 ? includeEvents
224 : Boolean.parseBoolean(includeEventsString.trim());
Jordan Halterman29718e62017-07-20 13:24:33 -0700225
Ray Milkey88dd7e22018-10-24 10:04:03 -0700226 String deterministicString = get(properties, DETERMINISTIC);
Jordan Halterman73296092018-03-08 16:06:28 -0500227 boolean newDeterministic = isNullOrEmpty(deterministicString)
228 ? includeEvents
229 : Boolean.parseBoolean(deterministicString.trim());
230
231 if (newNumClients != numClients
232 || newWritePercentage != writePercentage
233 || newNumKeys != numKeys
234 || newKeyLength != keyLength
235 || newNumValues != numValues
236 || newValueLength != valueLength
237 || newIncludeEvents != includeEvents
238 || newDeterministic != deterministic) {
Jordan Halterman29718e62017-07-20 13:24:33 -0700239 numClients = newNumClients;
240 writePercentage = newWritePercentage;
Jordan Halterman73296092018-03-08 16:06:28 -0500241 numKeys = newNumKeys;
242 keyLength = newKeyLength;
243 numValues = newNumValues;
244 valueLength = newValueLength;
245 includeEvents = newIncludeEvents;
246 deterministic = newDeterministic;
Jordan Halterman29718e62017-07-20 13:24:33 -0700247 logConfig("Reconfigured");
Jordan Halterman73296092018-03-08 16:06:28 -0500248 Boolean started = this.started.get();
249 if (started != null && started) {
Jordan Halterman29718e62017-07-20 13:24:33 -0700250 stop();
251 start();
252 }
Jordan Halterman73296092018-03-08 16:06:28 -0500253 } else {
254 Boolean started = this.started.get();
255 if (started != null && started) {
256 if (running) {
257 stopTestRun();
258 }
259 startTestRun();
260 }
Jordan Halterman29718e62017-07-20 13:24:33 -0700261 }
262 }
263
Jordan Halterman73296092018-03-08 16:06:28 -0500264 /**
265 * Starts a new test.
266 */
Jordan Halterman29718e62017-07-20 13:24:33 -0700267 public void start() {
Jordan Halterman73296092018-03-08 16:06:28 -0500268 // To stop the test, we simply update the "started" value. Events from the change will notify all
269 // nodes to start the test.
270 Boolean started = this.started.get();
271 if (started == null || !started) {
272 this.started.set(true);
Jordan Halterman29718e62017-07-20 13:24:33 -0700273 }
274 }
275
Jordan Halterman73296092018-03-08 16:06:28 -0500276 /**
277 * Stops a test.
278 */
Jordan Halterman29718e62017-07-20 13:24:33 -0700279 public void stop() {
Jordan Halterman73296092018-03-08 16:06:28 -0500280 // To stop the test, we simply update the "started" value. Events from the change will notify all
281 // nodes to stop the test.
282 Boolean started = this.started.get();
283 if (started != null && started) {
284 this.started.set(false);
Jordan Halterman29718e62017-07-20 13:24:33 -0700285 }
286 }
287
288 private void logConfig(String prefix) {
Jordan Halterman73296092018-03-08 16:06:28 -0500289 log.info("{} with " +
290 "numClients = {}; " +
291 "writePercentage = {}; " +
292 "numKeys = {}; " +
293 "keyLength = {}; " +
294 "numValues = {}; " +
295 "valueLength = {}; " +
296 "includeEvents = {}; " +
297 "deterministic = {}",
298 prefix,
299 numClients,
300 writePercentage,
301 numKeys,
302 keyLength,
303 numValues,
304 valueLength,
305 includeEvents,
306 deterministic);
Jordan Halterman29718e62017-07-20 13:24:33 -0700307 }
308
309 private void startTestRun() {
Jordan Halterman73296092018-03-08 16:06:28 -0500310 if (running) {
311 return;
312 }
313
Jordan Halterman29718e62017-07-20 13:24:33 -0700314 sampleCollector.clearSamples();
315
316 startTime = System.currentTimeMillis();
317 currentStartTime = startTime;
318 currentCounter = new AtomicLong();
319 overallCounter = new AtomicLong();
320
321 reporterTask = new ReporterTask();
Jordan Halterman73296092018-03-08 16:06:28 -0500322 reportTimer.scheduleAtFixedRate(
323 reporterTask, REPORT_PERIOD - currentTimeMillis() % REPORT_PERIOD, REPORT_PERIOD);
Jordan Halterman29718e62017-07-20 13:24:33 -0700324
Jordan Halterman73296092018-03-08 16:06:28 -0500325 running = true;
Jordan Halterman29718e62017-07-20 13:24:33 -0700326
327 Map<String, ControllerNode> nodes = new TreeMap<>();
328 for (ControllerNode node : clusterService.getNodes()) {
329 nodes.put(node.id().id(), node);
330 }
331
332 // Compute the index of the local node in a sorted list of nodes.
333 List<String> sortedNodes = Lists.newArrayList(nodes.keySet());
334 int nodeCount = nodes.size();
335 int index = sortedNodes.indexOf(nodeId.id());
336
337 // Count the number of workers assigned to this node.
338 int workerCount = 0;
339 for (int i = 1; i <= numClients; i++) {
340 if (i % nodeCount == index) {
341 workerCount++;
342 }
343 }
344
345 // Create a worker pool and start the workers for this node.
Jordan Haltermanf5333892017-08-29 10:42:13 -0700346 if (workerCount > 0) {
Jordan Halterman73296092018-03-08 16:06:28 -0500347 String[] keys = createStrings(keyLength, numKeys);
348 String[] values = createStrings(valueLength, numValues);
349
Jordan Haltermanf5333892017-08-29 10:42:13 -0700350 workers = Executors.newFixedThreadPool(workerCount, groupedThreads("onos/primitive-perf", "worker-%d"));
351 for (int i = 0; i < workerCount; i++) {
Jordan Halterman73296092018-03-08 16:06:28 -0500352 if (deterministic) {
353 workers.submit(new DeterministicRunner(keys, values));
354 } else {
355 workers.submit(new NonDeterministicRunner(keys, values));
356 }
Jordan Haltermanf5333892017-08-29 10:42:13 -0700357 }
Jordan Halterman29718e62017-07-20 13:24:33 -0700358 }
359 log.info("Started test run");
360 }
361
Jordan Halterman73296092018-03-08 16:06:28 -0500362 /**
363 * Creates a deterministic array of strings to write to the cluster.
364 *
365 * @param length the string lengths
366 * @param count the string count
367 * @return a deterministic array of strings
368 */
369 private String[] createStrings(int length, int count) {
370 Random random = new Random(length);
371 List<String> stringsList = new ArrayList<>(count);
372 for (int i = 0; i < count; i++) {
373 stringsList.add(randomString(length, random));
374 }
375 return stringsList.toArray(new String[stringsList.size()]);
376 }
377
378 /**
379 * Creates a deterministic string based on the given seed.
380 *
381 * @param length the seed from which to create the string
382 * @param random the random object from which to create the string characters
383 * @return the string
384 */
385 private String randomString(int length, Random random) {
386 char[] buffer = new char[length];
387 for (int i = 0; i < length; i++) {
388 buffer[i] = CHARS[random.nextInt(CHARS.length)];
389 }
390 return new String(buffer);
391 }
392
Jordan Halterman29718e62017-07-20 13:24:33 -0700393 private void stopTestRun() {
Jordan Halterman73296092018-03-08 16:06:28 -0500394 if (!running) {
395 return;
396 }
397
Jordan Halterman29718e62017-07-20 13:24:33 -0700398 if (reporterTask != null) {
399 reporterTask.cancel();
400 reporterTask = null;
401 }
402
Jordan Haltermanf5333892017-08-29 10:42:13 -0700403 if (workers != null) {
404 workers.shutdown();
405 try {
406 workers.awaitTermination(10, TimeUnit.MILLISECONDS);
407 } catch (InterruptedException e) {
408 log.warn("Failed to stop worker", e);
Ray Milkey5c7d4882018-02-05 14:50:39 -0800409 Thread.currentThread().interrupt();
Jordan Haltermanf5333892017-08-29 10:42:13 -0700410 }
Jordan Halterman29718e62017-07-20 13:24:33 -0700411 }
412
413 sampleCollector.recordSample(0, 0);
414 sampleCollector.recordSample(0, 0);
Jordan Halterman73296092018-03-08 16:06:28 -0500415
416 running = false;
Jordan Halterman29718e62017-07-20 13:24:33 -0700417
418 log.info("Stopped test run");
419 }
420
421 // Submits primitive operations.
Jordan Halterman73296092018-03-08 16:06:28 -0500422 abstract class Runner implements Runnable {
423 final String[] keys;
424 final String[] values;
425 final Random random = new Random();
426 ConsistentMap<String, String> map;
Jordan Halterman29718e62017-07-20 13:24:33 -0700427
Jordan Halterman73296092018-03-08 16:06:28 -0500428 Runner(String[] keys, String[] values) {
429 this.keys = keys;
430 this.values = values;
Jordan Halterman29718e62017-07-20 13:24:33 -0700431 }
432
433 @Override
434 public void run() {
435 setup();
Jordan Halterman73296092018-03-08 16:06:28 -0500436 while (running) {
Jordan Halterman29718e62017-07-20 13:24:33 -0700437 try {
438 submit();
439 } catch (Exception e) {
440 log.warn("Exception during cycle", e);
441 }
442 }
443 teardown();
444 }
445
Jordan Halterman73296092018-03-08 16:06:28 -0500446 void setup() {
Jordan Halterman29718e62017-07-20 13:24:33 -0700447 map = storageService.<String, String>consistentMapBuilder()
448 .withName("perf-test")
449 .withSerializer(Serializer.using(KryoNamespaces.BASIC))
450 .build();
Jordan Halterman73296092018-03-08 16:06:28 -0500451 if (includeEvents) {
452 map.addListener(event -> {
453 });
454 }
Jordan Halterman29718e62017-07-20 13:24:33 -0700455 }
456
Jordan Halterman73296092018-03-08 16:06:28 -0500457 abstract void submit();
458
459 void teardown() {
460 //map.destroy();
461 }
462 }
463
464 private class NonDeterministicRunner extends Runner {
465 NonDeterministicRunner(String[] keys, String[] values) {
466 super(keys, values);
467 }
468
469 @Override
470 void submit() {
471 currentCounter.incrementAndGet();
472 String key = keys[random.nextInt(keys.length)];
473 if (random.nextInt(100) < writePercentage) {
474 map.put(key, values[random.nextInt(values.length)]);
Jordan Halterman29718e62017-07-20 13:24:33 -0700475 } else {
476 map.get(key);
477 }
478 }
Jordan Halterman29718e62017-07-20 13:24:33 -0700479 }
480
Jordan Halterman73296092018-03-08 16:06:28 -0500481 private class DeterministicRunner extends Runner {
482 private int index;
483
484 DeterministicRunner(String[] keys, String[] values) {
485 super(keys, values);
486 }
487
Jordan Halterman29718e62017-07-20 13:24:33 -0700488 @Override
Jordan Halterman73296092018-03-08 16:06:28 -0500489 void submit() {
490 currentCounter.incrementAndGet();
491 if (random.nextInt(100) < writePercentage) {
492 map.put(keys[index++ % keys.length], values[random.nextInt(values.length)]);
Jordan Halterman29718e62017-07-20 13:24:33 -0700493 } else {
Jordan Halterman73296092018-03-08 16:06:28 -0500494 map.get(keys[random.nextInt(keys.length)]);
Jordan Halterman29718e62017-07-20 13:24:33 -0700495 }
496 }
497 }
498
499 private class ReporterTask extends TimerTask {
500 @Override
501 public void run() {
502 long endTime = System.currentTimeMillis();
503 long overallTime = endTime - startTime;
504 long currentTime = endTime - currentStartTime;
505 long currentCount = currentCounter.getAndSet(0);
506 long overallCount = overallCounter.addAndGet(currentCount);
507 sampleCollector.recordSample(overallTime > 0 ? overallCount / (overallTime / 1000d) : 0,
508 currentTime > 0 ? currentCount / (currentTime / 1000d) : 0);
509 currentStartTime = System.currentTimeMillis();
510 }
511 }
512
513}