mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
*
|
||||
* 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.activitytype.diag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.LongToIntFunction;
|
||||
|
||||
public class DiagOpData {
|
||||
|
||||
private final String description;
|
||||
private final List<String> diaglog = new ArrayList<>();
|
||||
|
||||
private LongToIntFunction resultFunc;
|
||||
private long simulatedDelayNanos;
|
||||
|
||||
public DiagOpData(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this function is provided, the result will be set to the value of the
|
||||
* evaluated function with the op cycle.
|
||||
*
|
||||
* This is known as "resultfunc" in parameter space.
|
||||
*
|
||||
* The function must be thread-safe.
|
||||
*
|
||||
* @param resultFunc A function to map the cycle to the result value
|
||||
* @return this, for method chaining
|
||||
*/
|
||||
public DiagOpData withResultFunction(LongToIntFunction resultFunc) {
|
||||
this.resultFunc = resultFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this function is provided, the completion of the operation will be
|
||||
* delayed until the system nanotime is at least the op start time in
|
||||
* addition to the provided delay.
|
||||
*
|
||||
* This is controlled as "delayfunc" in parameter space.
|
||||
*
|
||||
* @param simulatedDelayNanos The amount of nanos ensure as a minimum
|
||||
* of processing time for this op
|
||||
*/
|
||||
public DiagOpData setSimulatedDelayNanos(long simulatedDelayNanos) {
|
||||
this.simulatedDelayNanos = simulatedDelayNanos;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getSimulatedDelayNanos() {
|
||||
return simulatedDelayNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ", description:'" + description;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public void log(String logline) {
|
||||
this.diaglog.add(logline);
|
||||
}
|
||||
public List<String> getDiagLog() {
|
||||
return diaglog;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
*
|
||||
* 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.activitytype.diag;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class SequenceBlocker {
|
||||
private final static Logger logger = LogManager.getLogger(SequenceBlocker.class);
|
||||
private final AtomicLong sequence;
|
||||
private final AtomicLong waiting=new AtomicLong(0L);
|
||||
private final boolean errorsAreFatal;
|
||||
// private PriorityBlockingQueue<TakeANumber> queue = new PriorityBlockingQueue<>();
|
||||
private Exception fatalError;
|
||||
|
||||
public SequenceBlocker(long start, boolean errorsAreFatal) {
|
||||
this.sequence = new AtomicLong(start);
|
||||
this.errorsAreFatal = errorsAreFatal;
|
||||
}
|
||||
|
||||
public synchronized void awaitAndRun(long startAt, long endPlus, Runnable task) {
|
||||
waiting.incrementAndGet();
|
||||
|
||||
if (fatalError != null) {
|
||||
throw new RuntimeException("There was previously a fatal error, not allowing new tasks. Error=" + fatalError.getMessage());
|
||||
}
|
||||
|
||||
// queue.add(new TakeANumber(startAt, sequencePlusCount, task));
|
||||
while (sequence.get() != startAt) {
|
||||
try {
|
||||
wait(1_000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception e) {
|
||||
logger.error(() -> "Runnable errored in SequenceBlocker: " + e.getMessage());
|
||||
if (errorsAreFatal) {
|
||||
this.fatalError = e;
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
waiting.decrementAndGet();
|
||||
if (!sequence.compareAndSet(startAt,endPlus)) {
|
||||
throw new InvalidParameterException("Serious logic error in synchronizer. This should never fail.");
|
||||
}
|
||||
}
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public synchronized void awaitCompletion() {
|
||||
while (waiting.get()>0)
|
||||
try {
|
||||
wait(60_000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private final static class TakeANumber implements Comparable<TakeANumber> {
|
||||
private final long start;
|
||||
private final long endPlus;
|
||||
private final Runnable task;
|
||||
|
||||
public TakeANumber(long start, long endPlus, Runnable task) {
|
||||
this.start = start;
|
||||
this.endPlus = endPlus;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TakeANumber o) {
|
||||
return Long.compare(start, o.start);
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public long getEndPlus() {
|
||||
return endPlus;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[" + getStart() + "-" + getEndPlus() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -62,10 +62,13 @@ public class DiagOpDispenser extends BaseOpDispenser<DiagOp,DiagSpace> implement
|
||||
taskcfg.computeIfAbsent("name",l -> taskname);
|
||||
taskcfg.computeIfAbsent("type",l -> taskname);
|
||||
String optype = taskcfg.remove("type").toString();
|
||||
String opname = taskcfg.get("name").toString();
|
||||
|
||||
// Dynamically load the named task instance, based on the op field key AKA the taskname
|
||||
// and ensure that exactly one is found or throw an error
|
||||
DiagTask task = ServiceSelector.of(optype, ServiceLoader.load(DiagTask.class)).getOne();
|
||||
task.setLabelsFrom(op);
|
||||
task.setName(opname);
|
||||
|
||||
// Load the configuration model of the dynamically loaded task for type-safe configuration
|
||||
NBConfigModel cfgmodel = task.getConfigModel();
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.adapter.diag.optasks;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class BaseDiagTask implements DiagTask {
|
||||
private NBLabeledElement parentLabels;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public abstract Map<String, Object> apply(Long cycle, Map<String, Object> opstate);
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return parentLabels.getLabels();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabelsFrom(NBLabeledElement labeledElement) {
|
||||
this.parentLabels = labeledElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabeledElement getParentLabels() {
|
||||
return parentLabels;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,9 +16,9 @@
|
||||
|
||||
package io.nosqlbench.adapter.diag.optasks;
|
||||
|
||||
import io.nosqlbench.api.config.NBNamedElement;
|
||||
import io.nosqlbench.api.config.standard.NBReconfigurable;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.standard.NBConfigurable;
|
||||
import io.nosqlbench.api.config.standard.NBReconfigurable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -44,7 +44,13 @@ import java.util.function.BiFunction;
|
||||
public interface DiagTask extends
|
||||
BiFunction<Long,Map<String,Object>, Map<String,Object>>,
|
||||
NBConfigurable,
|
||||
NBNamedElement
|
||||
NBLabeledElement
|
||||
{
|
||||
Map<String, Object> apply(Long cycle, Map<String, Object> opstate);
|
||||
|
||||
void setName(String opname);
|
||||
|
||||
void setLabelsFrom(NBLabeledElement labeledElement);
|
||||
|
||||
NBLabeledElement getParentLabels();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package io.nosqlbench.adapter.diag.optasks;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.config.standard.*;
|
||||
import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter;
|
||||
import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiters;
|
||||
@@ -27,7 +25,7 @@ import io.nosqlbench.nb.annotations.Service;
|
||||
import java.util.Map;
|
||||
|
||||
@Service(value = DiagTask.class, selector = "diagrate")
|
||||
public class DiagTask_diagrate implements DiagTask, NBReconfigurable, NBLabeledElement {
|
||||
public class DiagTask_diagrate extends BaseDiagTask implements NBReconfigurable {
|
||||
private String name;
|
||||
private RateLimiter rateLimiter;
|
||||
private RateSpec rateSpec;
|
||||
@@ -73,15 +71,4 @@ public class DiagTask_diagrate implements DiagTask, NBReconfigurable, NBLabeledE
|
||||
rateLimiter.maybeWaitForOp();
|
||||
return stringObjectMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return NBLabels.forKV("diagop", name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -29,14 +29,12 @@ import java.util.Map;
|
||||
* of this owning operation for a number of milliseconds.
|
||||
*/
|
||||
@Service(value = DiagTask.class, selector = "erroroncycle")
|
||||
public class DiagTask_erroroncycle implements DiagTask {
|
||||
public class DiagTask_erroroncycle extends BaseDiagTask {
|
||||
|
||||
private String name;
|
||||
private long error_on_cycle;
|
||||
|
||||
@Override
|
||||
public void applyConfig(NBConfiguration cfg) {
|
||||
this.name = cfg.get("name", String.class);
|
||||
error_on_cycle = cfg.get("erroroncycle", long.class);
|
||||
}
|
||||
|
||||
@@ -55,9 +53,4 @@ public class DiagTask_erroroncycle implements DiagTask {
|
||||
}
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.adapter.diag.optasks;
|
||||
|
||||
import com.codahale.metrics.Gauge;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.config.standard.*;
|
||||
import io.nosqlbench.api.engine.metrics.ActivityMetrics;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
|
||||
import io.nosqlbench.virtdata.core.bindings.DataMapper;
|
||||
import io.nosqlbench.virtdata.core.bindings.VirtData;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.LongToDoubleFunction;
|
||||
|
||||
/**
|
||||
* <p>A diag gauge task allows you to create a source of metrics data for testing or demonstration.
|
||||
* You can customize the function used to produce the raw values, the number of buckets to use for
|
||||
* leavening the values over time, and the basic stat function used to summarize the buckets into
|
||||
* an aggregate double value.</p>
|
||||
*
|
||||
* <H2>Usage Notes</H2>
|
||||
* The data image for the gauge summary is updated consistently with respect to monotonic (whole step) cycle values.
|
||||
* There are a few parameters which can be adjusted in order to make the gauge data appear more realistic.
|
||||
* <UL>
|
||||
* <LI>label - This determines the metric label, normally used as the metric family name. Default is the task name.</LI>
|
||||
* <LI>buckets - The number of values to seed incrementally to produce a data image</LI>
|
||||
* <LI>binding - The binding recipe to use to create the value stored in a bin for a given cycle</LI>
|
||||
* <LI>modulo - The interval of cycle values at which a new bin value is computed and stored in a bin</LI>
|
||||
* <LI>stat - The aggregate statistic to use when computing the gauge value: min, avg, or max</LI>
|
||||
* </UL>
|
||||
*
|
||||
* <p>The buckets are updated incrementally and consistently based on the cycle value, modulated by the modulo value.
|
||||
* When the gauge is observed, the present value of the buckets is converted to a values image and the result is
|
||||
* summarized according to the selected stat.</p>
|
||||
*
|
||||
* <p>Practical values should be selected with awareness of the op rate and the rate of change desired in
|
||||
* the metrics over time. The buckets allow for the effective rate of change over cycles to be slowed, but it
|
||||
* is recommended to keep bin counts relative low by increasing modulo instead.</p>
|
||||
*
|
||||
* <H2>Examples</H2>
|
||||
* <p>Suppose you wanted to see a moving average, where a new value is presented every second.
|
||||
* A new value every second is obviously not needed in practical scenarios, but it makes a useful basis
|
||||
* for thinking about relative rates, since the rate limiters are specified in ops/s.
|
||||
* <UL>
|
||||
* <LI>activity rate=10 modulo=10 - a new update will be visible every second.</LI>
|
||||
* <LI>activity rate=1000 modulo=1000 - a new gauge value will be visible every second.</LI>
|
||||
* <LI>activity rate=1000 modulo=60000 - a new gauge value will be visible every minute.</LI>
|
||||
* <LI>activity rate=100 modulo=100 buckets=50 stat=avg - a new value will be visible every second,
|
||||
* however the rate of change will be reduced due to the large sample size.</LI>
|
||||
* </UL>
|
||||
*
|
||||
* <H2>Usage Notes</H2>
|
||||
* Changing the number of buckets has a different effect based on the stat. For avg, the higher the number of buckets,
|
||||
* the smaller the standard deviation of the results. For min and max, the higher the number of buckets, the more
|
||||
* extreme the value will become. This is true for uniform bindings and non-uniform binding functions as well,
|
||||
* although you can tailor the shape of the sample data as you like.
|
||||
*
|
||||
*/
|
||||
@Service(value= DiagTask.class,selector="gauge")
|
||||
public class DiagTask_gauge extends BaseDiagTask implements Gauge<Double> {
|
||||
private final static Logger logger = LogManager.getLogger("DIAG");
|
||||
|
||||
// TODO: allow for temporal filtering
|
||||
// TODO: allow for temporal cycles
|
||||
private String name;
|
||||
|
||||
private Gauge<Double> gauge;
|
||||
private LongToDoubleFunction function;
|
||||
private Double sampleValue;
|
||||
private long[] cycleMixer;
|
||||
private double[] valueMixer;
|
||||
private long modulo;
|
||||
private int buckets;
|
||||
private String label;
|
||||
|
||||
private enum Stats {
|
||||
min,
|
||||
avg,
|
||||
max
|
||||
}
|
||||
|
||||
private Stats stat;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> apply(Long cycleValue, Map<String, Object> stringObjectMap) {
|
||||
long cycle = cycleValue.longValue();
|
||||
if ((cycle%modulo)==0) {
|
||||
int bin=(int)(cycle/modulo)%cycleMixer.length;
|
||||
cycleMixer[bin]=cycleValue;
|
||||
logger.debug(() -> "updating bin " + bin + " with value " + cycle + ", now:" + Arrays.toString(cycleMixer));
|
||||
}
|
||||
return stringObjectMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyConfig(NBConfiguration cfg) {
|
||||
String binding = cfg.get("binding",String.class);
|
||||
this.buckets = cfg.get("buckets",Integer.class);
|
||||
this.modulo = cfg.get("modulo",Long.class);
|
||||
this.label = cfg.getOptional("label").orElse(super.getName());
|
||||
String stat = cfg.get("stat");
|
||||
|
||||
this.cycleMixer=new long[buckets];
|
||||
this.valueMixer=new double[buckets];
|
||||
|
||||
this.stat=Stats.valueOf(stat);
|
||||
|
||||
DataMapper<Object> mapper = VirtData.getMapper(binding, Map.of());
|
||||
Object example = mapper.get(0L);
|
||||
if (example instanceof Double) {
|
||||
this.function=l -> (double) mapper.get(l);
|
||||
} else {
|
||||
this.function= VirtDataConversions.adaptFunction(mapper,LongToDoubleFunction.class);
|
||||
}
|
||||
|
||||
logger.info("Registering gauge for diag task with labels:" + getParentLabels().getLabels() + " label:" + label);
|
||||
this.gauge=ActivityMetrics.gauge(this, label, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBConfigModel getConfigModel() {
|
||||
return ConfigModel.of(DiagTask_gauge.class)
|
||||
.add(Param.required("name",String.class))
|
||||
.add(Param.optional("label",String.class)
|
||||
.setDescription("A metric family name override. Defaults to the op name."))
|
||||
.add(Param.defaultTo("binding","HashRange(0L,1000000L)")
|
||||
.setDescription("A binding function to derive values from"))
|
||||
.add(Param.defaultTo("buckets", "3")
|
||||
.setDescription("how many slots to maintain in the mixer to aggregate over"))
|
||||
.add(Param.defaultTo("stat","avg")
|
||||
.setRegex("min|avg|max")
|
||||
.setDescription("min, avg, or max"))
|
||||
.add(Param.defaultTo("modulo",1L)
|
||||
.setDescription("A value used to divide down the relative rate of bin updates. 100 means 100x fewer updates"))
|
||||
.asReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getValue() {
|
||||
for (int idx = 0; idx < valueMixer.length; idx++) {
|
||||
valueMixer[idx]=function.applyAsDouble(this.cycleMixer[idx]);
|
||||
}
|
||||
|
||||
double sample= switch (this.stat) {
|
||||
case min -> Arrays.stream(this.valueMixer).reduce(Math::min).getAsDouble();
|
||||
case avg -> Arrays.stream(this.valueMixer).sum()/(double)this.valueMixer.length;
|
||||
case max -> Arrays.stream(this.valueMixer).reduce(Math::max).getAsDouble();
|
||||
};
|
||||
logger.debug(() -> "sample value for " + getParentLabels().getLabels() + ": " + sample);
|
||||
return sample;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return super.getLabels().and("stat",this.stat.toString());
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -29,13 +29,10 @@ import java.util.Map;
|
||||
* of this owning operation for a number of milliseconds.
|
||||
*/
|
||||
@Service(value= DiagTask.class,selector = "initdelay")
|
||||
public class DiagTask_initdelay implements DiagTask {
|
||||
|
||||
private String name;
|
||||
public class DiagTask_initdelay extends BaseDiagTask {
|
||||
|
||||
@Override
|
||||
public void applyConfig(NBConfiguration cfg) {
|
||||
this.name = cfg.get("name",String.class);
|
||||
long initdelay = cfg.get("initdelay",long.class);
|
||||
try {
|
||||
Thread.sleep(initdelay);
|
||||
@@ -54,12 +51,6 @@ public class DiagTask_initdelay implements DiagTask {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> apply(Long aLong, Map<String, Object> stringObjectMap) {
|
||||
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -25,12 +25,11 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.util.Map;
|
||||
|
||||
@Service(value= DiagTask.class,selector="log")
|
||||
public class DiagTask_log implements DiagTask, NBConfigurable {
|
||||
public class DiagTask_log extends BaseDiagTask {
|
||||
private final static Logger logger = LogManager.getLogger("DIAG");
|
||||
private Level level;
|
||||
private long modulo;
|
||||
private long interval;
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> apply(Long aLong, Map<String, Object> stringObjectMap) {
|
||||
@@ -43,7 +42,6 @@ public class DiagTask_log implements DiagTask, NBConfigurable {
|
||||
@Override
|
||||
public void applyConfig(NBConfiguration cfg) {
|
||||
String level = cfg.getOptional("level").orElse("INFO");
|
||||
this.name = cfg.get("name");
|
||||
this.level = Level.valueOf(level);
|
||||
this.modulo = cfg.get("modulo",long.class);
|
||||
this.interval = cfg.get("interval",long.class);
|
||||
@@ -58,9 +56,4 @@ public class DiagTask_log implements DiagTask, NBConfigurable {
|
||||
.add(Param.defaultTo("interval",1000))
|
||||
.asReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,22 +16,21 @@
|
||||
|
||||
package io.nosqlbench.adapter.diag.optasks;
|
||||
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import io.nosqlbench.api.config.standard.ConfigModel;
|
||||
import io.nosqlbench.api.config.standard.NBConfigModel;
|
||||
import io.nosqlbench.api.config.standard.NBConfiguration;
|
||||
import io.nosqlbench.api.config.standard.Param;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Service(value= DiagTask.class,selector = "noop")
|
||||
public class DiagTask_noop implements DiagTask {
|
||||
public class DiagTask_noop extends BaseDiagTask {
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public void applyConfig(NBConfiguration cfg) {
|
||||
this.name = cfg.get("name",String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,8 +45,4 @@ public class DiagTask_noop implements DiagTask {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
description: |
|
||||
A diag gauge task allows you to create a source of metrics data
|
||||
for testing or demonstration. You can customize the binding used
|
||||
to produce the raw values, the number of buckets to use for leavening
|
||||
the values over time, and the basic stat function used to summarize
|
||||
the buckets into an aggregate double value.
|
||||
Usage Notes
|
||||
The data image for the gauge summary is updated consistently with
|
||||
respect to monotonic (whole step) cycle values. There are a few parameters
|
||||
which can be adjusted in order to make the gauge data appear more realistic.
|
||||
label - A standard parameter for diag tasks. This determines the metric name as well.
|
||||
buckets - The number of values to seed incrementally to produce a data image
|
||||
binding - The binding used to create the value stored in a bin for a given cycle
|
||||
modulo - The interval of cycle values at which a new bin value is computed and stored in a bin
|
||||
stat - The aggregate statistic to use when computing the gauge value: min, avg, or max
|
||||
The buckets are updated incrementally and consistently based on the cycle value,
|
||||
modulated by the modulo value. When the gauge value is observed, the present
|
||||
value of the buckets is converted to a values image and the result is summarized
|
||||
according to the selected stat.
|
||||
Practical values should be selected with awareness of the op rate and the rate
|
||||
of change desired in the metrics over time. The buckets allow for the effective
|
||||
rate of change over cycles to be slowed, but it is recommended to keep bin counts
|
||||
relative low by increasing modulo instead.
|
||||
|
||||
scenarios:
|
||||
default:
|
||||
bysecond: start driver=diag tags=block:bysecond rate=10 cycles=6000
|
||||
byminute: start driver=diag tags=block:stable rate=10 cycles=6000
|
||||
for100bins: start driver=diag tags=block:byminute rate=10 cycles=6000
|
||||
for2bins: start driver=diag tags=block:randomish rate=10 cycles=6000
|
||||
bysecond:
|
||||
bysecond: start driver=diag tags=block:bysecond rate=10 cycles=6000
|
||||
byminute:
|
||||
byminute: start driver=diag tags=block:stable rate=10 cycles=6000
|
||||
stable:
|
||||
for100bins: start driver=diag tags=block:byminute rate=10 cycles=6000
|
||||
randomish:
|
||||
for2bins: start driver=diag tags=block:randomish rate=10 cycles=6000
|
||||
|
||||
|
||||
blocks:
|
||||
bysecond: # This assumes you are using a matching rate=10, for one update per second
|
||||
ops:
|
||||
tenbins:
|
||||
lower: type=gauge modulo=10 buckets=10 binding='HashRange(0L,100L)' stat=min
|
||||
middle: type=gauge modulo=10 buckets=10 binding='HashRange(0L,100L)' stat=avg
|
||||
higher: type=gauge modulo=10 buckets=10 binding='HashRange(0L,100L)' stat=max
|
||||
byminute: # This assumes you are using a matching rate=10, for one update per minute
|
||||
ops:
|
||||
tenbins:
|
||||
lower: type=gauge modulo=600 buckets=10 binding='HashRange(0L,100L)' stat=min
|
||||
middle: type=gauge modulo=600 buckets=10 binding='HashRange(0L,100L)' stat=avg
|
||||
higher: type=gauge modulo=600 buckets=10 binding='HashRange(0L,100L)' stat=max
|
||||
stable: # This assumes you are using a matching rate=10, for one update per minute
|
||||
ops:
|
||||
hundobins:
|
||||
lower: type=gauge modulo=600 buckets=100 binding='HashRange(0L,100L)' stat=min
|
||||
middle: type=gauge modulo=600 buckets=100 binding='HashRange(0L,100L)' stat=avg
|
||||
higher: type=gauge modulo=600 buckets=100 binding='HashRange(0L,100L)' stat=max
|
||||
randomish: # This assumes you are using a matching rate=10, for 1 update per second
|
||||
ops:
|
||||
threebins:
|
||||
lower: type=gauge modulo=60 buckets=3 binding='HashRange(0L,100L)' stat=min
|
||||
middle: type=gauge modulo=60 buckets=3 binding='HashRange(0L,100L)' stat=avg
|
||||
higher: type=gauge modulo=60 buckets=3 binding='HashRange(0L,100L)' stat=max
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
*
|
||||
* 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.activitytype.diag;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SequenceBlockerTest {
|
||||
private final static Logger logger = LogManager.getLogger(SequenceBlockerTest.class);
|
||||
|
||||
@Test
|
||||
public void await() throws Exception {
|
||||
|
||||
SequenceBlocker sb = new SequenceBlocker(234L, true);
|
||||
new Thread(() -> sb.awaitAndRun(249L,253L, new Printer(logger, "249-253"))).start();
|
||||
Thread.sleep(100);
|
||||
new Thread(() -> sb.awaitAndRun(247L,249L, new Printer(logger, "247-249"))).start();
|
||||
Thread.sleep(100);
|
||||
new Thread(() -> sb.awaitAndRun(234L,247L, new Printer(logger, "234-247"))).start();
|
||||
|
||||
sb.awaitCompletion();
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
private final static class Printer implements Runnable {
|
||||
|
||||
private final Logger logger;
|
||||
private final String out;
|
||||
|
||||
public Printer(Logger logger, String out) {
|
||||
this.logger = logger;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug(out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.adapter.diag.optasks;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.standard.NBConfiguration;
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DiagTask_gaugeTest {
|
||||
|
||||
@Test
|
||||
public void testAvg() {
|
||||
DiagTask_gauge gaugeTask = new DiagTask_gauge();
|
||||
gaugeTask.setName("test");
|
||||
gaugeTask.setLabelsFrom(NBLabeledElement.EMPTY);
|
||||
NBConfiguration taskConfig = gaugeTask.getConfigModel().apply(Map.of(
|
||||
"name","test",
|
||||
"buckets","5",
|
||||
"binding", "Identity()"
|
||||
));
|
||||
gaugeTask.applyConfig(taskConfig);
|
||||
for (long i = 0; i < 10; i++) {
|
||||
gaugeTask.apply(i,Map.of());
|
||||
}
|
||||
|
||||
assertThat(gaugeTask.getValue()).isCloseTo(7.0d, Offset.offset(0.0001d));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMin() {
|
||||
DiagTask_gauge gaugeTask = new DiagTask_gauge();
|
||||
gaugeTask.setName("test");
|
||||
gaugeTask.setLabelsFrom(NBLabeledElement.EMPTY);
|
||||
NBConfiguration taskConfig = gaugeTask.getConfigModel().apply(Map.of(
|
||||
"name","test",
|
||||
"buckets","5",
|
||||
"stat", "min",
|
||||
"binding", "Identity()"
|
||||
));
|
||||
gaugeTask.applyConfig(taskConfig);
|
||||
for (long i = 0; i < 10; i++) {
|
||||
gaugeTask.apply(i,Map.of());
|
||||
}
|
||||
|
||||
assertThat(gaugeTask.getValue()).isCloseTo(5.0d, Offset.offset(0.0001d));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMax() {
|
||||
DiagTask_gauge gaugeTask = new DiagTask_gauge();
|
||||
gaugeTask.setName("test");
|
||||
gaugeTask.setLabelsFrom(NBLabeledElement.EMPTY);
|
||||
NBConfiguration taskConfig = gaugeTask.getConfigModel().apply(Map.of(
|
||||
"name","test",
|
||||
"buckets","5",
|
||||
"stat", "max",
|
||||
"binding", "Identity()"
|
||||
));
|
||||
gaugeTask.applyConfig(taskConfig);
|
||||
for (long i = 0; i < 10; i++) {
|
||||
gaugeTask.apply(i,Map.of());
|
||||
}
|
||||
|
||||
assertThat(gaugeTask.getValue()).isCloseTo(9.0d, Offset.offset(0.0001d));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -164,7 +164,6 @@ public abstract class BaseDriverAdapter<R extends Op, S> implements DriverAdapte
|
||||
.add(Param.optional("cycles").setRegex("\\d+[KMBGTPE]?|\\d+[KMBGTPE]?\\.\\.\\d+[KMBGTPE]?").setDescription("cycle interval to use"))
|
||||
.add(Param.optional("recycles").setDescription("allow cycles to be re-used this many times"))
|
||||
.add(Param.optional(List.of("cyclerate", "targetrate", "rate"), String.class, "rate limit for cycles per second"))
|
||||
.add(Param.optional("phaserate", String.class, "rate limit for phases per second"))
|
||||
.add(Param.optional("seq", String.class, "sequencing algorithm"))
|
||||
.add(Param.optional("instrument", Boolean.class))
|
||||
.add(Param.optional(List.of("workload", "yaml"), String.class, "location of workload yaml file"))
|
||||
|
||||
@@ -911,6 +911,10 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, NBLabeledElement,
|
||||
return tmap.parseStaticCmdMap(key, mainField);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> parseStaticCmdMaps(String key, String mainField) {
|
||||
return tmap.parseStaticCmdMaps(key, mainField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.tmap.toString();
|
||||
|
||||
@@ -158,33 +158,8 @@ public interface Activity extends Comparable<Activity>, ActivityDefObserver, Pro
|
||||
*/
|
||||
RateLimiter getStrideRateLimiter(Supplier<? extends RateLimiter> supplier);
|
||||
|
||||
/**
|
||||
* Get the current phase rate limiter for this activity.
|
||||
* The phase rate limiter is used to throttle the rate at which
|
||||
* new phases are dispatched across all threads in an activity.
|
||||
* @return The stride {@link RateLimiter}
|
||||
*/
|
||||
RateLimiter getPhaseLimiter();
|
||||
|
||||
Timer getResultTimer();
|
||||
|
||||
/**
|
||||
* Set the phase rate limiter for this activity. This method should only
|
||||
* be used in a non-concurrent context. Otherwise, the supplier version
|
||||
* {@link #getPhaseRateLimiter(Supplier)}} should be used.
|
||||
* @param rateLimiter The phase {@link RateLimiter} for this activity.
|
||||
*/
|
||||
void setPhaseLimiter(RateLimiter rateLimiter);
|
||||
|
||||
/**
|
||||
* Get or create the phase {@link RateLimiter} in a concurrent-safe
|
||||
* way. Implementations should ensure that this method is synchronized or
|
||||
* that each requester gets the same phase rate limiter for the activity.
|
||||
* @param supplier A {@link RateLimiter} {@link Supplier}
|
||||
* @return An extant or newly created phase {@link RateLimiter}
|
||||
*/
|
||||
RateLimiter getPhaseRateLimiter(Supplier<? extends RateLimiter> supplier);
|
||||
|
||||
/**
|
||||
* Get or create the instrumentation needed for this activity. This provides
|
||||
* a single place to find and manage, and document instrumentation that is
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.nosqlbench.engine.api.activityimpl;
|
||||
import com.codahale.metrics.Timer;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.config.params.ParamsParser;
|
||||
import io.nosqlbench.api.config.standard.NBConfiguration;
|
||||
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
|
||||
import io.nosqlbench.api.engine.metrics.ActivityMetrics;
|
||||
@@ -79,7 +80,6 @@ public class SimpleActivity implements Activity {
|
||||
private RunState runState = RunState.Uninitialized;
|
||||
private RateLimiter strideLimiter;
|
||||
private RateLimiter cycleLimiter;
|
||||
private RateLimiter phaseLimiter;
|
||||
private ActivityController activityController;
|
||||
private ActivityInstrumentation activityInstrumentation;
|
||||
private PrintWriter console;
|
||||
@@ -277,30 +277,11 @@ public class SimpleActivity implements Activity {
|
||||
return strideLimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RateLimiter getPhaseLimiter() {
|
||||
return phaseLimiter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Timer getResultTimer() {
|
||||
return ActivityMetrics.timer(this, "result", getParams().getOptionalInteger("hdr_digits").orElse(4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPhaseLimiter(RateLimiter rateLimiter) {
|
||||
this.phaseLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized RateLimiter getPhaseRateLimiter(Supplier<? extends RateLimiter> supplier) {
|
||||
if (null == this.phaseLimiter) {
|
||||
phaseLimiter = supplier.get();
|
||||
}
|
||||
return phaseLimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ActivityInstrumentation getInstrumentation() {
|
||||
if (null == this.activityInstrumentation) {
|
||||
@@ -350,10 +331,6 @@ public class SimpleActivity implements Activity {
|
||||
.map(RateSpec::new).ifPresent(
|
||||
spec -> cycleLimiter = RateLimiters.createOrUpdate(this, "cycles", cycleLimiter, spec));
|
||||
|
||||
activityDef.getParams().getOptionalNamedParameter("phaserate")
|
||||
.map(RateSpec::new)
|
||||
.ifPresent(spec -> phaseLimiter = RateLimiters.createOrUpdate(this, "phases", phaseLimiter, spec));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -675,8 +652,13 @@ public class SimpleActivity implements Activity {
|
||||
Optional<String> stmt = activityDef.getParams().getOptionalString("op", "stmt", "statement");
|
||||
Optional<String> op_yaml_loc = activityDef.getParams().getOptionalString("yaml", "workload");
|
||||
if (stmt.isPresent()) {
|
||||
String op = stmt.get();
|
||||
workloadSource = "commandline:" + stmt.get();
|
||||
return OpsLoader.loadString(stmt.get(), OpTemplateFormat.inline, activityDef.getParams(), null);
|
||||
if (op.startsWith("{")||op.startsWith("[")) {
|
||||
return OpsLoader.loadString(stmt.get(), OpTemplateFormat.json, activityDef.getParams(), null);
|
||||
} else {
|
||||
return OpsLoader.loadString(stmt.get(), OpTemplateFormat.inline, activityDef.getParams(), null);
|
||||
}
|
||||
}
|
||||
if (op_yaml_loc.isPresent()) {
|
||||
workloadSource = "yaml:" + op_yaml_loc.get();
|
||||
|
||||
@@ -215,17 +215,14 @@ public class CoreMotor<D> implements ActivityDefObserver, Motor<D>, Stoppable {
|
||||
strideRateLimiter.start();
|
||||
}
|
||||
|
||||
|
||||
long strideDelay = 0L;
|
||||
long cycleDelay = 0L;
|
||||
long phaseDelay = 0L;
|
||||
|
||||
// Reviewer Note: This separate of code paths was used to avoid impacting the
|
||||
// previously logic for the SyncAction type. It may be consolidated later once
|
||||
// the async action is proven durable
|
||||
if (action instanceof AsyncAction) {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
AsyncAction<D> async = (AsyncAction) action;
|
||||
|
||||
@@ -387,12 +384,7 @@ public class CoreMotor<D> implements ActivityDefObserver, Motor<D>, Stoppable {
|
||||
long cycleStart = System.nanoTime();
|
||||
try {
|
||||
logger.trace(()->"cycle " + cyclenum);
|
||||
|
||||
// runCycle
|
||||
long phaseStart = System.nanoTime();
|
||||
result = sync.runCycle(cyclenum);
|
||||
long phaseEnd = System.nanoTime();
|
||||
|
||||
} catch (Exception e) {
|
||||
motorState.enterState(Errored);
|
||||
throw e;
|
||||
|
||||
@@ -18,10 +18,11 @@ package io.nosqlbench.engine.cli;
|
||||
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
import io.nosqlbench.api.annotations.Layer;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.content.Content;
|
||||
import io.nosqlbench.api.content.NBIO;
|
||||
import io.nosqlbench.api.engine.metrics.ActivityMetrics;
|
||||
import io.nosqlbench.api.engine.metrics.reporters.PromPushReporter;
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
import io.nosqlbench.api.logging.NBLogLevel;
|
||||
import io.nosqlbench.api.metadata.SessionNamer;
|
||||
@@ -66,7 +67,7 @@ import java.util.ServiceLoader.Provider;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NBCLI implements Function<String[], Integer> {
|
||||
public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
|
||||
private static Logger logger;
|
||||
private static final LoggerConfig loggerConfig;
|
||||
@@ -81,6 +82,9 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
|
||||
private final String commandName;
|
||||
|
||||
private NBLabels labels;
|
||||
private String sessionName;
|
||||
|
||||
public NBCLI(final String commandName) {
|
||||
this.commandName = commandName;
|
||||
}
|
||||
@@ -95,7 +99,7 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
try {
|
||||
final NBCLI cli = new NBCLI("nb");
|
||||
final NBCLI cli = new NBCLI("nb5");
|
||||
final int statusCode = cli.apply(args);
|
||||
System.exit(statusCode);
|
||||
} catch (final Exception e) {
|
||||
@@ -115,7 +119,7 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
@Override
|
||||
public Integer apply(final String[] args) {
|
||||
try {
|
||||
final NBCLI cli = new NBCLI("nb");
|
||||
final NBCLI cli = new NBCLI("nb5");
|
||||
final int result = cli.applyDirect(args);
|
||||
return result;
|
||||
} catch (final Exception e) {
|
||||
@@ -149,7 +153,8 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
NBCLI.loggerConfig.setConsoleLevel(NBLogLevel.ERROR);
|
||||
|
||||
final NBCLIOptions globalOptions = new NBCLIOptions(args, Mode.ParseGlobalsOnly);
|
||||
final String sessionName = SessionNamer.format(globalOptions.getSessionName());
|
||||
this.labels=NBLabels.forKV("command",commandName).and(globalOptions.getLabelMap());
|
||||
this.sessionName = SessionNamer.format(globalOptions.getSessionName());
|
||||
|
||||
NBCLI.loggerConfig
|
||||
.setSessionName(sessionName)
|
||||
@@ -432,7 +437,8 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
options.getReportSummaryTo(),
|
||||
String.join("\n", args),
|
||||
options.getLogsDirectory(),
|
||||
Maturity.Unspecified);
|
||||
Maturity.Unspecified,
|
||||
this);
|
||||
|
||||
final ScriptBuffer buffer = new BasicScriptBuffer()
|
||||
.add(options.getCommands()
|
||||
@@ -504,4 +510,8 @@ public class NBCLI implements Function<String[], Integer> {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return labels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class NBCLIOptions {
|
||||
private static final String userHome = System.getProperty("user.home");
|
||||
|
||||
|
||||
private static final Map<String,String> DEFAULT_LABELS=Map.of("appname","nosqlbench");
|
||||
private static final String METRICS_PREFIX = "--metrics-prefix";
|
||||
private static final String ANNOTATE_EVENTS = "--annotate";
|
||||
private static final String ANNOTATORS_CONFIG = "--annotators";
|
||||
@@ -82,6 +83,9 @@ public class NBCLIOptions {
|
||||
private static final String EXPERIMENTAL = "--experimental";
|
||||
private static final String MATURITY = "--maturity";
|
||||
|
||||
private static final String SET_LABELS = "--set-labels";
|
||||
private static final String ADD_LABELS = "--add-labels";
|
||||
|
||||
// Execution
|
||||
private static final String EXPORT_CYCLE_LOG = "--export-cycle-log";
|
||||
private static final String IMPORT_CYCLE_LOG = "--import-cycle-log";
|
||||
@@ -132,6 +136,7 @@ public class NBCLIOptions {
|
||||
// private static final String DEFAULT_CONSOLE_LOGGING_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
||||
|
||||
|
||||
private final Map<String,String> labels = new LinkedHashMap<>(DEFAULT_LABELS);
|
||||
private final List<Cmd> cmdList = new ArrayList<>();
|
||||
private int logsMax;
|
||||
private boolean wantsVersionShort;
|
||||
@@ -205,6 +210,9 @@ public class NBCLIOptions {
|
||||
return this.annotatorsConfig;
|
||||
}
|
||||
|
||||
public Map<String,String> getLabelMap() {
|
||||
return Collections.unmodifiableMap(this.labels);
|
||||
}
|
||||
|
||||
public String getChartHdrFileName() {
|
||||
return this.hdrForChartFileName;
|
||||
@@ -460,6 +468,16 @@ public class NBCLIOptions {
|
||||
arglist.removeFirst();
|
||||
final String maturity = this.readWordOrThrow(arglist, "maturity of components to allow");
|
||||
minMaturity = Maturity.valueOf(maturity.toLowerCase(Locale.ROOT));
|
||||
case NBCLIOptions.SET_LABELS:
|
||||
arglist.removeFirst();
|
||||
String setLabelData = arglist.removeFirst();
|
||||
setLabels(setLabelData);
|
||||
break;
|
||||
case NBCLIOptions.ADD_LABELS:
|
||||
arglist.removeFirst();
|
||||
String addLabeldata = arglist.removeFirst();
|
||||
addLabels(addLabeldata);
|
||||
break;
|
||||
default:
|
||||
nonincludes.addLast(arglist.removeFirst());
|
||||
}
|
||||
@@ -468,6 +486,29 @@ public class NBCLIOptions {
|
||||
return nonincludes;
|
||||
}
|
||||
|
||||
private void setLabels(String labeldata) {
|
||||
this.labels.clear();
|
||||
addLabels(labeldata);
|
||||
}
|
||||
|
||||
private void addLabels(String labeldata) {
|
||||
Map<String,String> newLabels = parseLabels(labeldata);
|
||||
this.labels.putAll(newLabels);
|
||||
}
|
||||
|
||||
private Map<String, String> parseLabels(String labeldata) {
|
||||
Map<String,String> setLabelsTo = new LinkedHashMap<>();
|
||||
for (String component : labeldata.split("[,; ]")) {
|
||||
String[] parts = component.split("\\W", 2);
|
||||
if (parts.length!=2) {
|
||||
throw new BasicError("Unable to parse labels to set:" + labeldata);
|
||||
}
|
||||
setLabelsTo.put(parts[0],parts[1]);
|
||||
}
|
||||
return setLabelsTo;
|
||||
}
|
||||
|
||||
|
||||
private Path setStatePath() {
|
||||
if (0 < statePathAccesses.size())
|
||||
throw new BasicError("The state dir must be set before it is used by other\n" +
|
||||
|
||||
@@ -208,6 +208,21 @@ automatically. It also imports a base dashboard for nosqlbench and configures gr
|
||||
export to share with a central DataStax grafana instance (grafana can be found on localhost:3000
|
||||
with the default credentials admin/admin).
|
||||
|
||||
### Metrics Labeling
|
||||
|
||||
Metrics have attached labels which identify which session, scenario, activity, and operation
|
||||
they are attached to. Not all labels will be present, as metrics are instanced at different
|
||||
levels and may or may not be op or activity specific.
|
||||
|
||||
By default, labels are added automatically to metrics. You can change this if needed.
|
||||
|
||||
# add labels to metrics, in addition to the default ones
|
||||
--add-labels label1=value1,label2=value2,...
|
||||
|
||||
# replace the initial set of labels
|
||||
--set-labels label1=value1,label2=value2,...
|
||||
|
||||
The default labels include appname, command, scenario, activity, op, and name.
|
||||
|
||||
### Summary Reporting
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -55,6 +55,9 @@ public class ActivityProgressIndicator implements Runnable {
|
||||
}
|
||||
|
||||
private void parseProgressSpec(String interval) {
|
||||
if (interval==null) {
|
||||
throw new RuntimeException("can't parse progress spec if it is null");
|
||||
}
|
||||
String[] parts = interval.split(":");
|
||||
switch (parts.length) {
|
||||
case 2:
|
||||
|
||||
@@ -74,6 +74,7 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
private ScenarioMetadata scenarioMetadata;
|
||||
|
||||
private ExecutionMetricsResult result;
|
||||
private final NBLabeledElement parentComponent;
|
||||
|
||||
public Optional<ExecutionMetricsResult> getResultIfComplete() {
|
||||
return Optional.ofNullable(result);
|
||||
@@ -82,7 +83,7 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return NBLabels.forKV("scenario", this.scenarioName);
|
||||
return this.parentComponent.getLabels().and("scenario", this.scenarioName);
|
||||
}
|
||||
|
||||
public enum State {
|
||||
@@ -100,10 +101,10 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
private ScenarioContext scriptEnv;
|
||||
private final String scenarioName;
|
||||
private ScriptParams scenarioScriptParams;
|
||||
private String scriptfile;
|
||||
private final String scriptfile;
|
||||
private Engine engine = Engine.Graalvm;
|
||||
private boolean wantsStackTraces;
|
||||
private boolean wantsCompiledScript;
|
||||
private final boolean wantsStackTraces;
|
||||
private final boolean wantsCompiledScript;
|
||||
private long startedAtMillis = -1L;
|
||||
private long endedAtMillis = -1L;
|
||||
|
||||
@@ -121,7 +122,8 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
final String reportSummaryTo,
|
||||
final String commandLine,
|
||||
final Path logsPath,
|
||||
final Maturity minMaturity) {
|
||||
final Maturity minMaturity,
|
||||
NBLabeledElement parentComponent) {
|
||||
|
||||
this.scenarioName = scenarioName;
|
||||
this.scriptfile = scriptfile;
|
||||
@@ -133,17 +135,22 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
this.commandLine = commandLine;
|
||||
this.logsPath = logsPath;
|
||||
this.minMaturity = minMaturity;
|
||||
this.parentComponent = parentComponent;
|
||||
}
|
||||
|
||||
public Scenario(final String name, final Engine engine, final String reportSummaryTo, final Maturity minMaturity) {
|
||||
scenarioName = name;
|
||||
this.reportSummaryTo = reportSummaryTo;
|
||||
this.engine = engine;
|
||||
commandLine = "";
|
||||
this.minMaturity = minMaturity;
|
||||
logsPath = Path.of("logs");
|
||||
public static Scenario forTesting(final String name, final Engine engine, final String reportSummaryTo, final Maturity minMaturity) {
|
||||
return new Scenario(name,null,engine,"console:10s",true,true,reportSummaryTo,"",Path.of("logs"),minMaturity, NBLabeledElement.forKV("test-name","name"));
|
||||
}
|
||||
|
||||
// public Scenario(final String name, final Engine engine, final String reportSummaryTo, final Maturity minMaturity) {
|
||||
// scenarioName = name;
|
||||
// this.reportSummaryTo = reportSummaryTo;
|
||||
// this.engine = engine;
|
||||
// commandLine = "";
|
||||
// this.minMaturity = minMaturity;
|
||||
// logsPath = Path.of("logs");
|
||||
// }
|
||||
//
|
||||
public Scenario setLogger(final Logger logger) {
|
||||
this.logger = logger;
|
||||
return this;
|
||||
|
||||
@@ -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.
|
||||
@@ -31,7 +31,7 @@ public class ScenarioTest {
|
||||
@Test
|
||||
public void shouldLoadScriptText() {
|
||||
ScriptEnvBuffer buffer = new ScriptEnvBuffer();
|
||||
Scenario scenario = new Scenario("testing", Scenario.Engine.Graalvm, "stdout:300", Maturity.Any);
|
||||
Scenario scenario = Scenario.forTesting("testing", Scenario.Engine.Graalvm, "stdout:300", Maturity.Any);
|
||||
scenario.addScriptText("print('loaded script environment...');\n");
|
||||
try {
|
||||
var result=scenario.call();
|
||||
|
||||
@@ -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.
|
||||
@@ -29,7 +29,7 @@ public class ScenariosExecutorTest {
|
||||
@Disabled
|
||||
public void testAwaitOnTime() {
|
||||
ScenariosExecutor e = new ScenariosExecutor(ScenariosExecutorTest.class.getSimpleName(), 1);
|
||||
Scenario s = new Scenario("testing", Scenario.Engine.Graalvm,"stdout:3000", Maturity.Any);
|
||||
Scenario s = Scenario.forTesting("testing", Scenario.Engine.Graalvm,"stdout:3000", Maturity.Any);
|
||||
s.addScriptText("load('classpath:scripts/asyncs.js');\nsetTimeout(\"print('waited')\",5000);\n");
|
||||
e.execute(s);
|
||||
ScenariosResults scenariosResults = e.awaitAllResults();
|
||||
|
||||
@@ -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.
|
||||
@@ -59,21 +59,21 @@ public class ServiceSelector<T> implements Predicate<ServiceLoader.Provider<? ex
|
||||
return false;
|
||||
}
|
||||
|
||||
public T getOne() {
|
||||
List<? extends T> services = getAll();
|
||||
if (services.size() == 0) {
|
||||
public ServiceLoader.Provider<? extends T> getOneProvider() {
|
||||
List<? extends ServiceLoader.Provider<? extends T>> providers = getAllProviders();
|
||||
if (providers.size()==0 || providers.size()>1) {
|
||||
throw new RuntimeException("You requested exactly one instance of a service by name '" + name + "', but got " +
|
||||
(services.stream().map(s -> s.getClass().getSimpleName())).collect(Collectors.joining(",")) + " (" + services.stream().count() + ")");
|
||||
} else if (services.size()==1) {
|
||||
return services.get(0);
|
||||
(providers.stream().map(s -> s.getClass().getSimpleName())).collect(Collectors.joining(",")) + " (" + providers.stream().count() + ")");
|
||||
}
|
||||
throw new RuntimeException("You requested exactly one instance of a service by name '" + name + "', but got " +
|
||||
(services.stream().map(s -> s.getClass().getSimpleName())).collect(Collectors.joining(",")) + " (" + services.stream().count() + ")");
|
||||
|
||||
return providers.get(0);
|
||||
}
|
||||
|
||||
public List<? extends T> getAll() {
|
||||
List<? extends T> services = loader
|
||||
public T getOne() {
|
||||
return getOneProvider().get();
|
||||
}
|
||||
|
||||
public List<? extends ServiceLoader.Provider<? extends T>> getAllProviders() {
|
||||
List<? extends ServiceLoader.Provider<? extends T>> providers = loader
|
||||
.stream()
|
||||
.peek(l -> {
|
||||
if (l.type().getAnnotation(Service.class) == null) {
|
||||
@@ -86,9 +86,14 @@ public class ServiceSelector<T> implements Predicate<ServiceLoader.Provider<? ex
|
||||
)
|
||||
.filter(l -> l.type().getAnnotation(Service.class) != null)
|
||||
.filter(l -> l.type().getAnnotation(Service.class).selector().equals(name))
|
||||
.toList();
|
||||
return providers;
|
||||
}
|
||||
public List<? extends T> getAll() {
|
||||
List<? extends ServiceLoader.Provider<? extends T>> providers = getAllProviders();
|
||||
return providers.stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.toList();
|
||||
return services;
|
||||
}
|
||||
|
||||
public Optional<? extends T> get() {
|
||||
|
||||
@@ -123,7 +123,14 @@ public class MapLabels implements NBLabels {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.linearize("name");
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
labels.forEach((k,v) -> {
|
||||
sb.append(k).append(":\\\"").append(v).append("\\\"").append(",");
|
||||
});
|
||||
sb.setLength(sb.length()-",".length());
|
||||
sb.append("}");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,13 +19,11 @@ package io.nosqlbench.api.engine.metrics.reporters;
|
||||
import com.codahale.metrics.*;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.testutils.Perf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -70,7 +68,7 @@ public enum PromExpositionFormat {
|
||||
|
||||
final long count = counting.getCount();
|
||||
buffer
|
||||
.append(labels.modifyValue("name", n -> n+"_total"))
|
||||
.append(labels.modifyValue("name", n -> n+"_total").linearize("name"))
|
||||
.append(' ')
|
||||
.append(count)
|
||||
.append(' ')
|
||||
@@ -84,37 +82,37 @@ public enum PromExpositionFormat {
|
||||
for (final double quantile : new double[]{0.5, 0.75, 0.90, 0.95, 0.98, 0.99, 0.999}) {
|
||||
final double value = snapshot.getValue(quantile);
|
||||
buffer
|
||||
.append(labels.and("quantile", String.valueOf(quantile)))
|
||||
.append(labels.and("quantile", String.valueOf(quantile)).linearize("name"))
|
||||
.append(' ')
|
||||
.append(value)
|
||||
.append('\n');
|
||||
}
|
||||
final double snapshotCount =snapshot.size();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_count"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_count").linearize("name"))
|
||||
.append(' ')
|
||||
.append(snapshotCount)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append("_max").append(" gauge\n");
|
||||
final long maxValue = snapshot.getMax();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_max"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_max").linearize("name"))
|
||||
.append(' ')
|
||||
.append(maxValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append("_min").append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_min").only("name")).append(" gauge\n");
|
||||
final long minValue = snapshot.getMin();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_min"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_min").linearize("name"))
|
||||
.append(' ')
|
||||
.append(minValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append("_mean").append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_mean").only("name")).append(" gauge\n");
|
||||
final double meanValue = snapshot.getMean();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_mean"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_mean").linearize("name"))
|
||||
.append(' ')
|
||||
.append(meanValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append("_stdev").append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_stdev").only("name")).append(" gauge\n");
|
||||
final double stdDev = snapshot.getStdDev();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_stdev"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_stdev").linearize("name"))
|
||||
.append(' ')
|
||||
.append(stdDev)
|
||||
.append('\n');
|
||||
@@ -125,18 +123,18 @@ public enum PromExpositionFormat {
|
||||
final Object value = gauge.getValue();
|
||||
if (value instanceof final Number number) {
|
||||
final double doubleValue = number.doubleValue();
|
||||
buffer.append(labels)
|
||||
buffer.append(labels.linearize("name"))
|
||||
.append(' ')
|
||||
.append(doubleValue)
|
||||
.append('\n');
|
||||
} else if (value instanceof final CharSequence sequence) {
|
||||
final String stringValue = sequence.toString();
|
||||
buffer.append(labels)
|
||||
buffer.append(labels.linearize("name"))
|
||||
.append(' ')
|
||||
.append(stringValue)
|
||||
.append('\n');
|
||||
} else if (value instanceof final String stringValue) {
|
||||
buffer.append(labels)
|
||||
buffer.append(labels.linearize("name"))
|
||||
.append(' ')
|
||||
.append(stringValue)
|
||||
.append('\n');
|
||||
@@ -147,28 +145,28 @@ public enum PromExpositionFormat {
|
||||
if (metric instanceof final Metered meter) {
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_1mRate").only("name")).append(" gauge\n");
|
||||
final double oneMinuteRate = meter.getOneMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_1mRate"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_1mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(oneMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_5mRate").only("name")).append(" gauge\n");
|
||||
final double fiveMinuteRate = meter.getFiveMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_5mRate"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_5mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(fiveMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_15mRate").only("name")).append(" gauge\n");
|
||||
final double fifteenMinuteRate = meter.getFifteenMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_15mRate"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_15mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(fifteenMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_meanRate").only("name")).append(" gauge\n");
|
||||
final double meanRate = meter.getMeanRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_meanRate"))
|
||||
buffer.append(labels.modifyValue("name",n->n+"_meanRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(meanRate)
|
||||
.append('\n');
|
||||
|
||||
@@ -74,6 +74,7 @@ public class PromPushReporter extends ScheduledReporter {
|
||||
}
|
||||
PromPushReporter.logger.debug("formatted {} metrics in prom expo format", total);
|
||||
final String exposition = sb.toString();
|
||||
logger.trace(() -> "prom exposition format:\n" + exposition);
|
||||
|
||||
final double backoffRatio=1.5;
|
||||
final double maxBackoffSeconds=10;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ScriptExampleTests {
|
||||
String scenarioName = "scenario " + scriptname;
|
||||
System.out.println("=".repeat(29) + " Running integration test for example scenario: " + scenarioName);
|
||||
ScenariosExecutor executor = new ScenariosExecutor(ScriptExampleTests.class.getSimpleName() + ":" + scriptname, 1);
|
||||
Scenario s = new Scenario(scenarioName, Scenario.Engine.Graalvm,"stdout:300", Maturity.Any);
|
||||
Scenario s = Scenario.forTesting(scenarioName, Scenario.Engine.Graalvm,"stdout:300", Maturity.Any);
|
||||
|
||||
s.addScenarioScriptParams(paramsMap);
|
||||
|
||||
@@ -261,7 +261,7 @@ public class ScriptExampleTests {
|
||||
public void testErrorPropagationFromAdapterOperation() {
|
||||
ExecutionMetricsResult scenarioResult = runScenario(
|
||||
"basicdiag",
|
||||
"type", "diag", "cyclerate", "5", "erroroncycle", "10", "cycles", "2000"
|
||||
"driver", "diag", "cyclerate", "5", "erroroncycle", "10", "cycles", "2000"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,19 +15,17 @@
|
||||
*/
|
||||
|
||||
activitydef1 = {
|
||||
"alias" : "activity_error",
|
||||
"driver" : "diag",
|
||||
"cycles" : "0..1500000",
|
||||
"threads" : "1",
|
||||
"targetrate" : "10",
|
||||
"op" : {
|
||||
"log": "type=log modulo=1"
|
||||
}
|
||||
"alias": "activity_error",
|
||||
"driver": "diag",
|
||||
"cycles": "0..1500000",
|
||||
"threads": "1",
|
||||
"targetrate": "10",
|
||||
"op": "log: modulo=1"
|
||||
};
|
||||
|
||||
print('starting activity activity_error');
|
||||
scenario.start(activitydef1);
|
||||
scenario.waitMillis(2000);
|
||||
activities.activity_error.threads="unparsable";
|
||||
activities.activity_error.threads = "unparsable";
|
||||
scenario.awaitActivity("activity_error");
|
||||
print("awaited activity");
|
||||
|
||||
@@ -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.
|
||||
@@ -20,7 +20,7 @@ co_cycle_delay_bursty = {
|
||||
"cycles": "0..1000000",
|
||||
"threads": "10",
|
||||
"cyclerate": "1000,1.5",
|
||||
"op" : '{"log":{"level":"info","modulo":1000},"diagrate":{"diagrate":"500"}}'
|
||||
"op" : "diagrate: diagrate=500"
|
||||
};
|
||||
|
||||
print('starting activity co_cycle_delay_bursty');
|
||||
|
||||
@@ -973,6 +973,17 @@ public class ParsedTemplateMap implements LongFunction<Map<String, ?>>, StaticFi
|
||||
return new LinkedHashMap<String,Object>(ParamsParser.parseToMap(mapsrc,mainField));
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> parseStaticCmdMaps(String key, String mainField) {
|
||||
Object mapsSrc = getStaticValue(key);
|
||||
List<Map<String,Object>> maps = new ArrayList<>();
|
||||
for (String spec : mapsSrc.toString().split("; +")) {
|
||||
LinkedHashMap<String, Object> map = new LinkedHashMap<>(ParamsParser.parseToMap(spec, mainField));
|
||||
maps.add(map);
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("protomap:\n");
|
||||
|
||||
@@ -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.
|
||||
@@ -29,5 +29,6 @@ public enum Category {
|
||||
statistics,
|
||||
general,
|
||||
objects,
|
||||
periodic,
|
||||
experimental
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -90,9 +90,7 @@ public class VirtDataConversions {
|
||||
|
||||
List<Class<?>> resultTypes = new ArrayList<>();
|
||||
resultTypes.add(functionType);
|
||||
for (Class<?> aClass : resultSignature) {
|
||||
resultTypes.add(aClass);
|
||||
}
|
||||
Collections.addAll(resultTypes, resultSignature);
|
||||
List<Class<?>> toSignature = linearizeSignature(resultTypes);
|
||||
|
||||
signature.addAll(fromSignature);
|
||||
@@ -335,9 +333,7 @@ public class VirtDataConversions {
|
||||
Class<?>[] argTypes = new Class[generics.length + 2];
|
||||
argTypes[0] = fromClass;
|
||||
argTypes[1] = toClass;
|
||||
for (int i = 0; i < generics.length; i++) {
|
||||
argTypes[i + 2] = generics[i];
|
||||
}
|
||||
System.arraycopy(generics, 0, argTypes, 2, generics.length);
|
||||
|
||||
try {
|
||||
return hostclass.getMethod("adapt", argTypes);
|
||||
@@ -354,9 +350,6 @@ public class VirtDataConversions {
|
||||
if (generics.length < typeParameters.length) {
|
||||
throw new RuntimeException("You must provide " + typeParameters.length + " generic parameter types for " + toClass.getCanonicalName());
|
||||
}
|
||||
// if (generics[i].isPrimitive()) {
|
||||
// throw new RuntimeException("You must declare non-primitive types in generic parameter placeholders, not " + generics[i].getSimpleName());
|
||||
// }
|
||||
genericsBuffer.append(generics[i].getSimpleName());
|
||||
genericsBuffer.append(",");
|
||||
}
|
||||
@@ -385,19 +378,6 @@ public class VirtDataConversions {
|
||||
}
|
||||
}
|
||||
|
||||
// private static void assertOutputAssignable(Object result, Class<?> clazz) {
|
||||
// if (!ClassUtils.isAssignable(result.getClass(), clazz, true)) {
|
||||
// throw new InvalidParameterException("Unable to assign type of " + result.getClass().getCanonicalName()
|
||||
// + " to " + clazz.getCanonicalName());
|
||||
// }
|
||||
//
|
||||
//// if (!clazz.isAssignableFrom(result.getClass())) {
|
||||
//// throw new InvalidParameterException("Unable to assign type of " + result.getClass().getCanonicalName()
|
||||
//// + " to " + clazz.getCanonicalName());
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
|
||||
/**
|
||||
* Given a base object and a wanted type to convert it to, assert that the type of the base object is assignable to
|
||||
* the wanted type. Further, if the wanted type is a generic type, assert that additional classes are assignable to
|
||||
@@ -444,16 +424,5 @@ public class VirtDataConversions {
|
||||
|
||||
return (T) (base);
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * Throw an error indicating a narrowing conversion was attempted for strict conversion.
|
||||
// * @param func The source function to convert from
|
||||
// * @param targetClass The target class which was requested
|
||||
// */
|
||||
// private static void throwNarrowingError(Object func, Class<?> targetClass) {
|
||||
// throw new BasicError("Converting from " + func.getClass().getCanonicalName() + " to " + targetClass.getCanonicalName() +
|
||||
// " is not allowed when strict conversion is requested.");
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_double.to_double;
|
||||
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.LongToDoubleFunction;
|
||||
|
||||
/**
|
||||
* Compute the sum of a set of functions.
|
||||
*/
|
||||
@ThreadSafeMapper
|
||||
@Categories({Category.general})
|
||||
public class SumFunctions implements LongToDoubleFunction {
|
||||
|
||||
private final List<LongToDoubleFunction> functions;
|
||||
public SumFunctions(Object... funcs) {
|
||||
this.functions = VirtDataConversions.adaptFunctionList(funcs, LongToDoubleFunction.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double applyAsDouble(long value) {
|
||||
double sum = 0.0d;
|
||||
for (LongToDoubleFunction function : functions) {
|
||||
sum+=function.applyAsDouble(value);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_double.to_double;
|
||||
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
|
||||
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
@ThreadSafeMapper
|
||||
@Categories(Category.periodic)
|
||||
public class TriangleWave implements DoubleUnaryOperator {
|
||||
private final double phaseLength;
|
||||
private final DoubleUnaryOperator scaleFunc;
|
||||
private final DoubleUnaryOperator normalizerFunc;
|
||||
|
||||
private final double halfWave;
|
||||
|
||||
public TriangleWave(double phaseLength, Object scaler) {
|
||||
this.halfWave = phaseLength*0.5d;
|
||||
|
||||
normalizerFunc=d -> d/(phaseLength/2.0);
|
||||
this.phaseLength=phaseLength;
|
||||
if (scaler instanceof Number number) {
|
||||
if (scaler instanceof Double adouble) {
|
||||
this.scaleFunc=d -> d*adouble;
|
||||
} else {
|
||||
this.scaleFunc= d -> d*number.doubleValue();
|
||||
}
|
||||
} else {
|
||||
this.scaleFunc = VirtDataConversions.adaptFunction(scaler, DoubleUnaryOperator.class);
|
||||
}
|
||||
}
|
||||
public TriangleWave(double phaseLength) {
|
||||
this(phaseLength, LongUnaryOperator.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double applyAsDouble(double operand) {
|
||||
double position = operand % phaseLength;
|
||||
int slot = (int) (4.0d*position/phaseLength);
|
||||
double sample = switch (slot) {
|
||||
case 0 -> position;
|
||||
case 1 -> halfWave-position;
|
||||
case 2 -> position-halfWave;
|
||||
case 4 -> phaseLength-position;
|
||||
default -> Double.NaN;
|
||||
};
|
||||
double normalized = normalizerFunc.applyAsDouble(sample);
|
||||
double scaled = scaleFunc.applyAsDouble(sample);
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_long.to_double;
|
||||
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
|
||||
import java.util.function.LongToDoubleFunction;
|
||||
|
||||
@ThreadSafeMapper
|
||||
@Categories({Category.general})
|
||||
public class Mul implements LongToDoubleFunction {
|
||||
private final double factor;
|
||||
|
||||
public Mul(double factor) {
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double applyAsDouble(long value) {
|
||||
return factor * value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_long.to_long;
|
||||
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
|
||||
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
/**
|
||||
* Computes the distance between the current input value and the
|
||||
* beginning of the phase, according to a phase length.
|
||||
* This means that for a phase length of 100, the values will
|
||||
* range from 0 (for cycle values 0 and 100 or any multiple thereof)
|
||||
* and 50, when the cycle value falls immediately at the middle
|
||||
* of the phase.
|
||||
*/
|
||||
@ThreadSafeMapper
|
||||
@Categories(Category.periodic)
|
||||
public class TriangleWave implements LongUnaryOperator {
|
||||
private final long phaseLength;
|
||||
private final LongUnaryOperator scaleFunc;
|
||||
|
||||
public TriangleWave(long phaseLength, Object scaleFunc) {
|
||||
this.phaseLength=phaseLength;
|
||||
this.scaleFunc = VirtDataConversions.adaptFunction(scaleFunc, LongUnaryOperator.class);
|
||||
}
|
||||
public TriangleWave(long phaseLength) {
|
||||
this(phaseLength, LongUnaryOperator.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long applyAsLong(long operand) {
|
||||
long position = operand % phaseLength;
|
||||
long minDistanceFromEnds = Math.min(Math.abs(phaseLength - position), position);
|
||||
long result = scaleFunc.applyAsLong(minDistanceFromEnds);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -14,10 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.diag;
|
||||
package io.nosqlbench.virtdata.library.basics.shared.periodic;
|
||||
|
||||
public class DiagDummyError extends RuntimeException {
|
||||
public DiagDummyError(String s) {
|
||||
super(s);
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
|
||||
@ThreadSafeMapper
|
||||
@Categories(Category.periodic)
|
||||
public class Sin implements DoubleUnaryOperator {
|
||||
|
||||
@Override
|
||||
public double applyAsDouble(double operand) {
|
||||
return Math.sin(operand);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -14,7 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.activitytype.diag;
|
||||
package io.nosqlbench.virtdata.library.basics.shared.temporal;
|
||||
|
||||
public class DiagResult {
|
||||
import io.nosqlbench.virtdata.api.annotations.Categories;
|
||||
import io.nosqlbench.virtdata.api.annotations.Category;
|
||||
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
|
||||
|
||||
import java.util.function.LongUnaryOperator;
|
||||
|
||||
@ThreadSafeMapper
|
||||
@Categories(Category.datetime)
|
||||
public class CurrentTimeMillis implements LongUnaryOperator {
|
||||
@Override
|
||||
public long applyAsLong(long operand) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_double.to_double;
|
||||
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.function.LongToDoubleFunction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class SumFunctionsTest {
|
||||
|
||||
@Test
|
||||
public void sumFunctionsTest() {
|
||||
LongToDoubleFunction f1 = d -> d*3.0d;
|
||||
LongToDoubleFunction f2 = d -> d+5.0d;
|
||||
SumFunctions ff = new SumFunctions(f1,f2);
|
||||
assertThat(ff.applyAsDouble(15L)).isEqualTo(65.0d, Offset.offset(0.0002d));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.virtdata.library.basics.shared.from_long.to_long;/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import io.nosqlbench.virtdata.library.basics.shared.from_double.to_double.TriangleWave;
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TriangleWaveTest {
|
||||
|
||||
@Test
|
||||
public void testLongValues() {
|
||||
io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.TriangleWave cyclicDistance =
|
||||
new io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.TriangleWave(100L);
|
||||
assertThat(cyclicDistance.applyAsLong(0)).isEqualTo(0);
|
||||
assertThat(cyclicDistance.applyAsLong(100)).isEqualTo(0);
|
||||
assertThat(cyclicDistance.applyAsLong(49)).isEqualTo(49);
|
||||
assertThat(cyclicDistance.applyAsLong(50)).isEqualTo(50);
|
||||
assertThat(cyclicDistance.applyAsLong(51)).isEqualTo(49);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>{@code
|
||||
* /\ ^0.5
|
||||
* / \
|
||||
* ---0----\----0----
|
||||
* \ /
|
||||
* \/ _-0.5
|
||||
* }</pre>
|
||||
*/
|
||||
@Test
|
||||
public void testDoubleValues() {
|
||||
TriangleWave cyclicDistance =
|
||||
new TriangleWave(100.0d,50.0d);
|
||||
assertThat(cyclicDistance.applyAsDouble(0.0d)).isCloseTo(0.0d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(12.5d)).isCloseTo(12.5d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(25.0d)).isCloseTo(25.0d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(37.5d)).isCloseTo(12.5d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(100.0d)).isCloseTo(0.0d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(49.0d)).isCloseTo(1.0d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(50.0d)).isCloseTo(0.0d, Offset.offset(0.0001d));
|
||||
assertThat(cyclicDistance.applyAsDouble(51.0d)).isCloseTo(1.0d, Offset.offset(0.0001d));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user