Merge remote-tracking branch 'origin/main' into jk-test-eng-95-expected-result-verification

# Conflicts:
#	adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/BaseOpDispenser.java
#	engine-api/src/test/java/io/nosqlbench/engine/api/activityapi/errorhandling/modular/NBErrorHandlerTest.java
#	engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java
This commit is contained in:
kijanowski
2023-05-17 11:21:11 +02:00
153 changed files with 5727 additions and 4560 deletions
+1 -1
View File
@@ -57,7 +57,7 @@
<dependency>
<groupId>com.databricks</groupId>
<artifactId>sjsonnet_2.13</artifactId>
<version>0.4.3</version>
<version>0.4.4</version>
</dependency>
<dependency>
@@ -18,6 +18,8 @@ package io.nosqlbench.engine.api.activityimpl;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Timer;
import io.nosqlbench.api.config.NBLabeledElement;
import io.nosqlbench.api.config.NBLabels;
import io.nosqlbench.api.engine.metrics.ActivityMetrics;
import io.nosqlbench.api.errors.MVELCompilationError;
import io.nosqlbench.engine.api.activityimpl.uniform.DriverAdapter;
@@ -30,7 +32,7 @@ import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* {@inheritDoc}
*
* See {@link OpDispenser} for details on how to use this type.
* <p>
* Some details are tracked per op template, which aligns to the life-cycle of the op dispenser.
@@ -38,11 +40,12 @@ import java.util.concurrent.TimeUnit;
*
* @param <T> The type of operation
*/
public abstract class BaseOpDispenser<T extends Op, S> implements OpDispenser<T> {
public abstract class BaseOpDispenser<T extends Op, S> implements OpDispenser<T>, NBLabeledElement {
private final String opName;
private Serializable expectedResultExpression;
protected final DriverAdapter<T, S> adapter;
private final NBLabels labels;
private boolean instrument;
private Histogram resultSizeHistogram;
private Timer successTimer;
@@ -50,24 +53,23 @@ public abstract class BaseOpDispenser<T extends Op, S> implements OpDispenser<T>
private final String[] timerStarts;
private final String[] timerStops;
protected BaseOpDispenser(DriverAdapter<T, S> adapter, ParsedOp op) {
this.opName = op.getName();
protected BaseOpDispenser(final DriverAdapter<T, S> adapter, final ParsedOp op) {
opName = op.getName();
this.adapter = adapter;
timerStarts = op.takeOptionalStaticValue("start-timers", String.class)
labels = op.getLabels();
this.timerStarts = op.takeOptionalStaticValue("start-timers", String.class)
.map(s -> s.split(", *"))
.orElse(null);
timerStops = op.takeOptionalStaticValue("stop-timers", String.class)
this.timerStops = op.takeOptionalStaticValue("stop-timers", String.class)
.map(s -> s.split(", *"))
.orElse(null);
if (timerStarts != null) {
for (String timerStart : timerStarts) {
ThreadLocalNamedTimers.addTimer(op, timerStart);
}
}
configureInstrumentation(op);
configureResultExpectations(op);
if (null != timerStarts)
for (final String timerStart : this.timerStarts) ThreadLocalNamedTimers.addTimer(op, timerStart);
this.configureInstrumentation(op);
this.configureResultExpectations(op);
}
public Serializable getExpectedResultExpression() {
@@ -91,55 +93,47 @@ public abstract class BaseOpDispenser<T extends Op, S> implements OpDispenser<T>
}
String getOpName() {
return opName;
return this.opName;
}
public DriverAdapter<T, S> getAdapter() {
return adapter;
return this.adapter;
}
protected String getDefaultMetricsPrefix(ParsedOp pop) {
return pop.getStaticConfigOr("alias", "UNKNOWN") + "-" + pop.getName() + "--";
}
private void configureInstrumentation(ParsedOp pop) {
this.instrument = pop.takeStaticConfigOr("instrument", false);
if (instrument) {
this.successTimer = ActivityMetrics.timer(getDefaultMetricsPrefix(pop) + "success");
this.errorTimer = ActivityMetrics.timer(getDefaultMetricsPrefix(pop) + "error");
this.resultSizeHistogram = ActivityMetrics.histogram(getDefaultMetricsPrefix(pop) + "resultset-size");
private void configureInstrumentation(final ParsedOp pop) {
instrument = pop.takeStaticConfigOr("instrument", false);
if (this.instrument) {
final int hdrDigits = pop.getStaticConfigOr("hdr_digits", 4).intValue();
successTimer = ActivityMetrics.timer(pop, "success",hdrDigits);
errorTimer = ActivityMetrics.timer(pop, "error", hdrDigits);
resultSizeHistogram = ActivityMetrics.histogram(pop, "resultset-size", hdrDigits);
}
}
@Override
public void onStart(long cycleValue) {
if (timerStarts != null) {
ThreadLocalNamedTimers.TL_INSTANCE.get().start(timerStarts);
}
public void onStart(final long cycleValue) {
if (null != timerStarts) ThreadLocalNamedTimers.TL_INSTANCE.get().start(this.timerStarts);
}
@Override
public void onSuccess(long cycleValue, long nanoTime, long resultSize) {
if (instrument) {
successTimer.update(nanoTime, TimeUnit.NANOSECONDS);
if (resultSize > -1) {
resultSizeHistogram.update(resultSize);
}
}
if (timerStops != null) {
ThreadLocalNamedTimers.TL_INSTANCE.get().stop(timerStops);
public void onSuccess(final long cycleValue, final long nanoTime, final long resultSize) {
if (this.instrument) {
this.successTimer.update(nanoTime, TimeUnit.NANOSECONDS);
if (-1 < resultSize) this.resultSizeHistogram.update(resultSize);
}
if (null != timerStops) ThreadLocalNamedTimers.TL_INSTANCE.get().stop(this.timerStops);
}
@Override
public void onError(long cycleValue, long resultNanos, Throwable t) {
public void onError(final long cycleValue, final long resultNanos, final Throwable t) {
if (instrument) {
errorTimer.update(resultNanos, TimeUnit.NANOSECONDS);
}
if (timerStops != null) {
ThreadLocalNamedTimers.TL_INSTANCE.get().stop(timerStops);
}
if (this.instrument) this.errorTimer.update(resultNanos, TimeUnit.NANOSECONDS);
if (null != timerStops) ThreadLocalNamedTimers.TL_INSTANCE.get().stop(this.timerStops);
}
@Override
public NBLabels getLabels() {
return this.labels;
}
}
@@ -28,6 +28,7 @@ package io.nosqlbench.engine.api.activityimpl.uniform.flowtypes;
* hand down the chain is more costly, so implementing this interface allows the runtime
* to be more optimized.</li>
* <li>{@link ChainingOp}</li>
* <li>{@link RunnableOp}</li>
* </ul>
* </p>
*/
@@ -1,24 +1,23 @@
package io.nosqlbench.engine.api.metrics;
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2023 nosqlbench
*
* 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
* 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.
* 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.
*/
package io.nosqlbench.engine.api.metrics;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -33,11 +32,11 @@ public class EndToEndMetricsAdapterUtil {
public final String label;
MSG_SEQ_ERROR_SIMU_TYPE(String label) {
MSG_SEQ_ERROR_SIMU_TYPE(final String label) {
this.label = label;
}
private static final Map<String, MSG_SEQ_ERROR_SIMU_TYPE> MAPPING = Stream.of(values())
private static final Map<String, MSG_SEQ_ERROR_SIMU_TYPE> MAPPING = Stream.of(MSG_SEQ_ERROR_SIMU_TYPE.values())
.flatMap(simuType ->
Stream.of(simuType.label,
simuType.label.toLowerCase(),
@@ -46,10 +45,10 @@ public class EndToEndMetricsAdapterUtil {
simuType.name().toLowerCase(),
simuType.name().toUpperCase())
.distinct().map(key -> Map.entry(key, simuType)))
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
.collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue));
public static Optional<MSG_SEQ_ERROR_SIMU_TYPE> parseSimuType(String simuTypeString) {
return Optional.ofNullable(MAPPING.get(simuTypeString.trim()));
public static Optional<MSG_SEQ_ERROR_SIMU_TYPE> parseSimuType(final String simuTypeString) {
return Optional.ofNullable(MSG_SEQ_ERROR_SIMU_TYPE.MAPPING.get(simuTypeString.trim()));
}
}
}
@@ -1,23 +1,22 @@
package io.nosqlbench.engine.api.metrics;
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2023 nosqlbench
*
* 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
* 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.
* 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.
*/
package io.nosqlbench.engine.api.metrics;
import io.nosqlbench.engine.api.metrics.EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE;
import org.apache.commons.lang3.RandomUtils;
import java.util.ArrayDeque;
@@ -33,76 +32,70 @@ public class MessageSequenceNumberSendingHandler {
long number = 1;
Queue<Long> outOfOrderNumbers;
public long getNextSequenceNumber(Set<EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes) {
return getNextSequenceNumber(simulatedErrorTypes, SIMULATED_ERROR_PROBABILITY_PERCENTAGE);
public long getNextSequenceNumber(final Set<MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes) {
return this.getNextSequenceNumber(simulatedErrorTypes, MessageSequenceNumberSendingHandler.SIMULATED_ERROR_PROBABILITY_PERCENTAGE);
}
long getNextSequenceNumber(Set<EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes, int errorProbabilityPercentage) {
simulateError(simulatedErrorTypes, errorProbabilityPercentage);
return nextNumber();
long getNextSequenceNumber(final Set<MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes, final int errorProbabilityPercentage) {
this.simulateError(simulatedErrorTypes, errorProbabilityPercentage);
return this.nextNumber();
}
private void simulateError(Set<EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes, int errorProbabilityPercentage) {
if (!simulatedErrorTypes.isEmpty() && shouldSimulateError(errorProbabilityPercentage)) {
private void simulateError(final Set<MSG_SEQ_ERROR_SIMU_TYPE> simulatedErrorTypes, final int errorProbabilityPercentage) {
if (!simulatedErrorTypes.isEmpty() && this.shouldSimulateError(errorProbabilityPercentage)) {
int selectIndex = 0;
int numberOfErrorTypes = simulatedErrorTypes.size();
if (numberOfErrorTypes > 1) {
// pick one of the simulated error type randomly
selectIndex = RandomUtils.nextInt(0, numberOfErrorTypes);
}
EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE errorType = simulatedErrorTypes.stream()
final int numberOfErrorTypes = simulatedErrorTypes.size();
// pick one of the simulated error type randomly
if (1 < numberOfErrorTypes) selectIndex = RandomUtils.nextInt(0, numberOfErrorTypes);
final MSG_SEQ_ERROR_SIMU_TYPE errorType = simulatedErrorTypes.stream()
.skip(selectIndex)
.findFirst()
.get();
switch (errorType) {
case OutOfOrder:
// simulate message out of order
injectMessagesOutOfOrder();
this.injectMessagesOutOfOrder();
break;
case MsgDup:
// simulate message duplication
injectMessageDuplication();
this.injectMessageDuplication();
break;
case MsgLoss:
// simulate message loss
injectMessageLoss();
this.injectMessageLoss();
break;
}
}
}
private boolean shouldSimulateError(int errorProbabilityPercentage) {
private boolean shouldSimulateError(final int errorProbabilityPercentage) {
// Simulate error with the specified probability
return RandomUtils.nextInt(0, 100) < errorProbabilityPercentage;
}
long nextNumber() {
if (outOfOrderNumbers != null) {
long nextNumber = outOfOrderNumbers.poll();
if (outOfOrderNumbers.isEmpty()) {
outOfOrderNumbers = null;
}
if (null != outOfOrderNumbers) {
final long nextNumber = this.outOfOrderNumbers.poll();
if (this.outOfOrderNumbers.isEmpty()) this.outOfOrderNumbers = null;
return nextNumber;
}
return number++;
long l = this.number;
this.number++;
return l;
}
void injectMessagesOutOfOrder() {
if (outOfOrderNumbers == null) {
outOfOrderNumbers = new ArrayDeque<>(Arrays.asList(number + 2, number, number + 1));
number += 3;
if (null == outOfOrderNumbers) {
this.outOfOrderNumbers = new ArrayDeque<>(Arrays.asList(this.number + 2, this.number, this.number + 1));
this.number += 3;
}
}
void injectMessageDuplication() {
if (outOfOrderNumbers == null) {
number--;
}
if (null == outOfOrderNumbers) this.number--;
}
void injectMessageLoss() {
if (outOfOrderNumbers == null) {
number++;
}
if (null == outOfOrderNumbers) this.number++;
}
}
@@ -1,22 +1,20 @@
package io.nosqlbench.engine.api.metrics;
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2023 nosqlbench
*
* 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
* 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.
* 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.
*/
package io.nosqlbench.engine.api.metrics;
import com.codahale.metrics.Counter;
@@ -46,20 +44,20 @@ public class ReceivedMessageSequenceTracker implements AutoCloseable {
private final int maxTrackSkippedSequenceNumbers;
private long expectedNumber = -1;
public ReceivedMessageSequenceTracker(Counter msgErrOutOfSeqCounter, Counter msgErrDuplicateCounter, Counter msgErrLossCounter) {
public ReceivedMessageSequenceTracker(final Counter msgErrOutOfSeqCounter, final Counter msgErrDuplicateCounter, final Counter msgErrLossCounter) {
this(msgErrOutOfSeqCounter, msgErrDuplicateCounter, msgErrLossCounter,
DEFAULT_MAX_TRACK_OUT_OF_ORDER_SEQUENCE_NUMBERS, DEFAULT_MAX_TRACK_SKIPPED_SEQUENCE_NUMBERS);
ReceivedMessageSequenceTracker.DEFAULT_MAX_TRACK_OUT_OF_ORDER_SEQUENCE_NUMBERS, ReceivedMessageSequenceTracker.DEFAULT_MAX_TRACK_SKIPPED_SEQUENCE_NUMBERS);
}
public ReceivedMessageSequenceTracker(Counter msgErrOutOfSeqCounter, Counter msgErrDuplicateCounter, Counter msgErrLossCounter,
int maxTrackOutOfOrderSequenceNumbers, int maxTrackSkippedSequenceNumbers) {
public ReceivedMessageSequenceTracker(final Counter msgErrOutOfSeqCounter, final Counter msgErrDuplicateCounter, final Counter msgErrLossCounter,
final int maxTrackOutOfOrderSequenceNumbers, final int maxTrackSkippedSequenceNumbers) {
this.msgErrOutOfSeqCounter = msgErrOutOfSeqCounter;
this.msgErrDuplicateCounter = msgErrDuplicateCounter;
this.msgErrLossCounter = msgErrLossCounter;
this.maxTrackOutOfOrderSequenceNumbers = maxTrackOutOfOrderSequenceNumbers;
this.maxTrackSkippedSequenceNumbers = maxTrackSkippedSequenceNumbers;
this.pendingOutOfSeqNumbers = new TreeSet<>();
this.skippedSeqNumbers = new TreeSet<>();
pendingOutOfSeqNumbers = new TreeSet<>();
skippedSeqNumbers = new TreeSet<>();
}
/**
@@ -67,84 +65,71 @@ public class ReceivedMessageSequenceTracker implements AutoCloseable {
*
* @param sequenceNumber the sequence number of the received message
*/
public void sequenceNumberReceived(long sequenceNumber) {
if (expectedNumber == -1) {
expectedNumber = sequenceNumber + 1;
public void sequenceNumberReceived(final long sequenceNumber) {
if (-1 == expectedNumber) {
this.expectedNumber = sequenceNumber + 1;
return;
}
if (sequenceNumber < expectedNumber) {
if (skippedSeqNumbers.remove(sequenceNumber)) {
if (sequenceNumber < this.expectedNumber) {
if (this.skippedSeqNumbers.remove(sequenceNumber)) {
// late out-of-order delivery was detected
// decrease the loss counter
msgErrLossCounter.dec();
this.msgErrLossCounter.dec();
// increment the out-of-order counter
msgErrOutOfSeqCounter.inc();
} else {
msgErrDuplicateCounter.inc();
}
this.msgErrOutOfSeqCounter.inc();
} else this.msgErrDuplicateCounter.inc();
return;
}
boolean messagesSkipped = false;
if (sequenceNumber > expectedNumber) {
if (pendingOutOfSeqNumbers.size() == maxTrackOutOfOrderSequenceNumbers) {
messagesSkipped = processLowestPendingOutOfSequenceNumber();
}
if (!pendingOutOfSeqNumbers.add(sequenceNumber)) {
msgErrDuplicateCounter.inc();
}
} else {
// sequenceNumber == expectedNumber
expectedNumber++;
}
processPendingOutOfSequenceNumbers(messagesSkipped);
cleanUpTooFarBehindOutOfSequenceNumbers();
// sequenceNumber == expectedNumber
if (sequenceNumber > this.expectedNumber) {
if (this.pendingOutOfSeqNumbers.size() == this.maxTrackOutOfOrderSequenceNumbers)
messagesSkipped = this.processLowestPendingOutOfSequenceNumber();
if (!this.pendingOutOfSeqNumbers.add(sequenceNumber)) this.msgErrDuplicateCounter.inc();
} else this.expectedNumber++;
this.processPendingOutOfSequenceNumbers(messagesSkipped);
this.cleanUpTooFarBehindOutOfSequenceNumbers();
}
private boolean processLowestPendingOutOfSequenceNumber() {
// remove the lowest pending out of sequence number
Long lowestOutOfSeqNumber = pendingOutOfSeqNumbers.first();
pendingOutOfSeqNumbers.remove(lowestOutOfSeqNumber);
if (lowestOutOfSeqNumber > expectedNumber) {
final Long lowestOutOfSeqNumber = this.pendingOutOfSeqNumbers.first();
this.pendingOutOfSeqNumbers.remove(lowestOutOfSeqNumber);
if (lowestOutOfSeqNumber > this.expectedNumber) {
// skip the expected number ahead to the number after the lowest sequence number
// increment the counter with the amount of sequence numbers that got skipped
// keep track of the skipped sequence numbers to detect late out-of-order message delivery
for (long l = expectedNumber; l < lowestOutOfSeqNumber; l++) {
msgErrLossCounter.inc();
skippedSeqNumbers.add(l);
if (skippedSeqNumbers.size() > maxTrackSkippedSequenceNumbers) {
skippedSeqNumbers.remove(skippedSeqNumbers.first());
}
for (long l = this.expectedNumber; l < lowestOutOfSeqNumber; l++) {
this.msgErrLossCounter.inc();
this.skippedSeqNumbers.add(l);
if (this.skippedSeqNumbers.size() > this.maxTrackSkippedSequenceNumbers)
this.skippedSeqNumbers.remove(this.skippedSeqNumbers.first());
}
expectedNumber = lowestOutOfSeqNumber + 1;
this.expectedNumber = lowestOutOfSeqNumber + 1;
return true;
} else {
msgErrLossCounter.inc();
}
this.msgErrLossCounter.inc();
return false;
}
private void processPendingOutOfSequenceNumbers(boolean messagesSkipped) {
private void processPendingOutOfSequenceNumbers(final boolean messagesSkipped) {
// check if there are previously received out-of-order sequence number that have been received
while (pendingOutOfSeqNumbers.remove(expectedNumber)) {
expectedNumber++;
if (!messagesSkipped) {
msgErrOutOfSeqCounter.inc();
}
while (this.pendingOutOfSeqNumbers.remove(this.expectedNumber)) {
this.expectedNumber++;
if (!messagesSkipped) this.msgErrOutOfSeqCounter.inc();
}
}
private void cleanUpTooFarBehindOutOfSequenceNumbers() {
// remove sequence numbers that are too far behind
for (Iterator<Long> iterator = pendingOutOfSeqNumbers.iterator(); iterator.hasNext(); ) {
Long number = iterator.next();
if (number < expectedNumber - maxTrackOutOfOrderSequenceNumbers) {
msgErrLossCounter.inc();
for (final Iterator<Long> iterator = this.pendingOutOfSeqNumbers.iterator(); iterator.hasNext(); ) {
final Long number = iterator.next();
if (number < (this.expectedNumber - this.maxTrackOutOfOrderSequenceNumbers)) {
this.msgErrLossCounter.inc();
iterator.remove();
} else {
break;
}
} else break;
}
}
@@ -154,16 +139,15 @@ public class ReceivedMessageSequenceTracker implements AutoCloseable {
*/
@Override
public void close() {
while (!pendingOutOfSeqNumbers.isEmpty()) {
processPendingOutOfSequenceNumbers(processLowestPendingOutOfSequenceNumber());
}
while (!this.pendingOutOfSeqNumbers.isEmpty())
this.processPendingOutOfSequenceNumbers(this.processLowestPendingOutOfSequenceNumber());
}
public int getMaxTrackOutOfOrderSequenceNumbers() {
return maxTrackOutOfOrderSequenceNumbers;
return this.maxTrackOutOfOrderSequenceNumbers;
}
public int getMaxTrackSkippedSequenceNumbers() {
return maxTrackSkippedSequenceNumbers;
return this.maxTrackSkippedSequenceNumbers;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
package io.nosqlbench.engine.api.metrics;
import com.codahale.metrics.Timer;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import com.codahale.metrics.Timer.Context;
import io.nosqlbench.api.engine.metrics.ActivityMetrics;
import io.nosqlbench.engine.api.templating.ParsedOp;
import org.apache.logging.log4j.LogManager;
@@ -32,59 +32,41 @@ import java.util.Map;
*/
public class ThreadLocalNamedTimers {
private final static Logger logger = LogManager.getLogger(ThreadLocalNamedTimers.class);
private static final Logger logger = LogManager.getLogger(ThreadLocalNamedTimers.class);
public transient final static ThreadLocal<ThreadLocalNamedTimers> TL_INSTANCE = ThreadLocal.withInitial(ThreadLocalNamedTimers::new);
private final static Map<String, Timer> timers = new HashMap<>();
private final Map<String, Timer.Context> contexts = new HashMap<>();
public final static ThreadLocal<ThreadLocalNamedTimers> TL_INSTANCE = ThreadLocal.withInitial(ThreadLocalNamedTimers::new);
private static final Map<String, Timer> timers = new HashMap<>();
private final Map<String, Context> contexts = new HashMap<>();
public static void addTimer(ActivityDef def, String name, int hdrdigits) {
if (timers.containsKey("name")) {
logger.warn("A timer named '" + name + "' was already defined and initialized.");
}
Timer timer = ActivityMetrics.timer(def, name, hdrdigits);
timers.put(name, timer);
public static void addTimer(final ParsedOp pop, final String name) {
if (ThreadLocalNamedTimers.timers.containsKey("name"))
ThreadLocalNamedTimers.logger.warn("A timer named '{}' was already defined and initialized.", name);
ThreadLocalNamedTimers.timers.put(name, ActivityMetrics.timer(pop,name,ActivityMetrics.DEFAULT_HDRDIGITS));
}
public static void addTimer(ParsedOp pop, String name) {
if (timers.containsKey("name")) {
logger.warn("A timer named '" + name + "' was already defined and initialized.");
}
Timer timer = ActivityMetrics.timer(pop.getStaticConfig("alias",String.class)+"."+name);
timers.put(name, timer);
public void start(final String name) {
final Context context = ThreadLocalNamedTimers.timers.get(name).time();
this.contexts.put(name, context);
}
public void start(String name) {
Timer.Context context = timers.get(name).time();
contexts.put(name, context);
}
public void stop(String name) {
Timer.Context context = contexts.get(name);
public void stop(final String name) {
final Context context = this.contexts.get(name);
context.stop();
}
public void start(List<String> timerNames) {
for (String timerName : timerNames) {
start(timerName);
}
public void start(final List<String> timerNames) {
for (final String timerName : timerNames) this.start(timerName);
}
public void start(String[] timerNames) {
for (String timerName : timerNames) {
start(timerName);
}
public void start(final String[] timerNames) {
for (final String timerName : timerNames) this.start(timerName);
}
public void stop(List<String> timerName) {
for (String stopTimer : timerName) {
stop(stopTimer);
}
public void stop(final List<String> timerName) {
for (final String stopTimer : timerName) this.stop(stopTimer);
}
public void stop(String[] timerStops) {
for (String timerStop : timerStops) {
stop(timerStop);
}
public void stop(final String[] timerStops) {
for (final String timerStop : timerStops) this.stop(timerStop);
}
}
@@ -16,6 +16,8 @@
package io.nosqlbench.engine.api.templating;
import io.nosqlbench.api.config.NBLabeledElement;
import io.nosqlbench.api.config.NBLabels;
import io.nosqlbench.api.config.fieldreaders.DynamicFieldReader;
import io.nosqlbench.api.config.fieldreaders.StaticFieldReader;
import io.nosqlbench.api.config.standard.NBConfigError;
@@ -292,9 +294,9 @@ import java.util.function.LongFunction;
* in the activity parameters if needed to find a missing configuration parameter, but this will only work if
* the specific named parameter is allowed at the activity level.</P>
*/
public class ParsedOp implements LongFunction<Map<String, ?>>, StaticFieldReader, DynamicFieldReader {
public class ParsedOp implements LongFunction<Map<String, ?>>, NBLabeledElement, StaticFieldReader, DynamicFieldReader {
private final static Logger logger = LogManager.getLogger(ParsedOp.class);
private static final Logger logger = LogManager.getLogger(ParsedOp.class);
/**
* The names of payload values in the result of the operation which should be saved.
@@ -307,36 +309,32 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, StaticFieldReader
private final OpTemplate _opTemplate;
private final NBConfiguration activityCfg;
private final ParsedTemplateMap tmap;
/**
* Create a parsed command from an Op template.
*
* @param ot An OpTemplate representing an operation to be performed in a native driver.
* @param activityCfg The activity configuration, used for reading config parameters
*/
public ParsedOp(OpTemplate ot, NBConfiguration activityCfg) {
this(ot, activityCfg, List.of());
}
private final NBLabels labels;
/**
* Create a parsed command from an Op template. This version is exactly like
* {@link ParsedOp (OpTemplate,NBConfiguration)} except that it allows
* except that it allows
* preprocessors. Preprocessors are all applied to the the op template before
* it is applied to the parsed command fields, allowing you to combine or destructure
* fields from more tha one representation into a single canonical representation
* for processing.
*
* @param opTemplate The OpTemplate as provided by a user via YAML, JSON, or API (data structure)
* @param activityCfg The activity configuration, used to resolve nested config parameters
* @param preprocessors Map->Map transformers.
* @param opTemplate
* The OpTemplate as provided by a user via YAML, JSON, or API (data structure)
* @param activityCfg
* The activity configuration, used to resolve nested config parameters
* @param preprocessors
* Map->Map transformers.
* @param labels
*/
public ParsedOp(
OpTemplate opTemplate,
NBConfiguration activityCfg,
List<Function<Map<String, Object>, Map<String, Object>>> preprocessors
) {
List<Function<Map<String, Object>, Map<String, Object>>> preprocessors,
NBLabeledElement parent) {
this._opTemplate = opTemplate;
this.activityCfg = activityCfg;
labels=parent.getLabels().and("op", this.getName());
Map<String, Object> map = opTemplate.getOp().orElseThrow(() ->
new OpConfigError("ParsedOp constructor requires a non-null value for the op field, but it was missing."));
@@ -542,7 +540,7 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, StaticFieldReader
* @param name The field name which must be defined as static or dynamic
* @return A function which can provide the named field value
*/
public LongFunction<? extends String> getAsRequiredFunction(String name) {
public LongFunction<String> getAsRequiredFunction(String name) {
return tmap.getAsRequiredFunction(name, String.class);
}
@@ -601,6 +599,7 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, StaticFieldReader
* @param field The requested field name
* @return true if the named field is defined as static or dynamic
*/
@Override
public boolean isDefined(String field) {
return tmap.isDefined(field);
}
@@ -920,4 +919,9 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, StaticFieldReader
public List<CapturePoint> getCaptures() {
return tmap.getCaptures();
}
@Override
public NBLabels getLabels() {
return labels;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package io.nosqlbench.engine.api.metrics;
import io.nosqlbench.engine.api.metrics.EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -31,59 +32,53 @@ class MessageSequenceNumberSendingHandlerTest {
@Test
void shouldAddMonotonicSequence() {
for (long l = 1; l <= 100; l++) {
assertEquals(l, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
}
for (long l = 1; 100 >= l; l++)
assertEquals(l, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
}
@Test
void shouldInjectMessageLoss() {
assertEquals(1L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(3L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE.MsgLoss), 100));
assertEquals(1L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(3L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(MSG_SEQ_ERROR_SIMU_TYPE.MsgLoss), 100));
}
@Test
void shouldInjectMessageDuplication() {
assertEquals(1L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(1L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE.MsgDup), 100));
assertEquals(1L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(1L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(MSG_SEQ_ERROR_SIMU_TYPE.MsgDup), 100));
}
@Test
void shouldInjectMessageOutOfOrder() {
assertEquals(1L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(4L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE.OutOfOrder), 100));
assertEquals(2L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(3L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(5L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(6, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(1L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(4L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.singleton(MSG_SEQ_ERROR_SIMU_TYPE.OutOfOrder), 100));
assertEquals(2L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(3L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(5L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(6, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
}
@Test
void shouldInjectOneOfTheSimulatedErrorsRandomly() {
Set<EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE> allErrorTypes = new HashSet<>(Arrays.asList(EndToEndMetricsAdapterUtil.MSG_SEQ_ERROR_SIMU_TYPE.values()));
final Set<MSG_SEQ_ERROR_SIMU_TYPE> allErrorTypes = new HashSet<>(Arrays.asList(MSG_SEQ_ERROR_SIMU_TYPE.values()));
assertEquals(1L, sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
assertEquals(1L, this.sequenceNumberSendingHandler.getNextSequenceNumber(Collections.emptySet()));
long previousSequenceNumber = 1L;
int outOfSequenceInjectionCounter = 0;
int messageDupCounter = 0;
int messageLossCounter = 0;
int successCounter = 0;
for (int i = 0; i < 1000; i++) {
long nextSequenceNumber = sequenceNumberSendingHandler.getNextSequenceNumber(allErrorTypes);
if (nextSequenceNumber >= previousSequenceNumber + 3) {
outOfSequenceInjectionCounter++;
} else if (nextSequenceNumber <= previousSequenceNumber) {
messageDupCounter++;
} else if (nextSequenceNumber >= previousSequenceNumber + 2) {
messageLossCounter++;
} else if (nextSequenceNumber == previousSequenceNumber + 1) {
successCounter++;
}
for (int i = 0; 1000 > i; i++) {
final long nextSequenceNumber = this.sequenceNumberSendingHandler.getNextSequenceNumber(allErrorTypes);
if (nextSequenceNumber >= (previousSequenceNumber + 3)) outOfSequenceInjectionCounter++;
else if (nextSequenceNumber <= previousSequenceNumber) messageDupCounter++;
else if (nextSequenceNumber >= (previousSequenceNumber + 2)) messageLossCounter++;
else if (nextSequenceNumber == (previousSequenceNumber + 1)) successCounter++;
previousSequenceNumber = nextSequenceNumber;
}
assertTrue(outOfSequenceInjectionCounter > 0);
assertTrue(messageDupCounter > 0);
assertTrue(messageLossCounter > 0);
assertTrue(0 < outOfSequenceInjectionCounter);
assertTrue(0 < messageDupCounter);
assertTrue(0 < messageLossCounter);
assertEquals(1000, outOfSequenceInjectionCounter + messageDupCounter + messageLossCounter + successCounter);
}
@@ -1,18 +1,17 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* 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
* 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.
* 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.
*/
package io.nosqlbench.engine.api.metrics;
@@ -28,220 +27,189 @@ class ReceivedMessageSequenceTrackerTest {
Counter msgErrOutOfSeqCounter = new Counter();
Counter msgErrDuplicateCounter = new Counter();
Counter msgErrLossCounter = new Counter();
ReceivedMessageSequenceTracker messageSequenceTracker = new ReceivedMessageSequenceTracker(msgErrOutOfSeqCounter, msgErrDuplicateCounter, msgErrLossCounter, 20, 20);
ReceivedMessageSequenceTracker messageSequenceTracker = new ReceivedMessageSequenceTracker(this.msgErrOutOfSeqCounter, this.msgErrDuplicateCounter, this.msgErrLossCounter, 20, 20);
@Test
void shouldCountersBeZeroWhenSequenceDoesntContainGaps() {
// when
for (long l = 0; l < 100L; l++) {
messageSequenceTracker.sequenceNumberReceived(l);
}
messageSequenceTracker.close();
for (long l = 0; 100L > l; l++) this.messageSequenceTracker.sequenceNumberReceived(l);
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
@ParameterizedTest
@ValueSource(longs = {10L, 11L, 19L, 20L, 21L, 100L})
void shouldDetectMsgLossWhenEverySecondMessageIsLost(long totalMessages) {
doShouldDetectMsgLoss(totalMessages, 2);
void shouldDetectMsgLossWhenEverySecondMessageIsLost(final long totalMessages) {
this.doShouldDetectMsgLoss(totalMessages, 2);
}
@ParameterizedTest
@ValueSource(longs = {10L, 11L, 19L, 20L, 21L, 100L})
void shouldDetectMsgLossWhenEveryThirdMessageIsLost(long totalMessages) {
doShouldDetectMsgLoss(totalMessages, 3);
void shouldDetectMsgLossWhenEveryThirdMessageIsLost(final long totalMessages) {
this.doShouldDetectMsgLoss(totalMessages, 3);
}
@ParameterizedTest
@ValueSource(longs = {20L, 21L, 40L, 41L, 42L, 43L, 100L})
void shouldDetectMsgLossWhenEvery21stMessageIsLost(long totalMessages) {
doShouldDetectMsgLoss(totalMessages, 21);
void shouldDetectMsgLossWhenEvery21stMessageIsLost(final long totalMessages) {
this.doShouldDetectMsgLoss(totalMessages, 21);
}
private void doShouldDetectMsgLoss(long totalMessages, int looseEveryNthMessage) {
private void doShouldDetectMsgLoss(final long totalMessages, final int looseEveryNthMessage) {
int messagesLost = 0;
// when
boolean lastMessageWasLost = false;
for (long l = 0; l < totalMessages; l++) {
if (l % looseEveryNthMessage == 1) {
if (1 == (l % looseEveryNthMessage)) {
messagesLost++;
lastMessageWasLost = true;
continue;
} else {
lastMessageWasLost = false;
}
messageSequenceTracker.sequenceNumberReceived(l);
lastMessageWasLost = false;
this.messageSequenceTracker.sequenceNumberReceived(l);
}
if (lastMessageWasLost) {
messageSequenceTracker.sequenceNumberReceived(totalMessages);
}
messageSequenceTracker.close();
if (lastMessageWasLost) this.messageSequenceTracker.sequenceNumberReceived(totalMessages);
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(messagesLost, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(messagesLost, this.msgErrLossCounter.getCount());
}
@ParameterizedTest
@ValueSource(longs = {10L, 11L, 19L, 20L, 21L, 100L})
void shouldDetectMsgDuplication(long totalMessages) {
void shouldDetectMsgDuplication(final long totalMessages) {
int messagesDuplicated = 0;
// when
for (long l = 0; l < totalMessages; l++) {
if (l % 2 == 1) {
if (1 == (l % 2)) {
messagesDuplicated++;
messageSequenceTracker.sequenceNumberReceived(l);
this.messageSequenceTracker.sequenceNumberReceived(l);
}
messageSequenceTracker.sequenceNumberReceived(l);
}
if (totalMessages % 2 == 0) {
messageSequenceTracker.sequenceNumberReceived(totalMessages);
}
if (totalMessages < 2 * messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers()) {
messageSequenceTracker.close();
this.messageSequenceTracker.sequenceNumberReceived(l);
}
if (0 == (totalMessages % 2)) this.messageSequenceTracker.sequenceNumberReceived(totalMessages);
if (totalMessages < (2L * this.messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers()))
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(messagesDuplicated, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(messagesDuplicated, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectSingleMessageOutOfSequence() {
// when
for (long l = 0; l < 10L; l++) {
messageSequenceTracker.sequenceNumberReceived(l);
}
messageSequenceTracker.sequenceNumberReceived(10L);
messageSequenceTracker.sequenceNumberReceived(12L);
messageSequenceTracker.sequenceNumberReceived(11L);
for (long l = 13L; l < 100L; l++) {
messageSequenceTracker.sequenceNumberReceived(l);
}
for (long l = 0; 10L > l; l++) this.messageSequenceTracker.sequenceNumberReceived(l);
this.messageSequenceTracker.sequenceNumberReceived(10L);
this.messageSequenceTracker.sequenceNumberReceived(12L);
this.messageSequenceTracker.sequenceNumberReceived(11L);
for (long l = 13L; 100L > l; l++) this.messageSequenceTracker.sequenceNumberReceived(l);
// then
assertEquals(1, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(1, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectMultipleMessagesOutOfSequence() {
// when
for (long l = 0; l < 10L; l++) {
messageSequenceTracker.sequenceNumberReceived(l);
}
messageSequenceTracker.sequenceNumberReceived(10L);
messageSequenceTracker.sequenceNumberReceived(14L);
messageSequenceTracker.sequenceNumberReceived(13L);
messageSequenceTracker.sequenceNumberReceived(11L);
messageSequenceTracker.sequenceNumberReceived(12L);
for (long l = 15L; l < 100L; l++) {
messageSequenceTracker.sequenceNumberReceived(l);
}
for (long l = 0; 10L > l; l++) this.messageSequenceTracker.sequenceNumberReceived(l);
this.messageSequenceTracker.sequenceNumberReceived(10L);
this.messageSequenceTracker.sequenceNumberReceived(14L);
this.messageSequenceTracker.sequenceNumberReceived(13L);
this.messageSequenceTracker.sequenceNumberReceived(11L);
this.messageSequenceTracker.sequenceNumberReceived(12L);
for (long l = 15L; 100L > l; l++) this.messageSequenceTracker.sequenceNumberReceived(l);
// then
assertEquals(2, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(2, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectIndividualMessageLoss() {
// when
for (long l = 0; l < 100L; l++) {
if (l != 11L) {
messageSequenceTracker.sequenceNumberReceived(l);
}
}
messageSequenceTracker.close();
for (long l = 0; 100L > l; l++) if (11L != l) this.messageSequenceTracker.sequenceNumberReceived(l);
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(1, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(1, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectGapAndMessageDuplication() {
// when
for (long l = 0; l < 100L; l++) {
if (l != 11L) {
messageSequenceTracker.sequenceNumberReceived(l);
}
if (l == 12L) {
messageSequenceTracker.sequenceNumberReceived(l);
}
for (long l = 0; 100L > l; l++) {
if (11L != l) this.messageSequenceTracker.sequenceNumberReceived(l);
if (12L == l) this.messageSequenceTracker.sequenceNumberReceived(12L);
}
messageSequenceTracker.close();
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(1, msgErrDuplicateCounter.getCount());
assertEquals(1, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(1, this.msgErrDuplicateCounter.getCount());
assertEquals(1, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectGapAndMessageDuplicationTimes2() {
// when
for (long l = 0; l < 100L; l++) {
if (l != 11L) {
messageSequenceTracker.sequenceNumberReceived(l);
}
if (l == 12L) {
messageSequenceTracker.sequenceNumberReceived(l);
messageSequenceTracker.sequenceNumberReceived(l);
for (long l = 0; 100L > l; l++) {
if (11L != l) this.messageSequenceTracker.sequenceNumberReceived(l);
if (12L == l) {
this.messageSequenceTracker.sequenceNumberReceived(12L);
this.messageSequenceTracker.sequenceNumberReceived(l);
}
}
messageSequenceTracker.close();
this.messageSequenceTracker.close();
// then
assertEquals(0, msgErrOutOfSeqCounter.getCount());
assertEquals(2, msgErrDuplicateCounter.getCount());
assertEquals(1, msgErrLossCounter.getCount());
assertEquals(0, this.msgErrOutOfSeqCounter.getCount());
assertEquals(2, this.msgErrDuplicateCounter.getCount());
assertEquals(1, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectDelayedOutOfOrderDelivery() {
// when
for (long l = 0; l < 5 * messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers(); l++) {
if (l != 10) {
messageSequenceTracker.sequenceNumberReceived(l);
}
if (l == messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers() * 2) {
messageSequenceTracker.sequenceNumberReceived(10);
}
for (long l = 0; l < (5L * this.messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers()); l++) {
if (10 != l) this.messageSequenceTracker.sequenceNumberReceived(l);
if (l == (this.messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers() * 2L))
this.messageSequenceTracker.sequenceNumberReceived(10);
}
messageSequenceTracker.close();
this.messageSequenceTracker.close();
// then
assertEquals(1, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(1, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
@Test
void shouldDetectDelayedOutOfOrderDeliveryOf2ConsecutiveSequenceNumbers() {
// when
for (long l = 0; l < 5 * messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers(); l++) {
if (l != 10 && l != 11) {
messageSequenceTracker.sequenceNumberReceived(l);
}
if (l == messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers() * 2) {
messageSequenceTracker.sequenceNumberReceived(10);
messageSequenceTracker.sequenceNumberReceived(11);
for (long l = 0; l < (5L * this.messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers()); l++) {
if ((10 != l) && (11 != l)) this.messageSequenceTracker.sequenceNumberReceived(l);
if (l == (this.messageSequenceTracker.getMaxTrackOutOfOrderSequenceNumbers() * 2L)) {
this.messageSequenceTracker.sequenceNumberReceived(10);
this.messageSequenceTracker.sequenceNumberReceived(11);
}
}
messageSequenceTracker.close();
this.messageSequenceTracker.close();
// then
assertEquals(2, msgErrOutOfSeqCounter.getCount());
assertEquals(0, msgErrDuplicateCounter.getCount());
assertEquals(0, msgErrLossCounter.getCount());
assertEquals(2, this.msgErrOutOfSeqCounter.getCount());
assertEquals(0, this.msgErrDuplicateCounter.getCount());
assertEquals(0, this.msgErrLossCounter.getCount());
}
}
@@ -16,6 +16,7 @@
package io.nosqlbench.engine.api.templating;
import io.nosqlbench.api.config.NBLabeledElement;
import io.nosqlbench.engine.api.activityconfig.OpsLoader;
import io.nosqlbench.engine.api.activityconfig.yaml.OpData;
import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate;
@@ -51,17 +52,19 @@ public class ParsedOpTest {
ConfigModel.of(ParsedOpTest.class)
.add(Param.defaultTo("testcfg", "testval"))
.asReadOnly()
.apply(Map.of())
.apply(Map.of()),
List.of(),
NBLabeledElement.forMap(Map.of())
);
@Test
public void testFieldDelegationFromDynamicToStaticToConfig() {
NBConfiguration cfg = ConfigModel.of(ParsedOpTest.class)
final NBConfiguration cfg = ConfigModel.of(ParsedOpTest.class)
.add(Param.defaultTo("puppy", "dog"))
.add(Param.required("surname", String.class))
.asReadOnly().apply(Map.of("surname", "yes"));
String opt = """
final String opt = """
ops:
op1:
d1: "{{NumberNameToString()}}"
@@ -69,10 +72,10 @@ public class ParsedOpTest {
params:
ps1: "param-one"
""";
OpsDocList stmtsDocs = OpsLoader.loadString(opt, OpTemplateFormat.yaml, cfg.getMap(), null);
final OpsDocList stmtsDocs = OpsLoader.loadString(opt, OpTemplateFormat.yaml, cfg.getMap(), null);
assertThat(stmtsDocs.getOps().size()).isEqualTo(1);
OpTemplate opTemplate = stmtsDocs.getOps().get(0);
ParsedOp parsedOp = new ParsedOp(opTemplate, cfg);
final OpTemplate opTemplate = stmtsDocs.getOps().get(0);
final ParsedOp parsedOp = new ParsedOp(opTemplate, cfg, List.of(), NBLabeledElement.forMap(Map.of()));
assertThat(parsedOp.getAsFunctionOr("d1","invalid").apply(1L)).isEqualTo("one");
assertThat(parsedOp.getAsFunctionOr("s1","invalid").apply(1L)).isEqualTo("static-one");
@@ -90,7 +93,7 @@ public class ParsedOpTest {
@Test
public void testSubMapTemplates() {
ParsedOp parsedOp = new ParsedOp(
final ParsedOp parsedOp = new ParsedOp(
new OpData().applyFields(Map.of(
"op", Map.of(
"field1-literal", "literalvalue1",
@@ -109,13 +112,15 @@ public class ParsedOpTest {
ConfigModel.of(ParsedOpTest.class)
.add(Param.defaultTo("testcfg", "testval"))
.asReadOnly()
.apply(Map.of())
.apply(Map.of()),
List.of(),
NBLabeledElement.forMap(Map.of())
);
LongFunction<? extends String> f1 = parsedOp.getAsRequiredFunction("field1-literal");
LongFunction<? extends String> f2 = parsedOp.getAsRequiredFunction("field2-object");
LongFunction<? extends String> f3 = parsedOp.getAsRequiredFunction("field3-template");
LongFunction<? extends Map> f4 = parsedOp.getAsRequiredFunction("field4-map-template",Map.class);
LongFunction<? extends Map> f5 = parsedOp.getAsRequiredFunction("field5-map-literal",Map.class);
final LongFunction<? extends String> f1 = parsedOp.getAsRequiredFunction("field1-literal");
final LongFunction<? extends String> f2 = parsedOp.getAsRequiredFunction("field2-object");
final LongFunction<? extends String> f3 = parsedOp.getAsRequiredFunction("field3-template");
final LongFunction<? extends Map> f4 = parsedOp.getAsRequiredFunction("field4-map-template",Map.class);
final LongFunction<? extends Map> f5 = parsedOp.getAsRequiredFunction("field5-map-literal",Map.class);
assertThat(f1.apply(1)).isNotNull();
assertThat(f2.apply(2)).isNotNull();
assertThat(f3.apply(3)).isNotNull();
@@ -126,7 +131,7 @@ public class ParsedOpTest {
@Test
public void testParsedOp() {
Map<String, Object> m1 = pc.apply(0);
final Map<String, Object> m1 = this.pc.apply(0);
assertThat(m1).containsEntry("stmt", "test");
assertThat(m1).containsEntry("dyna1", "zero");
assertThat(m1).containsEntry("dyna2", "zero");
@@ -135,22 +140,22 @@ public class ParsedOpTest {
@Test
public void testNewListBinder() {
LongFunction<List<Object>> lb = pc.newListBinder("dyna1", "identity", "dyna2", "identity");
List<Object> objects = lb.apply(1);
final LongFunction<List<Object>> lb = this.pc.newListBinder("dyna1", "identity", "dyna2", "identity");
final List<Object> objects = lb.apply(1);
assertThat(objects).isEqualTo(List.of("one", 1L, "one", 1L));
}
@Test
public void testNewMapBinder() {
LongFunction<Map<String, Object>> mb = pc.newOrderedMapBinder("dyna1", "identity", "dyna2");
Map<String, Object> objects = mb.apply(2);
final LongFunction<Map<String, Object>> mb = this.pc.newOrderedMapBinder("dyna1", "identity", "dyna2");
final Map<String, Object> objects = mb.apply(2);
assertThat(objects).isEqualTo(Map.<String, Object>of("dyna1", "two", "identity", 2L, "dyna2", "two"));
}
@Test
public void testNewAryBinder() {
LongFunction<Object[]> ab = pc.newArrayBinder("dyna1", "dyna1", "identity", "identity");
Object[] objects = ab.apply(3);
final LongFunction<Object[]> ab = this.pc.newArrayBinder("dyna1", "dyna1", "identity", "identity");
final Object[] objects = ab.apply(3);
assertThat(objects).isEqualTo(new Object[]{"three", "three", 3L, 3L});
}