mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2026-07-30 08:07:55 -05:00
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:
@@ -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>
|
||||
|
||||
+39
-45
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -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>
|
||||
*/
|
||||
|
||||
+14
-15
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
-42
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
+53
-69
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-41
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-30
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+94
-126
@@ -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});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user