diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 088a8cf21..8f7667d3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 name: checkout nosqlbench @@ -25,6 +25,9 @@ jobs: java-package: jdk java-version: '21' + - name: install fuse2 + run: sudo apt install libfuse2 + - name: Cache Maven packages uses: actions/cache@v4 with: @@ -74,7 +77,7 @@ jobs: builddocs: needs: build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: ${{ github.repository == 'nosqlbench/nosqlbench' && github.event_name == 'push' && github.ref_name == 'main' }} steps: diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 428b1ace5..d96180381 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -15,7 +15,7 @@ on: jobs: preview-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: preview_version: ${{ steps.versions.outputs.PREVIEW_VERSION }} docker_tags: ${{ steps.versions.outputs.DOCKER_TAGS }} @@ -46,6 +46,9 @@ jobs: docker rmi $(docker image ls -aq) df -h + - name: install fuse2 + run: sudo apt install libfuse2 + - name: Cache Maven packages uses: actions/cache@v4 with: @@ -204,7 +207,7 @@ jobs: preview-docs: needs: preview-build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: import env vars run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab5ed6d59..12992e8e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ on: jobs: release-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: checkout repo @@ -43,6 +43,9 @@ jobs: docker rmi $(docker image ls -aq) df -h + - name: install fuse2 + run: sudo apt install libfuse2 + - name: Cache Maven packages uses: actions/cache@v4 with: @@ -186,7 +189,7 @@ jobs: # javadocs: # needs: release -# runs-on: ubuntu-20.04 +# runs-on: ubuntu-22.04 # steps: # - name: set git username # run: git config --global user.email "${{ secrets.NBDROID_EMAIL }}" @@ -210,7 +213,7 @@ jobs: # # docs: # needs: release -# runs-on: ubuntu-20.04 +# runs-on: ubuntu-22.04 # steps: # # - name: set git username diff --git a/mvn-defaults/pom.xml b/mvn-defaults/pom.xml index e7fe6f394..9650dd581 100644 --- a/mvn-defaults/pom.xml +++ b/mvn-defaults/pom.xml @@ -573,6 +573,9 @@ false + + value + diff --git a/nb-adapters/adapter-cqld4/src/main/java/io/nosqlbench/cqlgen/core/CGWorkloadExporter.java b/nb-adapters/adapter-cqld4/src/main/java/io/nosqlbench/cqlgen/core/CGWorkloadExporter.java index 0a4ec8cde..4f7efcea8 100644 --- a/nb-adapters/adapter-cqld4/src/main/java/io/nosqlbench/cqlgen/core/CGWorkloadExporter.java +++ b/nb-adapters/adapter-cqld4/src/main/java/io/nosqlbench/cqlgen/core/CGWorkloadExporter.java @@ -72,6 +72,7 @@ public class CGWorkloadExporter implements BundledApp { private String namingTemplate; private double partitionMultiplier; private int quantizerDigits; + private boolean enableIfExists = true; private Map> blockplan = Map.of(); private final Map timeouts = new HashMap(Map.of( @@ -163,6 +164,9 @@ public class CGWorkloadExporter implements BundledApp { configureTimeouts(cfgmap.get("timeouts")); configureBlocks(cfgmap.get("blockplan")); configureQuantizerDigits(cfgmap.get("quantizer_digits")); + if (cfgmap.get("enable_if_exists").equals(false)) { + enableIfExists = false; + } this.model = CqlModelParser.parse(ddl, srcpath); List errorlist = model.getReferenceErrors(); @@ -639,7 +643,7 @@ public class CGWorkloadExporter implements BundledApp { ops.put( namer.nameFor(table, "optype", "drop", "blockname", blockname), Map.of( - "simple", "drop table if exists " + table.getFullName() + ";", + "simple", (enableIfExists ? "drop table if exists " : "drop table ") + table.getFullName() + ";", "timeout", timeouts.get("drop") ) ); @@ -655,7 +659,7 @@ public class CGWorkloadExporter implements BundledApp { ops.put( namer.nameFor(type, "optype", "drop-type", "blockname", blockname), Map.of( - "simple", "drop type if exists " + type.getKeyspace() + "." + type.getName() + ";", + "simple", (enableIfExists ? "drop type if exists " : "drop type ") + "." + type.getName() + ";", "timeout", timeouts.get("drop") ) ); @@ -671,7 +675,7 @@ public class CGWorkloadExporter implements BundledApp { ops.put( namer.nameFor(type, "optype", "drop-keyspace", "blockname", blockname), Map.of( - "simple", "drop keyspace if exists " + type.getKeyspace() + ";", + "simple", (enableIfExists ? "drop keyspace if exists " : "drop keyspace ") + type.getKeyspace() + ";", "timeout", timeouts.get("drop") ) ); @@ -736,9 +740,10 @@ public class CGWorkloadExporter implements BundledApp { private String genKeyspaceDDL(CqlKeyspaceDef keyspace) { return """ - create keyspace KEYSPACE + create keyspace IF_NOT_EXISTS KEYSPACE with replication = {REPLICATION}DURABLEWRITES?; """ + .replace("IF_NOT_EXISTS", enableIfExists ? "if not exists" : "") .replace("KEYSPACE", keyspace.getName()) .replace("REPLICATION", keyspace.getReplicationData()) .replace("DURABLEWRITES?", keyspace.isDurableWrites() ? "" : "\n and durable writes = false") @@ -766,10 +771,11 @@ public class CGWorkloadExporter implements BundledApp { private String genTypeDDL(CqlType type) { return """ - create type KEYSPACE.TYPENAME ( + create type IF_NOT_EXISTS KEYSPACE.TYPENAME ( TYPEDEF ); """ + .replace("IF_NOT_EXISTS", enableIfExists ? "if not exists" : "") .replace("KEYSPACE", type.getKeyspace().getName()) .replace("TYPENAME", type.getName()) .replace("TYPEDEF", type.getColumnDefs().stream() @@ -782,11 +788,12 @@ public class CGWorkloadExporter implements BundledApp { } return """ - create table if not exists KEYSPACE.TABLE ( + create table IF_NOT_EXISTS KEYSPACE.TABLE ( COLUMN_DEFS, primary key (PRIMARYKEY) )CLUSTERING; """ + .replace("IF_NOT_EXISTS", enableIfExists ? "if not exists" : "") .replace("KEYSPACE", cqltable.getKeyspace().getName()) .replace("TABLE", cqltable.getName()) .replace("COLUMN_DEFS", genTableColumnDDL(cqltable)) diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_iot.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_iot.yaml index 5c98d3535..fe55e33a0 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_iot.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_iot.yaml @@ -10,7 +10,7 @@ scenarios: rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10000000) threads=auto astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10000000) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_keyvalue.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_keyvalue.yaml index 9d8851c69..fd5979f36 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_keyvalue.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_keyvalue.yaml @@ -14,7 +14,7 @@ scenarios: rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags=='block:main-.*' cycles===TEMPLATE(main-cycles,10000000) threads=auto astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags=='block:main-.*' cycles===TEMPLATE(main-cycles,10000000) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_starter.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_starter.yaml index f58d45184..52a600938 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_starter.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_starter.yaml @@ -11,7 +11,7 @@ scenarios: main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10) threads=auto # rampdown: run driver=cql tags==block:rampdown threads==1 cycles==UNDEF astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_tabular.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_tabular.yaml index a8fba2a17..7e63cd2e6 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_tabular.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselines/cql_tabular.yaml @@ -7,7 +7,7 @@ scenarios: rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:main-*.* cycles===TEMPLATE(main-cycles,10000000) threads=auto astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:main-*.* cycles===TEMPLATE(main-cycles,10000000) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_tabular2.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_tabular2.yaml index 686d02a0a..39b82299a 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_tabular2.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_tabular2.yaml @@ -31,7 +31,7 @@ scenarios: rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,100) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,100) threads=auto astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,100) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,100) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_timeseries2.yaml b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_timeseries2.yaml index 20ead290b..f4b87fa24 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_timeseries2.yaml +++ b/nb-adapters/adapter-cqld4/src/main/resources/activities/baselinesv2/cql_timeseries2.yaml @@ -10,7 +10,7 @@ scenarios: rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10000000) threads=auto astra: - schema: run driver=cql tags==block:schema-astra threads==1 cycles==UNDEF + schema: run driver=cql tags==block:schema_astra threads==1 cycles==UNDEF rampup: run driver=cql tags==block:rampup cycles===TEMPLATE(rampup-cycles,10000000) threads=auto main: run driver=cql tags==block:"main.*" cycles===TEMPLATE(main-cycles,10000000) threads=auto basic_check: diff --git a/nb-adapters/adapter-cqld4/src/main/resources/cqlgen/cqlgen.conf b/nb-adapters/adapter-cqld4/src/main/resources/cqlgen/cqlgen.conf index 0aab9c35c..494fb4424 100644 --- a/nb-adapters/adapter-cqld4/src/main/resources/cqlgen/cqlgen.conf +++ b/nb-adapters/adapter-cqld4/src/main/resources/cqlgen/cqlgen.conf @@ -145,6 +145,6 @@ blockplan: # not needed when tags=block:'main.*' # main: insert, select, scan-10, update - - +# Configuration option for adding 'IF NOT EXISTS' or 'IF EXISTS' in all generated DDL statements +enable_if_exists: true diff --git a/nb-adapters/adapter-dataapi/src/main/java/io/nosqlbench/adapter/dataapi/opdispensers/DataApiDeleteOneOpDispenser.java b/nb-adapters/adapter-dataapi/src/main/java/io/nosqlbench/adapter/dataapi/opdispensers/DataApiDeleteOneOpDispenser.java index b095bb825..7af86c603 100644 --- a/nb-adapters/adapter-dataapi/src/main/java/io/nosqlbench/adapter/dataapi/opdispensers/DataApiDeleteOneOpDispenser.java +++ b/nb-adapters/adapter-dataapi/src/main/java/io/nosqlbench/adapter/dataapi/opdispensers/DataApiDeleteOneOpDispenser.java @@ -66,7 +66,10 @@ public class DataApiDeleteOneOpDispenser extends DataApiOpDispenser { } private float[] getVectorFromOp(ParsedOp op, long l) { - return getVectorValues(op.get("vector", l)); + if (op.isDefined("vector")) { + return getVectorValues(op.get("vector", l)); + } + return null; } @Override diff --git a/nb-adapters/adapter-dataapi/src/main/resources/activities/find_one_and_update.yaml b/nb-adapters/adapter-dataapi/src/main/resources/activities/find_one_and_update.yaml index e69de29bb..edff96902 100644 --- a/nb-adapters/adapter-dataapi/src/main/resources/activities/find_one_and_update.yaml +++ b/nb-adapters/adapter-dataapi/src/main/resources/activities/find_one_and_update.yaml @@ -0,0 +1,4 @@ +min_version: "5.21.0" + +description: | + TBD, similar to find_one_and_delete.yaml diff --git a/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collection_names.yaml b/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collection_names.yaml index abb0f4091..4234ccc58 100644 --- a/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collection_names.yaml +++ b/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collection_names.yaml @@ -6,4 +6,4 @@ blocks: list_collection_names: ops: op1: - list_collection_names: + list_collection_names: TBD diff --git a/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collections.yaml b/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collections.yaml index 421e2c1dd..87d2b9ac8 100644 --- a/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collections.yaml +++ b/nb-adapters/adapter-dataapi/src/main/resources/activities/list_collections.yaml @@ -6,4 +6,4 @@ blocks: list_collections: ops: op1: - list_collections: + list_collections: TBD diff --git a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java index f59717466..a8b7acb30 100644 --- a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java +++ b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/rawyaml/RawOpDef.java @@ -140,7 +140,7 @@ public class RawOpDef extends RawOpFields { if (v == null) { throw new OpConfigError("A map key '" + k.toString() + "' with a null value was encountered. This is not" + " allowed, and may be the result of using an unquoted binding, like {" + k + "}. You can simply wrap this in quotes" + - " like \"{"+ k +"\"} to avoid interpreting this as a JSON map." + + " like \"{"+ k +"}\" to avoid interpreting this as a JSON map." + (path.size()>0 ? String.join(".",path):"")); } else { if (v instanceof Map) { diff --git a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/uniform/fieldmappers/FieldDestructuringMapper.java b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/uniform/fieldmappers/FieldDestructuringMapper.java index 9e46f7163..5cfee65f8 100644 --- a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/uniform/fieldmappers/FieldDestructuringMapper.java +++ b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/uniform/fieldmappers/FieldDestructuringMapper.java @@ -53,7 +53,7 @@ public class FieldDestructuringMapper implements Function, M return stringObjectMap; } } else { - throw new RuntimeException("During op mapping, can't parse something that is not a CharSequence: '" + fieldname + "' (type is " + o.getClass().getCanonicalName() + ")"); + throw new RuntimeException("During op mapping, can't parse something that is not a CharSequence: '" + fieldname + "' (type is " + o.getClass().getCanonicalName() + ") (value is:" + o.toString()); } } else { return stringObjectMap; diff --git a/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java index 44b3217a4..c2b24ecd9 100644 --- a/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java +++ b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java @@ -99,12 +99,37 @@ public class NBCreators { } + public WindowSummaryGauge windowSummaryGauge( + String name, + int window, + List statspecs, + MetricCategory category, + String description + ) { + List stats = + statspecs.stream().map(WindowSummaryGauge.Stat::valueOf).toList(); + DoubleSummaryStatistics reservoir = new DoubleSummaryStatistics(); + WindowSummaryGauge anyGauge = null; + for (WindowSummaryGauge.Stat stat : stats) { + anyGauge = new WindowSummaryGauge( + window, + base.getLabels().and(NBLabels.forKV("name", name+"_w"+window, "stat", stat)), + stat, + description, + category + ); + base.addComponentMetric(anyGauge, category, description); + } + return anyGauge; + + } + public DoubleSummaryGauge summaryGauge(String name, List statspecs, MetricCategory category, String description) { List stats = statspecs.stream().map(DoubleSummaryGauge.Stat::valueOf).toList(); DoubleSummaryStatistics reservoir = new DoubleSummaryStatistics(); DoubleSummaryGauge anyGauge = null; for (DoubleSummaryGauge.Stat stat : stats) { - anyGauge = new DoubleSummaryGauge(base.getLabels().and(NBLabels.forKV("name",name,"stat", stat)), stat, reservoir, description, category); + anyGauge = new DoubleSummaryGauge(base.getLabels().and(NBLabels.forKV("name", name, "stat", stat)), stat, reservoir, description, category); base.addComponentMetric(anyGauge, category, description); } return anyGauge; @@ -113,6 +138,7 @@ public class NBCreators { public NBMetricHistogram histogram(String metricFamilyName, MetricCategory category, String description) { return histogram(metricFamilyName,4, category, description); } + public NBMetricHistogram histogram(String metricFamilyName, int hdrdigits, MetricCategory category, String description) { NBLabels labels = base.getLabels().and("name", metricFamilyName); NBMetricHistogram histogram = new NBMetricHistogram(labels, new DeltaHdrHistogramReservoir(labels, hdrdigits), description, category); @@ -120,7 +146,7 @@ public class NBCreators { return histogram; } -// public AttachedMetricsSummaryReporter summaryReporter(long millis, String... labelspecs) { + // public AttachedMetricsSummaryReporter summaryReporter(long millis, String... labelspecs) { // logger.debug("attaching summary reporter to " + base.description()); // NBLabels extraLabels = NBLabels.forKV((Object[]) labelspecs); // AttachedMetricsSummaryReporter reporter = new AttachedMetricsSummaryReporter(base, extraLabels, millis); @@ -198,7 +224,7 @@ public class NBCreators { private Logger logger = LogManager.getLogger(Log4JMetricsReporter.class); private Log4JMetricsReporter.LoggingLevel loggingLevel = Log4JMetricsReporter.LoggingLevel.INFO; private Marker marker; - private MetricFilter filter= new MetricInstanceFilter(); + private MetricFilter filter = new MetricInstanceFilter(); private boolean oneLastTime = false; private NBLabels labels; private long millis = 1000; @@ -206,34 +232,42 @@ public class NBCreators { public Log4jReporterBuilder(NBComponent component) { this.component = component; } + public Log4jReporterBuilder oneLastTime(final boolean oneLastTime) { this.oneLastTime = oneLastTime; return this; } + public Log4jReporterBuilder interval(final int interval) { this.millis = interval; return this; } + public Log4jReporterBuilder outputTo(final Logger logger) { this.logger = logger; return this; } + public Log4jReporterBuilder markWith(final Marker marker) { this.marker = marker; return this; } + public Log4jReporterBuilder labels(final NBLabels labels) { this.labels = labels; return this; } + public Log4jReporterBuilder filter(final MetricFilter filter) { this.filter = filter; return this; } + public Log4jReporterBuilder withLoggingLevel(final Log4JMetricsReporter.LoggingLevel loggingLevel) { this.loggingLevel = loggingLevel; return this; } + public Log4JMetricsReporter build() { final LoggerProxy loggerProxy = switch (this.loggingLevel) { case TRACE -> new TraceLoggerProxy(this.logger); @@ -245,6 +279,7 @@ public class NBCreators { return new Log4JMetricsReporter(this.component, loggerProxy, this.marker, this.filter, this.labels, this.millis, this.oneLastTime); } } + /* private class to allow logger configuration */ public abstract static class LoggerProxy { protected final Logger logger; @@ -356,22 +391,27 @@ public class NBCreators { this.component = component; this.output = output; } + public ConsoleReporterBuilder labels(NBLabels labels) { this.labels = labels; return this; } + public ConsoleReporterBuilder interval(int interval) { this.interval = interval; return this; } + public ConsoleReporterBuilder oneLastTime(boolean oneLastTime) { this.oneLastTime = oneLastTime; return this; } + public ConsoleReporterBuilder disabledMetricAttributes(Set disabledMetricAttributes) { this.disabledMetricAttributes = disabledMetricAttributes; return this; } + public ConsoleReporter build() { return new ConsoleReporter(component, labels, interval, oneLastTime, output, disabledMetricAttributes); } @@ -387,10 +427,12 @@ public class NBCreators { this.component = component; this.filename = filename; } + public CsvOutputWriterBuilder headers(String... headers) { this.headers = headers; return this; } + public CsvOutputPluginWriter build() { return new CsvOutputPluginWriter(component, filename, headers); } @@ -406,26 +448,32 @@ public class NBCreators { public CsvReporterBuilder(NBComponent component) { this.component = component; } + public CsvReporterBuilder labels(NBLabels labels) { this.labels = labels; return this; } + public CsvReporterBuilder path(Path reportTo) { this.reportTo = reportTo; return this; } + public CsvReporterBuilder path(String reportTo) { this.reportTo = Path.of(reportTo); return this; } + public CsvReporterBuilder interval(int interval) { this.interval = interval; return this; } + public CsvReporterBuilder filter(MetricInstanceFilter filter) { this.filter = filter; return this; } + public CsvReporter build() { return new CsvReporter(component, reportTo, interval, filter, labels); } diff --git a/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/WindowSummaryGauge.java b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/WindowSummaryGauge.java new file mode 100644 index 000000000..67213b2f8 --- /dev/null +++ b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/WindowSummaryGauge.java @@ -0,0 +1,97 @@ +/* + * 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.nb.api.engine.metrics; + +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; +import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; +import io.nosqlbench.nb.api.labels.NBLabels; +import io.nosqlbench.nb.api.stats.StatBucket; + +import java.util.DoubleSummaryStatistics; +import java.util.function.DoubleConsumer; + + +/** + * Create a discrete stat reservoir as a gauge. + */ +public class WindowSummaryGauge implements NBMetricGauge, DoubleConsumer { + private final NBLabels labels; + private final Stat stat; + private final StatBucket stats; + private final String description; + private final MetricCategory[] categories; + private final int window; + + @Override + public String typeName() { + return "gauge"; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } + + public enum Stat { + Min, + Max, + Average, + Count, + Sum + } + + + public WindowSummaryGauge(int window, NBLabels labels, Stat stat, String description, + MetricCategory... categories) { + this.labels = labels; + this.stat = stat; + this.description = description; + this.categories = categories; + this.window = window; + this.stats = new StatBucket(window); + } + + public synchronized void accept(double value) { + stats.apply(value); + } + + @Override +public synchronized Double getValue() { + return switch(stat) { + case Min -> stats.getMin(); + case Max -> stats.getMax(); + case Average -> stats.getAverage(); + case Count -> (double) stats.getCount(); + case Sum -> stats.getSum(); + }; + } + + @Override + public NBLabels getLabels() { + return labels; + } + + @Override + public String toString() { + return this.labels.toString()+":"+this.stats.toString(); + } +} diff --git a/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/WindowedRelevancyMeasures.java b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/WindowedRelevancyMeasures.java new file mode 100644 index 000000000..ef4d1c5ea --- /dev/null +++ b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/WindowedRelevancyMeasures.java @@ -0,0 +1,94 @@ +/* + * 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.nb.api.engine.metrics.wrappers; + +import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.WindowSummaryGauge; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; +import io.nosqlbench.nb.api.labels.NBLabeledElement; +import io.nosqlbench.nb.api.labels.NBLabels; +import io.nosqlbench.nb.api.stats.StatBucket; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class WindowedRelevancyMeasures implements NBLabeledElement { + + private final NBComponent parent; + private final NBLabels labels; + private final List functions = new ArrayList<>(); + private final List gauges = new ArrayList<>(); + private final int window; + private int offset = 0; + + public WindowedRelevancyMeasures(NBComponent parent, int window) { + this(parent, NBLabels.forKV(),window); + } + + public WindowedRelevancyMeasures(NBComponent parent, NBLabels labels, int window) { + this.parent = parent; + this.labels = labels; + this.window = window; + } + + public WindowedRelevancyMeasures(NBComponent parent, Map labels, int window) { + this(parent, NBLabels.forMap(labels), window); + } + + @Override + public NBLabels getLabels() { + return parent.getLabels().and(labels); + } + + public WindowedRelevancyMeasures addFunction(RelevancyFunction... f) { + for (RelevancyFunction function : f) { + this.functions.add(function); + function.prependLabels(this); + WindowSummaryGauge gauge = parent.create().windowSummaryGauge( + function.getUniqueName(), + window, + List.of("Average"), + MetricCategory.Accuracy, + WindowSummaryGauge.Stat.Average.toString() + ); + this.gauges.add(gauge); + } + return this; + } + + public void accept(int[] relevant, int[] actual) { + offset++; + if (offset >= window) { + offset = 0; + } + + for (int i = 0; i < functions.size(); i++) { + double metricValue = functions.get(i).apply(relevant, actual); + gauges.get(i).accept(metricValue); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (WindowSummaryGauge gauge : gauges) { + sb.append(gauge.toString()).append("\n"); + } + return sb.toString(); + } +} diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/DoubleRing.java b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/DoubleRing.java similarity index 76% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/DoubleRing.java rename to nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/DoubleRing.java index 537521178..e28b59f48 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/DoubleRing.java +++ b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/DoubleRing.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.stabilization; +package io.nosqlbench.nb.api.stats; public class DoubleRing { private final double[] dbuf; @@ -47,4 +47,21 @@ public class DoubleRing { public int count() { return count; } + + public double min() { + double min = Double.MAX_VALUE; + for (int i = 0; i < count; i++) { + min = Math.min(min,dbuf[i]); + } + return min; + } + + public double max() { + double max = Double.MIN_VALUE; + for (int i = 0; i < count; i++) { + max = Math.max(max,dbuf[i]); + } + return max; + } + } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/StatBucket.java similarity index 82% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java rename to nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/StatBucket.java index 3c489cd45..7ef39043c 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java +++ b/nb-apis/nb-api/src/main/java/io/nosqlbench/nb/api/stats/StatBucket.java @@ -14,10 +14,16 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.stabilization; +package io.nosqlbench.nb.api.stats; import java.util.Objects; +/** + * This is a relatively efficient statistics bucket which can maintain moving + * aggregates over a window of samples for count, mean, variance, stddev, sum. + * This is particularly useful when you know that each update to the data + * will likely be used in a query. + */ public final class StatBucket { DoubleRing ringbuf; private double mean; @@ -98,10 +104,31 @@ public final class StatBucket { return "StatBucket[" + "count=" + ringbuf.count() + ", " + "mean=" + mean + ", " + - "stddev=" + stddev() + ']'; + "stddev=" + stddev() + ", " + + "variance=" + variance() + ']'; } public boolean primed() { return this.count()== ringbuf.size(); } + + public double getMin() { + return ringbuf.min(); + } + + public double getMax() { + return ringbuf.max(); + } + + public double getAverage() { + return this.mean(); + } + + public double getCount() { + return count(); + } + + public double getSum() { + return this.mean() * this.count(); + } } diff --git a/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java b/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java index f2b5d3a5c..b9c751964 100644 --- a/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java +++ b/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/SystemIdTest.java @@ -56,14 +56,14 @@ public class SystemIdTest { @Test public void testPackedNodeId() { String packedNodeId = SystemId.getPackedNodeId(); - assertThat(packedNodeId).matches("[0-9A-Za-z_-]+"); + assertThat(packedNodeId).matches("[0-9A-Za-z_~-]+"); logger.info("packed node id: " + packedNodeId); } @Test public void testGenSessionCode() { String sessionCode=SystemId.genSessionCode(234L); - assertThat(sessionCode).matches("[0-9a-zA-Z~-]+"); + assertThat(sessionCode).matches("[0-9a-zA-Z_~-]+"); logger.info("session code: " + sessionCode); } diff --git a/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/stats/StatBucketTest.java b/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/stats/StatBucketTest.java new file mode 100644 index 000000000..be68d967d --- /dev/null +++ b/nb-apis/nb-api/src/test/java/io/nosqlbench/nb/api/stats/StatBucketTest.java @@ -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.nb.api.stats; + +import org.assertj.core.data.Offset; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class StatBucketTest { + @Test + public void testStreamingMean() { + var bucket = new StatBucket(); + bucket.apply(5.0d); + assertThat(bucket.mean()).isCloseTo(5.0d, Offset.offset(0.001d)); + bucket.apply(10.0d); + assertThat(bucket.mean()).isCloseTo(7.5d, Offset.offset(0.001d)); + bucket.apply(15.0d); + assertThat(bucket.mean()).isCloseTo(10.0d, Offset.offset(0.001d)); + bucket.apply(20.0d); + assertThat(bucket.mean()).isCloseTo(12.5d, Offset.offset(0.001d)); + } + + @Test + public void testStreamingComputations() { + double[] samples = new double[]{2, 4, 4, 4, 5, 5, 7, 9}; + + var bucket = new StatBucket(8); + for (int i = 0; i < samples.length * 10; i++) { + bucket.apply(samples[i % samples.length]); + if (i > 0 && (i % samples.length) == 0) { + assertThat(bucket.mean()).isCloseTo(5, Offset.offset(0.001d)); + assertThat(bucket.stddev()).isCloseTo(2.0, Offset.offset(0.001d)); + } + } + } + + @Test + public void testErrorAccumulation1() { + var bucket = new StatBucket(11); + for (long base = 1; base <10000000000000000L ; base*=10) { + for (int i = 0; i< 10; i++) { + long value = base+i; + bucket.apply(value); + } + for (int i = 10; i < 20; i++) { + long value = base+i; + bucket.apply(value); + double streamingMean = bucket.mean(); + assertThat(streamingMean).isCloseTo((double)(value-5), Offset.offset(0.03d)); + } + } + + } + +} diff --git a/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java b/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java index a474f4598..af9f5ad5b 100644 --- a/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java +++ b/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java @@ -19,6 +19,7 @@ package io.nosqlbench.engine.cli.atfiles; import io.nosqlbench.nb.api.nbio.Content; import io.nosqlbench.nb.api.nbio.NBIO; import io.nosqlbench.nb.api.nbio.NBPathsAPI; +import io.nosqlbench.nb.api.system.NBEnvironment; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -166,8 +167,10 @@ public class NBAtFile { while (iter.hasNext()) { String word = iter.next(); String modified = word.replaceAll("\\$\\{DIR}",parent.toString()); + Optional interpolatedString = NBEnvironment.INSTANCE.interpolate(modified); + String value = interpolatedString.orElseThrow(() -> new RuntimeException("Unable to find environment variable or property in text '"+modified+"' in atfile '" + atPath + "'")); iter.remove(); - iter.add(modified); + iter.add(value); } return formatted; } diff --git a/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java b/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java index be2b6e08e..5979472d7 100644 --- a/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java +++ b/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java @@ -91,4 +91,15 @@ class NBAtFileTest { assertThat(strings).containsExactly("arg1","arg1","arg1","arg2","arg3","arg3","arg3","deepval"); } + @Test + public void testAtfileEnvironmentVariable() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/environment_variable.yaml"); + assertThat(strings).containsExactly("My value environment"); + } + + @Test + public void testAtfileMissingEnvironmentVariable() { + assertThrows(RuntimeException.class, () -> NBAtFile.includeAt("@src/test/resources/atfiles/environment_variable_missing.yaml:>:")); + } + } diff --git a/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable.yaml b/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable.yaml new file mode 100644 index 000000000..71f5e2282 --- /dev/null +++ b/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable.yaml @@ -0,0 +1 @@ +- My ${TEST_ENV_VAR} environment diff --git a/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable_missing.yaml b/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable_missing.yaml new file mode 100644 index 000000000..48a8a26df --- /dev/null +++ b/nb-engine/nb-engine-cli/src/test/resources/atfiles/environment_variable_missing.yaml @@ -0,0 +1 @@ +- My ${MISSING_ENV_VAR} environment diff --git a/nb-engine/nb-engine-core/src/main/java/io/nosqlbench/engine/api/scenarios/NBCLIScenarioPreprocessor.java b/nb-engine/nb-engine-core/src/main/java/io/nosqlbench/engine/api/scenarios/NBCLIScenarioPreprocessor.java index 259323756..6168bbe84 100644 --- a/nb-engine/nb-engine-core/src/main/java/io/nosqlbench/engine/api/scenarios/NBCLIScenarioPreprocessor.java +++ b/nb-engine/nb-engine-core/src/main/java/io/nosqlbench/engine/api/scenarios/NBCLIScenarioPreprocessor.java @@ -313,7 +313,7 @@ public class NBCLIScenarioPreprocessor { } private static final Pattern templatePattern = Pattern.compile("TEMPLATE\\((.+?)\\)"); - private static final Pattern innerTemplatePattern = Pattern.compile("TEMPLATE\\((.+?)$"); + private static final Pattern innerTemplatePattern = Pattern.compile("TEMPLATE\\((.+?)\\)"); private static final Pattern templatePattern2 = Pattern.compile("<<(.+?)>>"); public static List filterForScenarios(List> candidates) { @@ -444,22 +444,18 @@ public class NBCLIScenarioPreprocessor { String match = matcher.group(1); Matcher innerMatcher = innerTemplatePattern.matcher(match); - String[] matchArray = match.split("[,:]"); - if (matchArray.length==1) { - matchArray = new String[]{matchArray[0],""}; - } -// if (matchArray.length!=2) { -// throw new BasicError("TEMPLATE form must have two arguments separated by a comma, like 'TEMPLATE(a,b), not '" + match +"'"); -// } - //TODO: support recursive matches if (innerMatcher.find()) { - String[] innerMatch = innerMatcher.group(1).split("[,:]"); - - //We want the outer name with the inner default value - templates.put(matchArray[0], innerMatch[1]); - } else { - templates.put(matchArray[0], matchArray[1]); + String innerMatch = innerMatcher.group(1); + templates = matchTemplates("TEMPLATE(" + innerMatch + ")", templates); + String resolvedInner = templates.getOrDefault(innerMatch.split("[,:]")[0], ""); + match = match.replace("TEMPLATE(" + innerMatch + ")", resolvedInner); } + + String[] matchArray = match.split("[,:]"); + if (matchArray.length == 1) { + matchArray = new String[]{matchArray[0], ""}; + } + templates.put(matchArray[0], matchArray[1]); } matcher = templatePattern2.matcher(line); diff --git a/nb5/nb-appimage/build-bin.sh b/nb5/nb-appimage/build-bin.sh index 2001f7a82..88ced43b8 100755 --- a/nb5/nb-appimage/build-bin.sh +++ b/nb5/nb-appimage/build-bin.sh @@ -75,6 +75,7 @@ printf "getting appimage tool and building image...\n"; chmod +x appimagetool-x86_64.AppImage fi + # note if your linux has errors with the following then see https://docs.appimage.org/user-guide/troubleshooting/fuse.html ARCH=x86_64 ./appimagetool-x86_64.AppImage NB.AppDir ${BIN_NAME} # && chmod +x ${BIN_NAME} ) diff --git a/nbr/build-bin-nbr.sh b/nbr/build-bin-nbr.sh index de7ca11b6..1f4c121bb 100755 --- a/nbr/build-bin-nbr.sh +++ b/nbr/build-bin-nbr.sh @@ -75,6 +75,7 @@ printf "getting appimage tool and building image...\n"; chmod +x appimagetool-x86_64.AppImage fi + # note if your linux has errors with the following then see https://docs.appimage.org/user-guide/troubleshooting/fuse.html ARCH=x86_64 ./appimagetool-x86_64.AppImage NB.AppDir ${BIN_NAME} # && chmod +x ${BIN_NAME} ) diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StabilityDetector.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StabilityDetector.java index 6cb5f4e58..e5f6f81f0 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StabilityDetector.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StabilityDetector.java @@ -16,12 +16,12 @@ package io.nosqlbench.scenarios.simframe.stabilization; +import io.nosqlbench.nb.api.stats.StatBucket; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Arrays; import java.util.List; -import java.util.OptionalDouble; import java.util.function.DoubleSupplier; import java.util.function.Supplier; diff --git a/nbr/src/test/java/io/nosqlbench/scenarios/simframe/stats/StatBucketTest.java b/nbr/src/test/java/io/nosqlbench/scenarios/simframe/stats/StatBucketTest.java deleted file mode 100644 index 679bcf185..000000000 --- a/nbr/src/test/java/io/nosqlbench/scenarios/simframe/stats/StatBucketTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.scenarios.simframe.stats; - -import io.nosqlbench.scenarios.simframe.stabilization.StatBucket; -import org.assertj.core.data.Offset; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class StatBucketTest { - @Test - public void testStreamingMean() { - var bucket = new StatBucket(); - bucket.apply(5.0d); - assertThat(bucket.mean()).isCloseTo(5.0d,Offset.offset(0.001d)); - bucket.apply(10.0d); - assertThat(bucket.mean()).isCloseTo(7.5d,Offset.offset(0.001d)); - bucket.apply(15.0d); - assertThat(bucket.mean()).isCloseTo(10.0d,Offset.offset(0.001d)); - bucket.apply(20.0d); - assertThat(bucket.mean()).isCloseTo(12.5d,Offset.offset(0.001d)); - } - - @Test - public void testStreamingComputations() { - double[] samples = new double[]{2,4,4,4,5,5,7,9}; - - var bucket = new StatBucket(8); - for (int i = 0; i < samples.length * 10; i++) { - bucket.apply(samples[i%samples.length]); - if (i>0&&(i%samples.length)==0) { - assertThat(bucket.mean()).isCloseTo(5,Offset.offset(0.001d)); - assertThat(bucket.stddev()).isCloseTo(2.0,Offset.offset(0.001d)); - } - } - } - -}