diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1177173e1..196d6e95e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,24 +14,20 @@ jobs: build: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: checkout nosqlbench - with: - node-version: '16' - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 name: setup java with: - node-version: '16' - java-version: '21' - java-package: jdk architecture: x64 distribution: 'oracle' + java-package: jdk + java-version: '21' - name: Cache Maven packages - uses: actions/cache@v1 + uses: actions/cache@v3 with: - node-version: '16' path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 @@ -71,9 +67,8 @@ jobs: - name: upload docs artifact if: success() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - node-version: '16' name: exported-docs path: exported_docs.zip @@ -90,9 +85,8 @@ jobs: run: git config --global user.name "${{ secrets.NBDROID_NAME }}" - name: download exported_docs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - node-version: '16' name: exported-docs - run: ls -la diff --git a/adapter-amqp/src/main/java/io/nosqlbench/adapter/amqp/util/AmqpAdapterMetrics.java b/adapter-amqp/src/main/java/io/nosqlbench/adapter/amqp/util/AmqpAdapterMetrics.java index c816ad18a..9f5f00a78 100644 --- a/adapter-amqp/src/main/java/io/nosqlbench/adapter/amqp/util/AmqpAdapterMetrics.java +++ b/adapter-amqp/src/main/java/io/nosqlbench/adapter/amqp/util/AmqpAdapterMetrics.java @@ -20,6 +20,7 @@ import com.codahale.metrics.Counter; import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.adapter.amqp.dispensers.AmqpBaseOpDispenser; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabeledElement; import io.nosqlbench.nb.api.labels.NBLabels; import org.apache.logging.log4j.LogManager; @@ -55,17 +56,45 @@ public class AmqpAdapterMetrics { public void initS4JAdapterInstrumentation() { // Histogram metrics - messageSizeHistogram = amqpBaseOpDispenser.create().histogram("message_size"); + messageSizeHistogram = amqpBaseOpDispenser.create().histogram( + "amqp_message_size", + MetricCategory.Driver, + "AMQP message size" + ); // Timer metrics - bindTimer = amqpBaseOpDispenser.create().timer("bind"); - executeTimer = amqpBaseOpDispenser.create().timer("execute"); + bindTimer = amqpBaseOpDispenser.create().timer( + "amqp_bind", + MetricCategory.Driver, + "AMQP bind timer" + ); + executeTimer = amqpBaseOpDispenser.create().timer( + "amqp_execute", + MetricCategory.Driver, + "AMQP execute timer" + ); // End-to-end metrics // Latency - e2eMsgProcLatencyHistogram = amqpBaseOpDispenser.create().histogram("e2e_msg_latency"); + e2eMsgProcLatencyHistogram = amqpBaseOpDispenser.create().histogram( + "amqp_e2e_msg_latency", + MetricCategory.Driver, + "AMQP end-to-end message processing latency" + ); // Error metrics - msgErrOutOfSeqCounter = amqpBaseOpDispenser.create().counter("err_msg_oos"); - msgErrLossCounter = amqpBaseOpDispenser.create().counter("err_msg_loss"); - msgErrDuplicateCounter = amqpBaseOpDispenser.create().counter("err_msg_dup"); + msgErrOutOfSeqCounter = amqpBaseOpDispenser.create().counter( + "amqp_err_msg_oos", + MetricCategory.Driver, + "AMQP out-of-sequence error count" + ); + msgErrLossCounter = amqpBaseOpDispenser.create().counter( + "amqp_err_msg_loss", + MetricCategory.Driver, + "AMQP lost message error count" + ); + msgErrDuplicateCounter = amqpBaseOpDispenser.create().counter( + "err_msg_dup", + MetricCategory.Driver, + "AMQP duplicate message count" + ); } public Timer getBindTimer() { return bindTimer; } diff --git a/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/Cqld4Space.java b/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/Cqld4Space.java index 96f2faa27..1ba0c73ff 100644 --- a/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/Cqld4Space.java +++ b/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/Cqld4Space.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.Logger; import javax.net.ssl.SSLContext; import java.io.IOException; +import java.io.InputStream; import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; @@ -97,8 +98,18 @@ public class Cqld4Space implements AutoCloseable { // int port = cfg.getOptional(int.class, "port").orElse(9042); Optional scb = cfg.getOptional(String.class, "secureconnectbundle", "scb"); - scb.flatMap(s -> NBIO.all().pathname(s).first().map(Content::getInputStream)) - .map(builder::withCloudSecureConnectBundle); + + if (scb.isPresent()) { + Optional stream = + scb.flatMap(s -> NBIO.all().pathname(s).first().map(Content::getInputStream)); + if (stream.isPresent()) { + stream.map(builder::withCloudSecureConnectBundle); + } else { + String error = String.format("Unable to load Secure Connect Bundle from path %s", scb.get()); + logger.error(error); + throw new RuntimeException(error); + } + } Optional> contactPointsOption = cfg .getOptional("host", "hosts") diff --git a/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/opdispensers/Cqld4BaseOpDispenser.java b/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/opdispensers/Cqld4BaseOpDispenser.java index 770a0eff9..1b9e17551 100644 --- a/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/opdispensers/Cqld4BaseOpDispenser.java +++ b/adapter-cqld4/src/main/java/io/nosqlbench/adapter/cqld4/opdispensers/Cqld4BaseOpDispenser.java @@ -31,6 +31,7 @@ import io.nosqlbench.adapters.api.activityimpl.BaseOpDispenser; import io.nosqlbench.adapters.api.activityimpl.uniform.DriverAdapter; import io.nosqlbench.adapters.api.templating.ParsedOp; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -57,9 +58,24 @@ public abstract class Cqld4BaseOpDispenser extends BaseOpDispenser>: ListSizedHashed(HashRange(3,7),ToInt())); list: ListStepped(NumberNameToString(),NumberNameToString()) map: MapSized(3, Combinations('A-Z;0-9'), NumberNameToString(), ToString()); @@ -47,11 +47,7 @@ blocks: ops: create-keyspace-ks_00001: simple: | - create keyspace ks_00001 - with replication = {'class': 'SimpleStrategy', - 'replication_factor': 'TEMPLATE(rf:1)' - } - and durable writes = false; + create keyspace IF NOT EXISTS ks_00001 with replication = {'class': 'SimpleStrategy','replication_factor': 'TEMPLATE(rf,1)'}; schema-tables: params: timeout: 60.0 @@ -150,7 +146,7 @@ blocks: timeout: 30.0 ops: scan-tb_00001-table-tb_00001: - prepared: "select * from ks_00001.tb_00001\nwhere \n LIMIT 10;\n" + prepared: "select * from ks_00001.tb_00001 LIMIT 10;" ratio: 1 main-update: params: @@ -159,7 +155,7 @@ blocks: update-tb_00001-table-tb_00001: prepared: | update ks_00001.tb_00001 - set col_00001={text}, col_00002={bigint}, col_00003={blob}, col_00004={boolean}, col_00005={date}, col_00006={decimal}, col_00007={double}, col_00008={duration}, col_00009={float}, col_00010={frozen>}, col_00011={list}, col_00012={map}, col_00013={set}, col_00014={smallint}, col_00015={text}, col_00016={time}, col_00017={timestamp}, col_00018={timeuuid}, col_00019={tinyint}, col_00020={uuid}, col_00021={text}, col_00022={varint}, col_00023={ascii}, col_00024={inet}, col_00025={int} + set col_00002={bigint}, col_00003={blob}, col_00004={boolean}, col_00005={date}, col_00006={decimal}, col_00007={double}, col_00008={duration}, col_00009={float}, col_00010={frozen>}, col_00011={list}, col_00012={map}, col_00013={set}, col_00014={smallint}, col_00015={text}, col_00016={time}, col_00017={timestamp}, col_00018={timeuuid}, col_00019={tinyint}, col_00020={uuid}, col_00021={text}, col_00022={varint}, col_00023={ascii}, col_00024={inet}, col_00025={int} where col_00001={text}; ratio: 1 params: diff --git a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagOpDispenser.java b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagOpDispenser.java index 203604981..6c3c72bc4 100644 --- a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagOpDispenser.java +++ b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagOpDispenser.java @@ -23,7 +23,7 @@ import io.nosqlbench.nb.api.config.standard.NBConfigModel; import io.nosqlbench.nb.api.config.standard.NBConfiguration; import io.nosqlbench.nb.api.config.standard.NBReconfigurable; import io.nosqlbench.nb.api.components.core.NBParentComponentInjection; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; import io.nosqlbench.nb.annotations.ServiceSelector; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java index a41b04898..7d0a8928a 100644 --- a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java +++ b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java @@ -17,7 +17,7 @@ package io.nosqlbench.adapter.diag; import io.nosqlbench.engine.api.activityapi.core.ActivityDefObserver; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import io.nosqlbench.nb.api.config.standard.ConfigModel; import io.nosqlbench.nb.api.config.standard.NBConfigModel; diff --git a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_diagrate.java b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_diagrate.java index 37c50367d..eef207739 100644 --- a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_diagrate.java +++ b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_diagrate.java @@ -16,9 +16,9 @@ package io.nosqlbench.adapter.diag.optasks; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiters; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiters; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.nb.api.config.standard.*; diff --git a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_gauge.java b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_gauge.java index 16cf3c0bc..7a47dafb0 100644 --- a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_gauge.java +++ b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/optasks/DiagTask_gauge.java @@ -21,6 +21,7 @@ import io.nosqlbench.nb.api.config.standard.ConfigModel; import io.nosqlbench.nb.api.config.standard.NBConfigModel; import io.nosqlbench.nb.api.config.standard.NBConfiguration; import io.nosqlbench.nb.api.config.standard.Param; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.components.core.NBParentComponentInjection; import io.nosqlbench.nb.annotations.Service; @@ -136,7 +137,12 @@ public class DiagTask_gauge extends BaseDiagTask implements Gauge, NBPar } logger.info("Registering gauge for diag task with labels:" + getParentLabels().getLabels() + " label:" + label); - this.gauge=parent.create().gauge(label,() -> this.sampleValue); + this.gauge=parent.create().gauge( + label, + () -> this.sampleValue, + MetricCategory.Verification, + "a diagnostic gauge for the purposes of testing " + description() + ); } @Override diff --git a/adapter-dynamodb/pom.xml b/adapter-dynamodb/pom.xml index d064e1c0f..516752909 100644 --- a/adapter-dynamodb/pom.xml +++ b/adapter-dynamodb/pom.xml @@ -43,7 +43,7 @@ com.amazonaws aws-java-sdk-dynamodb - 1.12.595 + 1.12.635 diff --git a/adapter-http/pom.xml b/adapter-http/pom.xml index 6b854efc2..5c43ad929 100644 --- a/adapter-http/pom.xml +++ b/adapter-http/pom.xml @@ -72,7 +72,7 @@ io.swagger.core.v3 swagger-core - 2.2.19 + 2.2.20 diff --git a/adapter-http/src/main/java/io/nosqlbench/adapter/http/core/HttpMetrics.java b/adapter-http/src/main/java/io/nosqlbench/adapter/http/core/HttpMetrics.java index 7437bda6f..8cd4a301a 100644 --- a/adapter-http/src/main/java/io/nosqlbench/adapter/http/core/HttpMetrics.java +++ b/adapter-http/src/main/java/io/nosqlbench/adapter/http/core/HttpMetrics.java @@ -17,6 +17,7 @@ package io.nosqlbench.adapter.http.core; import com.codahale.metrics.Histogram; +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.components.core.NBComponent; @@ -29,7 +30,12 @@ public class HttpMetrics implements NBLabeledElement { public HttpMetrics(NBComponent parent, HttpSpace space) { this.parent = parent; this.space = space; - statusCodeHistogram = parent.create().histogram("statuscode",space.getHdrDigits()); + statusCodeHistogram = parent.create().histogram( + "statuscode", + space.getHdrDigits(), + MetricCategory.Payload, + "A histogram of status codes received by the HTTP client" + ); } public String getName() { diff --git a/adapter-kafka/pom.xml b/adapter-kafka/pom.xml index 03b4cac18..6336d46f2 100644 --- a/adapter-kafka/pom.xml +++ b/adapter-kafka/pom.xml @@ -34,7 +34,7 @@ - 3.6.0 + 3.6.1 @@ -55,7 +55,7 @@ org.apache.commons commons-lang3 - 3.13.0 + 3.14.0 diff --git a/adapter-kafka/src/main/java/io/nosqlbench/adapter/kafka/util/KafkaAdapterMetrics.java b/adapter-kafka/src/main/java/io/nosqlbench/adapter/kafka/util/KafkaAdapterMetrics.java index 81c489a71..a3198c0aa 100644 --- a/adapter-kafka/src/main/java/io/nosqlbench/adapter/kafka/util/KafkaAdapterMetrics.java +++ b/adapter-kafka/src/main/java/io/nosqlbench/adapter/kafka/util/KafkaAdapterMetrics.java @@ -20,8 +20,10 @@ import com.codahale.metrics.Counter; import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.adapter.kafka.dispensers.KafkaBaseOpDispenser; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabeledElement; import io.nosqlbench.nb.api.labels.NBLabels; +import org.apache.kafka.common.Metric; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -50,37 +52,70 @@ public class KafkaAdapterMetrics { public KafkaAdapterMetrics(final KafkaBaseOpDispenser kafkaBaseOpDispenser, final NBLabeledElement labeledParent) { this.kafkaBaseOpDispenser = kafkaBaseOpDispenser; - labels=labeledParent.getLabels().and("name",KafkaAdapterMetrics.class.getSimpleName()); + labels = labeledParent.getLabels().and("name", KafkaAdapterMetrics.class.getSimpleName()); } public void initS4JAdapterInstrumentation() { // Histogram metrics - messageSizeHistogram = kafkaBaseOpDispenser.create().histogram("message_size"); + messageSizeHistogram = kafkaBaseOpDispenser.create().histogram( + "kafka_message_size", + MetricCategory.Payload, + "kafka message size" + ); // Timer metrics bindTimer = this.kafkaBaseOpDispenser.create().timer( - "bind"); + "kafka_bind", + MetricCategory.Payload, + "kafka bind timer"); executeTimer = this.kafkaBaseOpDispenser.create().timer( - "execute"); + "kafka_execute", + MetricCategory.Payload, + "kafka execute timer" + ); // End-to-end metrics // Latency e2eMsgProcLatencyHistogram = - kafkaBaseOpDispenser.create().histogram("e2e_msg_latency"); + kafkaBaseOpDispenser.create().histogram( + "e2e_msg_latency", + MetricCategory.Driver, + "End-to-end kafka message latency" + ); // Error metrics msgErrOutOfSeqCounter = - kafkaBaseOpDispenser.create().counter("err_msg_oos"); + kafkaBaseOpDispenser.create().counter( + "kafka_err_msg_oos", + MetricCategory.Driver, + "kafka Out-of-sequence errors" + ); msgErrLossCounter = - kafkaBaseOpDispenser.create().counter("err_msg_loss"); + kafkaBaseOpDispenser.create().counter( + "kafka_err_msg_loss", + MetricCategory.Driver, + "kafka message loss errors" + ); msgErrDuplicateCounter = - kafkaBaseOpDispenser.create().counter( "err_msg_dup"); + kafkaBaseOpDispenser.create().counter( + "kafka_err_msg_dup", + MetricCategory.Driver, + "kafka duplicate message errors" + ); } - public Timer getBindTimer() { return bindTimer; } - public Timer getExecuteTimer() { return executeTimer; } - public Histogram getMessagesizeHistogram() { return messageSizeHistogram; } + public Timer getBindTimer() { + return bindTimer; + } + + public Timer getExecuteTimer() { + return executeTimer; + } + + public Histogram getMessagesizeHistogram() { + return messageSizeHistogram; + } public Counter getMsgErrOutOfSeqCounter() { return msgErrOutOfSeqCounter; diff --git a/adapter-pulsar/pom.xml b/adapter-pulsar/pom.xml index 1ab4ab899..217e6f44c 100644 --- a/adapter-pulsar/pom.xml +++ b/adapter-pulsar/pom.xml @@ -34,7 +34,7 @@ - 3.1.1 + 3.1.2 @@ -61,7 +61,7 @@ org.apache.commons commons-lang3 - 3.13.0 + 3.14.0 diff --git a/adapter-pulsar/src/main/java/io/nosqlbench/adapter/pulsar/util/PulsarAdapterMetrics.java b/adapter-pulsar/src/main/java/io/nosqlbench/adapter/pulsar/util/PulsarAdapterMetrics.java index ae7bf6fb8..1e2d04c8a 100644 --- a/adapter-pulsar/src/main/java/io/nosqlbench/adapter/pulsar/util/PulsarAdapterMetrics.java +++ b/adapter-pulsar/src/main/java/io/nosqlbench/adapter/pulsar/util/PulsarAdapterMetrics.java @@ -20,6 +20,7 @@ import com.codahale.metrics.Counter; import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.adapter.pulsar.dispensers.PulsarBaseOpDispenser; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.pulsar.client.api.Consumer; @@ -66,22 +67,52 @@ public class PulsarAdapterMetrics { public void initPulsarAdapterInstrumentation() { // Counter metrics msgErrOutOfSeqCounter = - pulsarBaseOpDispenser.create().counter("err_msg_oos"); + pulsarBaseOpDispenser.create().counter("pulsar_err_msg_oos", + MetricCategory.Driver, + "pulsar out-of-sequence error counter" + ); msgErrLossCounter = - pulsarBaseOpDispenser.create().counter("err_msg_loss"); + pulsarBaseOpDispenser.create().counter("pulsar_err_msg_loss", + MetricCategory.Driver, + "pulsar lost message error counter" + ); msgErrDuplicateCounter = - pulsarBaseOpDispenser.create().counter("err_msg_dup"); + pulsarBaseOpDispenser.create().counter("pulsar_err_msg_dup", + MetricCategory.Driver, + "pulsar duplicate message error counter" + ); // Histogram metrics - messageSizeHistogram = pulsarBaseOpDispenser.create().histogram("message_size"); - e2eMsgProcLatencyHistogram = pulsarBaseOpDispenser.create().histogram("e2e_msg_latency"); - payloadRttHistogram = pulsarBaseOpDispenser.create().histogram("payload_rtt"); + messageSizeHistogram = pulsarBaseOpDispenser.create().histogram("pulsar_message_size", + MetricCategory.Driver, + "pulsar message size" + ); + e2eMsgProcLatencyHistogram = pulsarBaseOpDispenser.create().histogram("pulsar_e2e_msg_latency", + MetricCategory.Driver, + "pulsar end-to-end message latency" + ); + payloadRttHistogram = pulsarBaseOpDispenser.create().histogram("pulsar_payload_rtt", + MetricCategory.Driver, + "pulsar payload round-trip-time" + ); // Timer metrics - bindTimer = pulsarBaseOpDispenser.create().timer("bind"); - executeTimer = pulsarBaseOpDispenser.create().timer("execute"); - createTransactionTimer = pulsarBaseOpDispenser.create().timer("create_transaction"); - commitTransactionTimer = pulsarBaseOpDispenser.create().timer("commit_transaction"); + bindTimer = pulsarBaseOpDispenser.create().timer("pulsar_bind", + MetricCategory.Driver, + "pulsar bind timer" + ); + executeTimer = pulsarBaseOpDispenser.create().timer("pulsar_execute", + MetricCategory.Driver, + "pulsar execution timer" + ); + createTransactionTimer = pulsarBaseOpDispenser.create().timer("pulsar_create_transaction", + MetricCategory.Driver, + "pulsar create transaction timer" + ); + commitTransactionTimer = pulsarBaseOpDispenser.create().timer("pulsar_commit_transaction", + MetricCategory.Driver, + "pulsar commit transaction timer" + ); } public Counter getMsgErrOutOfSeqCounter() { @@ -154,12 +185,36 @@ public class PulsarAdapterMetrics { public void registerProducerApiMetrics(final Producer producer) { - pulsarBaseOpDispenser.create().gauge("total_bytes_sent", PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalBytesSent() + s.getNumBytesSent())); - pulsarBaseOpDispenser.create().gauge("total_msg_sent", PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalMsgsSent() + s.getNumMsgsSent())); - pulsarBaseOpDispenser.create().gauge("total_send_failed", PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalSendFailed() + s.getNumSendFailed())); - pulsarBaseOpDispenser.create().gauge("total_ack_received", PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalAcksReceived() + s.getNumAcksReceived())); - pulsarBaseOpDispenser.create().gauge("send_bytes_rate", PulsarAdapterMetrics.producerSafeExtractMetric(producer, ProducerStats::getSendBytesRate)); - pulsarBaseOpDispenser.create().gauge("send_msg_rate", PulsarAdapterMetrics.producerSafeExtractMetric(producer, ProducerStats::getSendMsgsRate)); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_bytes_sent", + PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalBytesSent() + s.getNumBytesSent()), + MetricCategory.Driver, + "pulsar total bytes sent" + ); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_msg_sent", + PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalMsgsSent() + s.getNumMsgsSent()), + MetricCategory.Driver, + "pulsar total message sent" + ); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_send_failed", + PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalSendFailed() + s.getNumSendFailed()), + MetricCategory.Driver, + "pulsar message send failures" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_total_ack_received", PulsarAdapterMetrics.producerSafeExtractMetric(producer, s -> (double) s.getTotalAcksReceived() + s.getNumAcksReceived()), + MetricCategory.Driver, + "pulsar total acknowledgements received" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_send_bytes_rate", PulsarAdapterMetrics.producerSafeExtractMetric(producer, ProducerStats::getSendBytesRate), + MetricCategory.Driver, + "pulsar rate of bytes sent" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_send_msg_rate", PulsarAdapterMetrics.producerSafeExtractMetric(producer, ProducerStats::getSendMsgsRate), + MetricCategory.Driver, + "pulsar rate of messages sent" + ); } @@ -193,17 +248,38 @@ public class PulsarAdapterMetrics { public void registerConsumerApiMetrics(final Consumer consumer, final String pulsarApiMetricsPrefix) { - pulsarBaseOpDispenser.create().gauge("total_bytes_recv", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalBytesReceived() + s.getNumBytesReceived())); - pulsarBaseOpDispenser.create().gauge("total_msg_recv", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalMsgsReceived() + s.getNumMsgsReceived())); - pulsarBaseOpDispenser.create().gauge("total_recv_failed", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalReceivedFailed() + s.getNumReceiveFailed())); - pulsarBaseOpDispenser.create().gauge("total_acks_sent", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalAcksSent() + s.getNumAcksSent())); - pulsarBaseOpDispenser.create().gauge("recv_bytes_rate", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getRateBytesReceived())); - pulsarBaseOpDispenser.create().gauge("recv_msg_rate", - PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getRateMsgsReceived())); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_bytes_recv", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalBytesReceived() + s.getNumBytesReceived()), + MetricCategory.Driver, + "pulsar total bytes received" + ); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_msg_recv", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalMsgsReceived() + s.getNumMsgsReceived()), + MetricCategory.Driver, + "pulsar total messages received" + ); + pulsarBaseOpDispenser.create().gauge( + "pulsar_total_recv_failed", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalReceivedFailed() + s.getNumReceiveFailed()), + MetricCategory.Driver, + "pulsar total receive failures" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_total_acks_sent", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getTotalAcksSent() + s.getNumAcksSent()), + MetricCategory.Driver, + "pulsar total acknowledgements sent" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_recv_bytes_rate", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getRateBytesReceived()), + MetricCategory.Driver, + "pulsar rate of bytes received" + ); + pulsarBaseOpDispenser.create().gauge("pulsar_recv_msg_rate", + PulsarAdapterMetrics.consumerSafeExtractMetric(consumer, s -> (double) s.getRateMsgsReceived()), + MetricCategory.Driver, + "pulsar rate of message received" + ); } } diff --git a/adapter-s4j/src/main/java/io/nosqlbench/adapter/s4j/util/S4JAdapterMetrics.java b/adapter-s4j/src/main/java/io/nosqlbench/adapter/s4j/util/S4JAdapterMetrics.java index cb6205be9..0f049b00a 100644 --- a/adapter-s4j/src/main/java/io/nosqlbench/adapter/s4j/util/S4JAdapterMetrics.java +++ b/adapter-s4j/src/main/java/io/nosqlbench/adapter/s4j/util/S4JAdapterMetrics.java @@ -19,6 +19,7 @@ package io.nosqlbench.adapter.s4j.util; import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.adapter.s4j.dispensers.S4JBaseOpDispenser; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -38,11 +39,23 @@ public class S4JAdapterMetrics { public void initS4JAdapterInstrumentation() { // Histogram metrics - this.messageSizeHistogram = s4jBaseOpDispenser.create().histogram("message_size"); + this.messageSizeHistogram = s4jBaseOpDispenser.create().histogram( + "s4j_message_size", + MetricCategory.Driver, + "S4J message size" + ); // Timer metrics - this.bindTimer = s4jBaseOpDispenser.create().timer("bind"); - this.executeTimer = s4jBaseOpDispenser.create().timer("execute"); + this.bindTimer = s4jBaseOpDispenser.create().timer( + "s4j_bind", + MetricCategory.Driver, + "S4J bind timer" + ); + this.executeTimer = s4jBaseOpDispenser.create().timer( + "s4j_execute", + MetricCategory.Driver, + "S4j execute timer" + ); } public Timer getBindTimer() { return bindTimer; } diff --git a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDef.java b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDef.java index e17834f6c..7dcce46dc 100644 --- a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDef.java +++ b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDef.java @@ -43,7 +43,7 @@ public class OpDef extends OpTemplate { @Override public String getName() { - return block.getName() + "__" + rawOpDef.getName(); + return rawOpDef.getName(); } @Override @@ -100,7 +100,7 @@ public class OpDef extends OpTemplate { private LinkedHashMap composeTags() { LinkedHashMap tagsWithName = new LinkedHashMap<>(new MultiMapLookup<>(rawOpDef.getTags(), block.getTags())); tagsWithName.put("block",block.getName()); - tagsWithName.put("name",getName()); + tagsWithName.put("name",this.rawOpDef.getName()); tagsWithName.put("op",this.rawOpDef.getName()); return tagsWithName; } diff --git a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/BaseOpDispenser.java b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/BaseOpDispenser.java index f514c240d..8ed2d3932 100644 --- a/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/BaseOpDispenser.java +++ b/adapters-api/src/main/java/io/nosqlbench/adapters/api/activityimpl/BaseOpDispenser.java @@ -23,6 +23,7 @@ import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.Op; import io.nosqlbench.adapters.api.evalctx.*; import io.nosqlbench.adapters.api.metrics.ThreadLocalNamedTimers; import io.nosqlbench.adapters.api.templating.ParsedOp; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.errors.OpConfigError; import io.nosqlbench.nb.api.components.core.NBBaseComponent; @@ -99,7 +100,12 @@ public abstract class BaseOpDispenser extends NBBaseComponent i verifiers = configureVerifiers(op); this._verifier = CycleFunctions.of((a, b) -> a && b, verifiers, true); this.tlVerifier = ThreadLocal.withInitial(_verifier::newInstance); - this.verifierTimer = create().timer("verifier",3); + this.verifierTimer = create().timer( + "verifier", + 3, + MetricCategory.Verification, + "Time verifier execution, if any." + ); } private CycleFunction cloneVerifiers() { @@ -126,7 +132,6 @@ public abstract class BaseOpDispenser extends NBBaseComponent i Binding variables = new Binding(); Map initBlocks = op.getTemplateMap().takeAsNamedTemplates(VERIFIER_INIT); - List> verifierInitFunctions = new ArrayList<>(); try { initBlocks.forEach((initName, stringTemplate) -> { GroovyCycleFunction initFunction = @@ -180,8 +185,18 @@ public abstract class BaseOpDispenser extends NBBaseComponent i if (this.instrument) { final int hdrDigits = pop.getStaticConfigOr("hdr_digits", 4); - successTimer = create().timer("successfor_"+getOpName(),hdrDigits); - errorTimer = create().timer("errorsfor_"+getOpName(),hdrDigits); + successTimer = create().timer( + "successfor_"+getOpName(), + hdrDigits, + MetricCategory.Core, + "Successful result timer for specific operation '" + pop.getName() + "'" + ); + errorTimer = create().timer( + "errorsfor_"+getOpName(), + hdrDigits, + MetricCategory.Core, + "Errored result timer for specific operation '" + pop.getName() + "'" + ); } } diff --git a/adapters-api/src/main/java/io/nosqlbench/adapters/api/metrics/ThreadLocalNamedTimers.java b/adapters-api/src/main/java/io/nosqlbench/adapters/api/metrics/ThreadLocalNamedTimers.java index 4051ec961..d7d3e1997 100644 --- a/adapters-api/src/main/java/io/nosqlbench/adapters/api/metrics/ThreadLocalNamedTimers.java +++ b/adapters-api/src/main/java/io/nosqlbench/adapters/api/metrics/ThreadLocalNamedTimers.java @@ -19,6 +19,7 @@ package io.nosqlbench.adapters.api.metrics; import com.codahale.metrics.Timer; import com.codahale.metrics.Timer.Context; import io.nosqlbench.adapters.api.templating.ParsedOp; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,7 +42,12 @@ public class ThreadLocalNamedTimers { public static void addTimer(final ParsedOp pop, final String name) { if (ThreadLocalNamedTimers.timers.containsKey("name")) ThreadLocalNamedTimers.logger.warn("A timer named '{}' was already defined and initialized.", name); - NBMetricTimer timer = pop.create().timer(name, 3); + NBMetricTimer timer = pop.create().timer( + name, + 3, + MetricCategory.User, + "User-provided metric, defined on op '" + pop.getName() + "'" + ); ThreadLocalNamedTimers.timers.put(name, timer); } diff --git a/adapters-api/src/main/resources/workload_definition/02_workload_structure.md b/adapters-api/src/main/resources/workload_definition/02_workload_structure.md index e4e2e24ec..ee6cc2792 100644 --- a/adapters-api/src/main/resources/workload_definition/02_workload_structure.md +++ b/adapters-api/src/main/resources/workload_definition/02_workload_structure.md @@ -375,24 +375,24 @@ blocks: [ { - "name": "namedblock1__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "namedblock1__op1", + "name": "op1", "block": "namedblock1", "op": "op1" } }, { - "name": "namedblock1__op2", + "name": "op2", "op": { "stmt": "insert into bar.table (a,b,c) values (1,2,3);", "type": "batch" }, "tags": { - "name": "namedblock1__op2", + "name": "op2", "block": "namedblock1", "op": "op2" } @@ -450,35 +450,35 @@ blocks: [ { - "name": "block1__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block1__op1", + "name": "op1", "block": "block1", "op": "op1" } }, { - "name": "block1__op2", + "name": "op2", "op": { "stmt": "insert into bar.table (a,b,c) values (1,2,3);", "type": "batch" }, "tags": { - "name": "block1__op2", + "name": "op2", "block": "block1", "op": "op2" } }, { - "name": "this_is_block_2__op3", + "name": "op3", "op": { "stmt": "select * from foo.table;" }, "tags": { - "name": "this_is_block_2__op3", + "name": "op3", "block": "this_is_block_2", "op": "op3" } @@ -523,12 +523,12 @@ blocks: [ { - "name": "myblock__stmt1", + "name": "stmt1", "op": { "stmt": "test op" }, "tags": { - "name": "myblock__stmt1", + "name": "stmt1", "block": "myblock", "op": "stmt1" } diff --git a/adapters-api/src/main/resources/workload_definition/04_op_template_basics.md b/adapters-api/src/main/resources/workload_definition/04_op_template_basics.md index 39b08b955..c65ad42e1 100644 --- a/adapters-api/src/main/resources/workload_definition/04_op_template_basics.md +++ b/adapters-api/src/main/resources/workload_definition/04_op_template_basics.md @@ -34,12 +34,12 @@ op: select * from bar.table; [ { - "name": "block0__stmt1", + "name": "stmt1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__stmt1", + "name": "stmt1", "block": "block0", "op": "stmt1" } @@ -73,12 +73,12 @@ ops: [ { - "name": "block0__stmt1", + "name": "stmt1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__stmt1", + "name": "stmt1", "block": "block0", "op": "stmt1" } @@ -117,12 +117,12 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" } @@ -160,12 +160,12 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" } @@ -199,12 +199,12 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" } @@ -241,12 +241,12 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "stmt": "select * from bar.table;" }, "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" } @@ -315,7 +315,7 @@ ops: "binding1": "NumberNameToString();" }, "description": "This is just an example operation", - "name": "block0__special-op-name", + "name": "special-op-name", "op": { "stmt": "select * from ks1.tb1;" }, @@ -324,7 +324,7 @@ ops: }, "tags": { "block": "block0", - "name": "block0__special-op-name", + "name": "special-op-name", "op": "special-op-name" } } @@ -414,7 +414,7 @@ blocks: "bindings": { "binding1": "NumberNameToString();" }, - "name": "block_named_fred__special-op-name", + "name": "special-op-name", "op": { "stmt": "select * from ks1.tb1;" }, @@ -424,7 +424,7 @@ blocks: "tags": { "block": "block_named_fred", "docleveltag": "is-tagging-everything", - "name": "block_named_fred__special-op-name", + "name": "special-op-name", "op": "special-op-name" } } diff --git a/adapters-api/src/main/resources/workload_definition/05_op_template_payloads.md b/adapters-api/src/main/resources/workload_definition/05_op_template_payloads.md index aaa0741e7..402edcbb0 100644 --- a/adapters-api/src/main/resources/workload_definition/05_op_template_payloads.md +++ b/adapters-api/src/main/resources/workload_definition/05_op_template_payloads.md @@ -39,14 +39,14 @@ ops: "cycle number '{{NumberNameToString}}'" [ { "tags": { - "name": "block0__stmt1", + "name": "stmt1", "block": "block0", "op": "stmt1" }, "op": { "stmt": "cycle number '{{NumberNameToString}}'" }, - "name": "block0__stmt1" + "name": "stmt1" } ] ``` @@ -83,25 +83,25 @@ ops: [ { "tags": { - "name": "block0__stmt1", + "name": "stmt1", "block": "block0", "op": "stmt1" }, "op": { "stmt": "even cycle '{{NumberNameToString}}'" }, - "name": "block0__stmt1" + "name": "stmt1" }, { "tags": { - "name": "block0__stmt2", + "name": "stmt2", "block": "block0", "op": "stmt2" }, "op": { "stmt": "odd cycle '{{NumberNameToString}}'" }, - "name": "block0__stmt2" + "name": "stmt2" } ] ``` @@ -138,25 +138,25 @@ ops: [ { "tags": { - "name": "block0__myop1", + "name": "myop1", "block": "block0", "op": "myop1" }, "op": { "stmt": "even cycle '{{NumberNameToString}}'" }, - "name": "block0__myop1" + "name": "myop1" }, { "tags": { - "name": "block0__myop2", + "name": "myop2", "block": "block0", "op": "myop2" }, "op": { "stmt": "odd cycle '{{NumberNameToString}}'" }, - "name": "block0__myop2" + "name": "myop2" } ] ``` @@ -198,7 +198,7 @@ ops: [ { "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" }, @@ -206,7 +206,7 @@ ops: "opfield1": "opvalue1", "opfield2": "opvalue2" }, - "name": "block0__op1" + "name": "op1" } ] ``` @@ -250,7 +250,7 @@ ops: [ { "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" }, @@ -260,7 +260,7 @@ ops: "opvalue2" ] }, - "name": "block0__op1" + "name": "op1" } ] ``` @@ -310,7 +310,7 @@ ops: [ { "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" }, @@ -324,7 +324,7 @@ ops: "by_session_len/@1h" ] }, - "name": "block0__op1" + "name": "op1" } ] @@ -384,7 +384,7 @@ ops: "user_index": "Mod(1000L); ToString();" }, "tags": { - "name": "block0__op1", + "name": "op1", "block": "block0", "op": "op1" }, @@ -398,7 +398,7 @@ ops: "by_session_len/@1h" ] }, - "name": "block0__op1" + "name": "op1" } ] ``` diff --git a/adapters-api/src/main/resources/workload_definition/06_op_template_variations.md b/adapters-api/src/main/resources/workload_definition/06_op_template_variations.md index 8a87da83e..e01e37bf4 100644 --- a/adapters-api/src/main/resources/workload_definition/06_op_template_variations.md +++ b/adapters-api/src/main/resources/workload_definition/06_op_template_variations.md @@ -42,13 +42,13 @@ ops: [ { - "name": "block0__special-op-name", + "name": "special-op-name", "op": { "stmt": "select * from ks1.tb1;" }, "tags": { "block": "block0", - "name": "block0__special-op-name", + "name": "special-op-name", "op": "special-op-name" } } @@ -94,14 +94,14 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "field1": "select * from ks1.tb1;", "field2": "field 2 value" }, "tags": { "block": "block0", - "name": "block0__op1", + "name": "op1", "op": "op1" } } @@ -141,14 +141,14 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "field1": "select * from ks1.tb1;", "field2": "field 2 value" }, "tags": { "block": "block0", - "name": "block0__op1", + "name": "op1", "op": "op1" } } @@ -191,7 +191,7 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "field1": "select * from ks1.tb1;", "field2": "field 2 value" @@ -201,7 +201,7 @@ ops: }, "tags": { "block": "block0", - "name": "block0__op1", + "name": "op1", "op": "op1" } } @@ -244,7 +244,7 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "field1": "select * from ks1.tb1;", "field2": "field 2 value" @@ -254,7 +254,7 @@ ops: }, "tags": { "block": "block0", - "name": "block0__op1", + "name": "op1", "op": "op1" } } @@ -302,7 +302,7 @@ ops: [ { - "name": "block0__op1", + "name": "op1", "op": { "field1": "select * from ks1.tb1;", "field2": "field 2 value" @@ -312,7 +312,7 @@ ops: }, "tags": { "block": "block0", - "name": "block0__op1", + "name": "op1", "op": "op1" } } @@ -351,14 +351,14 @@ ops: "my test op" "pname": "pvalue" }, "tags": { - "name": "block0__stmt1", + "name": "stmt1", "block": "block0", "op": "stmt1" }, "op": { "stmt": "my test op" }, - "name": "block0__stmt1" + "name": "stmt1" } ] ``` @@ -401,14 +401,14 @@ blocks: "pname": "pvalue" }, "tags": { - "name": "block1__stmt1", + "name": "stmt1", "block": "block1", "op": "stmt1" }, "op": { "stmt": "my test op" }, - "name": "block1__stmt1" + "name": "stmt1" } ] ``` @@ -458,14 +458,14 @@ blocks: "pname": "pvalue" }, "tags": { - "name": "block1__op1", + "name": "op1", "block": "block1", "op": "op1" }, "op": { "stmt": "my test op" }, - "name": "block1__op1" + "name": "op1" } ] ``` @@ -520,7 +520,7 @@ blocks: [ { "tags": { - "name": "block1__op1", + "name": "op1", "block": "block1", "op": "op1" }, @@ -530,7 +530,7 @@ blocks: "pname": "pvalue" } }, - "name": "block1__op1" + "name": "op1" } ] ``` @@ -583,18 +583,18 @@ blocks: [ { "tags": { - "name": "block1__op1", + "name": "op1", "block": "block1", "op": "op1" }, "op": { "stmt": "my test op" }, - "name": "block1__op1" + "name": "op1" }, { "tags": { - "name": "block1__params", + "name": "params", "block": "block1", "op": "params" }, @@ -602,7 +602,7 @@ blocks: "pname": "pvalue" }, - "name": "block1__params" + "name": "params" } ] ``` diff --git a/adapters-api/src/test/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDetailOverrideTest.java b/adapters-api/src/test/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDetailOverrideTest.java index 31d31efb1..21d4b5e63 100644 --- a/adapters-api/src/test/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDetailOverrideTest.java +++ b/adapters-api/src/test/java/io/nosqlbench/adapters/api/activityconfig/yaml/OpDetailOverrideTest.java @@ -43,32 +43,32 @@ public class OpDetailOverrideTest { OpsBlock doc1block0 = doc1.getBlocks().get(0); assertThat(doc1block0.getOps().size()).isEqualTo(1); OpTemplate s = doc1block0.getOps().get(0); - assertThat(s.getName()).isEqualTo("block0__stmt1"); + assertThat(s.getName()).isEqualTo("stmt1"); assertThat(s.getStmt()).contains("globalstatement1"); assertThat(s.getBindings()).hasSize(1); assertThat(s.getParams()).hasSize(1); - assertThat(s.getTags()).isEqualTo(Map.of("block","block0","global_tag1","tag value","name","block0__stmt1", "op","stmt1")); + assertThat(s.getTags()).isEqualTo(Map.of("block","block0","global_tag1","tag value","name","stmt1", "op","stmt1")); OpsBlock doc1block1 = doc1.getBlocks().get(1); List ops = doc1block1.getOps(); assertThat(ops).hasSize(4); s = ops.get(0); - assertThat(s.getName()).isEqualTo("testblock1__stmt1"); + assertThat(s.getName()).isEqualTo("stmt1"); assertThat(s.getStmt()).contains("astatement1"); - assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","testblock1__stmt1","op","stmt1")); + assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","stmt1","op","stmt1")); assertThat(s.getBindings()).hasSize(1); assertThat(s.getParams()).hasSize(1); s = ops.get(1); - assertThat(s.getName()).isEqualTo("testblock1__s2name"); + assertThat(s.getName()).isEqualTo("s2name"); assertThat(s.getStmt()).contains("s2statement data"); - assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","testblock1__s2name","op","s2name")); + assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","s2name","op","s2name")); assertThat(s.getBindings()).hasSize(1); assertThat(s.getParams()).hasSize(1); s = ops.get(2); - assertThat(s.getName()).isEqualTo("testblock1__s3"); + assertThat(s.getName()).isEqualTo("s3"); assertThat(s.getStmt()).contains("statement three"); assertThat(s.getTags()).containsEntry("tname1", "tval1"); assertThat(s.getTags()).containsEntry("global_tag1", "tag value"); @@ -76,9 +76,9 @@ public class OpDetailOverrideTest { assertThat(s.getParams()).hasSize(2); s = ops.get(3); - assertThat(s.getName()).isEqualTo("testblock1__s4"); + assertThat(s.getName()).isEqualTo("s4"); assertThat(s.getStmt()).contains("statement 4"); - assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","testblock1__s4","op","s4")); + assertThat(s.getTags()).isEqualTo(Map.of("block","testblock1","global_tag1","tag value","name","s4","op","s4")); assertThat(s.getBindings()).hasSize(1); assertThat(s.getParams()).hasSize(1); diff --git a/docsys/pom.xml b/docsys/pom.xml index be1e9778a..bdf08086e 100644 --- a/docsys/pom.xml +++ b/docsys/pom.xml @@ -22,7 +22,7 @@ docsys http://nosqlbench.io/ - 3.1.4 + 3.1.5 @@ -45,22 +45,22 @@ org.eclipse.jetty jetty-server - 11.0.18 + 11.0.19 org.eclipse.jetty jetty-servlets - 11.0.18 + 11.0.19 org.eclipse.jetty jetty-servlet - 11.0.18 + 11.0.19 org.eclipse.jetty jetty-rewrite - 11.0.18 + 11.0.19 @@ -94,13 +94,13 @@ org.glassfish.jersey.media jersey-media-json-jackson - 3.1.3 + 3.1.5 com.fasterxml.jackson.jaxrs jackson-jaxrs-json-provider - 2.16.0 + 2.16.1 diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java index afc422be1..353e4518d 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java @@ -45,7 +45,7 @@ import io.nosqlbench.engine.core.lifecycle.ExecutionResult; import io.nosqlbench.engine.core.lifecycle.process.NBCLIErrorHandler; import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader; import io.nosqlbench.engine.core.lifecycle.session.NBSession; -import io.nosqlbench.engine.core.logging.LoggerConfig; +import io.nosqlbench.engine.core.logging.NBLoggerConfig; import io.nosqlbench.engine.core.metadata.MarkdownFinder; import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.nb.annotations.ServiceSelector; @@ -68,13 +68,13 @@ import java.util.stream.Collectors; public class NBCLI implements Function, NBLabeledElement { private static Logger logger; - private static final LoggerConfig loggerConfig; + private static final NBLoggerConfig loggerConfig; private static final int EXIT_OK = 0; private static final int EXIT_WARNING = 1; private static final int EXIT_ERROR = 2; static { - loggerConfig = new LoggerConfig(); + loggerConfig = new NBLoggerConfig(); ConfigurationFactory.setConfigurationFactory(NBCLI.loggerConfig); } @@ -169,9 +169,9 @@ public class NBCLI implements Function, NBLabeledElement { .setAnsiEnabled(globalOptions.isEnableAnsi()) .setDedicatedVerificationLogger(globalOptions.isDedicatedVerificationLogger()) .activate(); - ConfigurationFactory.setConfigurationFactory(NBCLI.loggerConfig); + ConfigurationFactory.setConfigurationFactory(NBCLI.loggerConfig); // THIS should be the first time log4j2 is invoked! - NBCLI.logger = LogManager.getLogger("NBCLI"); + NBCLI.logger = LogManager.getLogger("NBCLI"); // TODO: Detect if the logger config was already initialized (error) NBCLI.loggerConfig.purgeOldFiles(LogManager.getLogger("SCENARIO")); if (NBCLI.logger.isInfoEnabled()) NBCLI.logger.info(() -> "Configured scenario log at " + NBCLI.loggerConfig.getLogfileLocation()); @@ -185,7 +185,7 @@ public class NBCLI implements Function, NBLabeledElement { } NBCLI.logger.info(() -> "Running NoSQLBench Version " + new VersionInfo().getVersion()); - NBCLI.logger.info(() -> "command-line: " + Arrays.stream(args).collect(Collectors.joining(" "))); + NBCLI.logger.info(() -> "command-line: " + String.join(" ", args)); NBCLI.logger.info(() -> "client-hardware: " + SystemId.getHostSummary()); @@ -216,7 +216,7 @@ public class NBCLI implements Function, NBLabeledElement { annotatorsConfig = gson.toJson(annotatorsConfigs); } - final NBCLIOptions options = new NBCLIOptions(args); + final NBCLIOptions options = new NBCLIOptions(args, Mode.ParseAllOptions); NBCLI.logger = LogManager.getLogger("NBCLI"); NBIO.addGlobalIncludes(options.wantsIncludes()); @@ -401,53 +401,64 @@ public class NBCLI implements Function, NBLabeledElement { // intentionally not shown for warn-only NBCLI.logger.info(() -> "console logging level is " + options.getConsoleLogLevel()); + Map props = Map.of( + "summary", options.getReportSummaryTo(), + "logsdir", options.getLogsDirectory().toString(), + "progress", options.getProgressSpec(), + "prompush_cache", "prompush_cache.txt", + "heartbeat", String.valueOf(options.wantsHeartbeatIntervalMs()) + ); /** * At this point, the command stream from the CLI should be handed into the session, and the session should * marshal and transform it for any scenario invocations directly. */ - NBSession session = new NBSession( - new NBBaseComponent(null, - options.getLabelMap() - .andDefault("jobname", "nosqlbench") - .andDefault("instance", "default") - ), - sessionName - ); - // TODO: Decide whether this should be part of ctor consistency - Map.of( - "summary", options.getReportSummaryTo(), - "logsdir", options.getLogsDirectory().toString(), - "progress", options.getProgressSpec() - ).forEach(session::setComponentProp); - options.wantsReportCsvTo().ifPresent(cfg -> { - MetricInstanceFilter filter = new MetricInstanceFilter(); - filter.addPattern(cfg.pattern); - new CsvReporter(session, Path.of(cfg.file), cfg.millis, filter); - }); + try ( + NBSession session = new NBSession( + new NBBaseComponent(null, + options.getLabelMap() + .andDefault("jobname", "nosqlbench") + .andDefault("instance", "default") + ), + sessionName, + props + )) { - options.wantsReportPromPushTo().ifPresent(cfg -> { - String[] words = cfg.split(","); - String uri; - long intervalMs = 10_000L; + options.wantsReportCsvTo().ifPresent(cfg -> { + MetricInstanceFilter filter = new MetricInstanceFilter(); + filter.addPattern(cfg.pattern); + new CsvReporter(session, Path.of(cfg.file), cfg.millis, filter); + }); - switch (words.length) { - case 2: - intervalMs = Unit.msFor(words[1]).orElseThrow(() -> new RuntimeException("can't parse '" + words[1] + "!")); - case 1: - uri = words[0]; - break; - default: - throw new RuntimeException("Unable to parse '" + cfg + "', must be in or ,ms form"); + options.wantsReportPromPushTo().ifPresent(cfg -> { + String[] words = cfg.split(","); + String uri; + long intervalMs = 10_000L; + + switch (words.length) { + case 2: + intervalMs = Unit.msFor(words[1]).orElseThrow(() -> new RuntimeException("can't parse '" + words[1] + "!")); + case 1: + uri = words[0]; + break; + default: + throw new RuntimeException("Unable to parse '" + cfg + "', must be in or ,ms form"); + } + session.create().pushReporter(uri, intervalMs, NBLabels.forKV()); + }); + + + ExecutionResult sessionResult = session.apply(options.getCommands()); + logger.info(sessionResult); + if (sessionResult.getException() instanceof RuntimeException rte) { + throw rte; + } else if (sessionResult.getException() instanceof Throwable t) { + throw new RuntimeException(t); } - session.create().pushReporter(uri, intervalMs, NBLabels.forKV()); - }); - - ExecutionResult sessionResult = session.apply(options.getCommands()); + return sessionResult.getStatus().code; + } // sessionResult.printSummary(System.out); - logger.info(sessionResult); - return sessionResult.getStatus().code; } @@ -464,10 +475,8 @@ public class NBCLI implements Function, NBLabeledElement { } basicHelp = basicHelp.replaceAll("PROG", this.commandName); return basicHelp; - } - @Override public NBLabels getLabels() { return labels; diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java index 9562b14f2..65cd3c61d 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java @@ -17,6 +17,7 @@ package io.nosqlbench.engine.cli; import io.nosqlbench.engine.api.scenarios.NBCLIScenarioPreprocessor; +import io.nosqlbench.engine.cli.atfiles.NBAtFile; import io.nosqlbench.engine.cmdstream.Cmd; import io.nosqlbench.engine.cmdstream.PathCanonicalizer; import io.nosqlbench.engine.core.lifecycle.session.CmdParser; @@ -62,6 +63,8 @@ public class NBCLIOptions { private static final String LABELSPEC = "--labelspec"; private static final String ANNOTATORS_CONFIG = "--annotators"; + private static final String HEARTBEAT_MILLIS = "--heartbeat-millis"; + // Enabled if the TERM env var is provided private static final String ANSI = "--ansi"; @@ -118,7 +121,7 @@ public class NBCLIOptions { private static final String GRAPHITE_LOG_LEVEL = "--graphite-log-level"; private static final String REPORT_CSV_TO = "--report-csv-to"; private static final String REPORT_SUMMARY_TO = "--report-summary-to"; - private static final String REPORT_SUMMARY_TO_DEFAULT = "stdout:60,_LOGS_/_SESSION_.summary"; + private static final String REPORT_SUMMARY_TO_DEFAULT = "stdout:60,_LOGS_/_SESSION__summary.txt"; private static final String PROGRESS = "--progress"; private static final String WITH_LOGGING_PATTERN = "--with-logging-pattern"; private static final String LOGGING_PATTERN = "--logging-pattern"; @@ -201,6 +204,7 @@ public class NBCLIOptions { private String annotateLabelSpec = ""; private String metricsLabelSpec = ""; private String wantsToCatResource =""; + private long heartbeatIntervalMs = 10000; public boolean wantsLoggedMetrics() { return this.wantsConsoleMetrics; @@ -275,10 +279,6 @@ public class NBCLIOptions { ParseAllOptions } - public NBCLIOptions(final String[] args) { - this(args, Mode.ParseAllOptions); - } - public NBCLIOptions(final String[] args, final Mode mode) { switch (mode) { case ParseGlobalsOnly: @@ -293,6 +293,8 @@ public class NBCLIOptions { private LinkedList parseGlobalOptions(final String[] args) { LinkedList arglist = new LinkedList<>(Arrays.asList(args)); + NBAtFile.includeAt(arglist); + if (null == arglist.peekFirst()) { this.wantsBasicHelp = true; return arglist; @@ -300,8 +302,8 @@ public class NBCLIOptions { // Process --include and --statedir, separately first // regardless of position - LinkedList nonincludes = new LinkedList<>(); - while (null != arglist.peekFirst()) { + LinkedList everythingButIncludes = new LinkedList<>(); + while (!arglist.isEmpty()) { final String word = arglist.peekFirst(); if (word.startsWith("--") && word.contains("=")) { final String wordToSplit = arglist.removeFirst(); @@ -322,13 +324,13 @@ public class NBCLIOptions { this.wantsToIncludePaths.add(include); break; default: - nonincludes.addLast(arglist.removeFirst()); + everythingButIncludes.addLast(arglist.removeFirst()); } } this.statepath = NBStatePath.initialize(statedirs); - arglist = nonincludes; - nonincludes = new LinkedList<>(); + arglist = everythingButIncludes; + everythingButIncludes = new LinkedList<>(); // Now that statdirs is settled, auto load argsfile if it is present final NBCLIArgsFile argsfile = new NBCLIArgsFile(); @@ -501,11 +503,11 @@ public class NBCLIOptions { this.metricsLabelSpec = this.readWordOrThrow(arglist, "labels validator specification for metric labels"); break; default: - nonincludes.addLast(arglist.removeFirst()); + everythingButIncludes.addLast(arglist.removeFirst()); } } - return nonincludes; + return everythingButIncludes; } private void setLabels(String labeldata) { @@ -639,6 +641,11 @@ public class NBCLIOptions { arglist.removeFirst(); this.wantsToCatResource = this.readWordOrThrow(arglist, "workload to cat"); break; + case HEARTBEAT_MILLIS: + arglist.removeFirst(); + this.heartbeatIntervalMs = + Long.parseLong(this.readWordOrThrow(arglist, "heartbeat interval in ms")); + break; default: nonincludes.addLast(arglist.removeFirst()); } @@ -666,7 +673,21 @@ public class NBCLIOptions { """ .replaceAll("ARG", cmdParam) .replaceAll("PROG", "nb5") - .replaceAll("INCLUDES", String.join(",", wantsIncludes())); + .replaceAll("INCLUDES", String.join(",", wantsIncludes())) + ; + + final String debugMessage = """ + + After parsing all global options out of the command line, the remaining commands were found, + and mapped to valid options as describe above. This command stream was: + COMMANDSTREAM + """ + .replaceAll("COMMANDSTREAM", + String.join(" ",arglist)); + if (consoleLevel.isGreaterOrEqualTo(NBLogLevel.INFO)) { + System.out.println(debugMessage); + } + throw new BasicError(helpmsg); } @@ -736,6 +757,10 @@ public class NBCLIOptions { return this.wantsActivityTypes; } + public long wantsHeartbeatIntervalMs() { + return this.heartbeatIntervalMs; + } + public boolean wantsTopicalHelp() { return this.wantsActivityHelp; } diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java new file mode 100644 index 000000000..f47ce4dfa --- /dev/null +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2024 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.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 org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.snakeyaml.engine.v2.api.Load; +import org.snakeyaml.engine.v2.api.LoadSettings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * TODO: provide before and after atfile processing logs for diagnostics + * TODO:ERRORHANDLER Cannot invoke "Object.getClass()" because "scopeOfInclude" is null on file full of comments only + */ +public class NBAtFile { +// private final static Logger logger = LogManager.getLogger(NBAtFile.class); + + /** + * This will take a command line in raw form, which may include some arguments + * in the
{@code @filepath:datapath>format}
format. For each of these, + * the contents are expanded from the specified file, interior data path, and in + * the format requested. + *
    + *
  • {@code >= } formats maps as key=value
  • + *
  • {@code >: } formats maps as key:value
  • + *
  • {@code >-- } asserts each value starts with global option syntax (--)
  • + *
+ * + * @param processInPlace The linked list which is statefully modified. If you need + * an unmodified copy, then this is the responsibility of the caller. + * @return An updated list with all values expanded and injected + * @throws RuntimeException for any errors finding, traversing, parsing, or rendering values + */ + public static LinkedList includeAt(LinkedList processInPlace) { +// logger.trace("argv stream before processing: " + String.join("|",processInPlace)); + ListIterator iter = processInPlace.listIterator(); + while (iter.hasNext()) { + String spec = iter.next(); + if (spec.startsWith("@")) { + iter.previous(); + iter.remove(); + LinkedList spliceIn = includeAt(spec); + for (String s : spliceIn) { + iter.add(s); + } + } + } +// logger.trace("argv stream after atfile processing: "+ String.join("|",processInPlace)); + + return processInPlace; + } + private final static Pattern includePattern = + Pattern.compile("@(?[a-zA-Z_][a-zA-Z0-9_./]*)(:(?[a-zA-Z_][a-zA-Z0-9_./]*))?(>(?.+))?"); + + /** + * Format specifiers: + *
{@code
+     * -- means each value must come from a list, and that each line should contain a global argument
+     *    Specifically, each line must start with a --
+     *
+     * =  means each entry should be a key-value pair, and that it will be formatted as key=value on insertion
+     *
+     * :  means each entry should be a key-value pair, and that it will be formatted as key:value on insertion*
+     * }
+ * @param spec The include-at specifier, in the form of @file[:datapath] + * @return The linked list of arguments which is to be spliced into the caller's command list + */ + public static LinkedList includeAt(String spec) { + Matcher matcher = includePattern.matcher(spec); + if (matcher.matches()) { + String filepathSpec = matcher.group("filepath"); + String dataPathSpec = matcher.group("datapath"); + String formatSpec = matcher.group("formatter"); + String[] datapath = (dataPathSpec!=null && !dataPathSpec.isBlank()) ? dataPathSpec.split("(/|\\.)") : new String[] {}; + + String[] parts = filepathSpec.split("\\.",2); + if (parts.length==2 && !parts[1].toLowerCase().matches("yaml")) { + throw new RuntimeException("Only the yaml format and extension is supported for at-files." + + " You specified " + parts[1]); + } + + filepathSpec=(filepathSpec.endsWith(".yaml") ? filepathSpec : filepathSpec+".yaml"); + Path atPath = Path.of(filepathSpec); + + String argsdata = ""; + try { + argsdata = Files.readString(atPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + + NBAtFileFormats fmt = (formatSpec!=null) ? NBAtFileFormats.valueOfSymbol(formatSpec) : NBAtFileFormats.Default; + + Object scopeOfInclude = null; + try { + Load yaml = new Load(LoadSettings.builder().build()); + scopeOfInclude= yaml.loadFromString(argsdata); + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (datapath.length>0) { + if (scopeOfInclude instanceof Map mapdata) { + scopeOfInclude = traverseData(filepathSpec,(Map) mapdata,datapath); + } else { + throw new RuntimeException("You can not traverse a non-map object type with spec '" + spec + "'"); + } + } + LinkedList formatted = formatted(scopeOfInclude, fmt); + formatted=interposePath(formatted, atPath); + return formatted; + } else { + throw new RuntimeException("Unable to match at-file specifier: " + spec + " to pattern '" + includePattern.pattern() + "'"); + } + + } + + private static LinkedList interposePath(LinkedList formatted, Path atPath) { + Path parent = (atPath.getNameCount()==1 ? Path.of(".") : atPath.getParent()); + + ListIterator iter = formatted.listIterator(); + while (iter.hasNext()) { + String word = iter.next(); + String modified = word.replaceAll("\\$\\{DIR}",parent.toString()); + iter.remove(); + iter.add(modified); + } + return formatted; + } + + private static LinkedList formatted(Object scopeOfInclude, NBAtFileFormats fmt) { + LinkedList emitted = new LinkedList<>(); + if (scopeOfInclude instanceof Map map) { + Map included = new LinkedHashMap<>(); + map.forEach((k,v) -> { + included.put(k.toString(),v.toString()); + }); + included.forEach((k,v) -> { + fmt.validate(new String[]{k,v}); + String formatted = fmt.format(new String[]{k,v}); + emitted.add(formatted); + }); + } else if (scopeOfInclude instanceof List list) { + List included = new LinkedList<>(); + list.forEach(item -> included.add(item.toString())); + included.forEach(item -> { + fmt.validate(new String[]{item}); + String formatted = fmt.format(new String[]{item}); + emitted.add(formatted); + }); + } else { + throw new RuntimeException(scopeOfInclude.getClass().getCanonicalName() + " is not a valid data structure at-file inclusion."); + } + return emitted; + } + + private static Object traverseData(String sourceName, Map map, String[] datapath) { + String leaf = datapath[datapath.length-1]; + String[] traverse = Arrays.copyOfRange(datapath,0,datapath.length-1); + + for (String name : traverse) { + if (map.containsKey(name)) { + Object nextMap = map.get(name); + if (nextMap instanceof Map nextmap) { + map = (Map) nextmap; + } + } else { + throw new RuntimeException( + "Unable to traverse to '" + name + "' node " + + " in path '" + String.join("/",Arrays.asList(datapath) + + " in included data from source '" + sourceName + "'") + ); + } + } + + if (map.containsKey(leaf)) { + return (map.get(leaf)); + } else { + throw new RuntimeException("Unable to find data path '" + String.join("/",Arrays.asList(datapath)) + " in included data from source '" + sourceName + "'"); + } + + } +} diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFileFormats.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFileFormats.java new file mode 100644 index 000000000..4713be970 --- /dev/null +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFileFormats.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 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.engine.cli.atfiles; + +import java.util.Arrays; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public enum NBAtFileFormats { + Default("", s -> s.length <= 2, NBAtFileFormats::formatDefaultDashOption), + MapWithEquals("=", s -> s.length <= 2, NBAtFileFormats::formatNameEqualsValue), + MapWithColons(":", s -> s.length <= 2, NBAtFileFormats::formatNameColonValue), + GlobalWithDoubleDashes("--", s -> s.length <= 2 && s[0].startsWith("--"), NBAtFileFormats::formatDashDashOption); + + private static String formatNameEqualsValue(String[] strings) { + if (strings.length == 2) { + return strings[0] + "=" + strings[1]; + } else if (strings.length == 1 && strings[0].matches("[a-zA-Z_][a-zA-Z_]*[=:].*")) { + String[] parts = strings[0].split("[=:]", 2); + return parts[0]+"="+parts[1]; + } else { + throw new RuntimeException("Unable to match data for namedd value form: " + String.join("|",Arrays.asList(strings))); + } + } + + private static String formatNameColonValue(String[] strings) { + if (strings.length == 2) { + return strings[0] + ":" + strings[1]; + } else if (strings.length == 1 && strings[0].matches("[a-zA-Z_][a-zA-Z_]*[=:].*")) { + String[] parts = strings[0].split("[=:]", 2); + return parts[0]+":"+parts[1]; + } else { + throw new RuntimeException("Unable to match data for namedd value form: " + String.join("|",Arrays.asList(strings))); + } + } + + private static String formatDefaultDashOption(String[] strings) { + if (strings.length == 1 && strings[0].startsWith("--")) { + return formatDashDashOption(strings); + } else if (strings.length == 1 && strings[0].matches("[a-zA-Z_][a-zA-Z0-9._]*[=:].*")) { + return formatNameEqualsValue(strings); + } else if (strings.length == 2) { + return formatNameEqualsValue(strings); + } else if (strings.length==1) { + return strings[0]; + } else { + throw new RuntimeException("Unable to match data for --global option form: " + String.join("|",Arrays.asList(strings))); + } + } + + private final String spec; + private final Predicate validator; + private final Function formatter; + + NBAtFileFormats(String spec, Predicate validator, Function formatter) { + this.spec = spec; + this.validator = validator; + this.formatter = formatter; + } + + public static NBAtFileFormats valueOfSymbol(String s) { + for (NBAtFileFormats value : values()) { + if (value.spec.equals(s)) { + return value; + } + } + throw new RuntimeException("Format for spec '" + s + "' not found."); + } + + public void validate(String[] ary) { + if (!validator.test(ary)) { + throw new RuntimeException("With fmt '" + this.name() + "': input data not valid for format specifier '" + spec + "': data:[" + String.join("],[", Arrays.asList(ary)) + "]"); + } + } + + private final static Pattern doubleDashOption = Pattern.compile( + "^(?--[a-zA-Z][a-zA-Z0-9_.-]*)((=|\\s+)(?.+))?$" + ); + + private static String formatDashDashOption(String[] words) { + + if (words.length > 1) { + throw new RuntimeException("too many values for rendering --option in at-file: " + Arrays.asList(words)); + } + Matcher matcher = doubleDashOption.matcher(words[0]); + if (matcher.matches()) { + String optname = matcher.group("optname"); + String optvalue = matcher.group("optvalue"); + if (optvalue!=null) { + optvalue = (optvalue.matches("'.+'") ? optvalue.substring(1, optvalue.length() - 1) : optvalue); + optvalue = (optvalue.matches("\".+\"") ? optvalue.substring(1, optvalue.length() - 1) : optvalue); + return optname + "=" + optvalue; + } else { + return optname; + } + } else { + throw new RuntimeException("Unable to match option '" + words[0] + "' with pattern " + doubleDashOption.pattern()); + } + } + + public String format(String[] ary) { + return formatter.apply(ary); + } +} diff --git a/engine-cli/src/test/java/io/nosqlbench/engine/cli/BasicScriptBufferTest.java b/engine-cli/src/test/java/io/nosqlbench/engine/cli/BasicScriptBufferTest.java index 8569f3d92..22ecae70e 100644 --- a/engine-cli/src/test/java/io/nosqlbench/engine/cli/BasicScriptBufferTest.java +++ b/engine-cli/src/test/java/io/nosqlbench/engine/cli/BasicScriptBufferTest.java @@ -28,7 +28,7 @@ public class BasicScriptBufferTest { @Test public void testScriptInterpolation() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=script_to_interpolate", "parameter1=replaced"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=script_to_interpolate", "parameter1=replaced"}, NBCLIOptions.Mode.ParseAllOptions); BasicScriptBuffer b = new BasicScriptBuffer(); b.add(opts.getCommands().toArray(new Cmd[0])); @@ -40,7 +40,7 @@ public class BasicScriptBufferTest { @Test public void testAutoScriptCommand() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "script","path=acommand" }); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "script","path=acommand" }, NBCLIOptions.Mode.ParseAllOptions); BasicScriptBuffer b = new BasicScriptBuffer(); b.add(opts.getCommands().toArray(new Cmd[0])); String s = b.getParsedScript(); @@ -53,8 +53,8 @@ public class BasicScriptBufferTest { NBCLIOptions opts = new NBCLIOptions(new String[] { "script", "path=testscripts/printscript.js", - "param1=value1" - }); + "param1=value1", + }, NBCLIOptions.Mode.ParseAllOptions); BasicScriptBuffer b = new BasicScriptBuffer(); b.add(opts.getCommands().toArray(new Cmd[0])); String script = b.getParsedScript(); @@ -72,7 +72,7 @@ public class BasicScriptBufferTest { "path=testscripts/printparam.js", "paramname=another", "param2=andanother" - }); + }, NBCLIOptions.Mode.ParseAllOptions); BasicScriptBuffer b = new BasicScriptBuffer(); b.add(opts.getCommands().toArray(new Cmd[0])); String script = b.getParsedScript(); @@ -84,6 +84,6 @@ public class BasicScriptBufferTest { @Test public void shouldThrowErrorForInvalidWaitMillisOperand() { assertThatExceptionOfType(NumberFormatException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{ "waitmillis", "ms=noway" })); + .isThrownBy(() -> new NBCLIOptions(new String[]{ "waitmillis", "ms=noway" }, NBCLIOptions.Mode.ParseAllOptions)); } } diff --git a/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTemplateVarTest.java b/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTemplateVarTest.java index b9dc21ccf..95ac26821 100644 --- a/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTemplateVarTest.java +++ b/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTemplateVarTest.java @@ -30,7 +30,7 @@ public class NBCLIScenarioPreprocessorTemplateVarTest { @Test public void testMultipleOccurencesOfSameTemplateVar() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example_scenarios_templatevars" }); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example_scenarios_templatevars" }, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); cmds.forEach(System.out::println); @@ -47,7 +47,7 @@ public class NBCLIScenarioPreprocessorTemplateVarTest { @Test public void testThatCLIOverridesWorkForTemplateVars() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example_scenarios_templatevars", "tvar1=overridden" }); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "local/example_scenarios_templatevars", "tvar1=overridden" }, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); cmds.forEach(System.out::println); @@ -59,7 +59,7 @@ public class NBCLIScenarioPreprocessorTemplateVarTest { @Test public void testThatAdditionalCLIParamIsAdded() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"local/example_scenarios_templatevars", "tvar3=tval3"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"local/example_scenarios_templatevars", "tvar3=tval3"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); cmds.forEach(System.out::println); assertThat(cmds).hasSize(2); diff --git a/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTest.java b/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTest.java index d8c82867b..2715dfa23 100644 --- a/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTest.java +++ b/engine-cli/src/test/java/io/nosqlbench/engine/cli/NBCLIScenarioPreprocessorTest.java @@ -33,39 +33,39 @@ public class NBCLIScenarioPreprocessorTest { @Test public void providePathForScenario() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"local/example_scenarios"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"local/example_scenarios"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); } @Test public void defaultScenario() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); } @Test public void defaultScenarioWithParams() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "cycles=100"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "cycles=100"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.get(0).getArgValue("cycles")).isEqualTo("100"); } @Test public void namedScenario() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); } @Test public void namedScenarioWithParams() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles=100"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles=100"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.get(0).getArgValue("cycles")).containsOnlyOnce("100"); } @Test public void testThatSilentFinalParametersPersist() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "type=foo"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "type=foo"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.get(0).getArgValue("driver")).isEqualTo("stdout"); } @@ -73,25 +73,25 @@ public class NBCLIScenarioPreprocessorTest { @Test public void testThatVerboseFinalParameterThrowsError() { assertThatExceptionOfType(BasicError.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{"scenario_test", "workload=canttouchthis"})); + .isThrownBy(() -> new NBCLIOptions(new String[]{"scenario_test", "workload=canttouchthis"}, NBCLIOptions.Mode.ParseAllOptions)); } @Test public void testThatMissingScenarioNameThrowsError() { assertThatExceptionOfType(BasicError.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{"scenario_test", "missing_scenario"})); + .isThrownBy(() -> new NBCLIOptions(new String[]{"scenario_test", "missing_scenario"}, NBCLIOptions.Mode.ParseAllOptions)); } @Test public void testThatMultipleScenariosConcatenate() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "default", "default"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "default", "default"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isEqualTo(6); } @Test public void testThatTemplatesAreExpandedDefault() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "template_test"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "template_test"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isEqualTo(1); assertThat(cmds.get(0).getArgValue("driver")).isEqualTo("stdout"); @@ -101,10 +101,11 @@ public class NBCLIScenarioPreprocessorTest { @Test public void testThatTemplateParamsAreExpandedAndNotRemovedOverride() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "template_test", "cycles-test=20"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "template_test", "cycles-test=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isEqualTo(1); assertThat(cmds.get(0).getArgMap()).isEqualTo(Map.of( + "_impl","run", "alias", "with_template", "container", "template_test", "cycles", "20", @@ -118,10 +119,11 @@ public class NBCLIScenarioPreprocessorTest { @Test public void testThatUndefValuesAreUndefined() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles-test=20"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles-test=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isEqualTo(1); assertThat(cmds.get(0).getArgMap()).isEqualTo(Map.of( + "_impl", "run", "alias", "schema", "container", "schema_only", "cycles-test", "20", @@ -131,7 +133,7 @@ public class NBCLIScenarioPreprocessorTest { "tags", "block:\"schema.*\"", "workload", "scenario_test" )); - NBCLIOptions opts1 = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "doundef=20"}); + NBCLIOptions opts1 = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "doundef=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds1 = opts1.getCommands(); assertThat(cmds1.size()).isEqualTo(1); assertThat(cmds1.get(0).getArgValueOrNull("cycles-test")).isNull(); @@ -148,7 +150,7 @@ public class NBCLIScenarioPreprocessorTest { Path absolute = rel.toAbsolutePath(); assertThat(absolute).exists(); - NBCLIOptions opts = new NBCLIOptions(new String[]{absolute.toString(), "schema_only", "cycles-test=20"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{absolute.toString(), "schema_only", "cycles-test=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isGreaterThan(0); } @@ -159,7 +161,7 @@ public class NBCLIScenarioPreprocessorTest { //TODO: This might change? String urlScenario = "https://raw.githubusercontent.com/nosqlbench/nosqlbench/main/engine-cli/src/test/resources/activities/scenario_test.yaml"; - NBCLIOptions opts = new NBCLIOptions(new String[]{urlScenario, "schema_only", "cycles-test=20"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{urlScenario, "schema_only", "cycles-test=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isGreaterThan(0); } @@ -172,10 +174,11 @@ public class NBCLIScenarioPreprocessorTest { @Test public void testSubStepSelection() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles-test=20"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"scenario_test", "schema_only", "cycles-test=20"}, NBCLIOptions.Mode.ParseAllOptions); List cmds = opts.getCommands(); assertThat(cmds.size()).isEqualTo(1); assertThat(cmds.get(0).getArgMap()).isEqualTo(Map.of( + "_impl","run", "alias", "schema", "container", "schema_only", "cycles-test", "20", @@ -185,7 +188,7 @@ public class NBCLIScenarioPreprocessorTest { "tags", "block:\"schema.*\"", "workload", "scenario_test" )); - NBCLIOptions opts1 = new NBCLIOptions(new String[]{"local/example_scenarios", "namedsteps.one", "testparam1=testvalue2"}); + NBCLIOptions opts1 = new NBCLIOptions(new String[]{"local/example_scenarios", "namedsteps.one", "testparam1=testvalue2"}, NBCLIOptions.Mode.ParseAllOptions); List cmds1 = opts1.getCommands(); assertThat(cmds1.size()).isEqualTo(1); assertThat(cmds1.get(0).getArgValueOrNull("cycles_test")).isNull(); diff --git a/engine-cli/src/test/java/io/nosqlbench/engine/cli/TestNBCLIOptions.java b/engine-cli/src/test/java/io/nosqlbench/engine/cli/TestNBCLIOptions.java index 0aa51f465..1ecac7f4e 100644 --- a/engine-cli/src/test/java/io/nosqlbench/engine/cli/TestNBCLIOptions.java +++ b/engine-cli/src/test/java/io/nosqlbench/engine/cli/TestNBCLIOptions.java @@ -36,7 +36,7 @@ public class TestNBCLIOptions { @Test public void shouldRecognizeActivities() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"start", "foo=wan", "start", "bar=lan"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"start", "foo=wan", "start", "bar=lan"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.getCommands()).isNotNull(); assertThat(opts.getCommands().size()).isEqualTo(2); // assertThat(opts.getCommands().get(0).getArgs()).containsEntry("foo","wan"); @@ -46,7 +46,7 @@ public class TestNBCLIOptions { @Test public void shouldParseLongActivityForm() { NBCLIOptions opts = new NBCLIOptions(new String[]{"start", "param1=param2", "param3=param4", - "--report-graphite-to", "woot", "--report-interval", "23"}); + "--report-graphite-to", "woot", "--report-interval", "23"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.getCommands().size()).isEqualTo(1); // assertThat(opts.getCommands().get(0).getArgs()).containsEntry("param1","param2"); // assertThat(opts.getCommands().get(0).getArgs()).containsEntry("param3","param4"); @@ -56,21 +56,21 @@ public class TestNBCLIOptions { @Test public void shouldRecognizeShortVersion() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"--version"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"--version"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.isWantsVersionShort()).isTrue(); assertThat(opts.wantsVersionCoords()).isFalse(); } @Test public void shouldRecognizeVersion() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"--version-coords"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"--version-coords"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.isWantsVersionShort()).isFalse(); assertThat(opts.wantsVersionCoords()).isTrue(); } @Test public void shouldRecognizeScripts() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=ascriptaone", "script", "path=ascriptatwo"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=ascriptaone", "script", "path=ascriptatwo"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.getCommands()).isNotNull(); assertThat(opts.getCommands().size()).isEqualTo(2); assertThat(opts.getCommands().get(0).getCmdType()).isEqualTo(CmdType.script); @@ -81,41 +81,41 @@ public class TestNBCLIOptions { @Test public void shouldRecognizeWantsActivityTypes() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"--list-activity-types"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"--list-activity-types"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsActivityTypes()).isTrue(); - opts = new NBCLIOptions(new String[]{"--version"}); + opts = new NBCLIOptions(new String[]{"--version"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsActivityTypes()).isFalse(); - opts = new NBCLIOptions(new String[]{"--list-drivers"}); + opts = new NBCLIOptions(new String[]{"--list-drivers"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsActivityTypes()).isTrue(); } @Test public void shouldRecognizeWantsBasicHelp() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"--help"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"--help"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsBasicHelp()).isTrue(); - opts = new NBCLIOptions(new String[]{"--version"}); + opts = new NBCLIOptions(new String[]{"--version"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsTopicalHelp()).isFalse(); } @Test public void shouldRecognizeWantsActivityHelp() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"--help", "foo"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"--help", "foo"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsTopicalHelp()).isTrue(); assertThat(opts.wantsTopicalHelpFor()).isEqualTo("foo"); - opts = new NBCLIOptions(new String[]{"--version"}); + opts = new NBCLIOptions(new String[]{"--version"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsTopicalHelp()).isFalse(); } @Test public void shouldErrorSanelyWhenNoMatch() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{"unrecognizable command"})); + .isThrownBy(() -> new NBCLIOptions(new String[]{"unrecognizable command"}, NBCLIOptions.Mode.ParseAllOptions)); } @Test public void testShouldRecognizeScriptParams() { - NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=ascript", "param1=value1"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{"script", "path=ascript", "param1=value1"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.getCommands().size()).isEqualTo(1); Cmd cmd = opts.getCommands().get(0); assertThat(cmd.getArgs().size()).isEqualTo(2); @@ -127,33 +127,33 @@ public class TestNBCLIOptions { @Test public void testShouldErrorSanelyWhenScriptNameSkipped() { assertThatExceptionOfType(InvalidParameterException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{"script", "param1=value1"})); + .isThrownBy(() -> new NBCLIOptions(new String[]{"script", "param1=value1"}, NBCLIOptions.Mode.ParseAllOptions)); } @Disabled("semantic parsing is deferred until later") @Test public void testShouldErrorForMissingScriptName() { assertThatExceptionOfType(InvalidParameterException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{"script"})); + .isThrownBy(() -> new NBCLIOptions(new String[]{"script"}, NBCLIOptions.Mode.ParseAllOptions)); } - @Test - public void shouldRecognizeStartActivityCmd() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "start", "driver=woot" }); - List cmds = opts.getCommands(); - assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start); - - } - - @Test - public void shouldRecognizeRunActivityCmd() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "run", "driver=runwoot" }); - List cmds = opts.getCommands(); - assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.run); - - } +// @Test +// public void shouldRecognizeStartActivityCmd() { +// NBCLIOptions opts = new NBCLIOptions(new String[]{ "start", "driver=woot" }); +// List cmds = opts.getCommands(); +// assertThat(cmds).hasSize(1); +// assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start); +// +// } +// +// @Test +// public void shouldRecognizeRunActivityCmd() { +// NBCLIOptions opts = new NBCLIOptions(new String[]{ "run", "driver=runwoot" }); +// List cmds = opts.getCommands(); +// assertThat(cmds).hasSize(1); +// assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.run); +// +// } // @Test // public void shouldRecognizeStopActivityCmd() { @@ -169,49 +169,49 @@ public class TestNBCLIOptions { @Test public void shouldThrowErrorForInvalidStopActivity() { assertThatExceptionOfType(InvalidParameterException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{ "stop", "woah=woah" })); + .isThrownBy(() -> new NBCLIOptions(new String[]{ "stop", "woah=woah" }, NBCLIOptions.Mode.ParseAllOptions)); } - @Test - public void shouldRecognizeAwaitActivityCmd() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "await", "activity=awaitme" }); - List cmds = opts.getCommands(); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.await); - assertThat(cmds.get(0).getArgValue("activity")).isEqualTo("awaitme"); - - } +// @Test +// public void shouldRecognizeAwaitActivityCmd() { +// NBCLIOptions opts = new NBCLIOptions(new String[]{ "await", "activity=awaitme" }); +// List cmds = opts.getCommands(); +// assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.await); +// assertThat(cmds.get(0).getArgValue("activity")).isEqualTo("awaitme"); +// +// } @Disabled("semantic parsing is reserved until later after generalizing syntax") @Test public void shouldThrowErrorForInvalidAwaitActivity() { assertThatExceptionOfType(InvalidParameterException.class) - .isThrownBy(() -> new NBCLIOptions(new String[]{ "await", "awaitme=notvalid" })); + .isThrownBy(() -> new NBCLIOptions(new String[]{ "await", "awaitme=notvalid" }, NBCLIOptions.Mode.ParseAllOptions)); } - @Test - public void shouldRecognizewaitMillisCmd() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "waitmillis", "ms=23234" }); - List cmds = opts.getCommands(); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.waitMillis); - assertThat(cmds.get(0).getArgValue("ms")).isEqualTo("23234"); - - } +// @Test +// public void shouldRecognizewaitMillisCmd() { +// NBCLIOptions opts = new NBCLIOptions(new String[]{ "waitmillis", "ms=23234" }); +// List cmds = opts.getCommands(); +// assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.waitMillis); +// assertThat(cmds.get(0).getArgValue("ms")).isEqualTo("23234"); +// +// } @Test public void listWorkloads() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-workloads"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-workloads"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsWorkloadsList()).isTrue(); } @Test public void listScenarios() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-scenarios"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-scenarios"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsScenariosList()).isTrue(); } @Test public void listScripts() { - NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-scripts"}); + NBCLIOptions opts = new NBCLIOptions(new String[]{ "--list-scripts"}, NBCLIOptions.Mode.ParseAllOptions); assertThat(opts.wantsListScripts()).isTrue(); } diff --git a/engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java b/engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java new file mode 100644 index 000000000..c42744096 --- /dev/null +++ b/engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 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.engine.cli.atfiles; + +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class NBAtFileTest { + + @Test + public void testParseSimpleListDefaultFmt() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/simple_list.yaml"); + assertThat(strings).containsExactly("arg1","arg2","arg3"); + } + + @Test + public void testRelativizedPaths() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/relativized.yaml"); + assertThat(strings).containsExactly("--option1=src/test/resources/atfiles/value1"); + } + + @Test + public void testParseSimpleMapDefaultFmt() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/simple_map.yaml"); + assertThat(strings).containsExactly("arg1=val1","arg2=val2","arg3=val3"); + } + + @Test + public void testThatEmptyPathWithPathSpecifierIsInvalid() { + assertThrows(RuntimeException.class, () -> NBAtFile.includeAt("@src/test/resources/atfiles/simple_map.yaml:>:")); + } + + @Test + public void testParseSimpleMapWithFormatter() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/simple_map.yaml>:"); + assertThat(strings).containsExactly("arg1:val1","arg2:val2","arg3:val3"); + } + + + @Test + public void testParseSimpleMapSlashesOrDots() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/mixed_structures.yaml:amap/ofamap.ofalist"); + assertThat(strings).containsExactly("option1","option2"); + } + + @Test + public void testMapPathWithColonFormat() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/mixed_structures.yaml:amap/ofamap.ofentries>:"); + assertThat(strings).containsExactly("key1:value1","key2:value2"); + } + + @Test + public void testMapPathWithEqualsFormat() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/mixed_structures.yaml:amap/ofamap.ofentries>="); + assertThat(strings).containsExactly("key1=value1","key2=value2"); + } + + @Test + public void testGlobalOptionForms() { + LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/global_opts.yaml>--"); + assertThat(strings).containsExactly("--option1", "--option2=value2", "--option3=value3", "--option4=value4"); + } + +} diff --git a/engine-cli/src/test/resources/atfiles/global_opts.yaml b/engine-cli/src/test/resources/atfiles/global_opts.yaml new file mode 100644 index 000000000..f29e952c5 --- /dev/null +++ b/engine-cli/src/test/resources/atfiles/global_opts.yaml @@ -0,0 +1,4 @@ +- --option1 +- --option2=value2 +- --option3='value3' +- --option4="value4" diff --git a/engine-cli/src/test/resources/atfiles/mixed_structures.yaml b/engine-cli/src/test/resources/atfiles/mixed_structures.yaml new file mode 100644 index 000000000..a6321c4e8 --- /dev/null +++ b/engine-cli/src/test/resources/atfiles/mixed_structures.yaml @@ -0,0 +1,17 @@ +# This not valid for traversas, as only maps are supported till the leaf node +alist: + - ofmaps: + oflist: + - arg1 + - arg2 + +# This is valid, and both types are valid for reference at the leaf level +amap: + ofamap: + ofentries: + key1: value1 + key2: value2 + ofalist: + - option1 + - option2 + diff --git a/engine-cli/src/test/resources/atfiles/relativized.yaml b/engine-cli/src/test/resources/atfiles/relativized.yaml new file mode 100644 index 000000000..caf5f50d4 --- /dev/null +++ b/engine-cli/src/test/resources/atfiles/relativized.yaml @@ -0,0 +1 @@ +- --option1=${DIR}/value1 diff --git a/engine-cli/src/test/resources/atfiles/simple_list.yaml b/engine-cli/src/test/resources/atfiles/simple_list.yaml new file mode 100644 index 000000000..ae7e46c85 --- /dev/null +++ b/engine-cli/src/test/resources/atfiles/simple_list.yaml @@ -0,0 +1,3 @@ +- arg1 +- arg2 +- arg3 diff --git a/engine-cli/src/test/resources/atfiles/simple_map.yaml b/engine-cli/src/test/resources/atfiles/simple_map.yaml new file mode 100644 index 000000000..0c7de7a24 --- /dev/null +++ b/engine-cli/src/test/resources/atfiles/simple_map.yaml @@ -0,0 +1,3 @@ +arg1: val1 +arg2: val2 +arg3: val3 diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java index 9dcd74d71..5ab967f9f 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java @@ -25,7 +25,7 @@ import io.nosqlbench.engine.api.activityapi.cyclelog.filters.IntPredicateDispens import io.nosqlbench.engine.api.activityapi.errorhandling.ErrorMetrics; import io.nosqlbench.engine.api.activityapi.input.InputDispenser; import io.nosqlbench.engine.api.activityapi.output.OutputDispenser; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; import io.nosqlbench.engine.api.activityimpl.SimpleActivity; import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ActivityInstrumentation.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ActivityInstrumentation.java index 4a3ce0550..9e9d7a11c 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ActivityInstrumentation.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ActivityInstrumentation.java @@ -21,6 +21,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.engine.api.activityapi.planning.OpSequence; import io.nosqlbench.adapters.api.activityimpl.OpDispenser; +import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; import java.util.concurrent.Future; @@ -33,6 +34,16 @@ import java.util.concurrent.Future; */ public interface ActivityInstrumentation { + NBMetricGauge getOrCreateErrorsTotal(); + + NBMetricGauge getOrCreateErrorRate1m(); + + NBMetricGauge getOrCreateErrorRate5m(); + + NBMetricGauge getOrCreateErrorRate15m(); + + NBMetricGauge getOrCreateErrorRateTotal(); + /** * The input timer measures how long it takes to get the cycle value to be used for * an operation. @@ -77,8 +88,6 @@ public interface ActivityInstrumentation { */ Counter getOrCreatePendingOpCounter(); - Counter getOrCreateOpTrackerBlockedCounter(); - /** * The bind timer keeps track of how long it takes for NoSQLBench to create an instance * of an executable operation, given the cycle. This is usually done by using an diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ComponentActivityInstrumentation.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ComponentActivityInstrumentation.java index c371f8109..fd7ef7fd8 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ComponentActivityInstrumentation.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/core/ComponentActivityInstrumentation.java @@ -21,9 +21,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.Timer; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import io.nosqlbench.nb.api.engine.activityimpl.ParameterMap; -import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricCounter; -import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricHistogram; -import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; +import io.nosqlbench.nb.api.engine.metrics.instruments.*; public class ComponentActivityInstrumentation implements ActivityInstrumentation { @@ -41,7 +39,6 @@ public class ComponentActivityInstrumentation implements ActivityInstrumentation private NBMetricTimer cyclesServiceTimer; private NBMetricTimer cyclesResponseTimer; private NBMetricCounter pendingOpsCounter; - private NBMetricCounter opTrackerBlockedCounter; private NBMetricTimer bindTimer; private NBMetricTimer executeTimer; private NBMetricTimer resultTimer; @@ -49,6 +46,12 @@ public class ComponentActivityInstrumentation implements ActivityInstrumentation private NBMetricHistogram triesHistogram; private NBMetricTimer verifierTimer; + private NBMetricGauge errorRate1m; + private NBMetricGauge errorRate5m; + private NBMetricGauge errorRate15m; + private NBMetricGauge errorRateTotal; + private NBMetricGauge errorsTotal; + public ComponentActivityInstrumentation(final Activity activity) { this.activity = activity; def = activity.getActivityDef(); @@ -58,95 +61,225 @@ public class ComponentActivityInstrumentation implements ActivityInstrumentation } private void initMetrics() { - readInputTimer=activity.create().timer("read_input",this.hdrdigits); - stridesServiceTimer=activity.create().timer("strides",this.hdrdigits); + readInputTimer = activity.create().timer( + "read_input", + this.hdrdigits, + MetricCategory.Internals, + "measures overhead of acquiring a cycle range for an activity thread" + ); + stridesServiceTimer = activity.create().timer( + "strides", + this.hdrdigits, + MetricCategory.Core, + "service timer for a stride, which is the same as the op sequence length by default" + ); if (null != activity.getStrideLimiter()) { this.stridesResponseTimer = activity.create().timer( "strides" + ComponentActivityInstrumentation.RESPONSE_TIME, - hdrdigits + hdrdigits, + MetricCategory.Core, + "response timer for a stride, which is the same as the op sequence length by default;" + + " response timers include scheduling delays which occur when an activity falls behind its target rate" ); } this.cyclesServiceTimer = activity.create().timer( - "cycles"+ComponentActivityInstrumentation.SERVICE_TIME, - hdrdigits + "cycles" + ComponentActivityInstrumentation.SERVICE_TIME, + hdrdigits, + MetricCategory.Core, + "service timer for a cycle, including all of bind, execute, result and result_success;" + + " service timers measure the time between submitting a request and receiving the response" ); if (null != activity.getCycleLimiter()) { this.cyclesResponseTimer = activity.create().timer( "cycles" + ComponentActivityInstrumentation.RESPONSE_TIME, - hdrdigits + hdrdigits, + MetricCategory.Core, + "response timer for a cycle, including all of bind, execute, result and result_success;" + + " response timers include scheduling delays which occur when an activity falls behind its target rate" ); } - this.pendingOpsCounter=activity.create().counter("pending_ops"); - this.opTrackerBlockedCounter=activity.create().counter("optracker_blocked"); + this.pendingOpsCounter = activity.create().counter( + "pending_ops", + MetricCategory.Core, + "Indicate the number of operations which have been started, but which have not been completed." + + " This starts " + ); - this.bindTimer = activity.create().timer("bind",hdrdigits); - this.executeTimer = activity.create().timer("execute",hdrdigits); - this.resultTimer = activity.create().timer("result",hdrdigits); - this.resultSuccessTimer = activity.create().timer("result_success",hdrdigits); - this.triesHistogram = activity.create().histogram("tries",hdrdigits); - this.verifierTimer = activity.create().timer("verifier",hdrdigits); + this.bindTimer = activity.create().timer( + "bind", + hdrdigits, + MetricCategory.Core, + "Time the step within a cycle which binds generated data to an op template to synthesize an executable operation." + ); + + this.executeTimer = activity.create().timer( + "execute", + hdrdigits, + MetricCategory.Core, + "Time how long it takes to submit a request and receive a result, including reading the result in the client." + ); + this.resultTimer = activity.create().timer( + "result", + hdrdigits, + MetricCategory.Core, + "Time how long it takes to submit a request, receive a result, including binding, reading results, " + + "and optionally verifying them, including all operations whether successful or not, for each attempted request." + ); + this.resultSuccessTimer = activity.create().timer( + "result_success", + hdrdigits, + MetricCategory.Core, + "The execution time of successful operations, which includes submitting the operation, waiting for a response, and reading the result" + ); + this.triesHistogram = activity.create().histogram( + "tries", + hdrdigits, + MetricCategory.Core, + "A histogram of all tries for an activity. Perfect results mean all quantiles return 1." + + " Slight saturation is indicated by p99 or p95 returning higher values." + + " Lower quantiles returning more than 1, or higher values at high quantiles indicate incremental overload." + ); + this.verifierTimer = activity.create().timer( + "verifier", + hdrdigits, + MetricCategory.Verification, + "Time the execution of verifier code, if any" + ); + this.errorRate1m = activity.create().gauge("error_rate_1m", + () -> { + double result_1m_rate = this.resultTimer.getOneMinuteRate(); + double result_success_1m_rate = this.resultSuccessTimer.getOneMinuteRate(); + if (result_1m_rate==0.0d || Double.isNaN(result_1m_rate)) { + return Double.NaN; + } + return (result_1m_rate-result_success_1m_rate)/result_1m_rate; + }, + MetricCategory.Core, + "The relative one minute error rate estimated from the one minute successful and non-successful op rates" + ); + this.errorRate5m = activity.create().gauge("error_rate_5m", + () -> { + double result_5m_rate = this.resultTimer.getFiveMinuteRate(); + double result_success_5m_rate = this.resultSuccessTimer.getFiveMinuteRate(); + if (result_5m_rate == 0.0d || Double.isNaN(result_5m_rate)) { + return Double.NaN; + } + return (result_5m_rate - result_success_5m_rate) / result_5m_rate; + }, + MetricCategory.Core, + "The relative five minute error rate estimated from the five minute successful and non-successful op rates" + ); + this.errorRate15m = activity.create().gauge("error_rate_15m", + () -> { + double result_15m_rate = this.resultTimer.getFifteenMinuteRate(); + double result_success_15m_rate = this.resultSuccessTimer.getFifteenMinuteRate(); + if (result_15m_rate == 0.0d || Double.isNaN(result_15m_rate)) { + return Double.NaN; + } + return (result_15m_rate - result_success_15m_rate) / result_15m_rate; + }, + MetricCategory.Core, + "The relative fifteen minute error rate estimated from the fifteen minute successful and non-successful op rates" + ); + this.errorRateTotal = activity.create().gauge("error_rate_total", + () -> { + double result_total = this.resultTimer.getCount(); + double result_success_total = this.resultSuccessTimer.getCount(); + if (result_total == 0.0d) { + return Double.NaN; + } + return (result_total - result_success_total) / result_total; + }, + MetricCategory.Core, + "The cumulative error ratio calculated from the cumulative successful and non-successful op totals" + ); + this.errorsTotal = activity.create().gauge("errors_total", + () -> { + double result_total = this.resultTimer.getCount(); + double result_success_total = this.resultSuccessTimer.getCount(); + return (result_total - result_success_total); + }, + MetricCategory.Core, + "The total number of errors calculated from the cumulative successful and non-successful op totals" + ); + } + + @Override + public NBMetricGauge getOrCreateErrorsTotal() { + return this.errorsTotal; + } + @Override + public NBMetricGauge getOrCreateErrorRate1m() { + return this.errorRate1m; + } + @Override + public NBMetricGauge getOrCreateErrorRate5m() { + return this.errorRate5m; + } + @Override + public NBMetricGauge getOrCreateErrorRate15m() { + return this.errorRate15m; + } + @Override + public NBMetricGauge getOrCreateErrorRateTotal() { + return this.errorRateTotal; } @Override - public Timer getOrCreateInputTimer() { + public Timer getOrCreateInputTimer() { return readInputTimer; } @Override - public Timer getOrCreateStridesServiceTimer() { + public Timer getOrCreateStridesServiceTimer() { return stridesServiceTimer; } @Override - public Timer getStridesResponseTimerOrNull() { + public Timer getStridesResponseTimerOrNull() { return stridesResponseTimer; } @Override - public Timer getOrCreateCyclesServiceTimer() { + public Timer getOrCreateCyclesServiceTimer() { return cyclesServiceTimer; } @Override - public Timer getCyclesResponseTimerOrNull() { + public Timer getCyclesResponseTimerOrNull() { return cyclesResponseTimer; } @Override - public Counter getOrCreatePendingOpCounter() { + public Counter getOrCreatePendingOpCounter() { return pendingOpsCounter; } @Override - public Counter getOrCreateOpTrackerBlockedCounter() { - return opTrackerBlockedCounter; - } - - @Override - public Timer getOrCreateBindTimer() { + public Timer getOrCreateBindTimer() { return bindTimer; } @Override - public Timer getOrCreateExecuteTimer() { + public Timer getOrCreateExecuteTimer() { return executeTimer; } @Override - public Timer getOrCreateResultTimer() { + public Timer getOrCreateResultTimer() { return resultTimer; } @Override - public Timer getOrCreateResultSuccessTimer() { + public Timer getOrCreateResultSuccessTimer() { return resultSuccessTimer; } @Override - public Histogram getOrCreateTriesHistogram() { + public Histogram getOrCreateTriesHistogram() { return triesHistogram; } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/CycleRateSpec.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/CycleRateSpec.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/CycleRateSpec.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/CycleRateSpec.java index b4c0b39b5..cf386ff31 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/CycleRateSpec.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/CycleRateSpec.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits.simrate; +package io.nosqlbench.engine.api.activityapi.simrate; import io.nosqlbench.nb.api.engine.activityimpl.ParameterMap; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiter.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiter.java similarity index 95% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiter.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiter.java index 01f95c7eb..8112c90ad 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiter.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiter.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits; +package io.nosqlbench.engine.api.activityapi.simrate; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import java.time.Duration; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiters.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiters.java similarity index 86% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiters.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiters.java index 1a52e0990..676a873e6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/RateLimiters.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/RateLimiters.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits; +package io.nosqlbench.engine.api.activityapi.simrate; import io.nosqlbench.nb.api.components.core.NBComponent; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRate; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRate.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRate.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRate.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRate.java index 646c2554c..9488b60d6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRate.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRate.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits.simrate; +package io.nosqlbench.engine.api.activityapi.simrate; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.components.core.NBBaseComponent; import io.nosqlbench.nb.api.components.core.NBComponent; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -86,9 +86,25 @@ public class SimRate extends NBBaseComponent implements RateLimiter, Thread.Unca } private void initMetrics() { - create().gauge("cycles_waittime",() -> (double)getWaitTimeDuration().get(ChronoUnit.NANOS)); - create().gauge("config_cyclerate", () -> spec.opsPerSec); - create().gauge("config_burstrate", () -> spec.burstRatio); + create().gauge( + "cycles_waittime", + () -> (double)getWaitTimeDuration().get(ChronoUnit.NANOS), + MetricCategory.Core, + "The cumulative scheduling delay which accrues when" + + " an activity is not able to execute operations as fast as requested." + ); + create().gauge( + "config_cyclerate", + () -> spec.opsPerSec, + MetricCategory.Config, + "The configured cycle rate in ops/s" + ); + create().gauge( + "config_burstrate", + () -> spec.burstRatio, + MetricCategory.Config, + "the configured burst rate as a multiplier to the configured cycle rate. ex: 1.05 means 5% faster is allowed." + ); } public long refill() { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRateSpec.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRateSpec.java similarity index 99% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRateSpec.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRateSpec.java index 710f0fa79..6dfdca1c6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimRateSpec.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimRateSpec.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits.simrate; +package io.nosqlbench.engine.api.activityapi.simrate; import io.nosqlbench.nb.api.engine.activityimpl.ParameterMap; import io.nosqlbench.nb.api.engine.util.Unit; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimThreads.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimThreads.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimThreads.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimThreads.java index ae42e790e..caca422a6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/SimThreads.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/SimThreads.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits.simrate; +package io.nosqlbench.engine.api.activityapi.simrate; import java.util.concurrent.Semaphore; import java.util.function.Function; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/StrideRateSpec.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/StrideRateSpec.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/StrideRateSpec.java rename to engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/StrideRateSpec.java index 64791cb31..23e679176 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/simrate/StrideRateSpec.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityapi/simrate/StrideRateSpec.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.api.activityapi.ratelimits.simrate; +package io.nosqlbench.engine.api.activityapi.simrate; import io.nosqlbench.nb.api.engine.activityimpl.ParameterMap; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java index 706cb3560..e391b3753 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java @@ -20,7 +20,6 @@ import io.nosqlbench.adapters.api.activityimpl.uniform.EmitterOpDispenserWrapper import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.CycleOp; import io.nosqlbench.engine.core.lifecycle.scenario.container.InvokableResult; import io.nosqlbench.nb.api.components.core.NBComponent; -import io.nosqlbench.nb.api.components.core.NBBaseComponent; import io.nosqlbench.nb.api.components.events.ParamChange; import io.nosqlbench.engine.api.activityapi.core.*; import io.nosqlbench.engine.api.activityapi.core.progress.ActivityMetricProgressMeter; @@ -29,11 +28,12 @@ import io.nosqlbench.engine.api.activityapi.errorhandling.ErrorMetrics; import io.nosqlbench.engine.api.activityapi.errorhandling.modular.NBErrorHandler; import io.nosqlbench.engine.api.activityapi.planning.OpSequence; import io.nosqlbench.engine.api.activityapi.planning.SequencerType; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiters; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiters; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import io.nosqlbench.adapters.api.activityimpl.OpDispenser; import io.nosqlbench.adapters.api.activityimpl.OpMapper; +import io.nosqlbench.nb.api.components.status.NBStatusComponent; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import io.nosqlbench.nb.api.errors.BasicError; @@ -42,12 +42,12 @@ import io.nosqlbench.engine.api.activityapi.cyclelog.filters.IntPredicateDispens import io.nosqlbench.engine.api.activityapi.input.InputDispenser; import io.nosqlbench.engine.api.activityapi.output.OutputDispenser; import io.nosqlbench.engine.api.activityapi.planning.SequencePlanner; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; import io.nosqlbench.adapters.api.activityconfig.OpsLoader; import io.nosqlbench.adapters.api.activityconfig.yaml.OpTemplate; import io.nosqlbench.adapters.api.activityconfig.yaml.OpTemplateFormat; import io.nosqlbench.adapters.api.activityconfig.yaml.OpsDocList; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.StrideRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.StrideRateSpec; import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally; import io.nosqlbench.adapters.api.activityimpl.uniform.DriverAdapter; import io.nosqlbench.adapters.api.activityimpl.uniform.DryRunOpDispenserWrapper; @@ -67,7 +67,7 @@ import java.util.function.Supplier; /** * A default implementation of an Activity, suitable for building upon. */ -public class SimpleActivity extends NBBaseComponent implements Activity, InvokableResult { +public class SimpleActivity extends NBStatusComponent implements Activity, InvokableResult { private static final Logger logger = LogManager.getLogger("ACTIVITY"); protected ActivityDef activityDef; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java index 85db5bde1..6a7628195 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java @@ -23,6 +23,7 @@ import io.nosqlbench.nb.api.components.core.NBBaseComponent; import io.nosqlbench.engine.api.activityapi.core.ActivityDefObserver; import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleSegment; import io.nosqlbench.engine.api.activityapi.input.Input; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -60,22 +61,62 @@ public class AtomicInput extends NBBaseComponent implements Input, ActivityDefOb super(parent); this.activityDef = activityDef; onActivityDefUpdate(activityDef); - create().gauge("input_cycles_first",() -> (double) this.cycles_min.get()); - create().gauge("input_cycles_last",() -> (double) this.cycles_max.get()); - create().gauge("input_cycle",() -> (double) this.cycle_value.get()); - create().gauge("input_cycles_total",this::getTotalCycles); - create().gauge("input_recycles_first",() -> (double) this.recycles_min.get()); - create().gauge("input_recycles_last",() -> (double) this.recycles_max.get()); - create().gauge("input_recycle",() -> (double) this.recycle_value.get()); - create().gauge("input_recycles_total",this::getTotalRecycles); + create().gauge( + "input_cycles_first", + () -> (double) this.cycles_min.get(), + MetricCategory.Config, + "The first cycle of the cycle interval, inclusive" + ); + create().gauge( + "input_cycles_last", + () -> (double) this.cycles_max.get(), + MetricCategory.Config, + "The last cycle of the cycle interval, exclusive" + ); + create().gauge( + "input_cycle", + () -> (double) this.cycle_value.get(), + MetricCategory.Core, + "The next input cycle that will be dispatched to a thread" + ); + create().gauge( + "input_cycles_total", + this::getTotalCycles, + MetricCategory.Config, + "The total number of cycles to be executed" + ); + create().gauge( + "input_recycles_first", + () -> (double) this.recycles_min.get(), + MetricCategory.Config, + "The first recycle value, inclusive" + ); + create().gauge( + "input_recycles_last", + () -> (double) this.recycles_max.get(), + MetricCategory.Config, + "The last recycle value, exclusive" + ); + create().gauge( + "input_recycle", + () -> (double) this.recycle_value.get(), + MetricCategory.Core, + "The next recycle value that will be dispatched once cycles are completed" + ); + create().gauge( + "input_recycles_total", + this::getTotalRecycles, + MetricCategory.Config, + "The total number of recycles to be executed, within which each set of cycles will be executed" + ); } private double getTotalRecycles() { - return 0.0d; + return ((double)this.recycles_max.get())-((double)this.recycles_min.get()); } private double getTotalCycles() { - return 0.0d; + return ((double)this.cycles_max.get())-((double)this.cycles_min.get()); } @Override diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java index 94a764651..ddaf76329 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java @@ -15,20 +15,16 @@ */ package io.nosqlbench.engine.api.activityimpl.motor; -import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; import io.nosqlbench.engine.api.activityapi.core.*; -import io.nosqlbench.engine.api.activityapi.core.ops.fluent.opfacets.TrackedOp; -import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.op_output.StrideOutputConsumer; import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleResultSegmentBuffer; import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleResultsSegment; import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleSegment; import io.nosqlbench.engine.api.activityimpl.MotorState; import io.nosqlbench.engine.api.activityapi.core.ops.fluent.OpTracker; -import io.nosqlbench.engine.api.activityapi.core.ops.fluent.OpTrackerImpl; import io.nosqlbench.engine.api.activityapi.input.Input; import io.nosqlbench.engine.api.activityapi.output.Output; -import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; +import io.nosqlbench.engine.api.activityapi.simrate.RateLimiter; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -75,8 +71,6 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { private int stride = 1; private OpTracker opTracker; - private Counter optrackerBlockCounter; - /** * Create an ActivityMotor. @@ -194,7 +188,6 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { inputTimer = activity.getInstrumentation().getOrCreateInputTimer(); strideServiceTimer = activity.getInstrumentation().getOrCreateStridesServiceTimer(); stridesResponseTimer = activity.getInstrumentation().getStridesResponseTimerOrNull(); - optrackerBlockCounter = activity.getInstrumentation().getOrCreateOpTrackerBlockedCounter(); strideRateLimiter = activity.getStrideLimiter(); cycleRateLimiter = activity.getCycleLimiter(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java index 62531a882..35d1642b9 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java @@ -26,6 +26,7 @@ import io.nosqlbench.adapters.api.activityimpl.uniform.DriverAdapter; import io.nosqlbench.adapters.api.activityimpl.uniform.decorators.SyntheticOpTemplateProvider; import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.Op; import io.nosqlbench.adapters.api.templating.ParsedOp; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.lifecycle.Shutdownable; import io.nosqlbench.nb.api.components.core.NBComponent; import io.nosqlbench.nb.api.config.standard.*; @@ -38,8 +39,8 @@ import io.nosqlbench.nb.api.components.events.ParamChange; import io.nosqlbench.nb.api.components.events.SetThreads; import io.nosqlbench.engine.api.activityapi.core.ActivityDefObserver; import io.nosqlbench.engine.api.activityapi.planning.OpSequence; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.StrideRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.StrideRateSpec; import io.nosqlbench.engine.api.activityimpl.SimpleActivity; import io.nosqlbench.nb.annotations.ServiceSelector; import org.apache.logging.log4j.LogManager; @@ -155,11 +156,23 @@ public class StandardActivity extends SimpleActivity implements } create().gauge( - "ops_pending", () -> this.getProgressMeter().getSummary().pending()); + "ops_pending", + () -> this.getProgressMeter().getSummary().pending(), + MetricCategory.Core, + "The current number of operations which have not been dispatched for processing yet." + ); create().gauge( - "ops_active", () -> this.getProgressMeter().getSummary().current()); + "ops_active", + () -> this.getProgressMeter().getSummary().current(), + MetricCategory.Core, + "The current number of operations which have been dispatched for processing, but which have not yet completed." + ); create().gauge( - "ops_complete", () -> this.getProgressMeter().getSummary().complete()); + "ops_complete", + () -> this.getProgressMeter().getSummary().complete(), + MetricCategory.Core, + "The current number of operations which have been completed" + ); } @Override diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/actions/StandardAction.java b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/actions/StandardAction.java index 3e0b2eedc..f5a09523f 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/actions/StandardAction.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/actions/StandardAction.java @@ -48,9 +48,6 @@ import java.util.concurrent.TimeUnit; */ public class StandardAction, R extends Op> implements SyncAction, ActivityDefObserver { private final static Logger logger = LogManager.getLogger("ACTION"); - - private final A activity; - private final int slot; private final Timer executeTimer; private final Histogram triesHistogram; private final Timer resultSuccessTimer; @@ -62,9 +59,7 @@ public class StandardAction, R extends Op> impl private final Timer verifierTimer; public StandardAction(A activity, int slot) { - this.activity = activity; this.opsequence = activity.getOpSequence(); - this.slot = slot; this.maxTries = activity.getMaxTries(); bindTimer = activity.getInstrumentation().getOrCreateBindTimer(); executeTimer = activity.getInstrumentation().getOrCreateExecuteTimer(); @@ -110,7 +105,7 @@ public class StandardAction, R extends Op> impl throw new RuntimeException("The op implementation did not implement any active logic. Implement " + "one of [RunnableOp, CycleOp, or ChainingOp]"); } - + // TODO: break out validation timer from execute try (Timer.Context ignored = verifierTimer.time()) { CycleFunction verifier = dispenser.getVerifier(); try { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionCountMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionCountMetrics.java index c7cab5177..153e5d405 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionCountMetrics.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionCountMetrics.java @@ -18,6 +18,7 @@ package io.nosqlbench.engine.api.metrics; import com.codahale.metrics.Counter; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,11 @@ public class ExceptionCountMetrics { public ExceptionCountMetrics(final NBComponent parent) { this.parent = parent; - this.allerrors=parent.create().counter( "errors_ALL"); + this.allerrors=parent.create().counter( + "errors_ALL", + MetricCategory.Errors, + "all errors, regardless of type type, for " + parent.description() + ); } public void count(final String name) { @@ -41,7 +46,11 @@ public class ExceptionCountMetrics { if (null == c) synchronized (this.counters) { c = this.counters.computeIfAbsent( name, - k -> parent.create().counter("errors_" + name) + k -> parent.create().counter( + "errors_" + name, + MetricCategory.Errors, + name + " errors for " + parent.description() + ) ); } c.inc(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionExpectedResultVerificationMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionExpectedResultVerificationMetrics.java index ccd8b212f..2b9b1e37d 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionExpectedResultVerificationMetrics.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionExpectedResultVerificationMetrics.java @@ -18,6 +18,7 @@ package io.nosqlbench.engine.api.metrics; import com.codahale.metrics.Counter; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; /** @@ -30,8 +31,16 @@ public class ExceptionExpectedResultVerificationMetrics { public ExceptionExpectedResultVerificationMetrics(final NBComponent parent) { this.parent = parent; - this.verificationRetries=parent.create().counter("verificationcounts_RETRIES"); - this.verificationErrors=parent.create().counter( "verificationcounts_ERRORS"); + this.verificationRetries=parent.create().counter( + "verificationcounts_RETRIES", + MetricCategory.Verification, + "The number of retries used by the optional verifier logic for a given operation" + ); + this.verificationErrors=parent.create().counter( + "verificationcounts_ERRORS", + MetricCategory.Verification, + "The number of errors encountered while attempting to verify the result of an soperation" + ); } public void countVerificationRetries() { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionHistoMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionHistoMetrics.java index 4f60bf0c0..a3baa8f5b 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionHistoMetrics.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionHistoMetrics.java @@ -19,6 +19,7 @@ package io.nosqlbench.engine.api.metrics; import com.codahale.metrics.Histogram; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import java.util.ArrayList; import java.util.List; @@ -38,7 +39,12 @@ public class ExceptionHistoMetrics { public ExceptionHistoMetrics(final NBComponent parent, final ActivityDef activityDef) { this.parent = parent; this.activityDef = activityDef; - this.allerrors = parent.create().histogram( "errorhistos_ALL", activityDef.getParams().getOptionalInteger("hdr_digits").orElse(4)); + this.allerrors = parent.create().histogram( + "errorhistos_ALL", + activityDef.getParams().getOptionalInteger("hdr_digits").orElse(4), + MetricCategory.Errors, + "A histogram for all exceptions" + ); } public void update(final String name, final long magnitude) { @@ -46,7 +52,12 @@ public class ExceptionHistoMetrics { if (null == h) synchronized (this.histos) { h = this.histos.computeIfAbsent( name, - errName -> parent.create().histogram( "errorhistos_"+errName, this.activityDef.getParams().getOptionalInteger("hdr_digits").orElse(4)) + errName -> parent.create().histogram( + "errorhistos_"+errName, + this.activityDef.getParams().getOptionalInteger("hdr_digits").orElse(4), + MetricCategory.Errors, + "error histogram for exception '" + errName + "'" + ) ); } h.update(magnitude); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionMeterMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionMeterMetrics.java index 16186b5a1..5ab207086 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionMeterMetrics.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionMeterMetrics.java @@ -18,6 +18,7 @@ package io.nosqlbench.engine.api.metrics; import com.codahale.metrics.Meter; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,9 @@ public class ExceptionMeterMetrics { public ExceptionMeterMetrics(final NBComponent parent) { this.parent = parent; - this.allerrors = parent.create().meter("errormeters_ALL"); + this.allerrors = parent.create().meter("errormeters_ALL", MetricCategory.Errors, + "all errors, regardless of type type, for the parent " + parent.description() + ); } public void mark(final String name) { @@ -41,7 +44,11 @@ public class ExceptionMeterMetrics { if (null == c) synchronized (this.meters) { c = this.meters.computeIfAbsent( name, - k -> parent.create().meter("errormeters_" + name) + k -> parent.create().meter( + "errormeters_" + name, + MetricCategory.Errors, + name + " errors for " + parent.description() + ) ); } c.mark(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionTimerMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionTimerMetrics.java index 3edcd751e..0dfd79ca6 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionTimerMetrics.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/api/metrics/ExceptionTimerMetrics.java @@ -19,6 +19,7 @@ package io.nosqlbench.engine.api.metrics; import com.codahale.metrics.Timer; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import java.util.ArrayList; import java.util.List; @@ -38,14 +39,24 @@ public class ExceptionTimerMetrics { this.activityDef = activityDef; this.parentLabels = parent; - this.allerrors=parent.create().timer("errortimers_ALL",4); + this.allerrors=parent.create().timer( + "errortimers_ALL", + 4, + MetricCategory.Errors, + "exception timers for all error types" + ); } public void update(final String name, final long nanosDuration) { Timer timer = this.timers.get(name); if (null == timer) synchronized (this.timers) { timer = this.timers.computeIfAbsent( - name, k -> parentLabels.create().timer("errortimers_" + name, 3) + name, k -> parentLabels.create().timer( + "errortimers_" + name, + 3, + MetricCategory.Errors, + "exception timers for specific error '" + name + "'" + ) ); } timer.update(nanosDuration, TimeUnit.NANOSECONDS); diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java b/engine-core/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java similarity index 72% rename from engine-cli/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java rename to engine-core/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java index 27357cc77..b60fdd638 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/cli/NBJavaNativeResolver.java @@ -27,11 +27,21 @@ import io.nosqlbench.nb.annotations.Service; @Service(value= NBInvokableResolver.class,selector = "java") public class NBJavaNativeResolver implements NBInvokableResolver { @Override - public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String phaseName) { + public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String stepname) { return switch (cmd.getCmdType()) { - case CmdType.indirect -> NBJavaCommandLoader.init(cmd.getArgValue("_impl"), parent, phaseName, cmd.getTargetContext()); - case CmdType.java -> NBJavaCommandLoader.init(cmd.getArgValue("class"), parent, phaseName, cmd.getTargetContext()); + case CmdType.indirect -> { + String implName = cmd.getArgValue("_impl"); + NBInvokableCommand invokable = NBJavaCommandLoader.init(implName, parent, stepname, cmd.getTargetContext()); + cmd.takeArgValue("_impl"); + yield invokable; + } + case CmdType.java -> NBJavaCommandLoader.init(cmd.getArgValue("class"), parent, stepname, cmd.getTargetContext()); default -> null; }; } + + @Override + public boolean verify(Cmd cmd) { + return NBJavaCommandLoader.oneExists(cmd.getArgValue("_impl")) != null; + } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/BasicScriptBuffer.java b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/BasicScriptBuffer.java index 1e7c26fa4..6ff7002b5 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/BasicScriptBuffer.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/BasicScriptBuffer.java @@ -74,12 +74,7 @@ public class BasicScriptBuffer implements ScriptBuffer { } break; case java: - case start: // start activity - case run: // run activity - case await: // await activity // case stop: // stop activity - case forceStop: // force stopping activity - case waitMillis: sb.append("controller.").append(cmd.asScriptText()).append("\n"); //// // Sanity check that this can parse before using it diff --git a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/Cmd.java b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/Cmd.java index 8cd551e22..cf1d0185a 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/Cmd.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/Cmd.java @@ -55,6 +55,12 @@ public class Cmd { return cmdArg.getValue(); } + + public String takeArgValue(String paramName) { + String argValue = getArgValue(paramName); + this.cmdArgs.remove(paramName); + return argValue; + } public String getArgValue(String paramName) { CmdArg cmdArg = this.cmdArgs.get(paramName); if (cmdArg==null) { @@ -127,8 +133,12 @@ public class Cmd { public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(cmdType.toString()); + sb.append(switch (cmdType) { + case indirect -> getArgs().containsKey("_impl") ? getArgs().get("_impl").getValue() : "[]"; + default -> cmdType.name(); + }); for (CmdArg value : getArgs().values()) { + if (value.getParam().name.startsWith("_impl")) continue; sb.append(" ").append(value); } return sb.toString(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/CmdType.java b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/CmdType.java index 5cf98c180..6de8dc98e 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/CmdType.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/CmdType.java @@ -23,14 +23,14 @@ package io.nosqlbench.engine.cmdstream; * or an error is thrown. */ public enum CmdType { - run(), - start(), +// run(), +// start(), // stop(CmdParam.of("activity")), - forceStop(CmdParam.of("activity")), +// forceStop(CmdParam.of("activity")), script(CmdParam.of("path", s -> s)), java(CmdParam.of("class", s -> s)), - await(CmdParam.of("activity")), - waitMillis(CmdParam.of("ms", Long::parseLong)), +// await(CmdParam.of("activity")), +// waitMillis(CmdParam.of("ms", Long::parseLong)), fragment(CmdParam.ofFreeform("fragment")), container(CmdParam.of("name")), indirect(CmdParam.of("indirect")); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/NBJavaCommandLoader.java b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/NBJavaCommandLoader.java index 391c51766..464ba0f72 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/NBJavaCommandLoader.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/cmdstream/NBJavaCommandLoader.java @@ -39,9 +39,9 @@ public class NBJavaCommandLoader { } } - public static NBInvokableCommand init(String cmdSelector, NBComponent parent, String cmdAlias, String ctxName) { + public static NBInvokableCommand init(String cmdSelector, NBComponent parent, String stepName, String ctxName) { NBCommandInfo cmdInfo = getSelector(cmdSelector).getOne(); - NBInvokableCommand command = cmdInfo.create(parent, cmdAlias, ctxName); + NBInvokableCommand command = cmdInfo.create(parent, cmdSelector, ctxName); return command; } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/clientload/MemInfoReader.java b/engine-core/src/main/java/io/nosqlbench/engine/core/clientload/MemInfoReader.java index 3a33fc060..f88db4a70 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/clientload/MemInfoReader.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/clientload/MemInfoReader.java @@ -46,9 +46,10 @@ public class MemInfoReader extends LinuxSystemFileReader { public Double getMemUsedkB() { Double memTotal = getMemTotalkB(); - Double memFree = getMemFreekB(); - if (memTotal != null && memFree != null) - return memTotal - memFree; + Double memAvailable = getMemAvailablekB(); +// Double memFree = getMemFreekB(); + if (memTotal != null && memAvailable != null) + return memTotal - memAvailable; return null; } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java index bd6e9743d..7ad32521b 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java @@ -16,6 +16,7 @@ package io.nosqlbench.engine.core.lifecycle.activity; import com.codahale.metrics.Gauge; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; import io.nosqlbench.nb.api.labels.NBLabeledElement; import io.nosqlbench.nb.api.labels.NBLabels; @@ -463,7 +464,12 @@ public class ActivityExecutor implements NBLabeledElement, ParameterMap.Listener // } private void registerMetrics() { - this.activity.create().gauge("threads",() -> (double) this.motors.size()); + this.activity.create().gauge( + "threads", + () -> (double) this.motors.size(), + MetricCategory.Core, + "The current number of threads in activity " + this.description() + ); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_await.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_await.java new file mode 100644 index 000000000..9d8d5b1d1 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_await.java @@ -0,0 +1,45 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import io.nosqlbench.nb.api.engine.util.Unit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; +import java.util.concurrent.locks.LockSupport; + +@Service(value = NBBaseCommand.class, selector = "await") +public class CMD_await extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("await"); + + public CMD_await(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + controller.await(params); + return null; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_example.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_example.java new file mode 100644 index 000000000..8585ad508 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_example.java @@ -0,0 +1,45 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import io.nosqlbench.nb.api.errors.BasicError; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; +import java.util.Optional; + +@Service(value = NBBaseCommand.class,selector = "example") +public class CMD_example extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("example"); + + public CMD_example(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + return null; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_forceStop.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_forceStop.java new file mode 100644 index 000000000..41c34f4be --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_forceStop.java @@ -0,0 +1,46 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import io.nosqlbench.nb.api.errors.BasicError; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; +import java.util.Optional; + +@Service(value = NBBaseCommand.class,selector = "force_stop") +public class CMD_forceStop extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("force_stop"); + + public CMD_forceStop(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + controller.forceStop(params); + return null; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_run.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_run.java new file mode 100644 index 000000000..6d9e9fc6e --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_run.java @@ -0,0 +1,43 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; + +@Service(value = NBBaseCommand.class, selector = "run") +public class CMD_run extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("run"); + + public CMD_run(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + controller.run(params); + return null; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_start.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_start.java new file mode 100644 index 000000000..d093fcb99 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_start.java @@ -0,0 +1,45 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import io.nosqlbench.nb.api.errors.BasicError; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; +import java.util.Optional; + +@Service(value = NBBaseCommand.class,selector = "start") +public class CMD_start extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("start"); + + public CMD_start(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + return controller.start(params); + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_wait.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_wait.java new file mode 100644 index 000000000..714ae5cc0 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/CMD_wait.java @@ -0,0 +1,66 @@ +/* + * 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.engine.core.lifecycle.commands; + +import com.amazonaws.services.s3.model.transform.Unmarshallers; +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.nb.annotations.Service; +import io.nosqlbench.nb.api.engine.util.Unit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.PrintWriter; +import java.io.Reader; +import java.util.Optional; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.LongStream; + +@Service(value = NBBaseCommand.class, selector = "example") +public class CMD_wait extends NBBaseCommand { + public final static Logger logger = LogManager.getLogger("example"); + + public CMD_wait(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + long ns = 0L; + ns += params.maybeGet("ms") + .or(() -> params.maybeGet("millis")) + .map(Long::parseLong) + .map(l -> l * 1_000_000L) + .orElse(0L); + ns += params.maybeGet("us") + .or(() -> params.maybeGet("micros")) + .map(Long::parseLong) + .map(l -> l * 1_000L) + .orElse(0L); + ns += params.maybeGet("ns") + .or(() -> params.maybeGet("nanos")) + .map(Long::parseLong) + .orElse(0L); + ns += params.maybeGet("unit") + .flatMap(Unit::nanosecondsFor) + .orElse(0L); + LockSupport.parkNanos(ns); + return ns; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_await.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_await.java new file mode 100644 index 000000000..2f5888f7a --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_await.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "await") +public class INFO_await extends NBCommandInfo { + @Override + public Class getType() { + return CMD_await.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_example.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_example.java new file mode 100644 index 000000000..6d9c2aa7b --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_example.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "example") +public class INFO_example extends NBCommandInfo { + @Override + public Class getType() { + return CMD_example.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_forceStop.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_forceStop.java new file mode 100644 index 000000000..cf77bbd44 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_forceStop.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "force_stop") +public class INFO_forceStop extends NBCommandInfo { + @Override + public Class getType() { + return CMD_forceStop.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_run.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_run.java new file mode 100644 index 000000000..b0574b2c6 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_run.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "run") +public class INFO_run extends NBCommandInfo { + @Override + public Class getType() { + return CMD_run.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_start.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_start.java new file mode 100644 index 000000000..2df93b41b --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_start.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "start") +public class INFO_start extends NBCommandInfo { + @Override + public Class getType() { + return CMD_start.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_wait.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_wait.java new file mode 100644 index 000000000..e2900a8c1 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/INFO_wait.java @@ -0,0 +1,29 @@ +/* + * 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.engine.core.lifecycle.commands; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "wait") +public class INFO_wait extends NBCommandInfo { + @Override + public Class getType() { + return CMD_wait.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_error.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_error.java new file mode 100644 index 000000000..a352f1e3c --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_error.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.commands.fortesting; + +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; + +import java.io.PrintWriter; +import java.io.Reader; + +public class CMD_error extends NBBaseCommand { + + + public CMD_error(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + throw new RuntimeException("Command '" + this + "' throws ERROR."); + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_ok.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_ok.java new file mode 100644 index 000000000..88c8af3bf --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/CMD_ok.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.commands.fortesting; + +import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.api.labels.NBLabels; + +import java.io.PrintWriter; +import java.io.Reader; + +public class CMD_ok extends NBBaseCommand { + + public CMD_ok(NBBufferedContainer parentComponent, String stepName, String targetScenario) { + super(parentComponent, stepName, targetScenario); + } + + @Override + public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContainerActivitiesController controller) { + stdout.write("Command '" + this.toString() + "' says OK and exits with no object or exception."); + for (String pkey : params.keySet()) { + stdout.println("diagnostic 'ok' command setting key '" + pkey + " to " + params.get(pkey)); + } + return params; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_error.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_error.java new file mode 100644 index 000000000..816f8bfdf --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_error.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.commands.fortesting; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "test_error") +public class INFO_error extends NBCommandInfo { + @Override + public Class getType() { + return CMD_error.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_test_ok.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_test_ok.java new file mode 100644 index 000000000..4b0079f77 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/commands/fortesting/INFO_test_ok.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.commands.fortesting; + +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.annotations.Service; + +@Service(value = NBCommandInfo.class,selector = "test_ok") +public class INFO_test_ok extends NBCommandInfo { + @Override + public Class getType() { + return CMD_ok.class; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/execution/NBCommandInfo.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/execution/NBCommandInfo.java index b98d57601..575196466 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/execution/NBCommandInfo.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/execution/NBCommandInfo.java @@ -40,7 +40,7 @@ public abstract class NBCommandInfo { cmdCtor = getType().getConstructor(NBBufferedContainer.class, String.class, String.class); return cmdCtor.newInstance(parent, cmdName, ctxName); } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException("Unable to instantiate command via ctor(parent,name,ctx): " + e,e); + throw new RuntimeException("Unable to instantiate command via ctor(parent,name,ctx): " + e + (e.getCause()!=null ? "cause: " + e.getCause().toString() : ""),e); } } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/CmdParser.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/CmdParser.java index 91ced2c37..58b223acb 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/CmdParser.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/CmdParser.java @@ -24,7 +24,6 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; - /** *

Take zero or more strings containing combined argv and return * a single {@link Cmd} list containing zero or more commands.

@@ -46,7 +45,7 @@ public class CmdParser { private record parameter(String name, String op, String value) {} private record command(String name){} private final static Pattern combinedPattern = - Pattern.compile("(?[a-zA-Z_][a-zA-Z0-9_.-]+)(?=+)(?.+)|(?[a-zA-Z_][a-zA-Z0-9_.]+)",Pattern.DOTALL); + Pattern.compile("(?[a-zA-Z_][a-zA-Z0-9_.-]*)(?=+)(?.+)|(?[a-zA-Z_][a-zA-Z0-9_.]*)",Pattern.DOTALL); private final static Pattern commandName =Pattern.compile("^$"); public static LinkedList parseArgvCommands(LinkedList args) { LinkedList cmdstructs = new LinkedList<>(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBAutoScriptResolver.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBAutoScriptResolver.java index 7fb6bad01..2923a088e 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBAutoScriptResolver.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBAutoScriptResolver.java @@ -34,7 +34,7 @@ import java.util.Optional; @Service(value = NBInvokableResolver.class, selector = "autojs") public class NBAutoScriptResolver implements NBInvokableResolver { @Override - public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String phaseName) { + public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String stepname) { Optional> scriptfile = NBIO.local() .searchPrefixes("scripts/auto") @@ -47,11 +47,21 @@ public class NBAutoScriptResolver implements NBInvokableResolver { Map newArgs = new LinkedHashMap<>(cmd.getArgs()); newArgs.put("path",new CmdArg(new CmdParam("name",s->s,false),"=",pathOf.toString())); Cmd reformattedCmd = new Cmd("script", newArgs); - return new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).add(reformattedCmd); + return new NBScriptedCommand(parent, stepname, cmd.getTargetContext()).add(reformattedCmd); } else { return null; } } + @Override + public boolean verify(Cmd cmd) { + return NBIO.local() + .searchPrefixes("scripts/auto") + .pathname(cmd.getArgValue("_impl")) + .extensionSet("js") + .first() + .isPresent(); + } + } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCommandAssembly.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCommandAssembly.java index c21fc3d73..f97d90537 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCommandAssembly.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCommandAssembly.java @@ -29,15 +29,35 @@ import java.util.function.Function; public class NBCommandAssembly { + private static NBCoreInvokableResolver core_resolver = new NBCoreInvokableResolver(); + private final static Logger logger = LogManager.getLogger(NBCommandAssembly.class); + public static NBCommandParams paramsFor(Cmd cmd) { + return switch (cmd.getCmdType()) { + case indirect, java, container -> { + Map params = cmd.getArgMap(); + params.remove("_impl"); + yield NBCommandParams.of(params); + } + default -> NBCommandParams.of(Map.of()); + }; + } + public static record CommandInvocation(NBInvokableCommand command, NBCommandParams params, String containerName) { } - public static List assemble(List cmds, Function ctxprovider) { + public static List assemble(List cmds, Function ctxprovider) { List mappedCmds = tagCommandsWithContext(cmds); - List invocations = prepareMappedPhases(mappedCmds, ctxprovider); - return invocations; + NBCoreInvokableResolver core_resolver = new NBCoreInvokableResolver(); + for (Cmd mappedCmd : mappedCmds) { + core_resolver.verify(mappedCmd); + } + + return mappedCmds; + +// List invocations = prepareMappedPhases(mappedCmds, ctxprovider); +// return invocations; } private static List tagCommandsWithContext(List cmds) { @@ -62,6 +82,40 @@ public class NBCommandAssembly { return new ArrayList<>(tagged); } + + public static NBInvokableCommand resolve(Cmd cmd, Function ctxProvider) { + try { + NBCommandParams params = switch (cmd.getCmdType()) { + case indirect, java, container -> NBCommandParams.of(cmd.getArgMap()); + default -> NBCommandParams.of(Map.of()); + }; + + String targetContext = cmd.getTargetContext(); + NBInvokableCommand command = core_resolver.resolve(cmd, ctxProvider.apply(targetContext), cmd.getStepName()); + return command; + } catch (Exception e) { + throw new UnresolvedCommand(cmd, e); + } + } + + public static CommandInvocation assembleCommand(Cmd cmd, Function ctxProvider) { + NBCommandParams params = switch (cmd.getCmdType()) { + case indirect, java, container -> NBCommandParams.of(cmd.getArgMap()); + default -> NBCommandParams.of(Map.of()); + }; + + String targetContext = cmd.getTargetContext(); + NBInvokableCommand command = core_resolver.resolve(cmd, ctxProvider.apply(targetContext), cmd.getStepName()); + if (command == null) { + throw new BasicError("Found zero commands for spec;" + cmd); + } + String containerName = cmd.getTargetContext(); + + // TODO, make this unnecessary by moving the impl out of the map to a dedicated cmd structure + params.remove("_impl"); + return new CommandInvocation(command, params, containerName); + } + private static List prepareMappedPhases(List mappedCmds, Function ctxProvider) { List parameterizedInvocations = new ArrayList<>(); NBCoreInvokableResolver core_resolver = new NBCoreInvokableResolver(); @@ -73,10 +127,13 @@ public class NBCommandAssembly { String targetContext = cmd.getTargetContext(); NBInvokableCommand command = core_resolver.resolve(cmd, ctxProvider.apply(targetContext), cmd.getStepName()); - if (command==null) { + if (command == null) { throw new BasicError("Found zero commands for spec;" + cmd); } String containerName = cmd.getTargetContext(); + + // TODO, make this unnecessary by moving the impl out of the map to a dedicated cmd structure + params.remove("_impl"); parameterizedInvocations.add(new CommandInvocation(command, params, containerName)); } return parameterizedInvocations; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCoreInvokableResolver.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCoreInvokableResolver.java index 90d800f5c..43445d688 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCoreInvokableResolver.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBCoreInvokableResolver.java @@ -45,6 +45,16 @@ public class NBCoreInvokableResolver implements NBInvokableResolver { return null; } + @Override + public boolean verify(Cmd cmd) { + for (NBInvokableResolver resolver : getResolvers().values()) { + if (resolver.verify(cmd)) { + return true; + } + } + return false; + } + private SequencedMap getResolvers() { if (this.resolvers == null || this.resolvers.isEmpty()) { SequencedMap resolverMap = new LinkedHashMap<>(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBInvokableResolver.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBInvokableResolver.java index 86b803882..cff15c437 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBInvokableResolver.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBInvokableResolver.java @@ -21,6 +21,8 @@ import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContaine import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; public interface NBInvokableResolver { - NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String phaseName); + NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String stepname); + + boolean verify(Cmd cmd); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBScriptCommandResolver.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBScriptCommandResolver.java index 842ac09fe..c80559bcc 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBScriptCommandResolver.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBScriptCommandResolver.java @@ -25,10 +25,10 @@ import io.nosqlbench.nb.annotations.Service; @Service(value = NBInvokableResolver.class, selector = "js") public class NBScriptCommandResolver implements NBInvokableResolver { @Override - public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String phaseName) { + public NBInvokableCommand resolve(Cmd cmd, NBBufferedContainer parent, String stepname) { return switch (cmd.getCmdType()) { - case run, await, forceStop, start, waitMillis, fragment, script-> - new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).add(cmd); + case fragment, script-> + new NBScriptedCommand(parent, stepname, cmd.getTargetContext()).add(cmd); // case fragment -> // new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).addScriptText(cmd.getArgValue("fragment")); // case script -> @@ -37,5 +37,8 @@ public class NBScriptCommandResolver implements NBInvokableResolver { }; } - + @Override + public boolean verify(Cmd cmd) { + return true; + } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSession.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSession.java index e7cab4a5e..ccc77c892 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSession.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSession.java @@ -16,16 +16,14 @@ package io.nosqlbench.engine.core.lifecycle.session; -import io.nosqlbench.nb.api.components.core.NBComponentProps; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand; +import io.nosqlbench.nb.api.components.status.NBHeartbeatComponent; import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef; -import io.nosqlbench.nb.api.engine.metrics.instruments.NBFunctionGauge; -import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; +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.components.core.NBBaseComponent; import io.nosqlbench.nb.api.components.decorators.NBTokenWords; import io.nosqlbench.engine.cmdstream.Cmd; -import io.nosqlbench.engine.core.clientload.*; import io.nosqlbench.engine.core.lifecycle.ExecutionResult; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBContainer; @@ -43,9 +41,9 @@ import java.util.function.Function; * on. * All NBScenarios are run within an NBSession. */ -public class NBSession extends NBBaseComponent implements Function, ExecutionResult>, NBTokenWords { +public class NBSession extends NBHeartbeatComponent implements Function, ExecutionResult>, NBTokenWords { private final static Logger logger = LogManager.getLogger(NBSession.class); - private final ClientSystemMetricChecker clientMetricChecker; +// private final ClientSystemMetricChecker clientMetricChecker; private final Map containers = new ConcurrentHashMap<>(); @@ -57,55 +55,53 @@ public class NBSession extends NBBaseComponent implements Function, Ex public NBSession( NBLabeledElement labelContext, - String sessionName + String sessionName, + Map props ) { super( null, labelContext.getLabels() - .and("session", sessionName) + .and("session", sessionName), + props, + "session" + ); + + new NBSessionSafetyMetrics(this); + + create().gauge( + "session_time", + () -> (double) System.nanoTime(), + MetricCategory.Core, + "session time in nanoseconds" ); - this.clientMetricChecker = new ClientSystemMetricChecker(this, NBLabels.forKV(), 10); - registerLoadAvgMetrics(); - registerMemInfoMetrics(); -// registerDiskStatsMetrics(); - registerNetworkInterfaceMetrics(); - registerCpuStatMetrics(); - clientMetricChecker.start(); bufferOrphanedMetrics = true; } - /** - * Notes on scenario names: - *
    - *
  • If none are provided, then all cmds are implicitly allocated to the "default" scenario.
  • - *
  • If the name "default" is provided directly, then this is considered an error.
  • - *
  • Otherwise, the most recently set scenario name is the one in which all following commands are run.
  • - *
  • - *
- * - * @param cmds - * the function argument - * @return - */ public ExecutionResult apply(List cmds) { // TODO: add container closing command // TODO: inject container closing commands after the last command referencing each container - List invocationCalls = NBCommandAssembly.assemble(cmds, this::getContext); + List assembledCommands = NBCommandAssembly.assemble(cmds, this::getContext); ResultCollector collector = new ResultCollector(); + try (ResultContext results = new ResultContext(collector).ok()) { - for (NBCommandAssembly.CommandInvocation invocation : invocationCalls) { - try { - String targetContext = invocation.containerName(); - NBBufferedContainer container = getContext(targetContext); - NBCommandResult cmdResult = container.apply(invocation.command(), invocation.params()); + for (Cmd cmd : assembledCommands) { + String explanation = " in context " + cmd.getTargetContext() + ", command '" + cmd.toString() + "'"; + try (NBInvokableCommand command = NBCommandAssembly.resolve(cmd,this::getContext)) { + NBCommandParams params = NBCommandAssembly.paramsFor(cmd); + NBBufferedContainer container = getContext(cmd.getTargetContext()); + NBCommandResult cmdResult = container.apply(command, params); results.apply(cmdResult); + if (cmdResult.hasException()) { + throw cmdResult.getException(); + } } catch (Exception e) { - String msg = "While running command '" + invocation.command() + "' in container '" + invocation.containerName() + "', an error occurred: " + e.toString(); - logger.error(msg); + String msg = "While running " + explanation + ", an error occurred: " + e.toString(); results.error(e); + onError(e); + logger.error(msg); break; } } @@ -124,79 +120,6 @@ public class NBSession extends NBBaseComponent implements Function, Ex } - private void registerLoadAvgMetrics() { - LoadAvgReader reader = new LoadAvgReader(); - if (!reader.fileExists()) - return; - - NBFunctionGauge load1m = create().gauge("loadavg_1min", reader::getOneMinLoadAverage); - clientMetricChecker.addMetricToCheck(load1m, 50.0); - - NBFunctionGauge load5m = create().gauge("loadavg_5min", reader::getFiveMinLoadAverage); - clientMetricChecker.addMetricToCheck(load5m, 50.0); - - NBFunctionGauge load15m = create().gauge("loadavg_15min", reader::getFifteenMinLoadAverage); - clientMetricChecker.addMetricToCheck(load15m, 50.0); - // add checking for CPU load averages; TODO: Modify thresholds - - } - - private void registerMemInfoMetrics() { - MemInfoReader reader = new MemInfoReader(); - if (!reader.fileExists()) - return; - - NBMetricGauge memTotalGauge = create().gauge("mem_total", reader::getMemTotalkB); - NBMetricGauge memUsedGauge = create().gauge("mem_used", reader::getMemUsedkB); - NBMetricGauge memFreeGauge = create().gauge("mem_free", reader::getMemFreekB); - NBMetricGauge memAvailableGauge = create().gauge("mem_avaialble", reader::getMemAvailablekB); - NBMetricGauge memCachedGauge = create().gauge("mem_cache", reader::getMemCachedkB); - NBMetricGauge memBufferedGauge = create().gauge("mem_buffered", reader::getMemBufferskB); - // add checking for percent memory used at some given time; TODO: Modify percent threshold - clientMetricChecker.addRatioMetricToCheck(memUsedGauge, memTotalGauge, 90.0, false); - - NBMetricGauge swapTotalGauge = create().gauge("swap_total", reader::getSwapTotalkB); - NBMetricGauge swapFreeGauge = create().gauge("swap_free", reader::getSwapFreekB); - NBMetricGauge swapUsedGauge = create().gauge("swap_used", reader::getSwapUsedkB); - } - - private void registerDiskStatsMetrics() { - DiskStatsReader reader = new DiskStatsReader(); - if (!reader.fileExists()) - return; - - for (String device : reader.getDevices()) { - create().gauge(device + "_transactions", () -> reader.getTransactionsForDevice(device)); - create().gauge(device + "_kB_read", () -> reader.getKbReadForDevice(device)); - create().gauge(device + "_kB_written", () -> reader.getKbWrittenForDevice(device)); - } - } - - private void registerNetworkInterfaceMetrics() { - NetDevReader reader = new NetDevReader(); - if (!reader.fileExists()) - return; - for (String iface : reader.getInterfaces()) { - create().gauge(iface + "_rx_bytes", () -> reader.getBytesReceived(iface)); - create().gauge(iface + "_rx_packets", () -> reader.getPacketsReceived(iface)); - create().gauge(iface + "_tx_bytes", () -> reader.getBytesTransmitted(iface)); - create().gauge(iface + "_tx_packets", () -> reader.getPacketsTransmitted(iface)); - } - } - - private void registerCpuStatMetrics() { - StatReader reader = new StatReader(); - if (!reader.fileExists()) - return; - NBMetricGauge cpuUserGauge = create().gauge("cpu_user", reader::getUserTime); - NBMetricGauge cpuSystemGauge = create().gauge("cpu_system", reader::getSystemTime); - NBMetricGauge cpuIdleGauge = create().gauge("cpu_idle", reader::getIdleTime); - NBMetricGauge cpuIoWaitGauge = create().gauge("cpu_iowait", reader::getIoWaitTime); - NBMetricGauge cpuTotalGauge = create().gauge("cpu_total", reader::getTotalTime); - // add checking for percent of time spent in user space; TODO: Modify percent threshold - clientMetricChecker.addRatioMetricToCheck(cpuUserGauge, cpuTotalGauge, 50.0, true); - } - private NBBufferedContainer getContext(String name) { return containers.computeIfAbsent( name, diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionSafetyMetrics.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionSafetyMetrics.java new file mode 100644 index 000000000..e8a9d50c1 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionSafetyMetrics.java @@ -0,0 +1,253 @@ +/* + * 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.engine.core.lifecycle.session; + +import io.nosqlbench.engine.core.clientload.*; +import io.nosqlbench.nb.api.components.core.NBBaseComponent; +import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.components.core.NBComponentMetrics; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; +import io.nosqlbench.nb.api.engine.metrics.instruments.NBFunctionGauge; +import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; +import io.nosqlbench.nb.api.labels.NBLabels; + +public class NBSessionSafetyMetrics extends NBBaseComponent { + + public NBSessionSafetyMetrics(NBSession parent) { + super(parent, NBLabels.forKV()); + ClientSystemMetricChecker metricsChecker = registerSessionSafetyMetrics(parent); + metricsChecker.start(); + } + + private ClientSystemMetricChecker registerSessionSafetyMetrics(NBSession nbSession) { + ClientSystemMetricChecker metricsChecker = new ClientSystemMetricChecker( + this, + NBLabels.forKV(), + 10 + ); + registerLoadAvgMetrics(metricsChecker); + registerMemInfoMetrics(metricsChecker); +// registerDiskStatsMetrics(); + registerNetworkInterfaceMetrics(metricsChecker); + registerCpuStatMetrics(metricsChecker); + return metricsChecker; + } + + private void registerCpuStatMetrics(ClientSystemMetricChecker metricsChecker) { + StatReader reader = new StatReader(); + if (!reader.fileExists()) + return; + NBMetricGauge cpuUserGauge = create().gauge( + "cpu_user", + reader::getUserTime, + MetricCategory.Internals, + "" + ); + NBMetricGauge cpuSystemGauge = create().gauge( + "cpu_system", + reader::getSystemTime, + MetricCategory.Internals, + "" + ); + NBMetricGauge cpuIdleGauge = create().gauge( + "cpu_idle", + reader::getIdleTime, + MetricCategory.Internals, + "" + ); + NBMetricGauge cpuIoWaitGauge = create().gauge( + "cpu_iowait", + reader::getIoWaitTime, + MetricCategory.Internals, + "" + ); + NBMetricGauge cpuTotalGauge = create().gauge( + "cpu_total", + reader::getTotalTime, + MetricCategory.Internals, + "" + ); + // add checking for percent of time spent in user space; TODO: Modify percent threshold + metricsChecker.addRatioMetricToCheck( + cpuUserGauge, + cpuTotalGauge, + 50.0, + true + ); + } + + private void registerDiskStatsMetrics(ClientSystemMetricChecker metricsChecker) { + DiskStatsReader reader = new DiskStatsReader(); + if (!reader.fileExists()) + return; + + for (String device : reader.getDevices()) { + create().gauge( + device + "_transactions", + () -> reader.getTransactionsForDevice(device), + MetricCategory.Internals, + "" + ); + create().gauge(device + "_kB_read", + () -> reader.getKbReadForDevice(device), + MetricCategory.Internals, + "" + ); + create().gauge(device + "_kB_written", + () -> reader.getKbWrittenForDevice(device), + MetricCategory.Internals, + "" + ); + } + } + + private void registerNetworkInterfaceMetrics(ClientSystemMetricChecker metricsChecker) { + NetDevReader reader = new NetDevReader(); + if (!reader.fileExists()) + return; + for (String iface : reader.getInterfaces()) { + create().gauge( + iface + "_rx_bytes", + () -> reader.getBytesReceived(iface), + MetricCategory.Internals, + "" + ); + create().gauge( + iface + "_rx_packets", + () -> reader.getPacketsReceived(iface), + MetricCategory.Internals, + "" + ); + create().gauge( + iface + "_tx_bytes", + () -> reader.getBytesTransmitted(iface), + MetricCategory.Internals, + "" + ); + create().gauge( + iface + "_tx_packets", + () -> reader.getPacketsTransmitted(iface), + MetricCategory.Internals, + "" + ); + } + } + + private void registerLoadAvgMetrics(ClientSystemMetricChecker metricsChecker) { + LoadAvgReader reader = new LoadAvgReader(); + if (!reader.fileExists()) + return; + + NBFunctionGauge load1m = create().gauge( + "loadavg_1min", + reader::getOneMinLoadAverage, + MetricCategory.Internals, + "the 1 minute load average of the test client client" + ); + metricsChecker.addMetricToCheck(load1m, 50.0); + + NBFunctionGauge load5m = create().gauge( + "loadavg_5min", + reader::getFiveMinLoadAverage, + MetricCategory.Internals, + "the 5 minute load average of the test client client" + ); + metricsChecker.addMetricToCheck(load5m, 50.0); + + NBFunctionGauge load15m = create().gauge( + "loadavg_15min", + reader::getFifteenMinLoadAverage, + MetricCategory.Internals, + "the 15 minute load average of the test client" + ); + metricsChecker.addMetricToCheck(load15m, 50.0); + // add checking for CPU load averages; TODO: Modify thresholds + + } + + private void registerMemInfoMetrics(ClientSystemMetricChecker metricsChecker) { + MemInfoReader reader = new MemInfoReader(); + if (!reader.fileExists()) + return; + + NBMetricGauge memTotalGauge = create().gauge( + "mem_total", + reader::getMemTotalkB, + MetricCategory.Internals, + "" + + ); + NBMetricGauge memUsedGauge = create().gauge( + "mem_used", + reader::getMemUsedkB, + MetricCategory.Internals, + "" + ); + NBMetricGauge memFreeGauge = create().gauge( + "mem_free", + reader::getMemFreekB, + MetricCategory.Internals, + "" + ); + NBMetricGauge memAvailableGauge = create().gauge( + "mem_available", + reader::getMemAvailablekB, + MetricCategory.Internals, + "" + ); + NBMetricGauge memCachedGauge = create().gauge( + "mem_cache", + reader::getMemCachedkB, + MetricCategory.Internals, + "" + ); + NBMetricGauge memBufferedGauge = create().gauge( + "mem_buffered", + reader::getMemBufferskB, + MetricCategory.Internals, + "" + ); + // add checking for percent memory used at some given time; TODO: Modify percent threshold + metricsChecker.addRatioMetricToCheck( + memUsedGauge, + memTotalGauge, + 90.0, + false + ); + + NBMetricGauge swapTotalGauge = create().gauge( + "swap_total", + reader::getSwapTotalkB, + MetricCategory.Internals, + "" + ); + NBMetricGauge swapFreeGauge = create().gauge( + "swap_free", + reader::getSwapFreekB, + MetricCategory.Internals, + "" + ); + NBMetricGauge swapUsedGauge = create().gauge( + "swap_used", + reader::getSwapUsedkB, + MetricCategory.Internals, + "" + ); + } + + +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/UnresolvedCommand.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/UnresolvedCommand.java new file mode 100644 index 000000000..13e69bf67 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/session/UnresolvedCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.session; + +import io.nosqlbench.engine.cmdstream.Cmd; +import scala.concurrent.impl.FutureConvertersImpl; + +public class UnresolvedCommand extends RuntimeException { + private final Cmd cmd; + + public UnresolvedCommand(Cmd cmd, Throwable cause) { + super(cause); + this.cmd = cmd; + } + + public String toString() { + final String helpmsg = """ + Could not recognize command 'ARG'. + This means that all of the following searches for a compatible command failed: + 1. commands: no scenario command named 'ARG' is known. (start, run, await, ...) + 2. scripts: no auto script named './scripts/auto/ARG.js' in the local filesystem. + 3. scripts: no auto script named 'scripts/auto/ARG.js' was found in the PROG binary. + 4. workloads: no workload file named ARG[.yaml] was found in the local filesystem, even in include paths INCLUDES. + 5. workloads: no workload file named ARG[.yaml] was bundled in PROG binary, even in include paths INCLUDES. + 6. apps: no application named ARG was bundled in PROG. + + You can discover available ways to invoke PROG by using the various --list-* commands: + [ --list-commands, --list-scripts, --list-workloads (and --list-scenarios), --list-apps ] + """ + .replaceAll("ARG", getCmdName()) + .replaceAll("PROG", "nb5"); + + return super.toString() + "\nadditional diagnostics:\n" + helpmsg; + } + + private String getCmdName() { + String impl = cmd.getArgMap().get("_impl"); + if (impl!=null) return impl; + return cmd.toString(); + } + +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/logging/LoggerConfig.java b/engine-core/src/main/java/io/nosqlbench/engine/core/logging/NBLoggerConfig.java similarity index 63% rename from engine-core/src/main/java/io/nosqlbench/engine/core/logging/LoggerConfig.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/logging/NBLoggerConfig.java index 1fe369603..dc9c2d73f 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/logging/LoggerConfig.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/logging/NBLoggerConfig.java @@ -19,11 +19,10 @@ package io.nosqlbench.engine.core.logging; import io.nosqlbench.nb.api.logging.NBLogLevel; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.appender.ConsoleAppender; -import org.apache.logging.log4j.core.appender.FileAppender; -import org.apache.logging.log4j.core.appender.RollingFileAppender; +import org.apache.logging.log4j.core.appender.*; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationSource; @@ -40,6 +39,8 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; //@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY) @@ -52,13 +53,14 @@ import java.util.stream.Collectors; * * @see
Pattern Layout */ -public class LoggerConfig extends ConfigurationFactory { +public class NBLoggerConfig extends ConfigurationFactory { + public static final String SESSION_APPENDER = "SESSION_APPENDER"; public static Map STANDARD_FORMATS = Map.of( - "TERSE", "%8r %-5level [%t] %-12logger{0} %msg%n%throwable", - "VERBOSE", "%d{DEFAULT}{GMT} [%t] %logger %-5level: %msg%n%throwable", - "TERSE-ANSI", "%8r %highlight{%-5level} %style{%C{1.} [%t] %-12logger{0}} %msg%n%throwable", - "VERBOSE-ANSI", "%d{DEFAULT}{GMT} [%t] %highlight{%logger %-5level}: %msg%n%throwable" + "TERSE", "%8r %-5level [%t] %-12logger{0} %msg%n%throwable", + "VERBOSE", "%d{DEFAULT}{GMT} [%t] %logger %-5level: %msg%n%throwable", + "TERSE-ANSI", "%8r %highlight{%-5level} %style{%C{1.} [%t] %-12logger{0}} %msg%n%throwable", + "VERBOSE-ANSI", "%d{DEFAULT}{GMT} [%t] %highlight{%logger %-5level}: %msg%n%throwable" ); /** @@ -66,8 +68,8 @@ public class LoggerConfig extends ConfigurationFactory { * we squelch them to some reasonable level so they aren't a nuisance. */ public static Map BUILTIN_OVERRIDES = Map.of( - // ERROR StatusConsoleListener Unable to locate appender "SCENARIO_APPENDER" for logger config "oshi.util" - "oshi.util", Level.INFO + // ERROR StatusConsoleListener Unable to locate appender "SESSION_APPENDER" for logger config "oshi.util" + "oshi.util", Level.INFO ); /** @@ -92,25 +94,25 @@ public class LoggerConfig extends ConfigurationFactory { private boolean isDedicatedVerificationLoggerEnabled = false; - public LoggerConfig() { + public NBLoggerConfig() { } - public LoggerConfig setAnsiEnabled(boolean ansiEnabled) { + public NBLoggerConfig setAnsiEnabled(boolean ansiEnabled) { this.ansiEnabled = ansiEnabled; return this; } - public LoggerConfig setConsoleLevel(NBLogLevel level) { + public NBLoggerConfig setConsoleLevel(NBLogLevel level) { this.consoleLevel = level; return this; } - public LoggerConfig setLogfileLevel(NBLogLevel level) { + public NBLoggerConfig setLogfileLevel(NBLogLevel level) { this.fileLevel = level; return this; } - public LoggerConfig setDedicatedVerificationLogger(boolean enabled) { + public NBLoggerConfig setDedicatedVerificationLogger(boolean enabled) { this.isDedicatedVerificationLoggerEnabled = enabled; return this; } @@ -128,7 +130,7 @@ public class LoggerConfig extends ConfigurationFactory { } } - public LoggerConfig setMaxLogs(int maxLogfiles) { + public NBLoggerConfig setMaxLogs(int maxLogfiles) { this.maxLogfiles = maxLogfiles; return this; } @@ -158,20 +160,20 @@ public class LoggerConfig extends ConfigurationFactory { builder.setStatusLevel(internalLoggingStatusThreshold); builder.add( - builder.newFilter( - "ThresholdFilter", - Filter.Result.ACCEPT, - Filter.Result.NEUTRAL - ).addAttribute("level", builderThresholdLevel) + builder.newFilter( + "ThresholdFilter", + Filter.Result.ACCEPT, + Filter.Result.NEUTRAL + ).addAttribute("level", builderThresholdLevel) ); // CONSOLE appender AppenderComponentBuilder appenderBuilder = - builder.newAppender("console", "CONSOLE") - .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("console", "CONSOLE") + .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add(builder.newLayout("PatternLayout") - .addAttribute("pattern", consolePattern)); + .addAttribute("pattern", consolePattern)); // appenderBuilder.add( // builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) @@ -181,8 +183,15 @@ public class LoggerConfig extends ConfigurationFactory { // Log4J internal logging builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) - .add(builder.newAppenderRef("console")) - .addAttribute("additivity", false)); + .add(builder.newAppenderRef("console")) + .addAttribute("additivity", false)); + + if (sessionName == null) { + sessionName = "LOGINIT"; + System.out.println("Session was not set in logger config. If you are getting this error, then it means" + + " that the session has not initialized properly, and that some unexpected error occurred before the logging" + + " system was initialized. Look for errors prior to this."); + } if (sessionName != null) { @@ -196,25 +205,32 @@ public class LoggerConfig extends ConfigurationFactory { // LOGFILE appender LayoutComponentBuilder logfileLayout = builder.newLayout("PatternLayout") - .addAttribute("pattern", logfilePattern); + .addAttribute("pattern", logfilePattern); String logfilePath = loggerDir.resolve(getFileBase() + ".log").toString(); this.logfileLocation = logfilePath; String archivePath = loggerDir.resolve(getFileBase() + "-TIMESTAMP.log.gz").toString() - .replaceAll("TIMESTAMP", "%d{MM-dd-yy}"); + .replaceAll("TIMESTAMP", "%d{MM-dd-yy}"); - ComponentBuilder triggeringPolicy = builder.newComponent("Policies") - .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?")) - .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M")); +// ComponentBuilder triggeringPolicy = builder.newComponent("Policies") +// .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?")) +// .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M")); +// AppenderComponentBuilder logsAppenderBuilder = +// builder.newAppender("SESSION_APPENDER", RollingFileAppender.PLUGIN_NAME) +// .addAttribute("fileName", logfilePath) +// .addAttribute("filePattern", archivePath) +// .addAttribute("append", false) +// .add(logfileLayout) +// .addComponent(triggeringPolicy); AppenderComponentBuilder logsAppenderBuilder = - builder.newAppender("SCENARIO_APPENDER", RollingFileAppender.PLUGIN_NAME) - .addAttribute("fileName", logfilePath) - .addAttribute("filePattern", archivePath) - .addAttribute("append", false) - .add(logfileLayout) - .addComponent(triggeringPolicy); + builder.newAppender("SESSION_APPENDER", FileAppender.PLUGIN_NAME) + .addAttribute("fileName", logfilePath) +// .addAttribute("filePattern", archivePath) + .addAttribute("append", false) + .add(logfileLayout); + builder.add(logsAppenderBuilder); if (isDedicatedVerificationLoggerEnabled) { @@ -222,46 +238,89 @@ public class LoggerConfig extends ConfigurationFactory { } // TODO: build stop-words transcoder, add padding to end of alphabet, substitute stopwords - if (fileLevel.isInRange(Level.INFO,Level.TRACE)) { + if (fileLevel.isInRange(Level.INFO, Level.TRACE)) { attachAuxLogger(builder, "RUNTIME", fileLevel); } rootBuilder.add( - builder.newAppenderRef("SCENARIO_APPENDER") - .addAttribute("level", fileLevel) + builder.newAppenderRef("SESSION_APPENDER") + .addAttribute("level", fileLevel) ); } rootBuilder.add( - builder.newAppenderRef("console") - .addAttribute("level", - consoleLevel - ) + builder.newAppenderRef("console") + .addAttribute("level", + consoleLevel + ) ); builder.add(rootBuilder); BUILTIN_OVERRIDES.forEach((k, v) -> { builder.add(builder.newLogger(k, v) - .add(builder.newAppenderRef("console")) - .add(builder.newAppenderRef("SCENARIO_APPENDER")) - .addAttribute("additivity", true)); + .add(builder.newAppenderRef("console")) + .add(builder.newAppenderRef("SESSION_APPENDER")) + .addAttribute("additivity", true)); }); logLevelOverrides.forEach((k, v) -> { Level olevel = Level.valueOf(v); builder.add(builder.newLogger(k, olevel) - .add(builder.newAppenderRef("console")) - .add(builder.newAppenderRef("SCENARIO_APPENDER")) - .addAttribute("additivity", true)); + .add(builder.newAppenderRef("console")) + .add(builder.newAppenderRef(SESSION_APPENDER)) + .addAttribute("additivity", true)); }); BuiltConfiguration builtConfig = builder.build(); + updateLink(logfileLocation); return builtConfig; } + private void updateLink(String logfileLocation) { + Path logpath = Path.of(logfileLocation); + Path logdir = logpath.getParent(); + if (Files.exists(logpath)) { + String basename = logpath.getFileName().toString(); + String linkname = basename.replace(getSessionName() + "_", ""); + Path linkPath = logdir.resolve(linkname); + try { + Files.deleteIfExists(linkPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + Path targetPath = Path.of(basename); + + try { + Files.createSymbolicLink( + linkPath, + targetPath.getFileName() + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + System.out.println("Unable to find " + logfileLocation + " for symlink update"); + } + } + + private void attachFileRotationObserver(BuiltConfiguration builtConfig) { + Appender appender = builtConfig.getAppender(SESSION_APPENDER); + if (appender instanceof AbstractOutputStreamAppender sa) { + OutputStreamManager manager = sa.getManager(); + + } + + } + + private final static Pattern validSessionName = Pattern.compile("^[a-zA-Z~][a-zA-Z0-9_~]*$"); + private String getFileBase() { - return getSessionName().replaceAll("\\s", "_"); + Matcher matcher = validSessionName.matcher(getSessionName()); + if (!matcher.matches()) { + throw new RuntimeException("Session name invalid. Must follow pattern " + validSessionName.pattern()); + } + return getSessionName() + "_session"; } private String getSessionName() { @@ -288,7 +347,7 @@ public class LoggerConfig extends ConfigurationFactory { if (!Files.exists(loggerDir)) { try { FileAttribute> attrs = PosixFilePermissions.asFileAttribute( - PosixFilePermissions.fromString("rwxrwx---") + PosixFilePermissions.fromString("rwxrwx---") ); Path directory = Files.createDirectory(loggerDir, attrs); } catch (Exception e) { @@ -298,24 +357,24 @@ public class LoggerConfig extends ConfigurationFactory { ConfigurationFactory.setConfigurationFactory(this); } - public LoggerConfig setConsolePattern(String consoleLoggingPattern) { + public NBLoggerConfig setConsolePattern(String consoleLoggingPattern) { consoleLoggingPattern = (ansiEnabled && STANDARD_FORMATS.containsKey(consoleLoggingPattern + "-ANSI")) - ? consoleLoggingPattern + "-ANSI" : consoleLoggingPattern; + ? consoleLoggingPattern + "-ANSI" : consoleLoggingPattern; this.consolePattern = STANDARD_FORMATS.getOrDefault(consoleLoggingPattern, consoleLoggingPattern); return this; } - public LoggerConfig setLogfilePattern(String logfileLoggingPattern) { + public NBLoggerConfig setLogfilePattern(String logfileLoggingPattern) { logfileLoggingPattern = (logfileLoggingPattern.endsWith("-ANSI") && STANDARD_FORMATS.containsKey(logfileLoggingPattern)) - ? logfileLoggingPattern.substring(logfileLoggingPattern.length() - 5) : logfileLoggingPattern; + ? logfileLoggingPattern.substring(logfileLoggingPattern.length() - 5) : logfileLoggingPattern; this.logfileLocation = STANDARD_FORMATS.getOrDefault(logfileLoggingPattern, logfileLoggingPattern); return this; } - public LoggerConfig setLoggerLevelOverrides(Map logLevelOverrides) { + public NBLoggerConfig setLoggerLevelOverrides(Map logLevelOverrides) { this.logLevelOverrides = logLevelOverrides; return this; } @@ -324,12 +383,12 @@ public class LoggerConfig extends ConfigurationFactory { return logLevelOverrides; } - public LoggerConfig setSessionName(String sessionName) { + public NBLoggerConfig setSessionName(String sessionName) { this.sessionName = sessionName; return this; } - public LoggerConfig purgeOldFiles(Logger logger) { + public NBLoggerConfig purgeOldFiles(Logger logger) { if (maxLogfiles == 0) { logger.debug("Not purging old files, since maxLogFiles is 0."); return this; @@ -354,9 +413,9 @@ public class LoggerConfig extends ConfigurationFactory { } List toDelete = filesList.stream() - .sorted(fileTimeComparator) - .limit(remove) - .collect(Collectors.toList()); + .sorted(fileTimeComparator) + .limit(remove) + .collect(Collectors.toList()); for (File file : toDelete) { logger.info(() -> "removing extra logfile: " + file.getPath()); @@ -384,18 +443,20 @@ public class LoggerConfig extends ConfigurationFactory { return logfileLocation; } - public LoggerConfig setLogsDirectory(Path logsDirectory) { + public NBLoggerConfig setLogsDirectory(Path logsDirectory) { this.loggerDir = logsDirectory; return this; } private void attachAuxLogger(ConfigurationBuilder builder, String loggerName, Level fileLevel) { - String appenderName = loggerName+(("_APPENDER").toUpperCase()); - String fileName = loggerDir.resolve(getFileBase() + "_"+loggerName+".log").toString().toLowerCase(); + String appenderName = loggerName + (("_APPENDER").toUpperCase()); + Path logPath = loggerDir.resolve(getFileBase() + "_" + loggerName.toLowerCase() + ".log"); + Path linkPath = loggerDir.resolve(loggerName.toLowerCase() + ".log"); + var appender = builder .newAppender(appenderName, FileAppender.PLUGIN_NAME) .addAttribute("append", false) - .addAttribute("fileName", fileName) + .addAttribute("fileName", logPath.toString()) .add(builder .newLayout("PatternLayout") .addAttribute("pattern", "%d %p %C{1.} [%t] %m%n") @@ -407,5 +468,13 @@ public class LoggerConfig extends ConfigurationFactory { builder.add(appender); builder.add(logger); + try { + Files.deleteIfExists(linkPath); + Files.createSymbolicLink(linkPath, logPath.getFileName()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } } diff --git a/engine-core/src/test/java/io/nosqlbench/engine/api/activityapi/ratelimits/SimRateSpecTest.java b/engine-core/src/test/java/io/nosqlbench/engine/api/activityapi/ratelimits/SimRateSpecTest.java index d6438d6a6..1ca9a6c1c 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/api/activityapi/ratelimits/SimRateSpecTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/api/activityapi/ratelimits/SimRateSpecTest.java @@ -16,7 +16,7 @@ package io.nosqlbench.engine.api.activityapi.ratelimits; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/HistoIntervalLoggerTest.java b/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/HistoIntervalLoggerTest.java index b97187cdd..637fc2804 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/HistoIntervalLoggerTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/HistoIntervalLoggerTest.java @@ -16,6 +16,7 @@ package io.nosqlbench.engine.api.metrics; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.engine.metrics.DeltaHdrHistogramReservoir; import io.nosqlbench.nb.api.engine.metrics.HistoIntervalLogger; @@ -47,7 +48,14 @@ public class HistoIntervalLoggerTest { final int significantDigits = 4; NBMetricHistogram NBHistogram = new NBMetricHistogram( - NBLabels.forKV("name", "histo1"), new DeltaHdrHistogramReservoir(NBLabels.forKV("name", "histo1"), significantDigits)); + NBLabels.forKV("name", "histo1"), + new DeltaHdrHistogramReservoir( + NBLabels.forKV("name", "histo1"), + significantDigits + ), + "test basic logger", + MetricCategory.Verification + ); hil.onHistogramAdded("histo1", NBHistogram); diff --git a/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/NBMetricHistogramTest.java b/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/NBMetricHistogramTest.java index ca70e5c07..5327f136f 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/NBMetricHistogramTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/api/metrics/NBMetricHistogramTest.java @@ -16,6 +16,7 @@ package io.nosqlbench.engine.api.metrics; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.engine.metrics.ConvenientSnapshot; import io.nosqlbench.nb.api.engine.metrics.DeltaHdrHistogramReservoir; @@ -28,8 +29,15 @@ public class NBMetricHistogramTest { @Test public void testNicerHistogramValues() { - NBMetricHistogram nh = new NBMetricHistogram(NBLabels.forKV("name","testhisto"), new DeltaHdrHistogramReservoir( - NBLabels.forKV("name", "testhisto"), 4)); + NBMetricHistogram nh = new NBMetricHistogram( + NBLabels.forKV("name","testhisto"), + new DeltaHdrHistogramReservoir( + NBLabels.forKV("name", "testhisto"), + 4 + ), + "test nicer histogram values", + MetricCategory.Verification + ); for (int i = 1; 100 >= i; i++) { nh.update(i); } diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/CmdParserTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/CmdParserTest.java index 66272b9ab..086d0dfe4 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/CmdParserTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/CmdParserTest.java @@ -19,6 +19,7 @@ package io.nosqlbench.engine.core.lifecycle.session; import io.nosqlbench.engine.cmdstream.Cmd; import io.nosqlbench.engine.cmdstream.CmdType; import io.nosqlbench.nb.api.errors.BasicError; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.LinkedList; @@ -34,36 +35,37 @@ class CmdParserTest { public void testSingleCommand() { List cmds = CmdParser.parse("testcmd42"); assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect); - assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd42"); + assertThat(cmds.getFirst().getCmdType()).isEqualTo(CmdType.indirect); + assertThat(cmds.getFirst().getArgValue("_impl")).isEqualTo("testcmd42"); } @Test public void testSingleCommandWithArgs() { List cmds = CmdParser.parse("testcmd43 param1=value1"); assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect); - assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd43"); - assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1"); + assertThat(cmds.getFirst().getCmdType()).isEqualTo(CmdType.indirect); + assertThat(cmds.getFirst().getArgValue("_impl")).isEqualTo("testcmd43"); + assertThat(cmds.getFirst().getArgValue("param1")).isEqualTo("value1"); } @Test public void testSingleDquotedArg() { List cmds = CmdParser.parse("testcmd44 param1=\"value1\""); assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect); - assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd44"); - assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1"); + assertThat(cmds.getFirst().getCmdType()).isEqualTo(CmdType.indirect); + assertThat(cmds.getFirst().getArgValue("_impl")).isEqualTo("testcmd44"); + assertThat(cmds.getFirst().getArgValue("param1")).isEqualTo("value1"); } + @Disabled @Test public void testSpecialSymbolValue() { List cmds = CmdParser.parse("start param1="+ CmdParser.SYMBOLS+ " param2='"+ CmdParser.SYMBOLS+ "' param3=\""+ CmdParser.SYMBOLS+ "\""); assertThat(cmds).hasSize(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start); - assertThat(cmds.get(0).getArgValue("param1")).isEqualTo(CmdParser.SYMBOLS); - assertThat(cmds.get(0).getArgValue("param2")).isEqualTo(CmdParser.SYMBOLS); - assertThat(cmds.get(0).getArgValue("param3")).isEqualTo(CmdParser.SYMBOLS); +// assertThat(cmds.getFirst().getCmdType()).isEqualTo(CmdType.start); + assertThat(cmds.getFirst().getArgValue("param1")).isEqualTo(CmdParser.SYMBOLS); + assertThat(cmds.getFirst().getArgValue("param2")).isEqualTo(CmdParser.SYMBOLS); + assertThat(cmds.getFirst().getArgValue("param3")).isEqualTo(CmdParser.SYMBOLS); } @Test @@ -76,23 +78,24 @@ class CmdParserTest { "an error should be thrown if a named parameter is specified without a prior command."); } + @Disabled @Test public void testThatSymbolsAreQuotedInStringForm() { List cmds = CmdParser.parse("start param1=value1 param2='~should be quoted'"); assertThat(cmds.size()).isEqualTo(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start); - assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1"); - assertThat(cmds.get(0).getArgValue("param2")).isEqualTo("~should be quoted"); - assertThat(cmds.get(0).toString()).isEqualTo("start param1=value1 param2='~should be quoted'"); +// assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start); + assertThat(cmds.getFirst().getArgValue("param1")).isEqualTo("value1"); + assertThat(cmds.getFirst().getArgValue("param2")).isEqualTo("~should be quoted"); + assertThat(cmds.getFirst().toString()).isEqualTo("start param1=value1 param2='~should be quoted'"); } @Test public void testBasicArgvParser() { LinkedList cmds = CmdParser.parseArgvCommands(new LinkedList<>(List.of("_cmd4", "param1=value1"))); assertThat(cmds.size()).isEqualTo(1); - assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect); - assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("_cmd4"); - assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1"); + assertThat(cmds.getFirst().getCmdType()).isEqualTo(CmdType.indirect); + assertThat(cmds.getFirst().getArgValue("_impl")).isEqualTo("_cmd4"); + assertThat(cmds.getFirst().getArgValue("param1")).isEqualTo("value1"); } } diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionTest.java new file mode 100644 index 000000000..9d37ddebb --- /dev/null +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/lifecycle/session/NBSessionTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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.engine.core.lifecycle.session; + +import io.nosqlbench.engine.cmdstream.Cmd; +import io.nosqlbench.engine.cmdstream.CmdArg; +import io.nosqlbench.nb.api.labels.NBLabeledElement; +import io.nosqlbench.nb.api.labels.NBLabels; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class NBSessionTest implements NBLabeledElement { + + @Test + public void testThatSessionShortCircuitsCommandErrors() { + NBSession session = new NBSession(this, "session_name", Map.of()); + Cmd c1 = new Cmd("test_ok", Map.of("test_ok", CmdArg.of("test_ok","key1","===","value1"))); + Cmd c2 = new Cmd("test_error", Map.of()); + Cmd c3 = new Cmd("test_ok", Map.of("test_ok", CmdArg.of("test_ok","key2","===","value2"))); + List cmdStream = List.of(c1, c2, c3); + + session.apply(cmdStream); + } + + @Override + public NBLabels getLabels() { + return NBLabels.forKV("testing","session"); + } +} diff --git a/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateSanityTest.java b/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateSanityTest.java index 761624069..a2498a4a3 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateSanityTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateSanityTest.java @@ -18,8 +18,8 @@ package io.nosqlbench.engine.sandbox; import io.nosqlbench.nb.api.config.standard.TestComponent; import io.nosqlbench.nb.api.components.core.NBComponent; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRate; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRate; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import org.junit.jupiter.api.Disabled; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; diff --git a/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateTest.java b/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateTest.java index dddef78be..17a9551cd 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/sandbox/SimRateTest.java @@ -18,8 +18,8 @@ package io.nosqlbench.engine.sandbox; import io.nosqlbench.nb.api.config.standard.TestComponent; import io.nosqlbench.nb.api.components.core.NBComponent; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRate; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRate; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openjdk.jmh.annotations.*; diff --git a/mvn-defaults/pom.xml b/mvn-defaults/pom.xml index 384654081..16508ceea 100644 --- a/mvn-defaults/pom.xml +++ b/mvn-defaults/pom.xml @@ -80,7 +80,7 @@ org.apache.groovy groovy - 4.0.15 + 4.0.17 org.snakeyaml @@ -96,12 +96,12 @@ net.java.dev.jna jna - 5.13.0 + 5.14.0 net.java.dev.jna jna-platform - 5.13.0 + 5.14.0 org.junit.jupiter @@ -122,12 +122,12 @@ io.dropwizard.metrics metrics-graphite - 4.2.22 + 4.2.23 io.dropwizard.metrics metrics-core - 4.2.22 + 4.2.23 org.apache.commons @@ -188,7 +188,7 @@ io.netty netty-handler - 4.1.101.Final + 4.1.104.Final io.netty @@ -280,7 +280,7 @@ com.github.oshi oshi-core-java11 - 6.4.7 + 6.4.9 com.google.code.gson @@ -290,7 +290,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.595 + 1.12.622 com.elega9t @@ -330,7 +330,7 @@ org.mvel mvel2 - 2.5.0.Final + 2.5.1.Final org.antlr @@ -361,7 +361,7 @@ org.graalvm.sdk graal-sdk - 23.1.1 + 23.0.2 org.graalvm.js @@ -397,30 +397,30 @@ org.apache.logging.log4j log4j-api - 2.22.0 + 2.22.1 org.apache.logging.log4j log4j-core - 2.22.0 + 2.22.1 org.apache.logging.log4j log4j-slf4j-impl - 2.22.0 + 2.22.1 org.apache.logging.log4j log4j-slf4j2-impl - 2.22.0 + 2.22.1 org.apache.logging.log4j log4j-jcl - 2.22.0 + 2.22.1 @@ -641,7 +641,6 @@ 21 ${javadoc.name} ${javadoc.name} - false false false false diff --git a/nb-annotations/src/main/java/io/nosqlbench/nb/annotations/ServiceSelector.java b/nb-annotations/src/main/java/io/nosqlbench/nb/annotations/ServiceSelector.java index 929234f0f..082a5e431 100644 --- a/nb-annotations/src/main/java/io/nosqlbench/nb/annotations/ServiceSelector.java +++ b/nb-annotations/src/main/java/io/nosqlbench/nb/annotations/ServiceSelector.java @@ -23,15 +23,16 @@ import java.util.function.Predicate; import java.util.stream.Collectors; /** - * A service loader filter which works with {@link io.nosqlbench.nb.annotations.Service} to load a named service. - * This version requires the caller to provide the service loader instance, since it is now caller sensitive. - * + *

A service loader filter which works with {@link io.nosqlbench.nb.annotations.Service} to load a named service. + * This version requires the caller to provide the service loader instance, since it is now caller sensitive.

+ *

* Use it like this:

{@code
  *       ResultValueFilterType filterType =
  *           ServiceSelector.of("core", ServiceLoader.load(ResultValueFilterType.class)).get();
  * }
* - * @param The service type + * @param + * The service type */ public class ServiceSelector implements Predicate> { private final String name; @@ -61,11 +62,13 @@ public class ServiceSelector implements Predicate getOneProvider() { List> 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 " + - (providers.stream().map(s -> s.getClass().getSimpleName())).collect(Collectors.joining(",")) + " (" + providers.stream().count() + ")"); + if (providers.size() != 1) { + throw new RuntimeException( + "You requested exactly one instance of a service by name '" + name + "', but got " + + (providers.stream().map(s -> s.getClass().getSimpleName())).collect(Collectors.joining(",")) + " (" + providers.size() + ")" + ); } - return providers.get(0); + return providers.getFirst(); } public T getOne() { @@ -73,7 +76,7 @@ public class ServiceSelector implements Predicate> getAllProviders() { - List> providers = loader + return loader .stream() .peek(l -> { if (l.type().getAnnotation(Service.class) == null) { @@ -87,8 +90,8 @@ public class ServiceSelector implements Predicate l.type().getAnnotation(Service.class) != null) .filter(l -> l.type().getAnnotation(Service.class).selector().equals(name)) .toList(); - return providers; } + public List getAll() { List> providers = getAllProviders(); return providers.stream() @@ -99,7 +102,7 @@ public class ServiceSelector implements Predicate get() { List services = getAll(); if (services.size() == 1) { - return Optional.of(services.get(0)); + return Optional.of(services.getFirst()); } else { return Optional.empty(); } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3ClientCache.java b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3ClientCache.java index 97f4a772b..df7e2b0b1 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3ClientCache.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3ClientCache.java @@ -38,9 +38,10 @@ public class S3ClientCache { } public AmazonS3 get(S3UrlFields fields) { - AmazonS3 s3 = cache.computeIfAbsent(fields.getCredentialsFingerprint(), - cfp -> createAuthorizedClient(fields)); - return s3; + return cache.computeIfAbsent( + fields.getCredentialsFingerprint(), + cfp -> createAuthorizedClient(fields) + ); } private AmazonS3 createAuthorizedClient(S3UrlFields fields) { diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlConnection.java b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlConnection.java index 1f24b3147..ce2c5b05a 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlConnection.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlConnection.java @@ -34,7 +34,7 @@ public class S3UrlConnection extends URLConnection { } @Override - public InputStream getInputStream() throws IOException { + public InputStream getInputStream() { S3UrlFields fields = new S3UrlFields(url); AmazonS3 s3 = clientCache.get(fields); S3Object object = s3.getObject(fields.bucket, fields.key); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlFields.java b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlFields.java index 08c4534c1..f512da81e 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlFields.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlFields.java @@ -16,9 +16,7 @@ package io.nosqlbench.nb.api.addins.s3.s3urlhandler; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLDecoder; +import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -31,13 +29,12 @@ public class S3UrlFields { private final String endpoint; public static S3UrlFields fromURLString(String urlString) { - URL url = null; try { - url = new URL(urlString); - } catch (MalformedURLException e) { + URL url = new URI(urlString).toURL(); + return new S3UrlFields(url); + } catch (MalformedURLException | URISyntaxException e) { throw new RuntimeException(e); } - return new S3UrlFields(url); } public S3UrlFields(URL url) { @@ -94,11 +91,11 @@ public class S3UrlFields { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - S3UrlFields that = (S3UrlFields) o; + CredentialsFingerprint that = (CredentialsFingerprint) o; - if (!Objects.equals(fields.secretKey, that.secretKey)) return false; - if (!Objects.equals(fields.accessKey, that.accessKey)) return false; - return Objects.equals(fields.endpoint, that.endpoint); + if (!Objects.equals(fields.secretKey, that.fields.secretKey)) return false; + if (!Objects.equals(fields.accessKey, that.fields.accessKey)) return false; + return Objects.equals(fields.endpoint, that.fields.endpoint); } @Override diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandler.java b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandler.java index 3431c9776..5345cff4b 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandler.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandler.java @@ -23,15 +23,13 @@ import java.net.URLStreamHandler; public class S3UrlStreamHandler extends URLStreamHandler { private final S3ClientCache clientCache; - private final String protocol; - public S3UrlStreamHandler(S3ClientCache clientCache, String protocol) { + public S3UrlStreamHandler(S3ClientCache clientCache) { this.clientCache = clientCache; - this.protocol = protocol; } @Override - protected S3UrlConnection openConnection(URL url) throws IOException { + protected S3UrlConnection openConnection(URL url) { return new S3UrlConnection(clientCache, url); } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandlerProvider.java b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandlerProvider.java index 5d135e890..19d1d80f7 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandlerProvider.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/addins/s3/s3urlhandler/S3UrlStreamHandlerProvider.java @@ -30,7 +30,7 @@ public class S3UrlStreamHandlerProvider extends URLStreamHandlerProvider { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("s3".equals(protocol)) { - return new S3UrlStreamHandler(clientCache, protocol); + return new S3UrlStreamHandler(clientCache); } return null; } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotation.java b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotation.java index 4c792d236..baeaefc5b 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotation.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotation.java @@ -56,7 +56,7 @@ public interface Annotation extends NBLabeledElement { * Annotations must be associated with a processing layer in NoSQLBench. * For more details on layers, see {@link Layer} * - * @return + * @return the {@link Layer} to which the annotations applies */ Layer getLayer(); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotator.java b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotator.java index 1fb73615d..4c273b484 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotator.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/Annotator.java @@ -23,12 +23,14 @@ package io.nosqlbench.nb.api.annotations; public interface Annotator { /** - * Submit an annotation to some type of annotation store, logging or eventing mechanism. + *

Submit an annotation to some type of annotation store, logging or eventing mechanism. * Implementations of this service are responsible for mapping the scenario and labels * into appropriate key data, and the details in to a native payload. The least surprising * and most obvious mapping should be used in each case. + *

* - * For details on constructing a useful annotation to submit to this service, see {@link Annotation#newBuilder()} + *

For details on constructing a useful annotation to submit to this service, see {@link Annotation#newBuilder()} + *

*/ void recordAnnotation(Annotation annotation); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/MutableAnnotation.java b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/MutableAnnotation.java index 20ba99e72..517fed31d 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/MutableAnnotation.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/annotations/MutableAnnotation.java @@ -35,7 +35,7 @@ import java.util.function.Function; public class MutableAnnotation implements Annotation { private final static Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - private String session = "SESSION_UNNAMED"; + public static String session = "SESSION_UNNAMED"; private final ZoneId GMT = ZoneId.of("GMT"); private final LinkedList> labelfuncs = new LinkedList<>(); @@ -79,7 +79,7 @@ public class MutableAnnotation implements Annotation { } public void setSession(String sessionName) { - this.session = sessionName; + session = sessionName; } public void setStartMillis(long intervalStart) { diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponent.java index fcd0efbf5..c8e978394 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponent.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponent.java @@ -21,7 +21,6 @@ import io.nosqlbench.nb.api.components.events.ComponentOutOfScope; import io.nosqlbench.nb.api.components.events.DownEvent; import io.nosqlbench.nb.api.components.events.NBEvent; import io.nosqlbench.nb.api.components.events.UpEvent; -import io.nosqlbench.nb.api.config.params.ElementData; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetric; import io.nosqlbench.nb.api.labels.NBLabels; import org.apache.logging.log4j.LogManager; @@ -30,30 +29,38 @@ import org.apache.logging.log4j.Logger; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class NBBaseComponent extends NBBaseComponentMetrics implements NBComponent, NBTokenWords { +public class NBBaseComponent extends NBBaseComponentMetrics implements NBComponent, NBTokenWords, NBComponentTimeline { private final static Logger logger = LogManager.getLogger("RUNTIME"); protected final NBComponent parent; protected final NBLabels labels; private final List children = new ArrayList<>(); - private long endAt=0L; - private final long startAt; protected NBMetricsBuffer metricsBuffer = new NBMetricsBuffer(); protected boolean bufferOrphanedMetrics = false; private ConcurrentHashMap props = new ConcurrentHashMap<>(); + protected Exception error; + protected long started_ns, teardown_ns, closed_ns, errored_ns, started_epoch_ms; + protected NBInvokableState state = NBInvokableState.STARTING; public NBBaseComponent(NBComponent parentComponent) { this(parentComponent, NBLabels.forKV()); } public NBBaseComponent(NBComponent parentComponent, NBLabels componentSpecificLabelsOnly) { + this.started_ns = System.nanoTime(); + this.started_epoch_ms = System.currentTimeMillis(); this.labels = componentSpecificLabelsOnly; - this.startAt = System.nanoTime(); if (parentComponent != null) { parent = parentComponent; parent.attachChild(this); } else { parent = null; } + state = (state==NBInvokableState.ERRORED) ? state : NBInvokableState.RUNNING; + } + + public NBBaseComponent(NBComponent parentComponent, NBLabels componentSpecificLabelsOnly, Map props) { + this(parentComponent,componentSpecificLabelsOnly); + props.forEach(this::setComponentProp); } @Override @@ -117,30 +124,40 @@ public class NBBaseComponent extends NBBaseComponentMetrics implements NBCompone @Override public final void close() throws RuntimeException { + state = (state==NBInvokableState.ERRORED) ? state : NBInvokableState.CLOSING; + closed_ns = System.nanoTime(); + try { logger.debug("cleaning up"); ArrayList children = new ArrayList<>(getChildren()); for (NBComponent child : children) { child.close(); } - teardown(); } catch (Exception e) { - logger.error(e); + onError(e); } finally { logger.debug("detaching " + description()); if (parent != null) { parent.detachChild(this); } + teardown(); } } + public void onError(Exception e) { + RuntimeException wrapped = new RuntimeException("While in state " + this.state + ", an error occured: " + e, e); + logger.error(wrapped); + this.error = wrapped; + state=NBInvokableState.ERRORED; + } /** * Override this method in your component implementations when you need to do something * to close out your component. */ protected void teardown() { logger.debug("tearing down " + description()); - this.endAt = System.nanoTime(); + this.teardown_ns = System.nanoTime(); + this.state=(state==NBInvokableState.ERRORED) ? state : NBInvokableState.STOPPED; } @Override @@ -229,10 +246,10 @@ public class NBBaseComponent extends NBBaseComponentMetrics implements NBCompone @Override public long getNanosSinceStart() { - if (endAt==0) { - return System.nanoTime()-startAt; + if (teardown_ns ==0) { + return System.nanoTime()- started_ns; } else { - return endAt-startAt; + return teardown_ns - started_ns; } } @@ -255,4 +272,34 @@ public class NBBaseComponent extends NBBaseComponentMetrics implements NBCompone props.put(name, value); return this; } + + @Override + public NBInvokableState getComponentState() { + return state; + } + + @Override + public long nanosof_start() { + return this.started_ns; + } + + @Override + public long nanosof_close() { + return this.closed_ns; + } + + @Override + public long nanosof_teardown() { + return this.teardown_ns; + } + + @Override + public long nanosof_error() { + return this.errored_ns; + } + + @Override + public long started_epoch_ms() { + return this.started_epoch_ms; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponentMetrics.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponentMetrics.java index 48ab89a65..5576d2ffc 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponentMetrics.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBBaseComponentMetrics.java @@ -16,6 +16,7 @@ package io.nosqlbench.nb.api.components.core; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.tagging.TagFilter; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetric; @@ -28,7 +29,7 @@ public class NBBaseComponentMetrics implements NBComponentMetrics { private final Lock lock = new ReentrantLock(false); private final Map metrics = new ConcurrentHashMap<>(); @Override - public String addComponentMetric(NBMetric metric) { + public String addComponentMetric(NBMetric metric, MetricCategory category, String requiredDescription) { try { lock.lock(); String openMetricsName = metric.getLabels().linearizeAsMetrics(); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentMetrics.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentMetrics.java index 47689cbca..d4dac5844 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentMetrics.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentMetrics.java @@ -16,6 +16,7 @@ package io.nosqlbench.nb.api.components.core; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetric; import java.util.Collection; @@ -29,7 +30,7 @@ import java.util.List; * */ public interface NBComponentMetrics { - String addComponentMetric(NBMetric metric); + String addComponentMetric(NBMetric metric, MetricCategory category, String requiredDescription); /** * If you have the serialized open metrics name of a metric, you can ask for it diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTimeline.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTimeline.java new file mode 100644 index 000000000..7dabcde51 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTimeline.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 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.components.core; + +public interface NBComponentTimeline { + + NBInvokableState getComponentState(); + /** + * This will be 0L if the component hasn't fully started, else it will be + * the {@link System#nanoTime()} of when the component entered its constructor + * @return nanosecond time of component construction + */ + long nanosof_start(); + + /** + * This will be 0L if the component hasn't began the process of closing down. + * @return nanosecond time of invoking {@link NBBaseComponent#close()} + */ + long nanosof_close(); + + /** + * This will be 0L if the component hasn't completed teardown. Otherwise it will be + * the {@link System#nanoTime()} when the base teardown logic in the component has completed. + * For this reason, it is imperative that any overrides to {@link NBBaseComponent#teardown()} + * are called, and called last in the overridden teardown method. + * @return nanosecond time of teardown completion + */ + long nanosof_teardown(); + + /** + * This will be 0L if the component hasn't logged an error. Otherwise it will be + * the {@link System#nanoTime()} of when the error was reported. + * @return nanosecond time of the error + */ + long nanosof_error(); + + long started_epoch_ms(); +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTraversal.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTraversal.java index 52defc699..f992ea1d6 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTraversal.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBComponentTraversal.java @@ -19,6 +19,7 @@ package io.nosqlbench.nb.api.components.core; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.function.Predicate; public class NBComponentTraversal { @@ -29,6 +30,7 @@ public class NBComponentTraversal { public static void visitDepthFirst(NBComponent component, Visitor visitor) { visitDepthFirst(component,visitor,0); } + private static void visitDepthFirst(NBComponent component, Visitor visitor, int depth) { visitor.visit(component,depth); List children = component.getChildren(); @@ -37,6 +39,35 @@ public class NBComponentTraversal { } } + + /** + * Visits each component. If the component does NOT match the predicate, then NON-matching visitor + * method applies. Otherwise the MATCHing visitor method applies and the search stops at that node, + * continuing again for every sibling. + * @param component The component to test and visit + * @param visitor The methods for non-matching and matching nodes + * @param predicate A test to determine whether to apply the matching predicate and stop searching deeper + */ + public static void visitDepthFirstLimited(NBComponent component, FilterVisitor visitor, Predicate predicate) { + visitDepthFirstLimited(component, visitor, 0, predicate); + } + + private static void visitDepthFirstLimited(NBComponent component, FilterVisitor visitor, int depth, Predicate predicate) { + if (predicate.test(component)) { + visitor.visitMatching(component,depth); + return; + } else { + visitor.visitNonMatching(component,depth); + List children = component.getChildren(); + + for (NBComponent child : children) { + visitDepthFirstLimited(child,visitor,depth+1,predicate); + } + } + + } + + public static Iterator traverseBreadth(NBComponent component) { return new IterBreadthFirst(component); } @@ -86,4 +117,8 @@ public class NBComponentTraversal { public interface Visitor { void visit(NBComponent component, int depth); } + public interface FilterVisitor { + void visitMatching(NBComponent component, int depth); + void visitNonMatching(NBComponent component, int depth); + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java index e52502471..24adfdf36 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBCreators.java @@ -27,7 +27,7 @@ import io.nosqlbench.nb.api.histo.HistoStats; import io.nosqlbench.nb.api.http.HttpPlugin; import io.nosqlbench.nb.api.labels.MapLabels; import io.nosqlbench.nb.api.optimizers.BobyqaOptimizerInstance; -import io.nosqlbench.nb.api.files.FileAccess; +import io.nosqlbench.nb.api.nbio.FileAccess; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.shutdown.NBShutdownHook; import io.nosqlbench.nb.api.loaders.BundledExtensionsLoader; @@ -53,63 +53,68 @@ public class NBCreators { this.base = base; } - public NBMetricTimer timer(String metricFamilyName) { - return timer(metricFamilyName,3); + public NBMetricTimer timer(String metricFamilyName, MetricCategory category, String description) { + return timer(metricFamilyName,3, category,description); } - public NBMetricTimer timer(String metricFamilyName, int hdrdigits) { + + public NBMetricTimer timer(String metricFamilyName, int hdrdigits, MetricCategory category, String description) { NBLabels labels = base.getLabels().and("name", metricFamilyName); - NBMetricTimer timer = new NBMetricTimer(labels, new DeltaHdrHistogramReservoir(labels, hdrdigits)); - base.addComponentMetric(timer); + NBMetricTimer timer = new NBMetricTimer( + labels, + new DeltaHdrHistogramReservoir(labels, hdrdigits), + description, category + ); + base.addComponentMetric(timer, category, description); return timer; } - public Meter meter(String metricFamilyName) { + public Meter meter(String metricFamilyName, MetricCategory category, String description) { NBLabels labels = base.getLabels().and("name", metricFamilyName); - NBMetricMeter meter = new NBMetricMeter(labels); - base.addComponentMetric(meter); + NBMetricMeter meter = new NBMetricMeter(labels,description, category); + base.addComponentMetric(meter, category, description); return meter; } - public NBMetricCounter counter(String metricFamilyName) { + public NBMetricCounter counter(String metricFamilyName, MetricCategory category, String description) { NBLabels labels = base.getLabels().and("name", metricFamilyName); - NBMetricCounter counter = new NBMetricCounter(labels); - base.addComponentMetric(counter); + NBMetricCounter counter = new NBMetricCounter(labels, description, category); + base.addComponentMetric(counter, category, description); return counter; } - public NBFunctionGauge gauge(String metricFamilyName, Supplier valueSource) { - NBFunctionGauge gauge = new NBFunctionGauge(base, valueSource, metricFamilyName); - base.addComponentMetric(gauge); + public NBFunctionGauge gauge(String metricFamilyName, Supplier valueSource, MetricCategory category, String description) { + NBFunctionGauge gauge = new NBFunctionGauge(base, valueSource, metricFamilyName, description, category); + base.addComponentMetric(gauge, category, description); return gauge; } - public NBVariableGauge variableGauge(String metricFamilyName, double initialValue, String... additionalLabels) { - NBVariableGauge gauge = new NBVariableGauge(base, metricFamilyName, initialValue, additionalLabels); - base.addComponentMetric(gauge); + public NBVariableGauge variableGauge(String metricFamilyName, double initialValue, MetricCategory category, String description, NBLabels additionalLabels) { + NBVariableGauge gauge = new NBVariableGauge(base, metricFamilyName, initialValue, additionalLabels, description, category); + base.addComponentMetric(gauge, category, description); return gauge; } - public DoubleSummaryGauge summaryGauge(String name, String... statspecs) { - List stats = Arrays.stream(statspecs).map(DoubleSummaryGauge.Stat::valueOf).toList(); + 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); - base.addComponentMetric(anyGauge); + anyGauge = new DoubleSummaryGauge(base.getLabels().and(NBLabels.forKV("name",name,"stat", stat)), stat, reservoir, description, category); + base.addComponentMetric(anyGauge, category, description); } return anyGauge; } - public NBMetricHistogram histogram(String metricFamilyName) { - return histogram(metricFamilyName,4); + public NBMetricHistogram histogram(String metricFamilyName, MetricCategory category, String description) { + return histogram(metricFamilyName,4, category, description); } - public NBMetricHistogram histogram(String metricFamilyName, int hdrdigits) { + 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)); - base.addComponentMetric(histogram); + NBMetricHistogram histogram = new NBMetricHistogram(labels, new DeltaHdrHistogramReservoir(labels, hdrdigits), description, category); + base.addComponentMetric(histogram, category, description); return histogram; } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBInvokableState.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBInvokableState.java new file mode 100644 index 000000000..d90a0ac3b --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/NBInvokableState.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 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.components.core; + +/** + *
{@code
+ * errored_at > 0   -> ERROR
+ * started_at == 0   -> STARTING
+ * 

+ *

+ *

+ * started_at > closed_at + * STARTING + * closed_at > started_at + * RUNNING + * teardown_at > closed_at + * STOPPING + * teardown_at + * STOPPED + * stopped_at + * }

+ */ +public enum NBInvokableState { + /** + * The component exists in some state but has not completed initialization / construction + */ + STARTING, + /** + * The component has completed initialization and is presumed to be running + */ + RUNNING, + /** + * The component has begun closing down, which means unwinding/closing any child components + */ + CLOSING, + /** + * The component has completed closing down, including its teardown logic + */ + STOPPED, + /** + * There was an error + */ + ERRORED; + +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/PeriodicTaskComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/PeriodicTaskComponent.java index 06bdd6c61..a488ba0c9 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/PeriodicTaskComponent.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/PeriodicTaskComponent.java @@ -25,90 +25,10 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -public abstract class PeriodicTaskComponent extends NBBaseComponent implements Runnable { +public abstract class PeriodicTaskComponent extends UnstartedPeriodicTaskComponent { - private static final Logger logger = LogManager.getLogger(PeriodicTaskComponent.class); - private final long intervalmillis; - private final Lock lock = new ReentrantLock(); - private final Condition shutdownSignal = lock.newCondition(); - private final boolean oneLastTime; - - Thread thread; - private boolean running = true; - - public PeriodicTaskComponent( - NBComponent node, - NBLabels extraLabels, - long millis, - boolean oneLastTime, - String threadName - ) { - super(node, extraLabels); - this.intervalmillis = millis; - thread = Thread.ofVirtual().name(threadName).start(this); - this.oneLastTime = oneLastTime; + public PeriodicTaskComponent(NBComponent node, NBLabels extraLabels, long millis, String threadName, FirstReport firstReport, LastReport lastReport) { + super(node, extraLabels, millis, threadName, firstReport, lastReport); + start(); } - - protected abstract void task(); - - @Override - public void run() { - long now = System.currentTimeMillis(); - long reportAt = now + intervalmillis; - long waitfor = reportAt - now; - - while (running) { - while (running && waitfor > 0) { - boolean signalReceived = false; - try { - lock.lock(); - signalReceived = shutdownSignal.await(waitfor, TimeUnit.MILLISECONDS); - } catch (InterruptedException ignored) { - } finally { - lock.unlock(); - } - if (signalReceived) { - logger.debug("signal shutting down " + this); - return; - } - now = System.currentTimeMillis(); - waitfor = reportAt - now; - } -// logger.info("summarizing metrics to console"); - try { - task(); - } catch (Exception e) { - logger.error(e); - throw new RuntimeException(e); - } finally { - reportAt = reportAt + (intervalmillis); - now = System.currentTimeMillis(); - waitfor = reportAt - now; - } - } - logger.info("shutting down periodic runnable component: " + description()); - } - - public void teardown() { - logger.debug("shutting down " + this); - - lock.lock(); - running = false; - shutdownSignal.signalAll(); - lock.unlock(); - logger.debug("signaled reporter thread to shut down " + description()); - - try { - thread.join(); - } catch (InterruptedException e) { - logger.warn("interrupted while joining thread"); - } - - if (oneLastTime) { - logger.debug("running " + this + " one last time."); - task(); - } - - } - } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/UnstartedPeriodicTaskComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/UnstartedPeriodicTaskComponent.java new file mode 100644 index 000000000..26a326524 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/core/UnstartedPeriodicTaskComponent.java @@ -0,0 +1,167 @@ +/* + * 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.components.core; + +import io.nosqlbench.nb.api.labels.NBLabels; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +/** + *

Because of ctor super calling order requirements, the task thread can't always be started + * automatically in super(...). If that is the case, then use this class directly and call + * start() at the end of your subtype ctor.

+ * + *

Otherwise, it is safe to use {@link PeriodicTaskComponent} directly.

+ */ +public abstract class UnstartedPeriodicTaskComponent extends NBBaseComponent implements Runnable { + + private static final Logger logger = LogManager.getLogger(UnstartedPeriodicTaskComponent.class); + protected final long intervalmillis; + private final Lock lock = new ReentrantLock(); + private final Condition shutdownSignal = lock.newCondition(); + private final FirstReport firstReport; + private final LastReport lastReport; + private final String threadName; + Thread thread; + private boolean running = true; + + public enum FirstReport { + Immediately, + OnInterval + } + public enum LastReport { + None, + onClose, + /** + * OnInterrupt is a stronger version of OnClose, including scenarios where the process is interrupted with a signal + */ + OnInterrupt + } + + public UnstartedPeriodicTaskComponent( + NBComponent node, + NBLabels extraLabels, + long millis, + String threadName, + FirstReport firstReport, + LastReport lastReport + ) { + super(node, extraLabels); + this.threadName = threadName; + this.intervalmillis = millis; + this.firstReport = firstReport; + this.lastReport = lastReport; + if(lastReport== LastReport.OnInterrupt) { + Thread hook=new Thread(this::task,"shutdownhook-"+threadName); + Runtime.getRuntime().addShutdownHook(hook); + } + // TODO: There is a potential race condition between init and invoke here, if millis is low enough and post-super() state is needed + } + + public void start() { + if (firstReport==FirstReport.Immediately) task(); + thread = Thread.ofVirtual().name(threadName).start(this); + } + /** + * This task should only do what is needed once each period. + * If it throws any exceptions, then these exceptions will cause the period task + * to exit. Thus, if you need to allow failures in some cases while keeping + * the caller (scheduler) active, all errors should be caught and handled + * internally. + */ + protected abstract void task(); + + @Override + public void run() { + long now = System.currentTimeMillis(); + long reportAt = now + intervalmillis; + long waitfor = reportAt - now; + + while (running) { + while (running && waitfor > 0) { + boolean signalReceived = false; + try { + lock.lock(); + signalReceived = shutdownSignal.await(waitfor, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignored) { + } finally { + lock.unlock(); + } + if (signalReceived) { + logger.debug("signal shutting down " + this); + return; + } + now = System.currentTimeMillis(); + waitfor = reportAt - now; + } +// logger.info("summarizing metrics to console"); + try { + lock.lock(); + task(); + } catch (Exception e) { + logger.error(e); + throw new RuntimeException(e); + } finally { + reportAt = reportAt + (intervalmillis); + now = System.currentTimeMillis(); + waitfor = reportAt - now; + lock.unlock(); + } + } + logger.info("shutting down periodic runnable component: " + description()); + } + + public void teardown() { + + logger.debug("shutting down " + this); + + lock.lock(); + running = false; + shutdownSignal.signalAll(); + lock.unlock(); + +// if (lastReport==LastReport.onClose || lastReport==LastReport.OnInterrupt) { +// logger.debug("final task() call for period component " + description()); +// task(); +// } + + logger.debug("signaled reporter thread to shut down " + description()); + + try { + thread.join(); + } catch (InterruptedException e) { + logger.warn("interrupted while joining thread"); + } + + if (this.lastReport== LastReport.onClose) { + logger.debug("running " + this + " one last time on close()."); + task(); + } + + super.teardown(); + + } + + + +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/ComponentPulse.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/ComponentPulse.java new file mode 100644 index 000000000..6c26090b5 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/ComponentPulse.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 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.components.status; + +import io.nosqlbench.nb.api.components.core.UnstartedPeriodicTaskComponent; +import io.nosqlbench.nb.api.labels.NBLabels; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +public class ComponentPulse extends UnstartedPeriodicTaskComponent { + private final static Logger logger = LogManager.getLogger(ComponentPulse.class); + private final Path hbpath; + private final NBHeartbeatComponent pulseOf; + private final Path linkpath; + + public ComponentPulse(NBHeartbeatComponent pulseOf, NBLabels extraLabels, String fileNameLabel, long millis) { + super( + pulseOf, + extraLabels, + millis, + "PULSE-" + pulseOf.description(), + FirstReport.Immediately, + LastReport.OnInterrupt + ); + this.pulseOf = pulseOf; + String logsdir = getComponentProp("logsdir").orElse("logs"); + String labelElement = pulseOf.getLabels().valueOf(fileNameLabel); + this.hbpath = Path.of(logsdir).resolve(labelElement +"_status.yaml"); + this.linkpath = Path.of(logsdir).resolve("status.yaml"); + start(); + } + + @Override + protected void task() { + + logger.debug("emitting pulse for :" + this.pulseOf.description()); + + Status heartbeat = pulseOf.status().withHeartbeatDetails(intervalmillis,System.currentTimeMillis()); + try { + Files.writeString(hbpath, heartbeat.toYaml(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + Files.deleteIfExists(linkpath); + Files.createSymbolicLink( + linkpath, + hbpath.getFileName() + ); + } catch (IOException e) { + logger.error("Unable to write heartbeat data to " + hbpath.toString() + ": " + e); + } + } +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/HeartbeatRepresenter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/HeartbeatRepresenter.java new file mode 100644 index 000000000..52812ee12 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/HeartbeatRepresenter.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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.components.status; + +import io.nosqlbench.nb.api.components.core.NBInvokableState; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.snakeyaml.engine.v2.api.RepresentToNode; +import org.snakeyaml.engine.v2.nodes.Node; +import org.snakeyaml.engine.v2.representer.StandardRepresenter; + +public class HeartbeatRepresenter extends StandardRepresenter { + public HeartbeatRepresenter(DumpSettings settings) { + super(settings); + this.representers.put(NBInvokableState.class, new RepresentEnumToString()); + this.representers.put(Status.class, new RepresentStatusToMap()); + } + + public class RepresentEnumToString implements RepresentToNode { + + @Override + public Node representData(Object o) { + if (o instanceof Enum e) { + String name = e.name(); + return HeartbeatRepresenter.this.represent(name); + } else { + throw new RuntimeException("Unable to represent as enum: " + o.toString() + " (class " + o.getClass().getSimpleName() + "'"); + } + } + } + + private class RepresentStatusToMap implements RepresentToNode { + @Override + public Node representData(Object data) { + if (data instanceof Status status) { + return represent(status.toMap()); + } else { + throw new RuntimeException("Unable to represent object " + data.toString() + "\n(" + data.getClass().getCanonicalName() + ")"); + } + } + } +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBHeartbeatComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBHeartbeatComponent.java new file mode 100644 index 000000000..5edb37503 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBHeartbeatComponent.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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.components.status; + +import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.labels.NBLabels; + +import java.util.Map; +import java.util.Optional; + +/** + * A heartbeat component is one which provides evidence that it is either + * in a healthy state or that it is not, via a heartbeat mechanism. This requires + * that a component property 'heartbeat' is provides which is the millisecond interval + * between beats. + */ +public class NBHeartbeatComponent extends NBStatusComponent { + + public NBHeartbeatComponent(NBComponent parentComponent) { + super(parentComponent); + } + + public NBHeartbeatComponent(NBComponent parentComponent, NBLabels componentSpecificLabelsOnly, Map props, String liveLabel) { + super(parentComponent, componentSpecificLabelsOnly, props); + getComponentProp("heartbeat") + .map(Long::parseLong) + .ifPresent( + // attaches, no further reference needed + hbmillis -> new ComponentPulse(this, NBLabels.forKV(), liveLabel, hbmillis) + ); + } + +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBStatusComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBStatusComponent.java new file mode 100644 index 000000000..1afb820b2 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/NBStatusComponent.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 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.components.status; + +import io.nosqlbench.nb.api.components.core.NBBaseComponent; +import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.components.core.NBComponentTraversal; +import io.nosqlbench.nb.api.components.core.NBInvokableState; +import io.nosqlbench.nb.api.labels.NBLabels; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class NBStatusComponent extends NBBaseComponent { + public NBStatusComponent(NBComponent parentComponent) { + super(parentComponent); + } + + public NBStatusComponent(NBComponent parentComponent, NBLabels componentSpecificLabelsOnly) { + super(parentComponent, componentSpecificLabelsOnly); + } + + public NBStatusComponent(NBComponent parentComponent, NBLabels componentSpecificLabelsOnly, Map props) { + super(parentComponent, componentSpecificLabelsOnly, props); + } + + public Status status() { + + List subbeats = new ArrayList<>(); + StatusVisitor statusVisitor = new StatusVisitor(subbeats); + for (NBComponent child : getChildren()) { + NBComponentTraversal.visitDepthFirstLimited(child,statusVisitor,c -> c instanceof NBStatusComponent); + } + + return new Status( + getLabels(), + this.getComponentState(), + started_epoch_ms(), + session_time_ms(), + 0L, + 0L, + subbeats + ); + } + + public long session_time_ms() { + NBInvokableState state = getComponentState(); + long nanos = switch (state) { + case ERRORED -> (nanosof_error() - nanosof_start()); + case STARTING, RUNNING -> (System.nanoTime() - nanosof_start()); + case CLOSING -> (nanosof_close() - nanosof_start()); + case STOPPED -> (nanosof_teardown() - nanosof_start()); + }; + return nanos / 1_000_000L; + } + + private final static class StatusVisitor implements NBComponentTraversal.FilterVisitor { + + private final List statusList; + + public StatusVisitor(List statusList) { + this.statusList = statusList; + } + + @Override + public void visitMatching(NBComponent component, int depth) { + if (component instanceof NBStatusComponent sc) { + statusList.add(sc.status()); + } + } + + @Override + public void visitNonMatching(NBComponent component, int depth) { + } + } + +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/Status.java b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/Status.java new file mode 100644 index 000000000..171b957de --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/components/status/Status.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 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.components.status; + +import io.nosqlbench.nb.api.components.core.NBInvokableState; +import io.nosqlbench.nb.api.labels.NBLabels; +import org.snakeyaml.engine.v2.api.Dump; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.snakeyaml.engine.v2.common.FlowStyle; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public record Status( + NBLabels labels, + NBInvokableState state, + long started_epoch_ms, + long session_time_ms, + long heartbeat_interval_ms, + long heartbeat_epoch_ms, + List substatus +) { + public final static Dump dump = createDump(); + + private static Dump createDump() { + DumpSettings settings = DumpSettings.builder().setDefaultFlowStyle(FlowStyle.BLOCK).build(); + return new Dump(settings, new HeartbeatRepresenter(settings)); + } + + public Status withHeartbeatDetails(long new_heartbeat_interval_ms, long new_heartbeat_ms_epoch) { + return new Status( + labels, + state, + started_epoch_ms, + session_time_ms, + new_heartbeat_interval_ms, + new_heartbeat_ms_epoch, + substatus + ); + } + + public String toYaml() { + return toString(); + } + + public Map toMap() { + return new LinkedHashMap<>() {{ + put("labels", labels.asMap()); + put("state", state); + put("started_at_epochms", started_epoch_ms); + put("session_time_ms", session_time_ms); + put("heartbeat_interval_ms", heartbeat_interval_ms); + put("heartbeat_epoch_ms", heartbeat_epoch_ms); + put("substatus", substatus); + }}; + } + + @Override + public String toString() { + return dump.dumpToString(toMap()); + } +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/DoubleSummaryGauge.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/DoubleSummaryGauge.java index 5d880913d..86ae33a0e 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/DoubleSummaryGauge.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/DoubleSummaryGauge.java @@ -16,6 +16,7 @@ package io.nosqlbench.nb.api.engine.metrics; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; @@ -30,12 +31,24 @@ public class DoubleSummaryGauge implements NBMetricGauge, DoubleConsumer { private final NBLabels labels; private final Stat stat; private final DoubleSummaryStatistics stats; + private final String description; + private final MetricCategory[] categories; @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, @@ -44,16 +57,20 @@ public class DoubleSummaryGauge implements NBMetricGauge, DoubleConsumer { Sum } - public DoubleSummaryGauge(NBLabels labels, Stat stat, DoubleSummaryStatistics stats) { + public DoubleSummaryGauge(NBLabels labels, Stat stat, DoubleSummaryStatistics stats, String description, MetricCategory... categories) { this.labels = labels; this.stat = stat; this.stats = stats; + this.description = description; + this.categories = categories; } - public DoubleSummaryGauge(NBLabels labels, Stat stat) { + public DoubleSummaryGauge(NBLabels labels, Stat stat,String description, MetricCategory... categories) { this.labels = labels; this.stat = stat; this.stats = new DoubleSummaryStatistics(); + this.description = description; + this.categories = categories; } public synchronized void accept(double value) { diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/MetricCategory.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/MetricCategory.java new file mode 100644 index 000000000..273f272e8 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/MetricCategory.java @@ -0,0 +1,83 @@ +/* + * 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.instruments; + +public enum MetricCategory { + /** + * Metrics which are essential to understanding the behavior of any activity + */ + Core, + /** + * Some metrics are provided only to inform the user of relative or absolute progress, + * in terms of cycles remaining or similar + */ + Progress, + /** + * Metrics which mirror configuration data, either static or dynamic during the lifetime + * of an activity, session, or container. These are shared because they may need to be known + * for further understanding or evaluation. For example, the target rate puts the achieved + * rate in context, and having them both together in downstream metrics view makes computation + * simple and direct. + */ + Config, + /** + * Metrics which are used to ascertain the validity of client behavior, such as the CPU load + * or other potentially contending effects. This is important because test results can be invalidated + * when the composed system
{@code client <-> infrastructure <-> target system }
relies + * too heavily on the testing apparatus. When the testing apparatus is under any degree of measurable + * stress, the ability to drive the target system to its capacity is compromised, as well as the client's + * ability to approximate real-time measurements due to task scheduling delays. + */ + Internals, + /** + * When drivers are used to augment the metrics views, such as with the CQL client, these metrics can be + * folded into metrics feeds. However, they are not part of the core NB metrics. Such auxiliary metric + * need to be identified separately. + */ + Driver, + /** + * Measurements of error rates, exception counts, and any other failure modes which can be counted or + * otherwise quantified + */ + Errors, + /** + * Verification logic is used to assert the validity or some property of results returned by individual + * operations. Verification is meant to indicate whether a result was valid or invalid, with no + * room for interpretation in between. + */ + Verification, + /** + * Metrics which describe payload properties, such as result size or similar + */ + Payload, + /** + * Sometimes users provide their own metrics instrumentation. These may or may not have descriptions provided. + */ + User, + /** + * Some metrics help provide insight into analysis methods and progress. This can include parameters for + * an optimizers configuration, parameters from a single frame of simulation, achieved results in the form of + * a value function, or similar. + */ + Analysis, + /** + * When the result returned by an operation is scrutinized for some degree of accuracy on a sliding scale, + * this category applies. This is distinct from Verification in that verification implies a pass/fail scenario, + * whereas accuracy measures are on a sliding scale where interpretation is often more subjective. + */ + Accuracy +} diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBBaseMetric.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBBaseMetric.java index 35504cae4..2bcf6872d 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBBaseMetric.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBBaseMetric.java @@ -20,9 +20,13 @@ import io.nosqlbench.nb.api.labels.NBLabels; public class NBBaseMetric implements NBMetric { private final NBLabels labels; + private String description; + private MetricCategory[] categories; - public NBBaseMetric(String... labels) { - this.labels = NBLabels.forKV((Object[]) labels); + public NBBaseMetric(NBLabels labels, String description, MetricCategory... categories) { + this.labels = labels; + this.description = description; + this.categories = categories; } @Override public NBLabels getLabels() { @@ -33,4 +37,14 @@ public class NBBaseMetric implements NBMetric { public String typeName() { return "basetype"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBFunctionGauge.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBFunctionGauge.java index 5e9b14300..9b2956a18 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBFunctionGauge.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBFunctionGauge.java @@ -28,14 +28,31 @@ public class NBFunctionGauge implements NBMetricGauge { private final Supplier source; private final NBLabeledElement parent; private final NBLabels labels; + private String description; + private MetricCategory[] categories; - public NBFunctionGauge(NBComponent parent, Supplier source, String metricFamilyName, Map additionalLabels) { + public NBFunctionGauge( + NBComponent parent, + Supplier source, + String metricFamilyName, + Map additionalLabels, + String description, + MetricCategory... categories + ) { this.parent = parent; this.labels = NBLabels.forMap(additionalLabels).and("name",metricFamilyName); this.source = source; + this.description = description; + this.categories = categories; } - public NBFunctionGauge(NBComponent parent, Supplier source, String metricFamilyName) { - this(parent, source, metricFamilyName,Map.of()); + public NBFunctionGauge( + NBComponent parent, + Supplier source, + String metricFamilyName, + String description, + MetricCategory... categories + ) { + this(parent, source, metricFamilyName,Map.of(), description, categories); } @Override public Double getValue() { @@ -56,6 +73,16 @@ public class NBFunctionGauge implements NBMetricGauge { public String typeName() { return "gauge"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetric.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetric.java index b65bc8402..7a18b0047 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetric.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetric.java @@ -24,4 +24,7 @@ public interface NBMetric extends Metric, NBLabeledElement { return this.getLabels().linearizeAsMetrics(); } String typeName(); + + String getDescription(); + MetricCategory[] getCategories(); } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricCounter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricCounter.java index 3a5d901ae..a6afb8992 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricCounter.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricCounter.java @@ -22,9 +22,13 @@ import io.nosqlbench.nb.api.labels.NBLabels; public class NBMetricCounter extends Counter implements NBMetric { private final NBLabels labels; + private String description; + private MetricCategory[] categories; - public NBMetricCounter(final NBLabels labels) { + public NBMetricCounter(final NBLabels labels, String description, MetricCategory... categories) { this.labels = labels; + this.description = description; + this.categories = categories; } @Override @@ -37,6 +41,16 @@ public class NBMetricCounter extends Counter implements NBMetric { return "counter"; } + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } + @Override public String toString() { return description(); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricGaugeWrapper.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricGaugeWrapper.java index 2323d4001..03eeb0533 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricGaugeWrapper.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricGaugeWrapper.java @@ -23,14 +23,18 @@ public class NBMetricGaugeWrapper implements NBMetricGauge, NBMetric { private final Gauge gauge; private final NBLabels labels; + private final String description; + private final MetricCategory[] categories; - public NBMetricGaugeWrapper(NBLabels labels, Gauge gauge) { + public NBMetricGaugeWrapper(NBLabels labels, Gauge gauge, String description, MetricCategory... categories) { this.gauge = gauge; if (gauge.getValue() instanceof Double d) { } else { throw new RuntimeException("NBMetricGauges only support Double values"); } this.labels = labels; + this.description = description; + this.categories = categories; } @Override @@ -47,4 +51,14 @@ public class NBMetricGaugeWrapper implements NBMetricGauge, NBMetric { public String typeName() { return "gauge"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricHistogram.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricHistogram.java index 9d5b2aea7..24ee07091 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricHistogram.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricHistogram.java @@ -31,17 +31,33 @@ public class NBMetricHistogram extends Histogram implements DeltaSnapshotter, Hd private long cacheExpiryMillis; private long cacheTimeMillis; private List mirrors; + private MetricCategory[] categories; + private String description; - public NBMetricHistogram(NBLabels labels, DeltaHdrHistogramReservoir hdrHistogramReservoir) { + public NBMetricHistogram( + NBLabels labels, + DeltaHdrHistogramReservoir hdrHistogramReservoir, + String description, + MetricCategory... categories + ) { super(hdrHistogramReservoir); this.labels = labels; this.hdrDeltaReservoir = hdrHistogramReservoir; + this.description = description; + this.categories = categories; } - public NBMetricHistogram(String name, DeltaHdrHistogramReservoir hdrHistogramReservoir) { + public NBMetricHistogram( + String name, + DeltaHdrHistogramReservoir hdrHistogramReservoir, + String description, + MetricCategory... categories + ) { super(hdrHistogramReservoir); this.labels = NBLabels.forKV("name",name); this.hdrDeltaReservoir = hdrHistogramReservoir; + this.description = description; + this.categories = categories; } @Override @@ -76,7 +92,7 @@ public class NBMetricHistogram extends Histogram implements DeltaSnapshotter, Hd mirrors = new CopyOnWriteArrayList<>(); } DeltaHdrHistogramReservoir mirrorReservoir = this.hdrDeltaReservoir.copySettings(); - NBMetricHistogram mirror = new NBMetricHistogram("mirror-" + this.labels.linearizeValues("name"), mirrorReservoir); + NBMetricHistogram mirror = new NBMetricHistogram("mirror-" + this.labels.linearizeValues("name"), mirrorReservoir, description, categories); mirrors.add(mirror); return mirror; } @@ -115,6 +131,16 @@ public class NBMetricHistogram extends Histogram implements DeltaSnapshotter, Hd return "histogram"; } + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } + @Override public String toString() { return description(); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricMeter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricMeter.java index 80c134c7c..c574e9f1c 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricMeter.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricMeter.java @@ -22,9 +22,13 @@ import io.nosqlbench.nb.api.labels.NBLabels; public class NBMetricMeter extends Meter implements NBMetric { private final NBLabels labels; + private final MetricCategory[] categories; + private final String description; - public NBMetricMeter(NBLabels labels) { + public NBMetricMeter(NBLabels labels, String description, MetricCategory... categories) { this.labels = labels; + this.description = description; + this.categories = categories; } @Override @@ -36,4 +40,14 @@ public class NBMetricMeter extends Meter implements NBMetric { public String typeName() { return "meter"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricTimer.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricTimer.java index 181fe73f4..33a1c3177 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricTimer.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBMetricTimer.java @@ -28,12 +28,21 @@ import java.util.concurrent.TimeUnit; public class NBMetricTimer extends Timer implements DeltaSnapshotter, HdrDeltaHistogramAttachment, TimerAttachment, NBMetric { private final DeltaHdrHistogramReservoir deltaHdrHistogramReservoir; + private final String description; + private final MetricCategory[] categories; private long cacheExpiry; private List mirrors; private final NBLabels labels; - public NBMetricTimer(final NBLabels labels, final DeltaHdrHistogramReservoir deltaHdrHistogramReservoir) { + public NBMetricTimer( + final NBLabels labels, + final DeltaHdrHistogramReservoir deltaHdrHistogramReservoir, + String description, + MetricCategory... categories + ) { super(deltaHdrHistogramReservoir); + this.description = description; + this.categories = categories; this.labels = labels; this.deltaHdrHistogramReservoir = deltaHdrHistogramReservoir; } @@ -60,7 +69,7 @@ public class NBMetricTimer extends Timer implements DeltaSnapshotter, HdrDeltaHi public synchronized NBMetricTimer attachHdrDeltaHistogram() { if (null == mirrors) this.mirrors = new CopyOnWriteArrayList<>(); final DeltaHdrHistogramReservoir sameConfigReservoir = deltaHdrHistogramReservoir.copySettings(); - final NBMetricTimer mirror = new NBMetricTimer(labels, sameConfigReservoir); + final NBMetricTimer mirror = new NBMetricTimer(labels, sameConfigReservoir, description, categories); this.mirrors.add(mirror); return mirror; } @@ -96,4 +105,14 @@ public class NBMetricTimer extends Timer implements DeltaSnapshotter, HdrDeltaHi public String typeName() { return "timer"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBVariableGauge.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBVariableGauge.java index 94c7d498d..daa20d758 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBVariableGauge.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/instruments/NBVariableGauge.java @@ -28,11 +28,22 @@ public class NBVariableGauge implements NBMetricGauge { private double value; private final NBLabeledElement parent; private final NBLabels labels; + private String description; + private MetricCategory[] categories; - public NBVariableGauge(NBComponent parent, String metricFamilyName, double initialValue, String... additionalLabels) { + public NBVariableGauge( + NBComponent parent, + String metricFamilyName, + double initialValue, + NBLabels additionalLabels, + String description, + MetricCategory... categories + ) { this.parent = parent; - this.labels = NBLabels.forKV((Object[]) additionalLabels).and("name", metricFamilyName); + this.labels = additionalLabels.and("name", metricFamilyName); this.value = initialValue; + this.description = description; + this.categories = categories; } @@ -50,4 +61,14 @@ public class NBVariableGauge implements NBMetricGauge { public String typeName() { return "gauge"; } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public MetricCategory[] getCategories() { + return this.categories; + } } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/ConsoleReporter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/ConsoleReporter.java index 2eb724350..40c15b9b0 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/ConsoleReporter.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/ConsoleReporter.java @@ -44,7 +44,7 @@ public class ConsoleReporter extends PeriodicTaskComponent { public ConsoleReporter(NBComponent node, NBLabels extraLabels, long millis, boolean oneLastTime, PrintStream output, Set disabledMetricAttributes) { - super(node, extraLabels, millis, oneLastTime, "REPORT-CONSOLE"); + super(node, extraLabels, millis, "REPORT-CONSOLE", FirstReport.OnInterval, LastReport.OnInterrupt); this.output = output; this.dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/CsvReporter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/CsvReporter.java index e51ccd9fd..56adc839c 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/CsvReporter.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/CsvReporter.java @@ -59,7 +59,7 @@ public class CsvReporter extends PeriodicTaskComponent { public CsvReporter(NBComponent node, Path reportTo, long intervalMs, MetricInstanceFilter filter, NBLabels extraLabels) { - super(node, extraLabels, intervalMs, false,"REPORT-CSV"); + super(node, extraLabels, intervalMs, "REPORT-CSV", FirstReport.OnInterval, LastReport.OnInterrupt); this.component = node; this.reportTo = reportTo; this.filter = filter; diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/Log4JMetricsReporter.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/Log4JMetricsReporter.java index e9fee0442..2c5c4f732 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/Log4JMetricsReporter.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/Log4JMetricsReporter.java @@ -51,7 +51,7 @@ public class Log4JMetricsReporter extends PeriodicTaskComponent { final NBLabels extraLabels, final long millis, final boolean oneLastTime) { - super(component, extraLabels, millis, oneLastTime,"REPORT-LOG4J"); + super(component, extraLabels, millis, "REPORT-LOG4J", FirstReport.OnInterval, LastReport.OnInterrupt); this.loggerProxy = loggerProxy; this.marker = marker; } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromExpositionFormat.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromExpositionFormat.java index f9de51827..be97ec94e 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromExpositionFormat.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromExpositionFormat.java @@ -17,6 +17,8 @@ package io.nosqlbench.nb.api.engine.metrics.reporters; import com.codahale.metrics.*; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; +import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetric; import io.nosqlbench.nb.api.labels.NBLabeledElement; import io.nosqlbench.nb.api.labels.NBLabels; import org.apache.logging.log4j.LogManager; @@ -26,7 +28,9 @@ import java.io.IOException; import java.io.Writer; import java.time.Clock; import java.time.Instant; +import java.util.Arrays; import java.util.Map; +import java.util.stream.Collectors; /** * Format NBMetrics according to the prometheus exposition format. @@ -59,6 +63,16 @@ public class PromExpositionFormat { for (final Object metric : metrics) { NBLabels labels = null; + if (metric instanceof NBMetric nbm) { + MetricCategory[] categories = nbm.getCategories(); + buffer.append("# CATEGORIES: ") + .append(Arrays.stream(categories).map(MetricCategory::name).collect(Collectors.joining(", "))) + .append("\n"); + String description = nbm.getDescription(); + buffer.append("# DESCRIPTION: ").append(description).append("\n"); + + } + if (metric instanceof final NBLabeledElement labeled) labels = labeled.getLabels(); else throw new RuntimeException( "Unknown label set for metric type '" + metric.getClass().getCanonicalName() + '\'' diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromPushReporterComponent.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromPushReporterComponent.java index 587ea2d4c..215d5357c 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromPushReporterComponent.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/reporters/PromPushReporterComponent.java @@ -33,8 +33,10 @@ import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.time.Clock; import java.time.Duration; import java.time.Instant; @@ -49,18 +51,18 @@ public class PromPushReporterComponent extends PeriodicTaskComponent { private String bearerToken; public PromPushReporterComponent(NBComponent parent, String endpoint, long intervalMs, NBLabels nbLabels) { - super(parent,nbLabels.and("_type","prom-push"),intervalMs,true,"REPORT-PROMPUSH"); + super(parent, nbLabels.and("_type", "prom-push"), intervalMs, "REPORT-PROMPUSH",FirstReport.OnInterval, LastReport.OnInterrupt); String jobname = getLabels().valueOfOptional("jobname").orElse("default"); String instance = getLabels().valueOfOptional("instance").orElse("default"); - if (jobname.equals("default")||instance.equals("default")) { + if (jobname.equals("default") || instance.equals("default")) { logger.warn("It is highly recommended that you set a value for labels jobname and instance other than 'default'."); } if (endpoint.matches("victoria:[a-zA-Z0-9._-]+:[0-9]+")) { String[] parts = endpoint.split(":", 2); - endpoint = "https://"+parts[1]+"/api/v1/import/prometheus/metrics/job/JOBNAME/instance/INSTANCE"; + endpoint = "https://" + parts[1] + "/api/v1/import/prometheus/metrics/job/JOBNAME/instance/INSTANCE"; } - endpoint=endpoint.replace("JOBNAME",jobname).replace("INSTANCE",instance); + endpoint = endpoint.replace("JOBNAME", jobname).replace("INSTANCE", instance); if (!endpoint.contains(jobname)) { throw new BasicError("Mismatch between jobname in prompush URI and specified jobname label. You should use the short form for --report-prompush-to victoria:addr:port and set the jobname with --add-labels"); } @@ -109,9 +111,24 @@ public class PromPushReporterComponent extends PeriodicTaskComponent { remainingRetries--; final HttpClient client = getCachedClient(); final HttpRequest.Builder rb = HttpRequest.newBuilder().uri(uri); - if (bearerToken!=null) { + if (bearerToken != null) { rb.setHeader("Authorization", "Bearer " + bearerToken); } + getComponentProp("prompush_cache") + .map(cache -> Path.of(getComponentProp("logsdir").orElse(".")) + .resolve("cache")).ifPresent( + prompush_cache_path -> { + try { + Files.writeString( + prompush_cache_path, + exposition, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); final HttpRequest request = rb.POST(BodyPublishers.ofString(exposition)).build(); final BodyHandler handler = HttpResponse.BodyHandlers.ofString(); HttpResponse response = null; diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/RelevancyMeasures.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/RelevancyMeasures.java index c0e2d4ba3..438cd59cf 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/RelevancyMeasures.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/metrics/wrappers/RelevancyMeasures.java @@ -16,6 +16,7 @@ package io.nosqlbench.nb.api.engine.metrics.wrappers; +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.engine.metrics.DoubleSummaryGauge; @@ -58,7 +59,7 @@ public class RelevancyMeasures implements NBLabeledElement { for (RelevancyFunction function : f) { this.functions.add(function); function.prependLabels(this); - DoubleSummaryGauge gauge = parent.create().summaryGauge(function.getUniqueName(),DoubleSummaryGauge.Stat.Average.toString()); + DoubleSummaryGauge gauge = parent.create().summaryGauge(function.getUniqueName(), List.of("Average"), MetricCategory.Accuracy, DoubleSummaryGauge.Stat.Average.toString()); this.gauges.add(gauge); } return this; diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/SSLKsFactory.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/SSLKsFactory.java index 698ca0106..3dff3dd2a 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/SSLKsFactory.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/SSLKsFactory.java @@ -69,7 +69,7 @@ public class SSLKsFactory implements NBMapConfigurable { }; /** - * Consider: https://gist.github.com/artem-smotrakov/bd14e4bde4d7238f7e5ab12c697a86a3 + * Consider: ... */ private SSLKsFactory() { } diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/Unit.java b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/Unit.java index 6984ddd25..eb98b23ba 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/Unit.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/engine/util/Unit.java @@ -59,7 +59,7 @@ public class Unit { Matcher m = numberFmtPattern.matcher(spec); if (m.matches()) { String numberpart = m.group("number"); - Double base = Double.valueOf(numberpart); + double base = Double.parseDouble(numberpart); String unitpart = m.group("unit"); if (unitpart != null) { Duration durationDuration = Duration.valueOfSuffix(unitpart); @@ -71,7 +71,7 @@ public class Unit { double multiplier = (double) specnanos / (double) resultnanos; base = base * multiplier; } - return Optional.of(base.longValue()); + return Optional.of((long)base); } else { logger.error("Parsing error for specifier: '" + spec + "'"); return Optional.empty(); @@ -86,15 +86,15 @@ public class Unit { public static Optional convertDoubleCount(Count resultUnit, String spec) { Matcher e = numberExponentPattern.matcher(spec); if (e.matches()) { - double base= Double.valueOf(e.group("number")); - double exponent = Double.valueOf(e.group("exponent")); + double base= Double.parseDouble(e.group("number")); + double exponent = Double.parseDouble(e.group("exponent")); double value= Math.pow(base, exponent); spec = e.group("pre")+ value + e.group("post"); } Matcher m = numberFmtPattern.matcher(spec); if (m.matches()) { String numberpart = m.group("number"); - double base = Double.valueOf(numberpart); + double base = Double.parseDouble(numberpart); String unitpart = m.group("unit"); if (unitpart != null) { Count specifierUnit = Count.valueOfSuffix(unitpart); @@ -130,7 +130,7 @@ public class Unit { Matcher m = numberFmtPattern.matcher(spec); if (m.matches()) { String numberpart = m.group("number"); - long base = Long.valueOf(numberpart); + long base = Long.parseLong(numberpart); String unitpart = m.group("unit"); if (unitpart != null) { Count specifierUnit = Count.valueOfSuffix(unitpart); @@ -140,7 +140,7 @@ public class Unit { double specifierScale = specifierUnit.getMultiplier(); double resultScale = resultUnit.getMultiplier(); double multiplier = (specifierScale / resultScale); - base *= multiplier; + base *= (long) multiplier; } return Optional.of(base); } else { @@ -162,14 +162,14 @@ public class Unit { if (scinoteMatcher.matches() && ( scinoteMatcher.group("to10power")!=null || scinoteMatcher.group("fractional")!=null)) { - Double doubleValue = Double.valueOf(scinoteMatcher.group("number")); - spec = spec.replace(scinoteMatcher.group("number"),String.valueOf(doubleValue.longValue())); + double doubleValue = Double.parseDouble(scinoteMatcher.group("number")); + spec = spec.replace(scinoteMatcher.group("number"),String.valueOf((long) doubleValue)); } Matcher exponentMatcher = numberExponentPattern.matcher(spec); if (exponentMatcher.matches()) { - long number = Long.valueOf(exponentMatcher.group("number")); - long exponent = Long.valueOf(exponentMatcher.group("exponent")); + long number = Long.parseLong(exponentMatcher.group("number")); + long exponent = Long.parseLong(exponentMatcher.group("exponent")); if (number == 1L) { logger.warn("If you are using exponent notation for '" + spec + "', you'll only ever get 1L. " + "Did you intend to use scientific notation, where the exponent is implied to the base 10? " + @@ -196,7 +196,7 @@ public class Unit { Matcher m = numberFmtPattern.matcher(spec); if (m.matches()) { String numberpart = m.group("number"); - double base = Double.valueOf(numberpart); + double base = Double.parseDouble(numberpart); String unitpart = m.group("unit"); if (unitpart != null) { Bytes specifierUnit = Bytes.valueOfSuffix(unitpart); diff --git a/nb-api/src/main/java/io/nosqlbench/nb/api/files/FileAccess.java b/nb-api/src/main/java/io/nosqlbench/nb/api/nbio/FileAccess.java similarity index 90% rename from nb-api/src/main/java/io/nosqlbench/nb/api/files/FileAccess.java rename to nb-api/src/main/java/io/nosqlbench/nb/api/nbio/FileAccess.java index a8fba134d..be5262a38 100644 --- a/nb-api/src/main/java/io/nosqlbench/nb/api/files/FileAccess.java +++ b/nb-api/src/main/java/io/nosqlbench/nb/api/nbio/FileAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 nosqlbench + * Copyright (c) 2022-2024 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,7 @@ * limitations under the License. */ -package io.nosqlbench.nb.api.files; +package io.nosqlbench.nb.api.nbio; import io.nosqlbench.nb.api.nbio.NBIO; diff --git a/nb-api/src/test/resources/log4j2.xml b/nb-api/src/main/log4j2.xml similarity index 98% rename from nb-api/src/test/resources/log4j2.xml rename to nb-api/src/main/log4j2.xml index 003d82533..1f356bc2d 100644 --- a/nb-api/src/test/resources/log4j2.xml +++ b/nb-api/src/main/log4j2.xml @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injava/NB_cocycledelay_bursty.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injava/NB_cocycledelay_bursty.java index eda7766f6..1d98d41e2 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injava/NB_cocycledelay_bursty.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injava/NB_cocycledelay_bursty.java @@ -24,7 +24,7 @@ import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; import io.nosqlbench.nb.api.components.core.NBComponent; import io.nosqlbench.nb.api.components.events.ParamChange; import io.nosqlbench.engine.api.activityapi.core.Activity; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injavascript/ScenarioExampleTests.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injavascript/ScenarioExampleTests.java index 8cac60e95..3a08dad86 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injavascript/ScenarioExampleTests.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/injavascript/ScenarioExampleTests.java @@ -30,6 +30,7 @@ import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; import java.util.List; +import java.util.Map; @Disabled @Execution(ExecutionMode.SAME_THREAD) @@ -41,10 +42,10 @@ public class ScenarioExampleTests { throw new RuntimeException("Found [" + sources.size() +"] sources for '" + params[0] +"'"); } - NBCLIOptions parser = new NBCLIOptions(params); + NBCLIOptions parser = new NBCLIOptions(params, NBCLIOptions.Mode.ParseAllOptions); List commands = parser.getCommands(); var myroot = new TestComponent("test_"+params[0]); - NBSession session = new NBSession(myroot,"session_"+params[0]); + NBSession session = new NBSession(myroot,"session_"+params[0], Map.of()); System.out.println("=".repeat(29) + " Running scenario test for example scenario: " + params[0]); ExecutionResult result = session.apply(commands); return result; diff --git a/nbr/src/main/java/io/nosqlbench/nb/PlaceHolder.java b/nbr/src/main/java/io/nosqlbench/nb/PlaceHolder.java deleted file mode 100644 index 0b0f52c25..000000000 --- a/nbr/src/main/java/io/nosqlbench/nb/PlaceHolder.java +++ /dev/null @@ -1,24 +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.nb; - -/** - * This class is just a place holder. It holds a place. - * If only nexus understood aggregator module without src... - */ -public class PlaceHolder { -} diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/SimFrameUtils.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/SimFrameUtils.java similarity index 92% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/SimFrameUtils.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/SimFrameUtils.java index ba93cd721..388e78c4d 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/SimFrameUtils.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/SimFrameUtils.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe; import io.nosqlbench.engine.api.activityapi.core.Activity; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; import io.nosqlbench.nb.api.components.events.ParamChange; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; @@ -28,7 +28,6 @@ import java.util.concurrent.locks.LockSupport; public class SimFrameUtils { - public static void awaitActivity(Activity flywheel) { // await flywheel actually spinning, or timeout with error NBMetricTimer result_success_timer = flywheel.find().timer("name:result_success"); diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/FindmaxFrameData.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/SimFrameValueData.java similarity index 89% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/FindmaxFrameData.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/SimFrameValueData.java index 8b0b40bdf..e5e1e8c68 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/FindmaxFrameData.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/SimFrameValueData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.findmax; +package io.nosqlbench.scenarios.simframe.capture; import io.nosqlbench.engine.api.activityapi.core.Activity; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge; @@ -22,8 +22,8 @@ import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricHistogram; import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture; -public class FindmaxFrameData extends SimFrameCapture { - public FindmaxFrameData(Activity activity) { +public class SimFrameValueData extends SimFrameCapture { + public SimFrameValueData(Activity activity) { NBMetricTimer result_timer = activity.find().timer("name:result"); NBMetricTimer result_success_timer = activity.find().timer("name:result_success"); NBMetricGauge cyclerate_gauge = activity.find().gauge("name=config_cyclerate"); @@ -31,13 +31,13 @@ public class FindmaxFrameData extends SimFrameCapture { NBMetricHistogram tries_histo = tries_histo_src.attachHdrDeltaHistogram(); addDirect("target_rate", - () -> cyclerate_gauge.getValue(), + cyclerate_gauge::getValue, Double.NaN); addDeltaTime("achieved_oprate", - () -> result_timer.getCount(), + result_timer::getCount, Double.NaN); addDeltaTime("achieved_ok_oprate", - () -> result_success_timer.getCount() + result_success_timer::getCount , 1.0); addRemix("achieved_success_ratio", vars -> { diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/generic/ParamsEffector.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/package-info.java similarity index 68% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/generic/ParamsEffector.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/package-info.java index a98b69b38..940b18330 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/generic/ParamsEffector.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/capture/package-info.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.generic; - -import io.nosqlbench.engine.api.activityapi.core.Activity; - -public interface ParamsEffector

{ - P applyParams(P ptype, Activity activity); -} +/** + * The types in this package are used to define observable dependent variables, + * specify how they are measured, formulate them into a value function, and provide + * a view of these over time as frames are executed. + */ +package io.nosqlbench.scenarios.simframe.capture; diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/FindmaxSurvey.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/FindmaxSurvey.java deleted file mode 100644 index 1bfd712ed..000000000 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/FindmaxSurvey.java +++ /dev/null @@ -1,68 +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.findmax.survey; - -import io.nosqlbench.engine.api.activityapi.core.Activity; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; -import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; -import io.nosqlbench.nb.api.components.events.ParamChange; -import io.nosqlbench.scenarios.simframe.capture.JournalView; -import io.nosqlbench.scenarios.simframe.planning.SimFrame; -import io.nosqlbench.scenarios.simframe.planning.SimFramePlanner; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class FindmaxSurvey extends SimFramePlanner { - private final Logger logger = LogManager.getLogger(FindmaxSurvey.class); - - public FindmaxSurvey(NBCommandParams params) { - super(params); - } - - - @Override - public SurveyConfig getConfig(NBCommandParams params) { - return new SurveyConfig(params); - } - - public SurveyFrameParams initialStep() { - return new SurveyFrameParams(config.max_rate(), config.steps(), "INITIAL"); - } - - /** - * Using a stateful history of all control parameters and all results, decide if there - * is additional search space and return a set of parameters for the next workload - * simulation frame. If the stopping condition has been met, return null - * - * @param journal - * All parameters and results, organized in enumerated simulation frames - * @return Optionally, a set of paramValues which indicates another simulation frame should be sampled, else null - */ - - @Override - public SurveyFrameParams nextStep(JournalView journal) { - return null; - } - - - @Override - public void applyParams(SurveyFrameParams params, Activity flywheel) { - flywheel.onEvent(ParamChange.of(new CycleRateSpec(params.rate(), 1.1d, SimRateSpec.Verb.restart))); - - } -} diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NB_findmax.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/CMD_optimize.java similarity index 81% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NB_findmax.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/CMD_optimize.java index 2f29e26a4..3d2888fcd 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NB_findmax.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/CMD_optimize.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.findmax; +package io.nosqlbench.scenarios.simframe.optimizers; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; @@ -23,8 +23,9 @@ import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitie import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture; -import io.nosqlbench.scenarios.simframe.findmax.planners.FindmaxPlannerType; -import io.nosqlbench.scenarios.simframe.optimo.SimFrameUtils; +import io.nosqlbench.scenarios.simframe.capture.SimFrameValueData; +import io.nosqlbench.scenarios.simframe.optimizers.planners.OptimizerPlannerTypes; +import io.nosqlbench.scenarios.simframe.SimFrameUtils; import io.nosqlbench.scenarios.simframe.planning.SimFramePlanner; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -52,11 +53,11 @@ import java.io.Reader; * *

This can be tested as

{@code nb5 --show-stacktraces java io.nosqlbench.scenarios.findmax.SC_findmax threads=36}

*/ -@Service(value = NBBaseCommand.class,selector = "findmax") -public class NB_findmax extends NBBaseCommand { - private final static Logger logger = LogManager.getLogger(NB_findmax.class); +@Service(value = NBBaseCommand.class,selector = "optimize") +public class CMD_optimize extends NBBaseCommand { + private final static Logger logger = LogManager.getLogger(CMD_optimize.class); - public NB_findmax(NBBufferedContainer parentComponent, String scenarioName, String context) { + public CMD_optimize(NBBufferedContainer parentComponent, String scenarioName, String context) { super(parentComponent, scenarioName, context); } @@ -65,10 +66,10 @@ public class NB_findmax extends NBBaseCommand { Activity flywheel = SimFrameUtils.findFlywheelActivity(controller, params.get("activity")); stdout.println("starting analysis on activity '" + flywheel.getAlias() + "'"); SimFrameUtils.awaitActivity(flywheel); - SimFrameCapture capture = new FindmaxFrameData(flywheel); + SimFrameCapture capture = new SimFrameValueData(flywheel); String plannerType = params.getOrDefault("planner", "ratchet"); - FindmaxPlannerType plannerImpl = FindmaxPlannerType.valueOf(plannerType); - SimFramePlanner planner = plannerImpl.createPlanner(params); + OptimizerPlannerTypes plannerImpl = OptimizerPlannerTypes.valueOf(plannerType); + SimFramePlanner planner = plannerImpl.createPlanner(this,params); Record result = planner.analyze(flywheel, capture, stdout, stderr, controller); stdout.println("result:\n" + result); return result; diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NBFindmaxInfo.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/INFO_optimize.java similarity index 77% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NBFindmaxInfo.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/INFO_optimize.java index 7d9b09e6d..352b1d387 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/NBFindmaxInfo.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/INFO_optimize.java @@ -14,17 +14,16 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.findmax; +package io.nosqlbench.scenarios.simframe.optimizers; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; import io.nosqlbench.nb.annotations.Service; -import io.nosqlbench.scenarios.simframe.optimo.NB_optimo; -@Service(value = NBCommandInfo.class,selector = "findmax") -public class NBFindmaxInfo extends NBCommandInfo { +@Service(value = NBCommandInfo.class,selector = "optimize") +public class INFO_optimize extends NBCommandInfo { @Override public Class getType() { - return NB_findmax.class; + return CMD_optimize.class; } } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NB_optimo.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/CMD_optimo.java similarity index 93% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NB_optimo.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/CMD_optimo.java index 3ecca4ebd..56564e13b 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NB_optimo.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/CMD_optimo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.optimizers.optimo; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBBufferedContainer; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; @@ -24,13 +24,14 @@ import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricTimer; import io.nosqlbench.nb.api.components.events.ParamChange; import io.nosqlbench.nb.api.components.events.SetThreads; import io.nosqlbench.engine.api.activityapi.core.Activity; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec; -import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.scenarios.simframe.SimFrameUtils; import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture; import io.nosqlbench.scenarios.simframe.capture.SimFrameJournal; -import io.nosqlbench.scenarios.simframe.findmax.NB_findmax; +import io.nosqlbench.scenarios.simframe.optimizers.CMD_optimize; import io.nosqlbench.scenarios.simframe.planning.SimFrame; import io.nosqlbench.scenarios.simframe.planning.SimFrameFunction; import io.nosqlbench.scenarios.simframe.stabilization.StatFunctions; @@ -62,10 +63,10 @@ import java.util.List; * to contain a reasonably representative character of the overall manifold * */ -public class NB_optimo extends NBBaseCommand { - private final static Logger logger = LogManager.getLogger(NB_findmax.class); +public class CMD_optimo extends NBBaseCommand { + private final static Logger logger = LogManager.getLogger(CMD_optimize.class); - public NB_optimo(NBBufferedContainer parentComponent, String phaseName, String targetScenario) { + public CMD_optimo(NBBufferedContainer parentComponent, String phaseName, String targetScenario) { super(parentComponent, phaseName, targetScenario); } @@ -82,7 +83,7 @@ public class NB_optimo extends NBBaseCommand { OptimoSearchSettings optimoSearchParams = new OptimoSearchSettings(params, model); - model.add("rate", 20, optimoSearchParams.startRate(), optimoSearchParams.startRate()*4, + model.add("rate", 10, optimoSearchParams.startRate(), optimoSearchParams.startRate()*4, rate -> flywheel.onEvent(ParamChange.of(new CycleRateSpec(rate, 1.1d, SimRateSpec.Verb.restart))) ); model.add("threads", 10, 50, 2000, diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NBOptimoInfo.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/NBOptimoInfo.java similarity index 91% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NBOptimoInfo.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/NBOptimoInfo.java index f0156a223..5297d8f49 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/NBOptimoInfo.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/NBOptimoInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.optimizers.optimo; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand; import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo; @@ -24,6 +24,6 @@ import io.nosqlbench.nb.annotations.Service; public class NBOptimoInfo extends NBCommandInfo { @Override public Class getType() { - return NB_optimo.class; + return CMD_optimo.class; } } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameFunction.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameFunction.java similarity index 97% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameFunction.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameFunction.java index df6106950..4219f4343 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameFunction.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameFunction.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.optimizers.optimo; import io.nosqlbench.engine.api.activityapi.core.Activity; import io.nosqlbench.engine.api.activityapi.core.RunState; diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameParams.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameParams.java similarity index 59% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameParams.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameParams.java index 67ca5b30e..d4534822a 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoFrameParams.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoFrameParams.java @@ -14,14 +14,18 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.optimizers.optimo; +import io.nosqlbench.engine.core.lifecycle.scenario.container.InvokableResult; +import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult; +import io.nosqlbench.nb.api.config.standard.Param; +import io.nosqlbench.scenarios.simframe.planning.GenericParamModel; + import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -public class OptimoFrameParams{ +public class OptimoFrameParams implements InvokableResult { OptimoParamModel model; double[] paramValues; @@ -40,4 +44,12 @@ public class OptimoFrameParams{ return paramValues; } + @Override + public Map asResult() { + Map result = new LinkedHashMap<>(); + for (int i = 0; i < this.paramValues.length; i++) { + result.put(model.getParams().get(i).name(),String.valueOf(paramValues[i])); + } + return result; + } } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoParamModel.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoParamModel.java similarity index 95% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoParamModel.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoParamModel.java index 05cbd7f71..588005626 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/OptimoParamModel.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/optimo/OptimoParamModel.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.optimizers.optimo; import io.nosqlbench.scenarios.simframe.planning.GenericParamModel; @@ -29,7 +29,7 @@ public class OptimoParamModel { public OptimoParamModel add(String name, double min, double initial, double max, DoubleConsumer effector) { if (min>initial || initial > max) { - throw new RuntimeException("parameters must be in min { - private final Logger logger = LogManager.getLogger(FindmaxRampup.class); - - public FindmaxRampup(NBCommandParams analyzerParams) { - super(analyzerParams); +public class FindmaxPlanner extends SimFramePlanner { + public FindmaxPlanner(NBBaseComponent parent, NBCommandParams analyzerParams) { + super(parent, analyzerParams); } @Override - public RampupConfig getConfig(NBCommandParams params) { - return new RampupConfig(params); + public FindmaxConfig getConfig(NBCommandParams params) { + return new FindmaxConfig(params); } - public RampupFrameParams initialStep() { - return new RampupFrameParams( + public FindmaxFrameParams initialStep() { + return new FindmaxFrameParams( config.rate_base(), config.rate_step(), config.sample_time_ms(), config.min_settling_ms(), "INITIAL" ); } @@ -57,11 +54,11 @@ public class FindmaxRampup extends SimFramePlanner journal) { - SimFrame last = journal.last(); - SimFrame best = journal.bestRun(); + public FindmaxFrameParams nextStep(JournalView journal) { + SimFrame last = journal.last(); + SimFrame best = journal.bestRun(); if (best.index() == last.index()) { // got better consecutively - return new RampupFrameParams( + return new FindmaxFrameParams( last.params().rate_shelf(), last.params().rate_delta() * config.rate_incr(), last.params().sample_time_ms(), @@ -74,7 +71,7 @@ public class FindmaxRampup extends SimFramePlanner nextWorseFrameWithHigherRate = journal.frames().stream() + SimFrame nextWorseFrameWithHigherRate = journal.frames().stream() .filter(f -> f.value() < best.value()) .filter(f -> f.params().computed_rate() > best.params().computed_rate()) .min(Comparator.comparingDouble(f -> f.params().computed_rate())) .orElseThrow(() -> new RuntimeException("inconsistent samples")); if ((nextWorseFrameWithHigherRate.params().computed_rate() - best.params().computed_rate()) > config.rate_step()) { - return new RampupFrameParams( + return new FindmaxFrameParams( best.params().computed_rate(), config.rate_step(), (long) (last.params().sample_time_ms() * config.sample_incr()), @@ -105,7 +102,7 @@ public class FindmaxRampup extends SimFramePlanner { - private final Logger logger = LogManager.getLogger(FindmaxRatchet.class); - - public FindmaxRatchet(NBCommandParams params) { - super(params); +public class RatchetPlanner extends SimFramePlanner { + public RatchetPlanner(NBBaseComponent parent, NBCommandParams params) { + super(parent, params); } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyConfig.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveConfig.java similarity index 66% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyConfig.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveConfig.java index 6a7eb67ca..d403e06a9 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyConfig.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveConfig.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.findmax.survey; +package io.nosqlbench.scenarios.simframe.optimizers.planners.rcurve; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; @@ -22,15 +22,21 @@ import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; * These search parameters are based on the original findmax algorithm, and * should be reduced down to the minimum set needed. */ -public record SurveyConfig( +public record RCurveConfig( double max_rate, - int steps + int max_step, + double min_sample_seconds ) { - public SurveyConfig(NBCommandParams params) { + public RCurveConfig(NBCommandParams params) { this( - params.maybeGet("max_rate").map(Double::parseDouble).orElse(1.2d), - params.maybeGet("steps").map(Integer::parseInt).orElse(3) + params.maybeGet("max_rate").map(Double::parseDouble).orElse(10.0), + params.maybeGet("max_step").map(Integer::parseInt).orElse(10), + params.maybeGet("min_sample_seconds").map(Double::parseDouble).orElse(10.0) ); - } + + double rateForStep(int step) { + return ((double)step/(double) max_step)* max_rate; + } + } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyFrameParams.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveFrameParams.java similarity index 74% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyFrameParams.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveFrameParams.java index 411becca1..23058d7b6 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/findmax/survey/SurveyFrameParams.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurveFrameParams.java @@ -14,12 +14,15 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.findmax.survey; +package io.nosqlbench.scenarios.simframe.optimizers.planners.rcurve; -public record SurveyFrameParams( +public record RCurveFrameParams( double rate, - double step, + int step, + int maxsteps, String description - ) { + public double ratio() { + return ((double)step/(double)maxsteps); + } } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurvePlanner.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurvePlanner.java new file mode 100644 index 000000000..10c4fa9fb --- /dev/null +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimizers/planners/rcurve/RCurvePlanner.java @@ -0,0 +1,105 @@ +/* + * 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.optimizers.planners.rcurve; + +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.api.activityapi.simrate.CycleRateSpec; +import io.nosqlbench.engine.api.activityapi.simrate.SimRateSpec; +import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.nb.api.components.core.NBBaseComponent; +import io.nosqlbench.nb.api.components.events.ParamChange; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; +import io.nosqlbench.scenarios.simframe.capture.JournalView; +import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture; +import io.nosqlbench.scenarios.simframe.planning.HoldAndSample; +import io.nosqlbench.scenarios.simframe.planning.SimFrame; +import io.nosqlbench.scenarios.simframe.planning.SimFramePlanner; + +import java.util.concurrent.locks.LockSupport; + +public class RCurvePlanner extends SimFramePlanner implements HoldAndSample { + private RCurveFrameParams lastFrame; + + public RCurvePlanner(NBBaseComponent parent, NBCommandParams params) { + super(parent,params); + create().gauge( + "rcurve_step", + () -> lastFrame==null ? 0 : (double)lastFrame.step(), + MetricCategory.Analysis, + "The current step which the response curve analyzer is on" + ); + create().gauge( + "rcurve_maxstep", + () -> lastFrame==null ? 0 : (double)lastFrame.maxsteps(), + MetricCategory.Analysis, + "The maximum step that the response curve analyzer will run" + ); + create().gauge( + "rcurve_ratio", + () -> lastFrame==null ? 0.0 : lastFrame.ratio(), + MetricCategory.Analysis, + "The fractional throughput capacity of the current response curve step" + ); + create().gauge( + "rcurve_rate",() -> lastFrame==null ? 0.0 : lastFrame.rate(), + MetricCategory.Analysis, + "The actual throughput target of the current response curve step" + ); + } + + @Override + public RCurveConfig getConfig(NBCommandParams params) { + return new RCurveConfig(params); + } + + public RCurveFrameParams initialStep() { + return new RCurveFrameParams(config.rateForStep(1), 1,config.max_step(),"INITIAL"); + } + + /** + * Using a stateful history of all control parameters and all results, decide if there + * is additional search space and return a set of parameters for the next workload + * simulation frame. If the stopping condition has been met, return null + * + * @param journal + * All parameters and results, organized in enumerated simulation frames + * @return Optionally, a set of paramValues which indicates another simulation frame should be sampled, else null + */ + + @Override + public RCurveFrameParams nextStep(JournalView journal) { + SimFrame last = journal.last(); + int nextStep = last.params().step() +1; + if (nextStep<=config.max_step()) { + return new RCurveFrameParams(config.rateForStep(nextStep),nextStep,config.max_step(),"Advancing to step " + nextStep); + } else { + return null; + } + } + + @Override + public void applyParams(RCurveFrameParams params, Activity flywheel) { + flywheel.onEvent(ParamChange.of(new CycleRateSpec(params.rate(), 1.1d, SimRateSpec.Verb.restart))); + } + + @Override + public void holdAndSample(SimFrameCapture capture) { + logger.info("holding and sampling for " + config.min_sample_seconds() + " seconds"); + LockSupport.parkNanos((long)(config.min_sample_seconds()*1_000_000_000)); + capture.awaitSteadyState(); + } +} diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/ThreadSpec.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/HoldAndSample.java similarity index 64% rename from nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/ThreadSpec.java rename to nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/HoldAndSample.java index 3672948be..434296092 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/optimo/ThreadSpec.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/HoldAndSample.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package io.nosqlbench.scenarios.simframe.optimo; +package io.nosqlbench.scenarios.simframe.planning; -public class ThreadSpec { - public ThreadSpec(double threads) { - } +import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture; + +public interface HoldAndSample { + /** + * This should block until it is ready to stop sampling. + * @param capture The capture data for the current frame. + */ + void holdAndSample(SimFrameCapture capture); } diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/SimFramePlanner.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/SimFramePlanner.java index 80e9ca08b..756095679 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/SimFramePlanner.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/planning/SimFramePlanner.java @@ -19,6 +19,8 @@ package io.nosqlbench.scenarios.simframe.planning; import io.nosqlbench.engine.api.activityapi.core.Activity; import io.nosqlbench.engine.core.lifecycle.scenario.container.ContainerActivitiesController; import io.nosqlbench.engine.core.lifecycle.scenario.container.NBCommandParams; +import io.nosqlbench.nb.api.components.core.NBBaseComponent; +import io.nosqlbench.nb.api.labels.NBLabels; import io.nosqlbench.scenarios.simframe.capture.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,12 +34,13 @@ import java.io.PrintWriter; * @param * The configuration type for the planner */ -public abstract class SimFramePlanner { - private final Logger logger = LogManager.getLogger(SimFramePlanner.class); +public abstract class SimFramePlanner extends NBBaseComponent { + protected final Logger logger = LogManager.getLogger(SimFramePlanner.class); protected final C config; protected final SimFrameJournal

journal; - public SimFramePlanner(NBCommandParams analyzerParams) { + public SimFramePlanner(NBBaseComponent parent, NBCommandParams analyzerParams) { + super(parent, NBLabels.forKV()); this.config = getConfig(analyzerParams); this.journal = initJournal(); } @@ -69,11 +72,11 @@ public abstract class SimFramePlanner { stdout.println(frameParams); applyParams(frameParams,flywheel); capture.startWindow(); - capture.awaitSteadyState(); -// applyParams(frameParams,flywheel); -// capture.restartWindow(); -//// controller.waitMillis(500); -// capture.awaitSteadyState(); + if (this instanceof HoldAndSample has) { + has.holdAndSample(capture); + } else { + capture.awaitSteadyState(); + } capture.stopWindow(); journal.record(frameParams, capture.last()); stdout.println(capture.last()); 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 356469861..6cb5f4e58 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 @@ -118,6 +118,9 @@ public class StabilityDetector implements Runnable { // if previous bigger window had a higher stddev than the one after, then it is converging double reductionFactor = (stddev[i + 1] / stddev[i]); basis *= reductionFactor; + if (Double.isNaN(basis)) { + throw new RuntimeException("basis is NaN"); + } } // TODO: investigate why we get NaN sometimes and what it means for stability checks @@ -155,6 +158,9 @@ public class StabilityDetector implements Runnable { } } + // TODO: Add a check for when stddev is lower than some fixed value, or when both + // (or all) windows are below some small threshold + // and perhaps add auto-correlation checks for (any of) style unblocking private void updateAndAwait() { int interval = (int) (this.timeSliceSeconds * 1000); startedAt = System.currentTimeMillis(); @@ -175,9 +181,9 @@ public class StabilityDetector implements Runnable { double value = source.getAsDouble(); apply(value); double stabilityFactor = computeStability(); -// if (Double.isNaN(stabilityFactor)) { -// System.out.println("NaN stability factor"); -// } + if (Double.isNaN(stabilityFactor)) { + throw new RuntimeException("NaN stability factor:" + this); + } if (stabilityFactor > threshold) { detectionTime = ((double) (nextCheckAt - startedAt)) / 1000d; diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java index 980138ca8..ffb98c5a2 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatBucket.java @@ -43,13 +43,18 @@ public final class StatBucket { } else if (Double.isNaN(popped)) { var newMean = mean + ((value - mean) / ringbuf.count()); var dSquaredIncrement = ((value - newMean) * (value - mean)); + // If this value is too small to be interpreted as a double it gets converted to + // zero, which is not what we want. So we use the smallest possible double value + if (dSquaredIncrement == 0) dSquaredIncrement = Double.MIN_VALUE; dSquared += dSquaredIncrement; mean = newMean; } else { var meanIncrement = (value - popped) / ringbuf.count(); var newMean = mean + meanIncrement; - var dSquaredIncrement = ((value - popped) * (value - newMean + popped - mean)); + // If this value is too small to be interpreted as a double it gets converted to + // zero, which is not what we want. So we use the smallest possible double value + if (dSquaredIncrement == 0) dSquaredIncrement = Double.MIN_VALUE; var newDSquared = this.dSquared + dSquaredIncrement; mean = newMean; dSquared = newDSquared; diff --git a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatFunctions.java b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatFunctions.java index 77aee3321..1b7de3c6f 100644 --- a/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatFunctions.java +++ b/nbr/src/main/java/io/nosqlbench/scenarios/simframe/stabilization/StatFunctions.java @@ -34,13 +34,16 @@ public class StatFunctions { return 1.0d/(1.0d+Math.pow(Math.E,(-10000.0d*(input-(lowcut-0.001d))))); } /** - * Like {@see #sigmoidE4HighPass, but inverted with respect to the Y axis. This has the same roll-off charater + * Like {@link #sigmoidE4HighPass}, but inverted with respect to the Y axis. This has the same roll-off charater * where the high (1.0) shelf in included through the cutoff value. * @param input x * @param highcut The point on the x axis at which all lower values should yield 1.0 */ public static double sigmoidE4LowPass(double input, double highcut) { double v = 1.0d/(1.0d + Math.pow(Math.E, (10000.0d * (input - (highcut + 0.001d))))); + // If this value is too small to be interpreted as a double it gets converted to + // zero, which is not what we want. So we use the smallest possible double value + if (v == 0) v = Double.MIN_VALUE; return v; } diff --git a/nbr/src/main/resources/examples/bindings_vectors.yaml b/nbr/src/main/resources/examples/bindings_vectors.yaml index a2413470c..95a034d69 100644 --- a/nbr/src/main/resources/examples/bindings_vectors.yaml +++ b/nbr/src/main/resources/examples/bindings_vectors.yaml @@ -21,7 +21,7 @@ bindings: # create a simple 2-d vector from a step function over the unit interval - # of 10 steps (the maximum number of characters per digit) + # of 10 max_step (the maximum number of characters per digit) v2d: DoubleVectors('0-2*2') # use Stringify() to visualize the value of numeric array types diff --git a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java index 5a21cebe7..334b6ef06 100644 --- a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java +++ b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java @@ -68,7 +68,7 @@ class ExitStatusIntegrationTests { "driver=diag", "cyclerate=10", "not_a_thing", "cycles=100", "-vvv" ); String stdout = String.join("\n", result.getStdoutData()); - assertThat(stdout).contains("internal error: You requested exactly one instance of a service by name 'not_a_thing'"); + assertThat(stdout).contains("You requested exactly one instance of a service by name 'not_a_thing'"); assertThat(result.exitStatus).isEqualTo(2); } diff --git a/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsIntegrationTest.java b/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsIntegrationTest.java index 87426d9e1..09bff454b 100644 --- a/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsIntegrationTest.java +++ b/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsIntegrationTest.java @@ -19,6 +19,7 @@ package io.nosqlbench.engine.core.script; import com.codahale.metrics.Histogram; import io.nosqlbench.nb.api.config.standard.TestComponent; import io.nosqlbench.nb.api.components.core.NBComponent; +import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -32,7 +33,12 @@ public class MetricsIntegrationTest { @Test public void testHistogramLogger() { NBComponent parent = new TestComponent("metricstest","metricstest","alias","foo","driver","diag","op","noop"); - final Histogram testhistogram = parent.create().histogram("testhistogram", 3); + final Histogram testhistogram = parent.create().histogram( + "testhistogram", + 3, + MetricCategory.Verification, + "test metric" + ); // TODO: metrics // ActivityMetrics.addHistoLogger("testsession", ".*","testhisto.log","1s"); testhistogram.update(400); diff --git a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/conversions/from_charbuffer/ToString.java b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/conversions/from_charbuffer/ToString.java new file mode 100644 index 000000000..815742c69 --- /dev/null +++ b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/conversions/from_charbuffer/ToString.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 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.conversions.from_charbuffer; + +import io.nosqlbench.virtdata.api.annotations.Categories; +import io.nosqlbench.virtdata.api.annotations.Category; +import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; + +import java.nio.CharBuffer; +import java.util.function.Function; + +/** + * Convert CharBuffer to String + */ +@ThreadSafeMapper +@Categories(Category.conversion) +public class ToString implements Function { + @Override + public String apply(CharBuffer charBuffer) { + return charBuffer.toString(); + } +} diff --git a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_angular1_v.java b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_angular1_v.java new file mode 100644 index 000000000..6c8296fba --- /dev/null +++ b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_angular1_v.java @@ -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.vectors.dnn; + +import io.nosqlbench.virtdata.api.annotations.Categories; +import io.nosqlbench.virtdata.api.annotations.Category; +import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; + +import java.util.Arrays; +import java.util.function.LongFunction; + +@ThreadSafeMapper +@Categories(Category.experimental) +public class DNN_angular1_v implements LongFunction { + + private final int d; + private final long n; + private final long m; + + /** + * @param D + * Dimensions in each vector + * @param N + * The number of vectors in the training set + * @param M + * The modulo which is used to construct equivalence classes + */ + public DNN_angular1_v(int D, long N, long M) { + d = D; + n = N; + m = M; + } + + @Override + public float[] apply(long i) { + float[] vector = new float[d]; + Arrays.fill(vector, i + 1); + vector[vector.length - 1] = (i + 1) * (i % m); + return vector; + } +} diff --git a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_euclidean_v_wrap.java b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_euclidean_v_wrap.java index 3cd8fb911..26eaec7b6 100644 --- a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_euclidean_v_wrap.java +++ b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNN_euclidean_v_wrap.java @@ -22,6 +22,11 @@ import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; import java.util.function.LongFunction; +/** + * This represents an enumerated population of vectors of some dimension, + * where any ordinal values which address outside of that enumeration + * simply wrap with modulo. + */ @ThreadSafeMapper @Categories(Category.experimental) public class DNN_euclidean_v_wrap implements LongFunction { diff --git a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/dnn_angular.svg b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/dnn_angular.svg new file mode 100644 index 000000000..2a9cc7503 --- /dev/null +++ b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/dnn_angular.svg @@ -0,0 +1,846 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + d=2,m=30->(1,0)1->(2,2)2->(3,6)3->(4,0)4->(5,5)5->(6,12)6->(7,0)7->(8,8)8->(9,18)9->(10,0)10->(11,11)11->(12,24) + 0 + 1 + 2 + 4 + 5 + 3 + 6 + 9 + 10 + 7 + 8 + 11 + + + diff --git a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNNAngular1VTest.java b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNNAngular1VTest.java new file mode 100644 index 000000000..7c9550919 --- /dev/null +++ b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/vectors/dnn/DNNAngular1VTest.java @@ -0,0 +1,82 @@ +/* + * 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.vectors.dnn; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +class DNNAngular1VTest { + + @Test + public void testCosineSimilarity() { + assertThat(cosine_similarity(new float[]{1, 2, 3, 4, 5, 6, 7}, new float[]{7, 6, 5, 4, 3, 2, 1})).isEqualTo(0.6); + assertThat(cosine_similarity(new float[]{1, 2, 3, 4, 5, 6, 7}, new float[]{1, 2, 3, 4, 5, 6, 7})).isEqualTo(1.0); + } + + @Test + public void testSimpleGeneration() { + DNN_angular1_v vs = new DNN_angular1_v(2, 100, 3); + assertThat(vs.apply(0)).isEqualTo(new float[]{1, 0}); + assertThat(vs.apply(1)).isEqualTo(new float[]{2, 2}); + assertThat(vs.apply(2)).isEqualTo(new float[]{3, 6}); + assertThat(vs.apply(3)).isEqualTo(new float[]{4, 0}); + assertThat(vs.apply(4)).isEqualTo(new float[]{5, 5}); + assertThat(vs.apply(5)).isEqualTo(new float[]{6, 12}); + assertThat(vs.apply(6)).isEqualTo(new float[]{7, 0}); + } + + @Test + public void testBasicAngularVectors() { + int M = 7; + DNN_angular1_v vf = new DNN_angular1_v(10, 100, M); + float[][] vectors = new float[100][]; + for (int i = 0; i < 100; i++) { + vectors[i] = vf.apply(i); + } + int[] same = new int[100]; + Arrays.fill(same, -1); + for (int vidx = 0; vidx < same.length; vidx++) { + for (int compare_to = 0; compare_to <= vidx; compare_to++) { + double similarity = cosine_similarity(vectors[vidx], vectors[compare_to]); + if (Math.abs(similarity - 1.0d) < 0.00000001d) { + same[vidx] = compare_to; + break; + } + } + } + for (int sameas = M; sameas < same.length; sameas++) { +// System.out.println("idx:" + sameas + ", same[sameas] -> " + same[sameas] + " sameas%7=" + sameas % M); + assertThat(same[sameas] % M).isEqualTo(sameas % M); + } + } + + private double cosine_similarity(float[] a, float[] b) { + double dp = 0.0d; + double as = 0.0d; + double bs = 0.0d; + for (int i = 0; i < a.length; i++) { + dp += (a[i] * b[i]); + as += (a[i] * a[i]); + bs += (b[i] * b[i]); + } + return dp / (Math.sqrt(as) * Math.sqrt(bs)); + } + +}