Merge pull request #929 from nosqlbench/doc-fixups

Some minor improvements to enable better docs
This commit is contained in:
Jonathan Shook authored and GitHub committed 2023-01-18 14:40:32 -06:00
commit 8276bab473
21 files changed
+440 -209

No files matched your search

@@ -17,12 +17,11 @@
package io.nosqlbench.analysis;
import io.nosqlbench.api.apps.BundledApp;
import io.nosqlbench.nb.annotations.Service;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
@Service(value=BundledApp.class, selector = "cql-ring-analyzer")
//@Service(value=BundledApp.class, selector = "cql-ring-analyzer")
public class RingAnalyzer implements BundledApp {
private final static Logger logger = LogManager.getLogger(RingAnalyzer.class);
@@ -21,7 +21,8 @@ op: select * from bar.table;
*json:*
```json5
```json
{
"op": "select * from bar.table;"
}
@@ -29,7 +30,8 @@ op: select * from bar.table;
*ops:*
```json5
```json
[
{
"name": "block0--stmt1",
@@ -55,7 +57,8 @@ ops:
*json:*
```json5
```json
{
"ops": [
"select * from bar.table;"
@@ -65,7 +68,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--stmt1",
@@ -94,7 +98,8 @@ ops:
*json:*
```json5
```json
{
"ops": [
{
@@ -106,7 +111,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -133,7 +139,8 @@ ops:
*json:*
```json5
```json
{
"ops": [
{
@@ -146,7 +153,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -172,7 +180,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": "select * from bar.table;"
@@ -182,7 +191,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -209,7 +219,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -221,7 +232,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -265,7 +277,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -288,7 +301,8 @@ ops:
*ops:*
```json5
```json
[
{
"bindings": {
@@ -350,7 +364,8 @@ blocks:
*json:*
```json5
```json
{
"tags": {
"docleveltag": "is-tagging-everything"
@@ -386,7 +401,8 @@ blocks:
*ops:*
```json5
```json
[
{
"bindings": {
@@ -25,7 +25,8 @@ ops: "cycle number '{{NumberNameToString}}'"
*json:*
```json5
```json
{
"ops": "cycle number '{{NumberNameToString}}'"
}
@@ -33,7 +34,8 @@ ops: "cycle number '{{NumberNameToString}}'"
*ops:*
```json5
```json
[
{
"tags": {
@@ -63,7 +65,8 @@ ops:
*json:*
```json5
```json
{
"ops": [
"even cycle '{{NumberNameToString}}'",
@@ -74,7 +77,8 @@ ops:
*ops:*
```json5
```json
[
{
"tags": {
@@ -114,7 +118,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"myop1": "even cycle '{{NumberNameToString}}'",
@@ -125,7 +130,8 @@ ops:
*ops:*
```json5
```json
[
{
"tags": {
@@ -168,7 +174,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -181,7 +188,8 @@ ops:
*ops:*
```json5
```json
[
{
"tags": {
@@ -217,7 +225,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": [
@@ -230,7 +239,8 @@ ops:
*ops:*
```json5
```json
[
{
"tags": {
@@ -268,7 +278,8 @@ ops:
```
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -287,7 +298,8 @@ ops:
*ops:*
```json5
```json
[
{
"tags": {
@@ -333,7 +345,8 @@ ops:
```
*json:*
```json5
```json
{
"bindings": {
"user_index": "Mod(1000L); ToString();"
@@ -355,7 +368,8 @@ ops:
*ops:*
```json5
```json
[
{
"bindings": {
@@ -24,7 +24,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -37,7 +38,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--special-op-name",
@@ -71,7 +73,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -86,7 +89,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -117,7 +121,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -130,7 +135,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -161,7 +167,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -177,7 +184,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -211,7 +219,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -227,7 +236,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -264,7 +274,8 @@ ops:
*json:*
```json5
```json
{
"ops": {
"op1": {
@@ -282,7 +293,8 @@ ops:
*ops:*
```json5
```json
[
{
"name": "block0--op1",
@@ -313,7 +325,8 @@ ops: "my test op"
*json:*
```json5
```json
{
"params": {
"pname": "pvalue"
@@ -324,7 +337,8 @@ ops: "my test op"
*ops:*
```json5
```json
[
{
"params": {
@@ -356,7 +370,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": {
"block1": {
@@ -371,7 +386,8 @@ blocks:
*ops:*
```json5
```json
[
{
"params": {
@@ -406,7 +422,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": {
"block1": {
@@ -425,7 +442,8 @@ blocks:
*ops:*
```json5
```json
[
{
"params": {
@@ -466,7 +484,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": {
"block1": {
@@ -487,7 +506,8 @@ blocks:
*ops:*
```json5
```json
[
{
"tags": {
@@ -526,7 +546,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": {
"block1": {
@@ -547,7 +568,8 @@ blocks:
*ops:*
```json5
```json
[
{
"tags": {
@@ -51,7 +51,8 @@ template of `select * from users where userid={userid}`.
Once an op template is parsed into a *parsed command*, it has the state shown in the data structure
schematic below:
```json5
```json
{
"name": "some-map-name",
"statics": {
@@ -71,7 +72,8 @@ schematic below:
If either an **op** or **stmt** field is provided, then the same structure as above is used:
```json5
```json
{
"name": "some-string-op",
"statics": {
@@ -194,7 +196,8 @@ provide. There should be one way to access all of these in a consistent and unam
Here is an example data structure which illustrates all the possible elements of a parsed command:
```json5
```json
{
"statics": {
"prepared": "true",
@@ -29,12 +29,14 @@ block is in array form, and thus compares 0 pair-wise elements.
```
*json:*
```json5
```json
[]
```
*ops:*
```json5
```json
[]
```
@@ -14,7 +14,8 @@ desc: <<mydesc:mydescription>>
```
*json:*
```json5
```json
{
"name": "thedefault",
"desc": "mydescription"
@@ -22,7 +23,8 @@ desc: <<mydesc:mydescription>>
```
*ops:*
```json5
```json
[]
```
@@ -36,14 +38,16 @@ name: TEMPLATE(myname,thedefault)
```
*json:*
```json5
```json
{
"name": "thedefault"
}
```
*ops:*
```json5
```json
[]
```
@@ -55,14 +59,16 @@ name: TEMPLATE(myname)
```
*json:*
```json5
```json
{
"name": "UNSET:myname"
}
```
*ops:*
```json5
```json
[]
```
@@ -74,13 +80,15 @@ name: TEMPLATE(myname,)
```
*json:*
```json5
```json
{
"name": null
}
```
*ops:*
```json5
```json
[]
```
@@ -32,7 +32,8 @@ description: |
*json:*
```json5
```json
{
"description": "summary of this workload\nand more details\n"
}
@@ -40,7 +41,8 @@ description: |
*ops:*
```json5
```json
[]
```
@@ -66,7 +68,8 @@ scenarios:
*json:*
```json5
```json
{
"scenarios": {
"default": "run driver=diag cycles=10"
@@ -76,7 +79,8 @@ scenarios:
*ops:*
```json5
```json
[]
```
@@ -93,7 +97,8 @@ scenarios:
*json:*
```json5
```json
{
"scenarios": {
"default": {
@@ -106,7 +111,8 @@ scenarios:
*ops:*
```json5
```json
[]
```
@@ -123,7 +129,8 @@ scenarios:
*json:*
```json5
```json
{
"scenarios": {
"default": [
@@ -136,7 +143,8 @@ scenarios:
*ops:*
```json5
```json
[]
```
@@ -158,7 +166,8 @@ scenarios:
*json:*
```json5
```json
{
"scenarios": {
"default": "run alias==first driver=diag cycles=10"
@@ -168,7 +177,8 @@ scenarios:
*ops:*
```json5
```json
[]
```
@@ -190,7 +200,8 @@ scenarios:
*json:*
```json5
```json
{
"scenarios": {
"default": "run alias===first driver=diag cycles=10"
@@ -200,7 +211,8 @@ scenarios:
*ops:*
```json5
```json
[]
```
@@ -223,7 +235,8 @@ bindings:
*json:*
```json5
```json
{
"bindings": {
"cycle": "Identity();",
@@ -234,7 +247,8 @@ bindings:
*ops:*
```json5
```json
[]
```
@@ -257,7 +271,8 @@ params:
*json:*
```json5
```json
{
"params": {
"param1": "pvalue1",
@@ -268,7 +283,8 @@ params:
*ops:*
```json5
```json
[]
```
@@ -291,7 +307,8 @@ tags:
*json:*
```json5
```json
{
"tags": {
"phase": "main"
@@ -301,7 +318,8 @@ tags:
*ops:*
```json5
```json
[]
```
@@ -334,7 +352,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": {
"namedblock1": {
@@ -352,7 +371,8 @@ blocks:
*ops:*
```json5
```json
[
{
"name": "namedblock1--op1",
@@ -401,7 +421,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": [
{
@@ -425,7 +446,8 @@ blocks:
*ops:*
```json5
```json
[
{
"name": "block1--op1",
@@ -482,7 +504,8 @@ blocks:
*json:*
```json5
```json
{
"blocks": [
{
@@ -495,7 +518,8 @@ blocks:
*ops:*
```json5
```json
[
{
"name": "myblock--stmt1",
@@ -75,7 +75,8 @@ All fenced code is expected to be annotated with a language specifier as above.
For example, consider this JSON data:
```json5
```json
{
"description": "This is a block of json, and it might be syntax highlighted."
}
@@ -17,7 +17,6 @@
package io.nosqlbench.docsys.core;
import io.nosqlbench.api.apps.BundledApp;
import io.nosqlbench.nb.annotations.Service;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -28,7 +27,7 @@ import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
@Service(value=NBWebServerApp.class,selector="appserver")
//@Service(value=NBWebServerApp.class,selector="appserver")
public class NBWebServerApp implements BundledApp {
private static final Logger logger = LogManager.getLogger(NBWebServerApp.class);
@@ -47,7 +47,7 @@ public class MutableFrontMatter extends LinkedHashMap<String,List<String>> {
put(TITLE,List.of(title));
}
public void setWeight(int weight) {
public void setWeight(long weight) {
put(WEIGHT,List.of(String.valueOf(weight)));
}
@@ -16,12 +16,13 @@
package io.nosqlbench.api.markdown.exporter;
import io.nosqlbench.api.apps.BundledApp;
import io.nosqlbench.api.markdown.aggregator.MarkdownDocs;
import io.nosqlbench.api.markdown.types.DocScope;
import io.nosqlbench.api.markdown.types.MarkdownInfo;
import io.nosqlbench.api.apps.BundledApp;
import io.nosqlbench.nb.annotations.Service;
import joptsimple.*;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -29,7 +30,7 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service(value = BundledApp.class, selector = "markdown-exporter")
//@Service(value = BundledApp.class, selector = "markdown-exporter")
public class MarkdownExporter implements BundledApp, Runnable {
public static final String APP_NAME = "exporter";
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,28 +22,55 @@ import java.util.Locale;
public class BundledFrontmatterInjector implements BundledMarkdownProcessor {
private final long floorWeight;
private final long readmeWeight;
public BundledFrontmatterInjector(int floorWeight, int readmeWeight) {
this.floorWeight = floorWeight;
this.readmeWeight = readmeWeight;
}
@Override
public MutableMarkdown apply(MutableMarkdown parsedMarkdown) {
if (parsedMarkdown.getFrontmatter().getWeight()<0) {
if (parsedMarkdown.getFrontmatter().getWeight()<=0) {
String title = parsedMarkdown.getFrontmatter().getTitle();
parsedMarkdown.getFrontmatter().setWeight(alphaWeightOf(title));
if (parsedMarkdown.getPath()!=null && parsedMarkdown.getPath().endsWith("README.md")) {
parsedMarkdown.getFrontmatter().setWeight(readmeWeight);
} else {
parsedMarkdown.getFrontmatter().setWeight(floorWeight +alphaWeightOf(title));
}
}
return parsedMarkdown;
}
private int alphaWeightOf(String name) {
private long alphaWeightOf(String name) {
name=name.toLowerCase(Locale.ROOT);
int sum=0;
int pow=26;
long sum=0;
long pow=26;
for (int i = 0; i < 6; i++) {
if (name.length()>i) {
int ord = name.charAt(i) - 'a';
double addend = Math.pow(pow, i) * ord;
sum += (int)addend;
int ord=name.charAt(i);
if (48 <= ord && ord <= 57) {
ord-=48;
} else if (65 <= ord && ord <= 90) {
ord-=65;
} else if (97 <= ord && ord <= 122) {
ord-=97;
} else {
ord=1;
}
double addendDouble = Math.pow(pow, i) * ord;
long addend = (long)addendDouble;
if (addend>(Long.MAX_VALUE-sum)) {
throw new RuntimeException("overflow on doc weight with value of " + addend + " with sum of " + sum + " for '" + name+"'");
}
sum += addend;
} else {
break;
}
}
sum = sum>0 ? sum : 1;
return sum;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,7 +52,7 @@ public class BundledMarkdownExporter implements BundledApp {
String zipfile = options.valueOf(zipfileSpec);
new BundledMarkdownZipExporter(new BundledFrontmatterInjector()).exportDocs(Path.of(zipfile));
new BundledMarkdownZipExporter(new BundledFrontmatterInjector(1000,100)).exportDocs(Path.of(zipfile));
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
# export-docs
This is the built-in app that allows NB5 to export docs for integration into other systems.
During a build of NB5, this app is used to auto-inject much of the content into the doc site.
Invoking it as `nb5 export-docs` creates (or overwrites) a file called `exported-docs.zip`,
containing the markdown source files for binding functions, bundled apps, specifications, and so on.
Using this mechanism ensures that:
1. NB5 contributors who build drivers, bindings, or other modular pieces can use idiomatic forms of
documentation, allowing them to work in one code base.
2. Contributors are encouraged to make integrated tests follow a literate programming pattern,
so that they work as examples as well as verification tools.
3. Users of NB5 will never see documentation which is included in integrated tests, because any
failing test will prevent a failed build from being published or considered for a release.
This is a relatively new mechanism that will be improved further.
@@ -0,0 +1,94 @@
---
weight: 225811593
title: virdata
---
# virdata
NB5 contains a bundled virtdata app which lets you verify bindings from the command line.
It is useful for sanity checking values as well as getting a concurrent performance baseline for
specific binding recipes.
# diagnostic mode
The `diagnose` mode of virtdata can be used to explain binding resolution logic and potentially
how to fix a binding that doesn't resolve properly:
```shell
nb5 virtdata diagnose "Combinations('0-90-9'); ToInt();"
```
# perftest mode
To see the options you can simply run `nb5 virtdata testmapper`, which gives you
```
ARGS: checkperf 'specifier' threads bufsize start end
example: 'timeuuid()' 100 1000 0 10000
specifier: A VirtData function specifier.
threads: The number of concurrent threads to run.
bufsize: The number of cycles to give each thread at a time.
start: The start cycle for the test, inclusive.
end: The end cycle for the test, exclusive.
```
Assuming you have a working binding recipe that you want to measure for
concurrent performance, you can use the form below:
```shell
nb5 virtdata testmapper "Combinations('0-90-9'); ToInt();" 96 1000 0 1000000
```
Here is what happens when you run this:
1. The binding is resolved.
2. A single thread (the first thread) generates the values into a reference buffer.
3. The specified number of threads is started, and synchronized for simultaneous start.
4. Each thread is given successive batches of input values from the cycle rage specified, in
chunks of bufsize each. A dot `.` is printed to the console for each completed chunk.
5. Once all threads are complete, each checks their values against the reference buffer, and an
exception is thrown if any difference are found. (This would mean concurrency is affecting
the values, which is not allowed for binding functions.)
6. After all chunks are generated and verified, statistics are displayed to the console:
## output details
```text,linenos
run data = [derived values in brackets]
specifier = 'Combinations('0-90-9'); ToInt();'
threads = 96
min = 0
max = 1000000
[count] = 1000000
buffersize = 1000
[totalGenTimeMs] = 2408.874399
[totalCmpTimeMs] = 2274.510746
[genPerMs] = 39852.638
[cmpPerMs] = 42206.879
[genPerS] = 39852638.245
[cmpPerS] = 42206879.070
```
This shows that on a 12 core (24 thread) system, Around **40 million** variates are able to be
generated from the above recipe (across all cores, of course).
In detail:
* `[totalGenTimeMs]` tracks the total time the thread pool spent generating data, in milliseconds.
* `[totalCmpTimeMs]` tracks the total time the thread pool spent cross-checking data across threads.
* `[genPerMs]` and `[cmpPerMs]` show the calculated rates for generation and validation _per
millisecond_, respectively.
* `[genPerS]` and `[cmpPerS]` show the calculated rates for generation and validation _per second_,
respectively.
## interpretation
This example shows how effective variate generation can be. This doesn't mean that you can
easily simulate 40 million operations with this data. However, it does _anecdotally_ indicate
the proportional load that generation puts on the system. For example, if you were generating
around 400K ops/s from a client with only this binding, it would be reasonable that variate
generation consumes around (400,000/40,000,000) of the system's cycles, or around 1%.
More realistic testing scenarios are likely to use proportionally more due to the amount of data
generation which is needed. Still, generating synthetic data makes for a more capable testing
harnesses because of the extra headroom you leave in your system for other necessary work, like
managing a driver's connection pool or serdes on requests and responses.
@@ -17,7 +17,6 @@
package io.nosqlbench.virtdata.userlibs.apps.summarizer;
import io.nosqlbench.api.apps.BundledApp;
import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.virtdata.core.bindings.DataMapper;
import io.nosqlbench.virtdata.core.bindings.VirtData;
import org.apache.logging.log4j.Level;
@@ -29,7 +28,10 @@ import picocli.CommandLine.Option;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.*;
import java.util.ArrayList;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@@ -40,7 +42,7 @@ import java.util.function.Supplier;
helpCommand = true,
showDefaultValues = true
)
@Service(value = BundledApp.class, selector = "summarize-values")
//@Service(value = BundledApp.class, selector = "summarize-values")
public class ValueSummarizerApp implements BundledApp, Callable<Integer> {
private final static Logger logger = LogManager.getLogger(ValueSummarizerApp.class);
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ public class RunData {
public long min;
public long max;
public int buffersize;
public boolean isolated;
public double totalGenTimeMs;
public double totalCmpTimeMs;
@@ -35,7 +34,6 @@ public class RunData {
this.min = min;
this.max = max;
this.buffersize = buffersize;
this.isolated = isolated;
this.totalGenTimeMs = totalGenTimeMs;
this.totalCmpTimeMs = totalCmpTimeMs;
}
@@ -49,12 +47,11 @@ public class RunData {
" max = " + max + "\n" +
" [count] = " + (max - min) + "\n" +
" buffersize = " + buffersize + "\n" +
" isolated = " + isolated + "\n" +
" [totalGenTimeMs] = " + totalGenTimeMs + "\n" +
" [totalCmpTimeMs] = " + totalCmpTimeMs + "\n" +
String.format(" [genPerMs] = %.3f\n", ((double) threads * (double) (max - min)) / totalGenTimeMs) +
String.format(" [cmpPerMs] = %.3f\n", ((double) threads * (double) (max - min)) / totalCmpTimeMs) +
String.format(" [genPerS] = %.3f\n", 1000.0d * ((double) threads * (double) (max-min)) / totalGenTimeMs) +
String.format(" [cmpPerS] = %.3f\n", 1000.0d * ((double) threads * (double) (max-min)) / totalCmpTimeMs);
String.format(" [genPerMs] = %.3f\n", ((double) threads * (double) (max - min)) / totalGenTimeMs) +
String.format(" [cmpPerMs] = %.3f\n", ((double) threads * (double) (max - min)) / totalCmpTimeMs) +
String.format(" [genPerS] = %.3f\n", 1000.0d * ((double) threads * (double) (max-min)) / totalGenTimeMs) +
String.format(" [cmpPerS] = %.3f\n", 1000.0d * ((double) threads * (double) (max-min)) / totalCmpTimeMs);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,14 +28,14 @@ import java.util.concurrent.locks.ReentrantLock;
public class ValuesCheckerCoordinator implements Callable<RunData> {
private static final Logger logger =
LogManager.getLogger(ValuesCheckerCoordinator.class);
LogManager.getLogger(ValuesCheckerCoordinator.class);
private final String specifier;
private final int threads;
private final int bufsize;
private final long end;
private final long start;
private final boolean isolated;
private final boolean printValues;
private final ReentrantLock lock;
private final Condition goTime;
private final ConcurrentLinkedDeque<Throwable> errors = new ConcurrentLinkedDeque<>();
@@ -47,18 +47,18 @@ public class ValuesCheckerCoordinator implements Callable<RunData> {
private long cmpTimeAccumulator = 0L;
public ValuesCheckerCoordinator(
String specifier,
int threads,
int bufsize,
long start,
long end,
boolean isolated) {
String specifier,
int threads,
int bufsize,
long start,
long end,
boolean printValues) {
this.specifier = specifier;
this.threads = threads;
this.bufsize = bufsize;
this.start = start;
this.end = end;
this.isolated = isolated;
this.printValues = printValues;
this.lock = new ReentrantLock();
this.goTime = lock.newCondition();
}
@@ -76,53 +76,41 @@ public class ValuesCheckerCoordinator implements Callable<RunData> {
private void testConcurrentValues(
int threads,
long start,
long end,
String mapperSpec) {
int threads,
long start,
long end,
String mapperSpec) {
// Generate reference values in single-threaded mode.
DataMapper<Object> mapper =
VirtData.getOptionalMapper(specifier).orElseThrow(
() -> new RuntimeException("Unable to map function for specifier: " + specifier)
);
VirtData.getOptionalMapper(specifier).orElseThrow(
() -> new RuntimeException("Unable to map function for specifier: " + specifier)
);
final List<Object> reference = new CopyOnWriteArrayList<>();
// Setup concurrent generator pool
ValuesCheckerExceptionHandler valuesCheckerExceptionHandler =
new ValuesCheckerExceptionHandler(this);
new ValuesCheckerExceptionHandler(this);
IndexedThreadFactory tf =
new IndexedThreadFactory("values-checker", valuesCheckerExceptionHandler);
new IndexedThreadFactory("values-checker", valuesCheckerExceptionHandler);
pool =
Executors.newFixedThreadPool(threads, tf);
Executors.newFixedThreadPool(threads, tf);
logger.info("Checking [{}..{}) in chunks of {}", start, end, bufsize);
if (!isolated) {
logger.debug(() ->
"Sharing data mapper, only expect success for " +
"explicitly thread-safe generators.");
}
for (int t = 0; t < threads; t++) {
ValuesCheckerRunnable runnable;
if (isolated) {
runnable = new ValuesCheckerRunnable(
start, end, bufsize, t, mapperSpec, null,
readyQueue, goTime, lock, reference
DataMapper<?> threadMapper = VirtData.getOptionalMapper(mapperSpec)
.orElseThrow(
() -> new RuntimeException("Unable to map function for specifier: " + specifier)
);
} else {
DataMapper<?> threadMapper = VirtData.getOptionalMapper(mapperSpec)
.orElseThrow(
() -> new RuntimeException("Unable to map function for specifier: " + specifier)
);
runnable = new ValuesCheckerRunnable(
start, end, bufsize, t,null, threadMapper,
readyQueue, goTime, lock, reference
);
}
runnable = new ValuesCheckerRunnable(
start, end, bufsize, t, null, threadMapper,
readyQueue, goTime, lock, reference, printValues
);
pool.execute(runnable);
}
@@ -182,7 +170,7 @@ public class ValuesCheckerCoordinator implements Callable<RunData> {
synchronized void handleException(Thread t, Throwable e) {
this.errors.add(e);
if (pool!=null) {
if (pool != null) {
pool.shutdownNow();
}
@@ -215,14 +203,14 @@ public class ValuesCheckerCoordinator implements Callable<RunData> {
public RunData call() throws Exception {
run();
return new RunData(
this.specifier,
this.threads,
this.start,
this.end,
this.bufsize,
this.isolated,
((double) genTimeAccumulator / 1000000.0D),
((double) cmpTimeAccumulator / 1000000.0D)
this.specifier,
this.threads,
this.start,
this.end,
this.bufsize,
this.printValues,
((double) genTimeAccumulator / 1000000.0D),
((double) cmpTimeAccumulator / 1000000.0D)
);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ public class ValuesCheckerRunnable implements Runnable {
private final int threadNum;
private final ConcurrentLinkedQueue<Integer> readyQueue;
private final int bufsize;
private final boolean printValues;
public ValuesCheckerRunnable(
long start,
@@ -51,7 +52,8 @@ public class ValuesCheckerRunnable implements Runnable {
ConcurrentLinkedQueue<Integer> readyQueue,
Condition goTime,
Lock lock,
List<Object> expected
List<Object> expected,
boolean printValues
) {
this.start = start;
this.end = end;
@@ -61,6 +63,7 @@ public class ValuesCheckerRunnable implements Runnable {
this.expected = expected;
this.goTime = goTime;
this.lock = lock;
this.printValues = printValues;
this.mapper = (dataMapper != null) ? dataMapper : VirtData.getOptionalMapper(mapperSpec)
.orElseThrow(
@@ -78,19 +81,19 @@ public class ValuesCheckerRunnable implements Runnable {
String rangeInfo = "t:" + threadNum + " [" + rangeStart + ".." + (rangeStart+bufsize) + ")";
synchronizeFor("generation start " + rangeInfo);
// logger.debug(() -> "generating for " + "range: " + rangeStart + ".." + (rangeStart + bufsize));
logger.debug("generating for " + "range: " + rangeStart + ".." + (rangeStart + bufsize));
for (int i = 0; i < output.length; i++) {
output[i] = mapper.get(i + rangeStart);
// if (i==0) {
// logger.debug(() -> "gen i:" + i + ", cycle: " + (i + rangeStart) + ": " + output[i]);
// }
if (i==0) {
logger.debug("gen i:" + i + ", cycle: " + (i + rangeStart) + ": " + output[i]);
}
}
if (this.threadNum==0) {
logger.trace(() -> "Thread " + threadNum + " putting values into comparable array before acking");
expected.clear();
expected.addAll(Arrays.asList(output));
if (System.getProperties().containsKey("PRINTVALUES")) {
if (printValues) {
for (int i=0; i<output.length; i++) {
System.out.println(start+i + "->" + output[i]);
}
@@ -99,7 +102,7 @@ public class ValuesCheckerRunnable implements Runnable {
synchronizeFor("generation complete " + rangeInfo);
synchronizeFor("verification " + rangeInfo);
// logger.debug(() -> "checker " + this.threadNum + " verifying range [" + start + ".." + (start + end) + ")");
logger.debug(() -> "checker " + this.threadNum + " verifying range [" + start + ".." + (start + end) + ")");
for (int bufidx = 0; bufidx < expected.size(); bufidx++) {
if (!expected.get(bufidx).equals(output[bufidx])) {
String errmsg = "Value differs: " +
@@ -108,16 +111,12 @@ public class ValuesCheckerRunnable implements Runnable {
throw new RuntimeException(errmsg);
}
// else
// {
// System.out.println("Equal " + expected[bufidx] + " == " + output[bufidx]);
// }
}
synchronizeFor("verification complete" + rangeInfo);
// logger.info(() -> "verified values for thread " + Thread.currentThread().getLibname() + " in range " +
// rangeStart + ".." + (rangeStart + bufsize)
// );
logger.debug("verified values for thread " + Thread.currentThread() + " in range " +
rangeStart + ".." + (rangeStart + bufsize)
);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,38 +21,56 @@ import io.nosqlbench.virtdata.core.bindings.VirtData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
public class VirtDataCheckPerfApp {
private final static Logger logger = LogManager.getLogger(VirtDataCheckPerfApp.class);
public static void main(String[] args) {
if (args.length==1) {
checkperf(new String[]{args[0],"1","1","1","1"});
} else if (args.length==5) {
checkperf(args);
} else {
System.out.println(" ARGS: checkperf 'specifier' threads bufsize start end");
System.out.println(" example: 'timeuuid()' 100 1000 0 10000");
System.out.println(" specifier: A VirtData function specifier.");
System.out.println(" threads: The number of concurrent threads to run.");
System.out.println(" bufsize: The number of cycles to give each thread at a time.");
System.out.println(" start: The start cycle for the test, inclusive.");
System.out.println(" end: The end cycle for the test, exclusive.");
System.out.println(" OR");
System.out.println(" ARGS: diagnose 'specifier'");
String spec="Identity()";
int threads=1;
int bufsize=1;
long startCycle=0;
long endCycle=1;
boolean printValues=false;
if ((args.length>0) && args[args.length-1].equals("-p")) {
printValues=true;
args= Arrays.copyOfRange(args,0,args.length-1);
}
switch (args.length) {
case 5:
endCycle=Integer.parseInt(args[4]);
case 4:
startCycle=Integer.parseInt(args[3]);
case 3:
bufsize=Integer.parseInt(args[2]);
case 2:
threads = Integer.parseInt(args[1]);
case 1:
spec = args[0];
break;
case 0:
System.out.println(" ARGS: virtdata testmapper 'specifier' threads bufsize start end");
System.out.println(" example: 'timeuuid()' 100 1000 0 10000");
System.out.println(" specifier: A VirtData function specifier.");
System.out.println(" threads: The number of concurrent threads to run.");
System.out.println(" bufsize: The number of cycles to give each thread at a time.");
System.out.println(" start: The start cycle for the test, inclusive.");
System.out.println(" end: The end cycle for the test, exclusive.");
System.out.println(" [-p]: print the values as a sanity check. (must appear last)");
break;
default:
throw new RuntimeException("Error parsing args for " + String.join(" ",args));
}
checkperf(spec,threads,bufsize,startCycle,endCycle,printValues);
}
private static void checkperf(String[] args) {
String spec = args[0];
int threads = Integer.parseInt(args[1]);
int bufsize = Integer.parseInt(args[2]);
long start = Long.parseLong(args[3]);
long end = Long.parseLong(args[4]);
boolean isolated = false;
ValuesCheckerCoordinator checker = new ValuesCheckerCoordinator(spec, threads, bufsize, start, end, isolated);
private static void checkperf(String spec, int threads, int bufsize, long start, long end, boolean printValues) {
ValuesCheckerCoordinator checker = new ValuesCheckerCoordinator(spec, threads, bufsize, start, end, printValues);
RunData runData;
try {