You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ma...@apache.org on 2021/09/09 16:29:25 UTC
[solr] branch main updated: SOLR-15581: New DSL/impl for benchmark
random data generation. (#254)
This is an automated email from the ASF dual-hosted git repository.
markrmiller pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 8880139 SOLR-15581: New DSL/impl for benchmark random data generation. (#254)
8880139 is described below
commit 88801396adf702ad394f557cdd610c4928a2e372
Author: Mark Robert Miller <ma...@gmail.com>
AuthorDate: Thu Sep 9 11:29:14 2021 -0500
SOLR-15581: New DSL/impl for benchmark random data generation. (#254)
---
.gitignore | 1 +
gradle/validation/validate-source-patterns.gradle | 11 +-
solr/benchmark/README.md | 353 +-
solr/benchmark/build.gradle | 2 +
solr/benchmark/jmh.sh | 12 +-
solr/benchmark/{log4j2.xml => log4j2-bench.xml} | 38 +-
.../java/org/apache/solr/bench/BaseBenchState.java | 150 +
.../src/java/org/apache/solr/bench/DocMaker.java | 299 -
.../src/java/org/apache/solr/bench/Docs.java | 227 +
.../src/java/org/apache/solr/bench/FieldDef.java | 170 -
.../org/apache/solr/bench/MiniClusterState.java | 259 +-
.../java/org/apache/solr/bench/SolrGenerate.java | 382 +
.../apache/solr/bench/SolrRandomnessSource.java | 40 +
.../solr/bench/SplittableRandomGenerator.java | 119 +
.../apache/solr/bench/generators/ArraysDSL.java | 133 +
.../apache/solr/bench/generators/BooleansDSL.java | 31 +
.../org/apache/solr/bench/generators/DatesDSL.java | 97 +
.../apache/solr/bench/generators/Distribution.java | 27 +
.../org/apache/solr/bench/generators/Doubles.java | 66 +
.../apache/solr/bench/generators/DoublesDSL.java | 103 +
.../org/apache/solr/bench/generators/Floats.java | 58 +
.../apache/solr/bench/generators/FloatsDSL.java | 105 +
.../apache/solr/bench/generators/IntegersDSL.java | 225 +
.../org/apache/solr/bench/generators/LazyGen.java | 59 +
.../org/apache/solr/bench/generators/Lists.java | 70 +
.../org/apache/solr/bench/generators/ListsDSL.java | 172 +
.../org/apache/solr/bench/generators/LongsDSL.java | 97 +
.../org/apache/solr/bench/generators/Maps.java | 110 +
.../org/apache/solr/bench/generators/MapsDSL.java | 104 +
.../apache/solr/bench/generators/MultiString.java | 20 +
.../apache/solr/bench/generators/NamedListGen.java | 38 +
.../solr/bench/generators/RandomDataHistogram.java | 544 +
.../org/apache/solr/bench/generators/SolrGen.java | 425 +
.../apache/solr/bench/generators/SourceDSL.java | 180 +
.../org/apache/solr/bench/generators/Strings.java | 103 +
.../apache/solr/bench/generators/StringsDSL.java | 481 +
.../org/apache/solr/bench/generators/Tracker.java | 170 +
.../apache/solr/bench/generators/package-info.java | 18 +
.../org/apache/solr/bench/index/CloudIndexing.java | 183 +-
.../org/apache/solr/bench/search/JsonFaceting.java | 88 +-
.../quicktheories/impl/BenchmarkRandomSource.java | 126 +
.../quicktheories/impl/SplittableRandomSource.java | 75 +
.../configs/cloud-minimal/conf/schema.xml | 51 +-
.../configs/cloud-minimal/conf/solrconfig.xml | 44 +-
solr/benchmark/src/resources/words.txt | 466550 ++++++++++++++++++
solr/benchmark/{ => src/test-files}/log4j2.xml | 70 +-
.../test/org/apache/solr/bench/DockMakerTest.java | 234 +-
.../solr/bench/MiniClusterBenchStateTest.java | 133 +
.../test/org/apache/solr/bench/NestedMapTest.java | 96 +
.../solr/index/SimpleMergePolicyFactory.java | 20 +-
solr/licenses/jctools-LICENSE-ASL.txt | 201 +
solr/licenses/jctools-NOTICE.txt | 0
solr/licenses/jctools-core-3.3.0.jar.sha1 | 1 +
solr/licenses/quicktheories-0.26.jar.sha1 | 1 +
solr/licenses/quicktheories-LICENSE-ASL.txt | 202 +
solr/licenses/quicktheories-NOTICE.txt | 0
.../apache/solr/common/util/BytesOutputStream.java | 141 +
.../apache/solr/cloud/MiniSolrCloudCluster.java | 2 +-
versions.lock | 2 +
versions.props | 2 +
60 files changed, 472793 insertions(+), 928 deletions(-)
diff --git a/.gitignore b/.gitignore
index 4ff1ee0..03cd821 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ build/
# IntelliJ IDEA
/.idea/
+.run
**/*.iml
**/*.ipr
**/*.iws
diff --git a/gradle/validation/validate-source-patterns.gradle b/gradle/validation/validate-source-patterns.gradle
index 009c6d3..f48fcb8 100644
--- a/gradle/validation/validate-source-patterns.gradle
+++ b/gradle/validation/validate-source-patterns.gradle
@@ -78,6 +78,7 @@ subprojects {
// default excludes.
exclude 'build/**'
+ exclude '**/.idea/**'
}
}
@@ -113,6 +114,7 @@ configure(rootProject) {
// default excludes.
exclude '**/build/**'
+ exclude '**/.idea/**'
exclude 'dev-tools/missing-doclet/src/**/*.java' // <-- TODO: remove once we allow "var" on master
// ourselves :-)
@@ -211,11 +213,12 @@ class ValidateSourcePatternsTask extends DefaultTask {
progress.start(this.name, this.name)
sourceFiles.each{ f ->
+
progress.progress("Scanning ${f.name}")
logger.debug('Scanning source file: {}', f);
def text = f.getText('UTF-8');
- invalidPatterns.each{ pattern,name ->
+ invalidPatterns.each { pattern, name ->
if (pattern.matcher(text).find()) {
reportViolation(f, name);
}
@@ -231,9 +234,15 @@ class ValidateSourcePatternsTask extends DefaultTask {
if (f.name.endsWith('.java')) {
if (text.contains('org.slf4j.LoggerFactory')) {
if (!validLoggerPattern.matcher(text).find()) {
+ if (text.contains("// nowarn_valid_logger")) {
+ return
+ }
reportViolation(f, 'invalid logging pattern [not private static final, uses static class name]');
}
if (!validLoggerNamePattern.matcher(text).find()) {
+ if (text.contains("// nowarn_valid_logger")) {
+ return
+ }
reportViolation(f, 'invalid logger name [log, uses static class name, not specialized logger]')
}
}
diff --git a/solr/benchmark/README.md b/solr/benchmark/README.md
index 81e0633..e861b19 100644
--- a/solr/benchmark/README.md
+++ b/solr/benchmark/README.md
@@ -1,159 +1,324 @@
-### JMH-Benchmarks module
+JMH-Benchmarks module
+=====================
This module contains benchmarks written using [JMH](https://openjdk.java.net/projects/code-tools/jmh/) from OpenJDK.
-Writing correct micro-benchmarks in Java (or another JVM language) is difficult and there are many non-obvious pitfalls (many
-due to compiler optimizations). JMH is a framework for running and analyzing benchmarks (micro or macro) written in Java (or
-another JVM language).
-
-### Running benchmarks
-
-If you want to set specific JMH flags or only run certain benchmarks, passing arguments via
-gradle tasks is cumbersome. The process has been simplified by the provided `jmh.sh` script.
+Writing correct micro-benchmarks in Java (or another JVM language) is difficult and there are many non-obvious
+pitfalls (many due to compiler optimizations). JMH is a framework for running and analyzing benchmarks (micro or macro)
+written in Java (or another JVM language).
+
+* [JMH-Benchmarks module](#jmh-benchmarks-module)
+ * [Running benchmarks](#running-benchmarks)
+ * [Using JMH with async profiler](#using-jmh-with-async-profiler)
+ * [Using JMH GC profiler](#using-jmh-gc-profiler)
+ * [Using JMH Java Flight Recorder profiler](#using-jmh-java-flight-recorder-profiler)
+ * [JMH Options](#jmh-options)
+ * [Writing benchmarks](#writing-benchmarks)
+ * [SolrCloud MiniCluster Benchmark Setup](#solrcloud-minicluster-benchmark-setup)
+ * [MiniCluster Metrics](#minicluster-metrics)
+ * [Benchmark Repeatability](#benchmark-repeatability)
+
+## Running benchmarks
+
+If you want to set specific JMH flags or only run certain benchmarks, passing arguments via gradle tasks is cumbersome.
+The process has been simplified by the provided `jmh.sh` script.
The default behavior is to run all benchmarks:
- ./jmh.sh
+`./jmh.sh`
Pass a pattern or name after the command to select the benchmarks:
- ./jmh.sh CloudIndexing
+`./jmh.sh CloudIndexing`
Check which benchmarks match the provided pattern:
- ./jmh.sh -l CloudIndexing
+`./jmh.sh -l CloudIndexing`
-Run a specific test and override the number of forks, iterations and warm-up iteration to `2`:
+Run a specific test and overrides the number of forks, iterations and sets warm-up iterations to `2`:
- ./jmh.sh -f 2 -i 2 -wi 2 CloudIndexing
+`./jmh.sh -f 2 -i 2 -wi 2 CloudIndexing`
Run a specific test with async and GC profilers on Linux and flame graph output:
- ./jmh.sh -prof gc -prof async:libPath=/path/to/libasyncProfiler.so\;output=flamegraph\;dir=profile-results CloudIndexing
+`./jmh.sh -prof gc -prof async:libPath=/path/to/libasyncProfiler.so\;output=flamegraph\;dir=profile-results CloudIndexing`
### Using JMH with async profiler
It's good practice to check profiler output for micro-benchmarks in order to verify that they represent the expected
-application behavior and measure what you expect to measure. Some example pitfalls include the use of expensive mocks
-or accidental inclusion of test setup code in the benchmarked code. JMH includes
+application behavior and measure what you expect to measure. Some example pitfalls include the use of expensive mocks or
+accidental inclusion of test setup code in the benchmarked code. JMH includes
[async-profiler](https://github.com/jvm-profiling-tools/async-profiler) integration that makes this easy:
- ./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;dir=profile-results
+`./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;dir=profile-results`
With flame graph output:
- ./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;output=flamegraph\;dir=profile-results
+`./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;output=flamegraph\;dir=profile-results`
Simultaneous cpu, allocation and lock profiling with async profiler 2.0 and jfr output:
- ./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;output=jfr\;alloc\;lock\;dir=profile-results CloudIndexing
+`./jmh.sh -prof async:libPath=/path/to/libasyncProfiler.so\;output=jfr\;alloc\;lock\;dir=profile-results CloudIndexing`
A number of arguments can be passed to configure async profiler, run the following for a description:
- ./jmh.sh -prof async:help
+`./jmh.sh -prof async:help`
+
+You can also skip specifying libPath if you place the async profiler lib in a predefined location, such as one of the
+locations in the env variable `LD_LIBRARY_PATH` if it has been set (many Linux distributions set this env variable, Arch
+by default does not), or `/usr/lib` should work.
+
+#### OS Permissions for Async Profiler
+
+Async Profiler uses perf to profile native code in addition to Java code. It will need the following for the necessary
+access.
+
+```bash
+echo 0 > /proc/sys/kernel/kptr_restrict
+echo 1 > /proc/sys/kernel/perf_event_paranoid
+```
+
+or
-You can also skip specifying libPath if you place the async profiler lib in a predefined location such as /usr/java/packages/lib
+```bash
+sudo sysctl -w kernel.kptr_restrict=0
+sudo sysctl -w kernel.perf_event_paranoid=1
+```
### Using JMH GC profiler
You can run a benchmark with `-prof gc` to measure its allocation rate:
- ./jmh.sh -prof gc:dir=profile-results
+`./jmh.sh -prof gc:dir=profile-results`
-Of particular importance is the `norm` alloc rates, which measure the allocations per operation rather than allocations per second.
+Of particular importance is the `norm` alloc rates, which measure the allocations per operation rather than allocations
+per second.
### Using JMH Java Flight Recorder profiler
JMH comes with a variety of built-in profilers. Here is an example of using JFR:
- ./jmh.sh -prof jfr:dir=profile-results\;configName=jfr-profile.jfc
+`./jmh.sh -prof jfr:dir=profile-results\;configName=jfr-profile.jfc`
-In this example we point to the included configuration file with configName, but you could also do something like settings=default or settings=profile.
+In this example we point to the included configuration file with configName, but you could also do something like
+settings=default or settings=profile.
-### Benchmark Repeatability
+### JMH Options
-Indexes created for the benchmarks often involve randomness when generating terms, term length and number of terms in a field. In order to make benchmarks repeatable, a static seed is used for randoms. This allows for generating varying data while ensuring that data is consistent across runs.
+Some common JMH options are:
-You can vary that seed by setting a system property to explore a wider range of variation in the benchmark:
+```text
- -jvmArgsAppend -Dsolr.bench.seed=6624420638116043983
+ Usage: ./jmh.sh [regexp*] [options]
+ [opt] means optional argument.
+ <opt> means required argument.
+ "+" means comma-separated list of values.
+ "time" arguments accept time suffixes, like "100ms".
-The seed used for a given benchmark run will be printed out near the top of the output.
+Command line options usually take precedence over annotations.
- --> benchmark random seed: 6624420638116043983
+ [arguments] Benchmarks to run (regexp+). (default: .*)
-You can also specify where to place the mini-cluster with a system property:
+ -bm <mode> Benchmark mode. Available modes are: [Throughput/thrpt,
+ AverageTime/avgt, SampleTime/sample, SingleShotTime/ss,
+ All/all]. (default: Throughput)
- -jvmArgsAppend -DminiClusterBaseDir=/benchmark-data/mini-cluster
+ -bs <int> Batch size: number of benchmark method calls per
+ operation. Some benchmark modes may ignore this
+ setting, please check this separately. (default:
+ 1)
-In this case, new data will not be generated for the benchmark, even if you change parameters. The use case for this if you are running a query based benchmark and want to create a large index for testing and reuse (say hundreds of GB's). Be aware that with this system property set, that same mini-cluster will be reused for any benchmarks run, regardless of if that makes sense or not.
+ -e <regexp+> Benchmarks to exclude from the run.
-### Writing benchmarks
+ -f <int> How many times to fork a single benchmark. Use 0 to
+ disable forking altogether. Warning: disabling
+ forking may have detrimental impact on benchmark
+ and infrastructure reliability, you might want
+ to use different warmup mode instead. (default:
+ 5)
-For help in writing correct JMH tests, the best place to start is the [sample code](https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/) provided
-by the JMH project.
+ -foe <bool> Should JMH fail immediately if any benchmark had
+ experienced an unrecoverable error? This helps
+ to make quick sanity tests for benchmark suites,
+ as well as make the automated runs with checking error
+ codes. (default: false)
-JMH is highly configurable and users are encouraged to look through the samples for suggestions
-on what options are available. A good tutorial for using JMH can be found [here](http://tutorials.jenkov.com/java-performance/jmh.html#return-value-from-benchmark-method)
+ -gc <bool> Should JMH force GC between iterations? Forcing
+ the GC may help to lower the noise in GC-heavy benchmarks,
+ at the expense of jeopardizing GC ergonomics decisions.
+ Use with care. (default: false)
-Many Solr JMH benchmarks are actually closer to a full integration benchmark in that they run a single action against a full Solr mini cluster.
+ -h Display help, and exit.
-See org.apache.solr.bench.index.CloudIndexing for an example of this.
+ -i <int> Number of measurement iterations to do. Measurement
+ iterations are counted towards the benchmark score.
+ (default: 1 for SingleShotTime, and 5 for all other
+ modes)
-#### MiniCluster Metrics
+ -jvm <string> Use given JVM for runs. This option only affects forked
+ runs.
-After every iteration, the metrics collected by Solr will be dumped to the build/work/metrics-results folder. You can disable metrics collection using the metricsEnabled method of the MiniClusterState, in which case the same output files will be dumped, but the values will all be 0/null.
+ -jvmArgs <string> Use given JVM arguments. Most options are inherited
+ from the host VM options, but in some cases you want
+ to pass the options only to a forked VM. Either single
+ space-separated option line, or multiple options
+ are accepted. This option only affects forked runs.
-### JMH Options
-Some common JMH options are:
+ -jvmArgsAppend <string> Same as jvmArgs, but append these options after the
+ already given JVM args.
-```text
+ -jvmArgsPrepend <string> Same as jvmArgs, but prepend these options before
+ the already given JVM arg.
+
+ -l List the benchmarks that match a filter, and exit.
+
+ -lp List the benchmarks that match a filter, along with
+ parameters, and exit.
+
+ -lprof List profilers, and exit.
+
+ -lrf List machine-readable result formats, and exit.
+
+ -o <filename> Redirect human-readable output to a given file.
+
+ -opi <int> Override operations per invocation, see @OperationsPerInvocation
+ Javadoc for details. (default: 1)
+
+ -p <param={v,}*> Benchmark parameters. This option is expected to
+ be used once per parameter. Parameter name and parameter
+ values should be separated with equals sign. Parameter
+ values should be separated with commas.
+
+ -prof <profiler> Use profilers to collect additional benchmark data.
+ Some profilers are not available on all JVMs and/or
+ all OSes. Please see the list of available profilers
+ with -lprof.
+
+ -r <time> Minimum time to spend at each measurement iteration.
+ Benchmarks may generally run longer than iteration
+ duration. (default: 10 s)
+
+ -rf <type> Format type for machine-readable results. These
+ results are written to a separate file (see -rff).
+ See the list of available result formats with -lrf.
+ (default: CSV)
+
+ -rff <filename> Write machine-readable results to a given file.
+ The file format is controlled by -rf option. Please
+ see the list of result formats for available formats.
+ (default: jmh-result.<result-format>)
- -e <regexp+> Benchmarks to exclude from the run.
-
- -f <int> How many times to fork a single benchmark. Use 0 to
- disable forking altogether. Warning: disabling
- forking may have detrimental impact on benchmark
- and infrastructure reliability, you might want
- to use different warmup mode instead.
-
- -i <int> Number of measurement iterations to do. Measurement
- iterations are counted towards the benchmark score.
- (default: 1 for SingleShotTime, and 5 for all other
- modes)
-
- -l List the benchmarks that match a filter, and exit.
-
- -lprof List profilers, and exit.
-
- -o <filename> Redirect human-readable output to a given file.
-
- -prof <profiler> Use profilers to collect additional benchmark data.
- Some profilers are not available on all JVMs and/or
- all OSes. Please see the list of available profilers
- with -lprof.
-
- -v <mode> Verbosity mode. Available modes are: [SILENT, NORMAL,
- EXTRA]
-
- -wi <int> Number of warmup iterations to do. Warmup iterations
- are not counted towards the benchmark score. (default:
- 0 for SingleShotTime, and 5 for all other modes)
-
- -t <int> Number of worker threads to run with. 'max' means
- the maximum number of hardware threads available
- on the machine, figured out by JMH itself.
-
- -rf <type> Format type for machine-readable results. These
- results are written to a separate file (see -rff).
- See the list of available result formats with -lrf.
-
- -rff <filename> Write machine-readable results to a given file.
- The file format is controlled by -rf option. Please
- see the list of result formats for available formats.
-
- -bm <mode> Benchmark mode. Available modes are: [Throughput/thrpt,
- AverageTime/avgt, SampleTime/sample, SingleShotTime/ss,
- All/all]
+ -si <bool> Should JMH synchronize iterations? This would significantly
+ lower the noise in multithreaded tests, by making
+ sure the measured part happens only when all workers
+ are running. (default: true)
+
+ -t <int> Number of worker threads to run with. 'max' means
+ the maximum number of hardware threads available
+ on the machine, figured out by JMH itself. (default:
+ 1)
+
+ -tg <int+> Override thread group distribution for asymmetric
+ benchmarks. This option expects a comma-separated
+ list of thread counts within the group. See @Group/@GroupThreads
+ Javadoc for more information.
+
+ -to <time> Timeout for benchmark iteration. After reaching
+ this timeout, JMH will try to interrupt the running
+ tasks. Non-cooperating benchmarks may ignore this
+ timeout. (default: 10 min)
+
+ -tu <TU> Override time unit in benchmark results. Available
+ time units are: [m, s, ms, us, ns]. (default: SECONDS)
+
+ -v <mode> Verbosity mode. Available modes are: [SILENT, NORMAL,
+ EXTRA]. (default: NORMAL)
+
+ -w <time> Minimum time to spend at each warmup iteration. Benchmarks
+ may generally run longer than iteration duration.
+ (default: 10 s)
+
+ -wbs <int> Warmup batch size: number of benchmark method calls
+ per operation. Some benchmark modes may ignore this
+ setting. (default: 1)
+
+ -wf <int> How many warmup forks to make for a single benchmark.
+ All iterations within the warmup fork are not counted
+ towards the benchmark score. Use 0 to disable warmup
+ forks. (default: 0)
+
+ -wi <int> Number of warmup iterations to do. Warmup iterations
+ are not counted towards the benchmark score. (default:
+ 0 for SingleShotTime, and 5 for all other modes)
+
+ -wm <mode> Warmup mode for warming up selected benchmarks.
+ Warmup modes are: INDI = Warmup each benchmark individually,
+ then measure it. BULK = Warmup all benchmarks first,
+ then do all the measurements. BULK_INDI = Warmup
+ all benchmarks first, then re-warmup each benchmark
+ individually, then measure it. (default: INDI)
+
+ -wmb <regexp+> Warmup benchmarks to include in the run in addition
+ to already selected by the primary filters. Harness
+ will not measure these benchmarks, but only use them
+ for the warmup.
+```
+
+## Writing benchmarks
+
+For help in writing correct JMH tests, the best place to start is
+the [sample code](https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/)
+provided by the JMH project.
+
+JMH is highly configurable and users are encouraged to look through the samples for suggestions on what options are
+available. A good tutorial for using JMH can be
+found [here](http://tutorials.jenkov.com/java-performance/jmh.html#return-value-from-benchmark-method)
+
+Many Solr JMH benchmarks are actually closer to a full integration benchmark in that they run a single action against a
+full Solr mini cluster.
+
+See [org.apache.solr.bench.index.CloudIndexing](https://github.com/apache/solr/blob/main/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java)
+for an example of this.
+
+### SolrCloud MiniCluster Benchmark Setup
+
+#### MiniCluster Setup
+
+- CloudIndexing.java
+
+```java
+ @Setup(Level.Trial)
+ public void doSetup(MiniClusterState.MiniClusterBenchState miniClusterState) throws Exception {
+ System.setProperty("mergePolicyFactory", "org.apache.solr.index.NoMergePolicyFactory");
+ miniClusterState.startMiniCluster(nodeCount);
+ miniClusterState.createCollection(COLLECTION, numShards, numReplicas);
+ }
```
-To view all options run jmh with the -h flag.
\ No newline at end of file
+#### MiniCluster Metrics
+
+After every iteration, the metrics collected by Solr will be dumped to the build/work/metrics-results folder. You can
+disable metrics collection using the metricsEnabled method of the MiniClusterState, in which case the same output files
+will be dumped, but the values will all be 0/null.
+
+### Benchmark Repeatability
+
+Indexes created for the benchmarks often involve randomness when generating terms, term length and number of terms in a
+field. In order to make benchmarks repeatable, a static seed is used for randoms. This allows for generating varying
+data while ensuring that data is consistent across runs.
+
+You can vary that seed by setting a system property to explore a wider range of variation in the benchmark:
+
+`-jvmArgsAppend -Dsolr.bench.seed=6624420638116043983`
+
+The seed used for a given benchmark run will be printed out near the top of the output.
+
+> --> benchmark random seed: 6624420638116043983
+
+You can also specify where to place the mini-cluster with a system property:
+
+`-jvmArgsAppend -DminiClusterBaseDir=/benchmark-data/mini-cluster`
+
+In this case, new data will not be generated for the benchmark, even if you change parameters. The use case for this if
+you are running a query based benchmark and want to create a large index for testing and reuse (say hundreds of GB's).
+Be aware that with this system property set, that same mini-cluster will be reused for any benchmarks run, regardless of
+if that makes sense or not.
diff --git a/solr/benchmark/build.gradle b/solr/benchmark/build.gradle
index bf6c9a3..dfbd334 100644
--- a/solr/benchmark/build.gradle
+++ b/solr/benchmark/build.gradle
@@ -46,6 +46,8 @@ task echoCp {
dependencies {
implementation project(':solr:test-framework')
+ implementation "org.quicktheories:quicktheories"
+ implementation 'org.jctools:jctools-core'
implementation 'org.openjdk.jmh:jmh-core'
annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess'
}
diff --git a/solr/benchmark/jmh.sh b/solr/benchmark/jmh.sh
index 16cbe33..30c72b7 100755
--- a/solr/benchmark/jmh.sh
+++ b/solr/benchmark/jmh.sh
@@ -46,20 +46,22 @@ echo "running JMH with args: $@"
# -XX:+PreserveFramePointer is not necessary with async profiler and they claim can be up to 10% hit
# -XX:+UseStringDeduplication should experiment with this
-# -XX:MaxMetaspaceExpansion=64M # and this note: Avoids triggering full GC when we just allocate a bit more metaspace, and metaspace automatically gets cleaned anyway
+# -XX:MaxMetaspaceExpansion=64M # and this note: Avoids triggering full GC when we just allocate a bit more metaspace, and metaspace automatically gets cleaned anyway.
+# MRM: Metaspace should default to 1GB with compressed ops on I believe, but I think even that is low for Solr in this scenario.
# -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 # and this note: Prevents G1 undermining young gen, which otherwise causes a cascade of issues
+# MRM: I've also seen 15 claimed as a sweet spot.
# -XX:-UseBiasedLocking - should be unreflective in recent JVMs and removed in the latest.
-jvmArgs="-jvmArgs -Djmh.shutdownTimeout=5 -jvmArgs -Djmh.separateClassLoader=true -jvmArgs -XX:-UseBiasedLocking -jvmArgs -XX:+UnlockDiagnosticVMOptions -jvmArgs -XX:+DebugNonSafepoints -jvmArgs --add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
+jvmArgs="-jvmArgs -Djmh.shutdownTimeout=5 -jvmArgs -Djmh.shutdownTimeout.step=3 -jvmArgs -Djava.security.egd=file:/dev/./urandom -jvmArgs -XX:-UseBiasedLocking -jvmArgs -XX:+UnlockDiagnosticVMOptions -jvmArgs -XX:+DebugNonSafepoints -jvmArgs --add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
gcArgs="-jvmArgs -XX:+UseG1GC -jvmArgs -XX:+ParallelRefProcEnabled"
-loggingArgs="-jvmArgs -Dlog4jConfigurationFile=./log4j2.xml -jvmArgs -Dlog4j2.is.webapp=false -jvmArgs -Dlog4j2.garbagefreeThreadContextMap=true -jvmArgs -Dlog4j2.enableDirectEncoders=true -jvmArgs -Dlog4j2.enable.threadlocals=true"
-logDir="work/solr-logs"
+# -jvmArgs -Dlog4j2.debug
+loggingArgs="-jvmArgs -Dlog4jConfigurationFile=./log4j2-bench.xml -jvmArgs -Dlog4j2.is.webapp=false -jvmArgs -Dlog4j2.garbagefreeThreadContextMap=true -jvmArgs -Dlog4j2.enableDirectEncoders=true -jvmArgs -Dlog4j2.enable.threadlocals=true"
#set -x
# shellcheck disable=SC2086
-exec java -cp "$classpath" --add-opens=java.base/java.io=ALL-UNNAMED -Djdk.module.illegalAccess.silent=true org.openjdk.jmh.Main $jvmArgs $loggingArgs $gcArgs -jvmArgs "-Dsolr.log.dir=$logDir" "$@"
+exec java -cp "$classpath" --add-opens=java.base/java.io=ALL-UNNAMED -Djdk.module.illegalAccess.silent=true org.openjdk.jmh.Main $jvmArgs $loggingArgs $gcArgs "$@"
echo "JMH benchmarks done"
diff --git a/solr/benchmark/log4j2.xml b/solr/benchmark/log4j2-bench.xml
similarity index 70%
copy from solr/benchmark/log4j2.xml
copy to solr/benchmark/log4j2-bench.xml
index eba3495..ae4cecb 100644
--- a/solr/benchmark/log4j2.xml
+++ b/solr/benchmark/log4j2-bench.xml
@@ -23,23 +23,25 @@
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout>
<Pattern>
- %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.} %m%notEmpty{ =>%ex{short}}}{10240}%n
+ %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.}
+ %m%notEmpty{ =>%ex{short}}}{10240}%n
</Pattern>
</PatternLayout>
</Console>
<RollingRandomAccessFile
name="MainLogFile"
- fileName="${sys:solr.log.dir}/${sys:solr.log.name}.log"
- filePattern="${sys:solr.log.dir}/${sys:solr.log.name}.log.%i" >
+ fileName="${sys:solr.log.dir:-work/solr-logs}/${sys:solr.log.name:-solr}.log"
+ filePattern="${sys:solr.log.dir:-work/solr-logs}/${sys:solr.log.name:-solr}.log.%i">
<PatternLayout>
<Pattern>
- %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.} %m%notEmpty{ =>%ex{short}}}{10240}%n
+ %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.}
+ %m%notEmpty{ =>%ex{short}}}{10240}%n
</Pattern>
</PatternLayout>
<Policies>
- <OnStartupTriggeringPolicy />
- <SizeBasedTriggeringPolicy size="32 MB"/>
+ <OnStartupTriggeringPolicy/>
+ <SizeBasedTriggeringPolicy size="128 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
@@ -47,19 +49,35 @@
<RollingRandomAccessFile
name="SlowLogFile"
fileName="${sys:solr.log.dir}/${sys:solr.log.name}_slow_requests.log"
- filePattern="${sys:solr.log.dir}/${sys:solr.log.name}_slow_requests.log.%i" >
+ filePattern="${sys:solr.log.dir}/${sys:solr.log.name}_slow_requests.log.%i">
<PatternLayout>
<Pattern>
- %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.} %m%notEmpty{ =>%ex{short}}}{10240}%n
+ %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.}
+ %m%notEmpty{ =>%ex{short}}}{10240}%n
</Pattern>
</PatternLayout>
<Policies>
- <OnStartupTriggeringPolicy />
- <SizeBasedTriggeringPolicy size="32 MB"/>
+ <OnStartupTriggeringPolicy/>
+ <SizeBasedTriggeringPolicy size="128 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
+
+ <RandomAccessFile
+ name="RandomCountsFile"
+ fileName="${sys:solr.log.dir:-build/work/solr-logs}/${sys:solr.log.name:-solr}_random_counts.log">
+ <PatternLayout>
+ <Pattern>
+ %maxLen{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p (%t) [%X{collection} %X{shard} %X{replica} %X{core}] %c{1.}
+ %m%notEmpty{ =>%ex{short}}}{10240}%n
+ </Pattern>
+ </PatternLayout>
+ </RandomAccessFile>
+ <AsyncLogger name="org.apache.solr.bench.BaseBenchState.RandomCounts" level="info" additivity="false">
+ <AppenderRef ref="RandomCountsFile"/>
+ </AsyncLogger>
+
</Appenders>
<Loggers>
<!-- Use <AsyncLogger/<AsyncRoot and <Logger/<Root for asynchronous logging or synchonous logging respectively -->
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/BaseBenchState.java b/solr/benchmark/src/java/org/apache/solr/bench/BaseBenchState.java
new file mode 100644
index 0000000..8bf2baf
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/BaseBenchState.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench;
+
+import com.sun.management.HotSpotDiagnosticMXBean;
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.management.ManagementFactory;
+import java.util.SplittableRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.management.MBeanServer;
+import org.apache.commons.io.FileUtils;
+import org.apache.solr.bench.generators.SolrGen;
+import org.apache.solr.common.util.SuppressForbidden;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.infra.BenchmarkParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** The type Base bench state. */
+@State(Scope.Benchmark)
+public class BaseBenchState {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final long RANDOM_SEED = 6624420638116043983L;
+
+ private static final SplittableRandom random = new SplittableRandom(getInitRandomeSeed());
+
+ /**
+ * Gets random seed.
+ *
+ * @return the random seed
+ */
+ public static Long getRandomSeed() {
+ return random.split().nextLong();
+ }
+
+ private static final AtomicBoolean HEAP_DUMPED = new AtomicBoolean();
+
+ /** The constant QUIET_LOG. */
+ public static final boolean QUIET_LOG = Boolean.getBoolean("quietLog");
+
+ /**
+ * Log.
+ *
+ * @param value the value
+ */
+ @SuppressForbidden(reason = "JMH uses std out for user output")
+ public static void log(String value) {
+ if (!QUIET_LOG) {
+ System.out.println((value.isEmpty() ? "" : "--> ") + value);
+ }
+ }
+
+ /** The Work dir. */
+ public String workDir;
+
+ /**
+ * Do setup.
+ *
+ * @param benchmarkParams the benchmark params
+ */
+ @Setup(Level.Trial)
+ public void doSetup(BenchmarkParams benchmarkParams) {
+
+ workDir = System.getProperty("workBaseDir", "build/work");
+
+ System.setProperty("solr.log.dir", workDir + "/logs");
+ System.setProperty("solr.log.name", benchmarkParams.id());
+ }
+
+ /**
+ * Do tear down.
+ *
+ * @param benchmarkParams the benchmark params
+ * @throws Exception the exception
+ */
+ @TearDown(Level.Trial)
+ public static void doTearDown(BenchmarkParams benchmarkParams) throws Exception {
+
+ dumpHeap(benchmarkParams);
+
+ Logger randomCountsLog =
+ LoggerFactory.getLogger(
+ MethodHandles.lookup().lookupClass().getName()
+ + ".RandomCounts"); // nowarn_valid_logger
+
+ SolrGen.countsReport().forEach(randomCountsLog::info);
+ SolrGen.COUNTS.clear();
+ }
+
+ /**
+ * Dump heap.
+ *
+ * @param benchmarkParams the benchmark params
+ * @throws IOException the io exception
+ */
+ @SuppressForbidden(reason = "access to force heapdump")
+ public static void dumpHeap(BenchmarkParams benchmarkParams) throws IOException {
+ String heapDump = System.getProperty("dumpheap");
+ if (heapDump != null) {
+
+ boolean dumpHeap = HEAP_DUMPED.compareAndExchange(false, true);
+ if (dumpHeap) {
+ File file = new File(heapDump);
+ FileUtils.deleteDirectory(file);
+ file.mkdirs();
+ File dumpFile = new File(file, benchmarkParams.id() + ".hprof");
+
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ HotSpotDiagnosticMXBean mxBean =
+ ManagementFactory.newPlatformMXBeanProxy(
+ server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
+ mxBean.dumpHeap(dumpFile.getAbsolutePath(), true);
+ }
+ }
+ }
+
+ private static Long getInitRandomeSeed() {
+ Long seed = Long.getLong("solr.bench.seed");
+
+ if (seed == null) {
+ seed = RANDOM_SEED;
+ }
+
+ log("benchmark random seed: " + seed);
+
+ return seed;
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/DocMaker.java b/solr/benchmark/src/java/org/apache/solr/bench/DocMaker.java
deleted file mode 100644
index 71d2691..0000000
--- a/solr/benchmark/src/java/org/apache/solr/bench/DocMaker.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.solr.bench;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Queue;
-import java.util.Random;
-import java.util.SplittableRandom;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.commons.lang3.Validate;
-import org.apache.lucene.util.RamUsageEstimator;
-import org.apache.lucene.util.TestUtil;
-import org.apache.solr.common.SolrInputDocument;
-import org.apache.solr.common.util.SolrNamedThreadFactory;
-import org.apache.solr.common.util.SuppressForbidden;
-
-/**
- * A tool to generate controlled random data for a benchmark. {@link SolrInputDocument}s are created
- * based on supplied FieldDef definitions.
- *
- * <p>You can call getDocument to build and retrieve one {@link SolrInputDocument} at a time, or you
- * can call {@link #preGenerateDocs} to generate the given number of documents in RAM, and then
- * retrieve them via {@link #getGeneratedDocsIterator}.
- */
-public class DocMaker {
-
- private Queue<SolrInputDocument> docs = new ConcurrentLinkedQueue<>();
-
- private final Map<String, FieldDef> fields = new HashMap<>();
-
- private static final AtomicInteger ID = new AtomicInteger();
-
- private ExecutorService executorService;
-
- public DocMaker() {}
-
- @SuppressForbidden(reason = "This module does not need to deal with logging context")
- public void preGenerateDocs(int numDocs, SplittableRandom random) throws InterruptedException {
- MiniClusterState.log("preGenerateDocs " + numDocs + " ...");
- docs.clear();
- executorService =
- Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors() + 1,
- new SolrNamedThreadFactory("SolrJMH DocMaker"));
-
- for (int i = 0; i < numDocs; i++) {
- executorService.submit(
- new Runnable() {
- SplittableRandom threadRandom = random.split();
-
- @Override
- public void run() {
- try {
- SolrInputDocument doc = DocMaker.this.getDocument(threadRandom);
- docs.add(doc);
- } catch (Exception e) {
- executorService.shutdownNow();
- throw new RuntimeException(e);
- }
- }
- });
- }
-
- executorService.shutdown();
- boolean result = executorService.awaitTermination(10, TimeUnit.MINUTES);
- if (!result) {
- throw new RuntimeException("Timeout waiting for doc adds to finish");
- }
- MiniClusterState.log(
- "done preGenerateDocs docs="
- + docs.size()
- + " ram="
- + RamUsageEstimator.humanReadableUnits(RamUsageEstimator.sizeOfObject(docs)));
-
- if (numDocs != docs.size()) {
- throw new IllegalStateException("numDocs != " + docs.size());
- }
- }
-
- public Iterator<SolrInputDocument> getGeneratedDocsIterator() {
- return docs.iterator();
- }
-
- public SolrInputDocument getDocument(SplittableRandom random) {
- SolrInputDocument doc = new SolrInputDocument();
-
- for (Map.Entry<String, FieldDef> entry : fields.entrySet()) {
- doc.addField(entry.getKey(), getValue(entry.getValue(), random));
- }
-
- return doc;
- }
-
- public void addField(String name, FieldDef.FieldDefBuilder builder) {
- fields.put(name, builder.build());
- }
-
- private Object getValue(FieldDef fieldDef, SplittableRandom threadRandom) {
-
- switch (fieldDef.getContent()) {
- case UNIQUE_INT:
- return ID.incrementAndGet();
- case INTEGER:
- if (fieldDef.getMaxCardinality() > 0) {
- long start = fieldDef.getCardinalityStart();
- long seed = nextLong(start, start + fieldDef.getMaxCardinality(), threadRandom.split());
- return nextInt(0, Integer.MAX_VALUE, new SplittableRandom(seed));
- }
-
- return threadRandom.nextInt(Integer.MAX_VALUE);
- case ALPHEBETIC:
- return getString(
- fieldDef, value -> getAlphabeticString(fieldDef, threadRandom), threadRandom);
- case UNICODE:
- return getString(fieldDef, value -> getUnicodeString(fieldDef, threadRandom), threadRandom);
- default:
- throw new UnsupportedOperationException(
- "Unsupported content type type=" + fieldDef.getContent());
- }
- }
-
- private String getString(
- FieldDef fieldDef, StringSupplier supplier, SplittableRandom threadRandom) {
- if (fieldDef.getNumTokens() > 1 || fieldDef.getMaxNumTokens() > 1) {
- StringBuilder sb =
- new StringBuilder(
- fieldDef.getNumTokens()
- * (Math.max(fieldDef.getLength(), fieldDef.getMaxLength()) + 1));
- SplittableRandom random = threadRandom.split();
- for (int i = 0;
- i
- < (fieldDef.getMaxNumTokens() > 1
- ? random.nextInt(1, fieldDef.getMaxNumTokens())
- : fieldDef.getNumTokens());
- i++) {
- if (i > 0) {
- sb.append(' ');
- }
- sb.append(supplier.getString(fieldDef));
- }
- return sb.toString();
- }
- return supplier.getString(fieldDef);
- }
-
- private String getUnicodeString(FieldDef fieldDef, SplittableRandom threadRandom) {
- try {
- if (fieldDef.getMaxCardinality() > 0) {
- long start = fieldDef.getCardinalityStart();
- long seed = nextLong(start, start + fieldDef.getMaxCardinality(), threadRandom.split());
- if (fieldDef.getLength() > -1) {
- return TestUtil.randomRealisticUnicodeString(
- new Random(seed), fieldDef.getLength(), fieldDef.getLength());
- } else {
- return TestUtil.randomRealisticUnicodeString(
- new Random(seed), 1, fieldDef.getMaxLength());
- }
- }
-
- if (fieldDef.getLength() > -1) {
- return TestUtil.randomRealisticUnicodeString(
- new Random(threadRandom.nextLong()), fieldDef.getLength(), fieldDef.getLength());
- } else {
- return TestUtil.randomRealisticUnicodeString(
- new Random(threadRandom.nextLong()), 1, fieldDef.getMaxLength());
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed getting UnicodeString with FieldDef=" + fieldDef, e);
- }
- }
-
- private String getAlphabeticString(FieldDef fieldDef, SplittableRandom threadRandom) {
- try {
- if (fieldDef.getMaxCardinality() > 0) {
- long start = fieldDef.getCardinalityStart();
- long seed = nextLong(start, start + fieldDef.getMaxCardinality(), threadRandom.split());
- SplittableRandom random = new SplittableRandom(seed);
- if (fieldDef.getLength() > -1) {
- return RandomStringUtils.random(
- nextInt(fieldDef.getLength(), fieldDef.getLength(), random),
- 0,
- 0,
- true,
- false,
- null,
- new Random(seed));
- } else {
- return RandomStringUtils.random(
- nextInt(1, fieldDef.getMaxLength(), random),
- 0,
- 0,
- true,
- false,
- null,
- new Random(seed));
- }
- }
-
- SplittableRandom random = threadRandom.split();
- Random r = new Random(random.nextLong());
- if (fieldDef.getLength() > -1) {
- return RandomStringUtils.random(
- nextInt(fieldDef.getLength(), fieldDef.getLength(), random),
- 0,
- 0,
- true,
- false,
- null,
- r);
- } else {
- return RandomStringUtils.random(
- nextInt(1, fieldDef.getMaxLength(), random), 0, 0, true, false, null, r);
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed getting AlphabeticString with FieldDef=" + fieldDef, e);
- }
- }
-
- public void clear() {
- docs.clear();
- }
-
- public enum Content {
- UNICODE,
- ALPHEBETIC,
- INTEGER,
- UNIQUE_INT
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DocMaker that = (DocMaker) o;
- return fields.equals(that.fields);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(fields);
- }
-
- public static int nextInt(
- final int startInclusive, final int endExclusive, SplittableRandom random) {
- Validate.isTrue(
- endExclusive >= startInclusive, "Start value must be smaller or equal to end value.");
- Validate.isTrue(startInclusive >= 0, "Both range values must be non-negative.");
-
- if (startInclusive == endExclusive) {
- return startInclusive;
- }
-
- return startInclusive + random.nextInt(endExclusive - startInclusive);
- }
-
- public static long nextLong(
- final long startInclusive, final long endExclusive, SplittableRandom random) {
- Validate.isTrue(
- endExclusive >= startInclusive, "Start value must be smaller or equal to end value.");
- Validate.isTrue(
- startInclusive >= 0,
- "Both range values must be non-negative startInclusive="
- + startInclusive
- + " endExclusive="
- + endExclusive);
-
- if (startInclusive == endExclusive) {
- return startInclusive;
- }
-
- return startInclusive + random.nextLong(endExclusive - startInclusive);
- }
-
- private interface StringSupplier {
- String getString(FieldDef value);
- }
-}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/Docs.java b/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
new file mode 100644
index 0000000..eb96ca3
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench;
+
+import static org.apache.solr.bench.BaseBenchState.log;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.apache.solr.bench.generators.MultiString;
+import org.apache.solr.bench.generators.SolrGen;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
+import org.apache.solr.common.util.SuppressForbidden;
+import org.quicktheories.core.Gen;
+import org.quicktheories.impl.BenchmarkRandomSource;
+
+/**
+ * A tool to generate controlled random data for a benchmark. {@link SolrInputDocument}s are created
+ * based on supplied FieldDef definitions.
+ *
+ * <p>You can call getDocument to build and retrieve one {@link SolrInputDocument} at a time, or you
+ * can call {@link #preGenerate} to generate the given number of documents in RAM, and then retrieve
+ * them via {@link #generatedDocsIterator}.
+ */
+public class Docs {
+
+ private final ThreadLocal<SolrRandomnessSource> random;
+ private final Queue<SolrInputDocument> docs = new ConcurrentLinkedQueue<>();
+
+ private final Map<String, Gen<?>> fields = Collections.synchronizedMap(new HashMap<>(16));
+
+ private ExecutorService executorService;
+ private int stringFields;
+ private int multiStringFields;
+ private int integerFields;
+ private int longFields;
+ private int booleanFields;
+ private int floatFields;
+ private int dateFields;
+ private int doubleFields;
+
+ /**
+ * Docs docs.
+ *
+ * @return the docs
+ */
+ public static Docs docs() {
+ return new Docs(BaseBenchState.getRandomSeed());
+ }
+
+ /**
+ * Docs docs.
+ *
+ * @param seed the seed
+ * @return the docs
+ */
+ public static Docs docs(Long seed) {
+ return new Docs(seed);
+ }
+
+ private Docs(Long seed) {
+ this.random =
+ ThreadLocal.withInitial(
+ () ->
+ new BenchmarkRandomSource(
+ new SplittableRandomGenerator(seed))); // TODO: pluggable RandomGenerator
+ }
+
+ /**
+ * Pre generate iterator.
+ *
+ * @param numDocs the num docs
+ * @return the iterator
+ * @throws InterruptedException the interrupted exception
+ */
+ @SuppressForbidden(reason = "This module does not need to deal with logging context")
+ public Iterator<SolrInputDocument> preGenerate(int numDocs) throws InterruptedException {
+ log("preGenerate docs " + numDocs + " ...");
+ docs.clear();
+ executorService =
+ Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors() + 1,
+ new SolrNamedThreadFactory("SolrJMH DocMaker"));
+
+ for (int i = 0; i < numDocs; i++) {
+ executorService.submit(
+ () -> {
+ try {
+ SolrInputDocument doc = Docs.this.inputDocument();
+ docs.add(doc);
+ } catch (Exception e) {
+ e.printStackTrace();
+ executorService.shutdownNow();
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ executorService.shutdown();
+ boolean result = executorService.awaitTermination(10, TimeUnit.MINUTES);
+ if (!result) {
+ throw new RuntimeException("Timeout waiting for doc adds to finish");
+ }
+ log(
+ "done preGenerateDocs docs="
+ + docs.size()
+ + " ram="
+ + RamUsageEstimator.humanReadableUnits(RamUsageEstimator.sizeOfObject(docs)));
+
+ if (numDocs != docs.size()) {
+ throw new IllegalStateException("numDocs != " + docs.size());
+ }
+
+ return docs.iterator();
+ }
+
+ /**
+ * Generated docs iterator iterator.
+ *
+ * @return the iterator
+ */
+ public Iterator<SolrInputDocument> generatedDocsIterator() {
+ return docs.iterator();
+ }
+
+ /**
+ * Input document solr input document.
+ *
+ * @return the solr input document
+ */
+ public SolrInputDocument inputDocument() {
+ SolrInputDocument doc = new SolrInputDocument();
+ SolrRandomnessSource randomSource = random.get();
+ for (Map.Entry<String, Gen<?>> entry : fields.entrySet()) {
+ doc.addField(entry.getKey(), entry.getValue().generate(randomSource));
+ }
+
+ return doc;
+ }
+
+ /**
+ * Document solr document.
+ *
+ * @return the solr document
+ */
+ public SolrDocument document() {
+ SolrDocument doc = new SolrDocument();
+ SolrRandomnessSource randomSource = random.get();
+ for (Map.Entry<String, Gen<?>> entry : fields.entrySet()) {
+ doc.addField(entry.getKey(), entry.getValue().generate(randomSource));
+ }
+
+ return doc;
+ }
+
+ /**
+ * Field docs.
+ *
+ * @param name the name
+ * @param generator the generator
+ * @return the docs
+ */
+ public Docs field(String name, Gen<?> generator) {
+ fields.put(name, generator);
+ return this;
+ }
+
+ /**
+ * Field docs.
+ *
+ * @param generator the generator
+ * @return the docs
+ */
+ public Docs field(SolrGen<?> generator) {
+ Class<?> type = generator.type();
+ if (String.class == type) {
+ fields.put("string" + (stringFields++ > 0 ? stringFields : "") + "_s", generator);
+ } else if (MultiString.class == type) {
+ fields.put("text" + (multiStringFields++ > 0 ? multiStringFields : "") + "_t", generator);
+ } else if (Integer.class == type) {
+ fields.put("int" + (integerFields++ > 0 ? integerFields : "") + "_i", generator);
+ } else if (Long.class == type) {
+ fields.put("long" + (longFields++ > 0 ? longFields : "") + "_l", generator);
+ } else if (Boolean.class == type) {
+ fields.put("boolean" + (booleanFields++ > 0 ? booleanFields : "") + "_b", generator);
+ } else if (Float.class == type) {
+ fields.put("float" + (floatFields++ > 0 ? floatFields : "") + "_f", generator);
+ } else if (Date.class == type) {
+ fields.put("date" + (dateFields++ > 0 ? dateFields : "") + "_dt", generator);
+ } else if (Double.class == type) {
+ fields.put("double" + (doubleFields++ > 0 ? doubleFields : "") + "_d", generator);
+ } else {
+ throw new IllegalArgumentException("Unknown type: " + generator.type());
+ }
+
+ return this;
+ }
+
+ /** Clear. */
+ public void clear() {
+ docs.clear();
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/FieldDef.java b/solr/benchmark/src/java/org/apache/solr/bench/FieldDef.java
deleted file mode 100644
index 933a759..0000000
--- a/solr/benchmark/src/java/org/apache/solr/bench/FieldDef.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.solr.bench;
-
-import java.util.SplittableRandom;
-import org.apache.solr.common.SolrInputDocument;
-
-/**
- * Provides the definition for a randomly generated field in a {@link SolrInputDocument} created by
- * a {@link DocMaker}.
- */
-public class FieldDef {
- public static final int DEFAULT_MAX_LENGTH = 64;
-
- private DocMaker.Content content;
- private int numTokens = 1;
- private int maxNumTokens = -1;
- private int maxCardinality = -1;
- private int maxLength = -1;
- private int length = -1;
- private long cardinalityStart;
-
- public int getNumTokens() {
- return numTokens;
- }
-
- public int getMaxNumTokens() {
- return maxNumTokens;
- }
-
- public int getMaxCardinality() {
- return maxCardinality;
- }
-
- public long getCardinalityStart() {
- return cardinalityStart;
- }
-
- public int getMaxLength() {
- return maxLength;
- }
-
- public int getLength() {
- return length;
- }
-
- public DocMaker.Content getContent() {
- return content;
- }
-
- public static final class FieldDefBuilder {
-
- private DocMaker.Content content;
- private int numTokens = 1;
- private int maxNumTokens = -1;
- private int maxCardinality = -1;
- private int maxLength = -1;
- private int length = -1;
- private long cardinalityStart;
-
- private FieldDefBuilder() {}
-
- public static FieldDefBuilder aFieldDef() {
- return new FieldDefBuilder();
- }
-
- public FieldDefBuilder withContent(DocMaker.Content content) {
- this.content = content;
- return this;
- }
-
- public FieldDefBuilder withTokenCount(int numTokens) {
- if (numTokens > 1 && content == DocMaker.Content.UNIQUE_INT) {
- throw new UnsupportedOperationException(
- "UNIQUE_INT content type cannot be used with token count > 1");
- }
- if (maxCardinality > 1) {
- throw new UnsupportedOperationException(
- "tokenCount cannot be used with maxCardinality > 0");
- }
- this.numTokens = numTokens;
- return this;
- }
-
- public FieldDefBuilder withMaxTokenCount(int maxNumTokens) {
- if (numTokens > 1 && content == DocMaker.Content.UNIQUE_INT) {
- throw new UnsupportedOperationException(
- "UNIQUE_INT content type cannot be used with token count > 1");
- }
- if (maxCardinality > 1) {
- throw new UnsupportedOperationException(
- "maxNumTokens cannot be used with maxCardinality > 0");
- }
- this.maxNumTokens = maxNumTokens;
- return this;
- }
-
- public FieldDefBuilder withMaxCardinality(int maxCardinality, SplittableRandom random) {
- if (numTokens > 1) {
- throw new UnsupportedOperationException(
- "maxCardinality cannot be used with token count > 1");
- }
- this.maxCardinality = maxCardinality;
- this.cardinalityStart = random.nextLong(0, Long.MAX_VALUE - maxCardinality);
- return this;
- }
-
- public FieldDefBuilder withMaxLength(int maxLength) {
- if (length > -1) {
- throw new UnsupportedOperationException("maxLength cannot be used with length");
- }
- this.maxLength = maxLength;
- return this;
- }
-
- public FieldDefBuilder withLength(int length) {
- if (maxLength > -1) {
- throw new UnsupportedOperationException("length cannot be used with maxLength");
- }
- this.length = length;
- return this;
- }
-
- public FieldDef build() {
- FieldDef fieldDef = new FieldDef();
- fieldDef.numTokens = this.numTokens;
- fieldDef.maxNumTokens = this.maxNumTokens;
- fieldDef.content = this.content;
- fieldDef.maxCardinality = this.maxCardinality;
- fieldDef.maxLength = this.maxLength == -1 ? DEFAULT_MAX_LENGTH : this.maxLength;
- fieldDef.length = this.length;
- fieldDef.cardinalityStart = this.cardinalityStart;
- return fieldDef;
- }
- }
-
- @Override
- public String toString() {
- return "FieldDef{"
- + "content="
- + content
- + ", numTokens="
- + numTokens
- + ", maxNumTokens="
- + maxNumTokens
- + ", maxCardinality="
- + maxCardinality
- + ", maxLength="
- + maxLength
- + ", length="
- + length
- + ", cardinalityStart="
- + cardinalityStart
- + '}';
- }
-}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
index b9085a9..b2beb12 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
@@ -17,16 +17,19 @@
package org.apache.solr.bench;
import static org.apache.commons.io.file.PathUtils.deleteDirectory;
+import static org.apache.solr.bench.BaseBenchState.log;
import com.codahale.metrics.Meter;
+import java.io.File;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
import java.lang.management.ManagementFactory;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
-import java.util.Random;
import java.util.SplittableRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -54,55 +57,72 @@ import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Control;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/** The base class for Solr JMH benchmarks that operate against a {@code MiniSolrCloudCluster}. */
public class MiniClusterState {
+ /** The constant DEBUG_OUTPUT. */
public static final boolean DEBUG_OUTPUT = false;
- private static final long RANDOM_SEED = 6624420638116043983L;
-
+ /** The constant PROC_COUNT. */
public static final int PROC_COUNT =
ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
- private static boolean quietLog = Boolean.getBoolean("quietLog");
-
- @SuppressForbidden(reason = "JMH uses std out for user output")
- public static void log(String value) {
- if (!quietLog) {
- System.out.println((value.equals("") ? "" : "--> ") + value);
- }
- }
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ /** The type Mini cluster bench state. */
@State(Scope.Benchmark)
public static class MiniClusterBenchState {
+ /** The Metrics enabled. */
boolean metricsEnabled = true;
+ /** The Nodes. */
public List<String> nodes;
+
+ /** The Cluster. */
MiniSolrCloudCluster cluster;
+
+ /** The Client. */
public SolrClient client;
+ /** The Run cnt. */
int runCnt = 0;
+ /** The Create collection and index. */
boolean createCollectionAndIndex = true;
+ /** The Delete mini cluster. */
boolean deleteMiniCluster = true;
- Path baseDir;
+ /** The Mini cluster base dir. */
+ Path miniClusterBaseDir;
+
+ /** The Allow cluster reuse. */
boolean allowClusterReuse = false;
+ /** The Is warmup. */
boolean isWarmup;
private SplittableRandom random;
-
+ private String workDir;
+
+ /**
+ * Tear down.
+ *
+ * @param benchmarkParams the benchmark params
+ * @throws Exception the exception
+ */
@TearDown(Level.Iteration)
public void tearDown(BenchmarkParams benchmarkParams) throws Exception {
// dump Solr metrics
Path metricsResults =
Paths.get(
- "work/metrics-results",
+ workDir,
+ "metrics-results",
benchmarkParams.id(),
String.valueOf(runCnt++),
benchmarkParams.getBenchmark() + ".txt");
@@ -110,50 +130,68 @@ public class MiniClusterState {
Files.createDirectories(metricsResults.getParent());
}
- cluster.outputMetrics(
+ cluster.dumpMetrics(
metricsResults.getParent().toFile(), metricsResults.getFileName().toString());
}
+ /**
+ * Check warm up.
+ *
+ * @param control the control
+ * @throws Exception the exception
+ */
@Setup(Level.Iteration)
public void checkWarmUp(Control control) throws Exception {
isWarmup = control.stopMeasurement;
}
+ /**
+ * Shutdown mini cluster.
+ *
+ * @param benchmarkParams the benchmark params
+ * @throws Exception the exception
+ */
@TearDown(Level.Trial)
- public void shutdownMiniCluster() throws Exception {
+ public void shutdownMiniCluster(BenchmarkParams benchmarkParams, BaseBenchState baseBenchState)
+ throws Exception {
+
+ log.info("MiniClusterState tear down - baseBenchState is {}", baseBenchState);
+
+ BaseBenchState.dumpHeap(benchmarkParams);
+
if (DEBUG_OUTPUT) log("closing client and shutting down minicluster");
IOUtils.closeQuietly(client);
cluster.shutdown();
}
+ /**
+ * Do setup.
+ *
+ * @param benchmarkParams the benchmark params
+ * @param baseBenchState the base bench state
+ * @throws Exception the exception
+ */
@Setup(Level.Trial)
- public void doSetup(BenchmarkParams benchmarkParams) throws Exception {
+ public void doSetup(BenchmarkParams benchmarkParams, BaseBenchState baseBenchState)
+ throws Exception {
+
+ workDir = System.getProperty("workBaseDir", "build/work");
- MiniClusterState.log("");
+ log("");
Path currentRelativePath = Paths.get("");
String s = currentRelativePath.toAbsolutePath().toString();
log("current relative path is: " + s);
+ log("work path is: " + workDir);
- Long seed = Long.getLong("solr.bench.seed");
-
- if (seed == null) {
- seed = RANDOM_SEED;
- }
-
- log("benchmark random seed: " + seed);
-
- // set the seed used by ThreadLocalRandom
- System.setProperty("randomSeed", Long.toString(new Random(seed).nextLong()));
-
- this.random = new SplittableRandom(seed);
+ System.setProperty("doNotWaitForMergesOnIWClose", "true");
System.setProperty("pkiHandlerPrivateKeyPath", "");
System.setProperty("pkiHandlerPublicKeyPath", "");
- System.setProperty("solr.log.name", benchmarkParams.id());
-
System.setProperty("solr.default.confdir", "../server/solr/configsets/_default");
+ this.random = new SplittableRandom(BaseBenchState.getRandomSeed());
+
// not currently usable, but would enable JettySolrRunner's ill-conceived jetty.testMode and
// allow using SSL
@@ -163,48 +201,58 @@ public class MiniClusterState {
String baseDirSysProp = System.getProperty("miniClusterBaseDir");
if (baseDirSysProp != null) {
deleteMiniCluster = false;
- baseDir = Paths.get(baseDirSysProp);
- if (Files.exists(baseDir)) {
+ miniClusterBaseDir = Paths.get(baseDirSysProp);
+ if (Files.exists(miniClusterBaseDir)) {
createCollectionAndIndex = false;
allowClusterReuse = true;
}
} else {
- baseDir = Paths.get("work/mini-cluster");
+ miniClusterBaseDir = Paths.get(workDir, "mini-cluster");
}
System.setProperty("metricsEnabled", String.valueOf(metricsEnabled));
}
+ /**
+ * Metrics enabled.
+ *
+ * @param metricsEnabled the metrics enabled
+ */
public void metricsEnabled(boolean metricsEnabled) {
this.metricsEnabled = metricsEnabled;
}
+ /**
+ * Start mini cluster.
+ *
+ * @param nodeCount the node count
+ */
public void startMiniCluster(int nodeCount) {
- log("starting mini cluster at base directory: " + baseDir.toAbsolutePath());
+ log("starting mini cluster at base directory: " + miniClusterBaseDir.toAbsolutePath());
- if (!allowClusterReuse && Files.exists(baseDir)) {
+ if (!allowClusterReuse && Files.exists(miniClusterBaseDir)) {
log("mini cluster base directory exists, removing ...");
try {
- deleteDirectory(baseDir);
+ deleteDirectory(miniClusterBaseDir);
} catch (IOException e) {
throw new RuntimeException(e);
}
createCollectionAndIndex = true;
- } else if (Files.exists(baseDir)) {
+ } else if (Files.exists(miniClusterBaseDir)) {
createCollectionAndIndex = false;
deleteMiniCluster = false;
}
try {
cluster =
- new MiniSolrCloudCluster.Builder(nodeCount, baseDir)
+ new MiniSolrCloudCluster.Builder(nodeCount, miniClusterBaseDir)
.formatZkServer(false)
- .addConfig("conf", Paths.get("src/resources/configs/cloud-minimal/conf"))
+ .addConfig("conf", getFile("src/resources/configs/cloud-minimal/conf").toPath())
.configure();
} catch (Exception e) {
- if (Files.exists(baseDir)) {
+ if (Files.exists(miniClusterBaseDir)) {
try {
- deleteDirectory(baseDir);
+ deleteDirectory(miniClusterBaseDir);
} catch (IOException ex) {
e.addSuppressed(ex);
}
@@ -224,10 +272,23 @@ public class MiniClusterState {
log("");
}
+ /**
+ * Gets random.
+ *
+ * @return the random
+ */
public SplittableRandom getRandom() {
return random;
}
+ /**
+ * Create collection.
+ *
+ * @param collection the collection
+ * @param numShards the num shards
+ * @param numReplicas the num replicas
+ * @throws Exception the exception
+ */
public void createCollection(String collection, int numShards, int numReplicas)
throws Exception {
if (createCollectionAndIndex) {
@@ -242,16 +303,24 @@ public class MiniClusterState {
cluster.waitForActiveCollection(
collection, 15, TimeUnit.SECONDS, numShards, numShards * numReplicas);
} catch (Exception e) {
- if (Files.exists(baseDir)) {
- deleteDirectory(baseDir);
+ if (Files.exists(miniClusterBaseDir)) {
+ deleteDirectory(miniClusterBaseDir);
}
throw e;
}
}
}
+ /**
+ * Index.
+ *
+ * @param collection the collection
+ * @param docs the docs
+ * @param docCount the doc count
+ * @throws Exception the exception
+ */
@SuppressForbidden(reason = "This module does not need to deal with logging context")
- public void index(String collection, DocMaker docMaker, int docCount) throws Exception {
+ public void index(String collection, Docs docs, int docCount) throws Exception {
if (createCollectionAndIndex) {
log("indexing data for benchmark...");
@@ -259,10 +328,10 @@ public class MiniClusterState {
ExecutorService executorService =
Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(),
- new SolrNamedThreadFactory("SolrJMH Indexer Progress"));
+ new SolrNamedThreadFactory("SolrJMH Indexer"));
ScheduledExecutorService scheduledExecutor =
Executors.newSingleThreadScheduledExecutor(
- new SolrNamedThreadFactory("SolrJMH Indexer"));
+ new SolrNamedThreadFactory("SolrJMH Indexer Progress"));
scheduledExecutor.scheduleAtFixedRate(
() -> {
if (meter.getCount() == docCount) {
@@ -277,14 +346,14 @@ public class MiniClusterState {
for (int i = 0; i < docCount; i++) {
executorService.submit(
new Runnable() {
- SplittableRandom threadRandom = random.split();
+ final SplittableRandom threadRandom = random.split();
@Override
public void run() {
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.setBasePath(
nodes.get(threadRandom.nextInt(cluster.getJettySolrRunners().size())));
- SolrInputDocument doc = docMaker.getDocument(threadRandom);
+ SolrInputDocument doc = docs.inputDocument();
// log("add doc " + doc);
updateRequest.add(doc);
meter.mark();
@@ -301,13 +370,13 @@ public class MiniClusterState {
log("done adding docs, waiting for executor to terminate...");
executorService.shutdown();
- boolean result = executorService.awaitTermination(600, TimeUnit.MINUTES);
+ boolean result = false;
+ while (!result) {
+ result = executorService.awaitTermination(600, TimeUnit.MINUTES);
+ }
scheduledExecutor.shutdown();
- if (!result) {
- throw new RuntimeException("Timeout waiting for doc adds to finish");
- }
log("done indexing data for benchmark");
log("committing data ...");
@@ -325,18 +394,31 @@ public class MiniClusterState {
NamedList<Object> result = client.request(queryRequest, collection);
- if (DEBUG_OUTPUT) MiniClusterState.log("result: " + result);
+ if (DEBUG_OUTPUT) log("result: " + result);
- MiniClusterState.log("");
+ log("");
- MiniClusterState.log("Dump Core Info");
+ log("Dump Core Info");
dumpCoreInfo();
}
+ /**
+ * Wait for merges.
+ *
+ * @param collection the collection
+ * @throws Exception the exception
+ */
public void waitForMerges(String collection) throws Exception {
forceMerge(collection, Integer.MAX_VALUE);
}
+ /**
+ * Force merge.
+ *
+ * @param collection the collection
+ * @param maxMergeSegments the max merge segments
+ * @throws Exception the exception
+ */
public void forceMerge(String collection, int maxMergeSegments) throws Exception {
if (createCollectionAndIndex) {
// we control segment count for a more informative benchmark *and* because background
@@ -356,16 +438,77 @@ public class MiniClusterState {
}
}
+ /**
+ * Dump core info.
+ *
+ * @throws IOException the io exception
+ */
@SuppressForbidden(reason = "JMH uses std out for user output")
public void dumpCoreInfo() throws IOException {
- cluster.dumpCoreInfo(!quietLog ? System.out : new NullPrintStream());
+ cluster.dumpCoreInfo(!BaseBenchState.QUIET_LOG ? System.out : new NullPrintStream());
}
}
+ /**
+ * Params modifiable solr params.
+ *
+ * @param moreParams the more params
+ * @return the modifiable solr params
+ */
+ public static ModifiableSolrParams params(String... moreParams) {
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ for (int i = 0; i < moreParams.length; i += 2) {
+ params.add(moreParams[i], moreParams[i + 1]);
+ }
+ return params;
+ }
+
+ /**
+ * Params modifiable solr params.
+ *
+ * @param params the params
+ * @param moreParams the more params
+ * @return the modifiable solr params
+ */
public static ModifiableSolrParams params(ModifiableSolrParams params, String... moreParams) {
for (int i = 0; i < moreParams.length; i += 2) {
params.add(moreParams[i], moreParams[i + 1]);
}
return params;
}
+
+ /**
+ * Gets file.
+ *
+ * @param name the name
+ * @return the file
+ */
+ public static File getFile(String name) {
+ final URL url =
+ MiniClusterState.class.getClassLoader().getResource(name.replace(File.separatorChar, '/'));
+ if (url != null) {
+ try {
+ return new File(url.toURI());
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Resource was found on classpath, but cannot be resolved to a "
+ + "normal file (maybe it is part of a JAR file): "
+ + name);
+ }
+ }
+ File file = new File(name);
+ if (file.exists()) {
+ return file;
+ } else {
+ file = new File("../../../", name);
+ if (file.exists()) {
+ return file;
+ }
+ }
+ throw new RuntimeException(
+ "Cannot find resource in classpath or in file-system (relative to CWD): "
+ + name
+ + " CWD="
+ + Paths.get("").toAbsolutePath());
+ }
}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/SolrGenerate.java b/solr/benchmark/src/java/org/apache/solr/bench/SolrGenerate.java
new file mode 100644
index 0000000..2b2827f
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/SolrGenerate.java
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import org.apache.solr.bench.generators.SolrGen;
+import org.quicktheories.api.Pair;
+import org.quicktheories.core.DetatchedRandomnessSource;
+import org.quicktheories.core.Gen;
+import org.quicktheories.core.RandomnessSource;
+
+/** The type Solr generate. */
+public class SolrGenerate {
+
+ private SolrGenerate() {}
+
+ /**
+ * Constant solr gen.
+ *
+ * @param <T> the type parameter
+ * @param constant the constant
+ * @return the solr gen
+ */
+ public static <T> SolrGen<T> constant(T constant) {
+ return new SolrGen<>() {
+ @Override
+ public T generate(RandomnessSource in) {
+ return constant;
+ }
+
+ @Override
+ public T generate(SolrRandomnessSource in) {
+ return constant;
+ }
+ };
+ }
+
+ /**
+ * Range solr gen.
+ *
+ * @param startInclusive the start inclusive
+ * @param endInclusive the end inclusive
+ * @return the solr gen
+ */
+ public static SolrGen<Integer> range(final int startInclusive, final int endInclusive) {
+ return new SolrGen<>() {
+ {
+ this.start = startInclusive;
+ this.end = endInclusive;
+ }
+
+ @Override
+ public Integer generate(RandomnessSource in) {
+ return (int) ((SolrRandomnessSource) in).next(startInclusive, endInclusive);
+ }
+
+ @Override
+ public Integer generate(SolrRandomnessSource in) {
+ return (int) in.next(startInclusive, endInclusive);
+ }
+ };
+ }
+
+ /**
+ * Long range solr gen.
+ *
+ * @param startInclusive the start inclusive
+ * @param endInclusive the end inclusive
+ * @return the solr gen
+ */
+ public static SolrGen<Long> longRange(final long startInclusive, final long endInclusive) {
+ return new SolrGen<>() {
+ {
+ this.start = startInclusive;
+ this.end = endInclusive;
+ }
+
+ @Override
+ public Long generate(RandomnessSource in) {
+ return ((SolrRandomnessSource) in).next(startInclusive, endInclusive);
+ }
+
+ @Override
+ public Long generate(SolrRandomnessSource in) {
+ return in.next(startInclusive, endInclusive);
+ }
+ };
+ }
+
+ /**
+ * Int arrays solr gen.
+ *
+ * @param sizes the sizes
+ * @param contents the contents
+ * @return the solr gen
+ */
+ public static SolrGen<int[]> intArrays(SolrGen<Integer> sizes, SolrGen<Integer> contents) {
+ return new SolrGen<>() {
+
+ @Override
+ public int[] generate(SolrRandomnessSource td) {
+ int size = sizes.generate(td);
+ int[] is = new int[size];
+ for (int i = 0; i != size; i++) {
+ is[i] = contents.generate(td);
+ }
+ return is;
+ }
+ };
+ }
+
+ /**
+ * Code points solr gen.
+ *
+ * @param startInclusive the start inclusive
+ * @param endInclusive the end inclusive
+ * @return the solr gen
+ */
+ public static SolrGen<Integer> codePoints(int startInclusive, int endInclusive) {
+
+ checkArguments(
+ startInclusive >= Character.MIN_CODE_POINT,
+ "(%s) is less than the minimum codepoint (%s)",
+ startInclusive,
+ Character.MIN_CODE_POINT);
+
+ checkArguments(
+ endInclusive <= Character.MAX_CODE_POINT,
+ "%s is greater than the maximum codepoint (%s)",
+ endInclusive,
+ Character.MAX_CODE_POINT);
+
+ return new Retry<>(SolrGenerate.range(startInclusive, endInclusive), Character::isDefined);
+ }
+
+ /**
+ * Returns a generator that provides a value from a generator chosen with probability in
+ * proportion to the weight supplied in the {@link Pair}. Shrinking is towards the first non-zero
+ * weight in the list. At least one generator must have a positive weight and non-positive
+ * generators will never be chosen.
+ *
+ * @param <T> Type to generate
+ * @param mandatory Generator to sample from
+ * @param others Other generators to sample
+ * @return A gen of T
+ */
+ @SafeVarargs
+ public static <T> SolrGen<T> frequency(
+ Pair<Integer, SolrGen<T>> mandatory, Pair<Integer, SolrGen<T>>... others) {
+
+ return frequency(SolrGenerate.FrequencyGen.makeGeneratorList(mandatory, others));
+ }
+
+ /**
+ * Returns a generator that provides a value from a generator chosen with probability in
+ * proportion to the weight supplied in the {@link Pair}. Shrinking is towards the first non-zero
+ * weight in the list. At least one generator must have a positive weight and non-positive
+ * generators will never be chosen.
+ *
+ * @param <T> Type to generate
+ * @param weightedGens pairs of weight and generators to sample in proportion to their weighting
+ * @return A gen of T
+ */
+ public static <T> SolrGen<T> frequency(List<Pair<Integer, SolrGen<T>>> weightedGens) {
+ return SolrGenerate.FrequencyGen.fromList(weightedGens);
+ }
+
+ /**
+ * Booleans solr gen.
+ *
+ * @return the solr gen
+ */
+ public static SolrGen<Boolean> booleans() {
+ return SolrGenerate.pick(Arrays.asList(false, true));
+ }
+
+ /**
+ * Randomly returns one of the supplied values
+ *
+ * @param <T> type of value to generate *
+ * @param ts Values to pick from
+ * @return A Gen of T
+ */
+ public static <T> SolrGen<T> pick(List<T> ts) {
+ Gen<Integer> index = range(0, ts.size() - 1);
+ return new SolrGen<>(prng -> ts.get(index.generate(prng)), null);
+ }
+
+ /**
+ * Returns a generator that provides a value from a random generator provided.
+ *
+ * @param <T> Type to generate
+ * @param mandatory Generator to sample from
+ * @param others Other generators to sample from with equal weighting
+ * @return A gen of T
+ */
+ @SafeVarargs
+ public static <T> SolrGen<T> oneOf(SolrGen<T> mandatory, SolrGen<T>... others) {
+ SolrGen<T>[] generators = Arrays.copyOf(others, others.length + 1);
+ generators[generators.length - 1] = mandatory;
+ SolrGen<Integer> index = range(0, generators.length - 1);
+ return new SolrGen<>(prng -> generators[(index.generate(prng))].generate(prng), null);
+ }
+
+ /**
+ * The type Frequency gen.
+ *
+ * @param <T> the type parameter
+ */
+ static class FrequencyGen<T> extends SolrGen<T> {
+ private final NavigableMap<Integer, SolrGen<T>> weightedMap;
+ private final SolrGen<Integer> indexGen;
+
+ private FrequencyGen(SolrGen<Integer> indexGen, NavigableMap<Integer, SolrGen<T>> weightedMap) {
+ this.weightedMap = weightedMap;
+ this.indexGen = indexGen;
+ }
+
+ /**
+ * Make generator list list.
+ *
+ * @param <T> the type parameter
+ * @param mandatory the mandatory
+ * @param others the others
+ * @return the list
+ */
+ static <T> List<Pair<Integer, SolrGen<T>>> makeGeneratorList(
+ Pair<Integer, SolrGen<T>> mandatory, Pair<Integer, SolrGen<T>>[] others) {
+ List<Pair<Integer, SolrGen<T>>> ts = new ArrayList<>(others.length + 1);
+ ts.add(mandatory);
+ Collections.addAll(ts, others);
+ return ts;
+ }
+
+ /**
+ * From list solr generate . frequency gen.
+ *
+ * @param <T> the type parameter
+ * @param ts the ts
+ * @return the solr generate . frequency gen
+ */
+ /* First the generator normalizes the weights and their total by the greatest common factor
+ * to keep integers small and improve the chance of moving to a new generator as the
+ * index generator shrinks.
+ *
+ * Then assigns each generator a range of integers according to their normalized weight
+ * For example, with three normalized weighted generators {3, g1}, {4, g2}, {5, g3},
+ * total 12 it assigns [0, 2] to g1, [3, 6] to g2, [7, 11] to g3.
+ *
+ * At generation time, the generator picks an integer between [0, total weight) and finds
+ * the generator responsible for the range.
+ */
+ static <T> SolrGenerate.FrequencyGen<T> fromList(List<Pair<Integer, SolrGen<T>>> ts) {
+ if (ts.isEmpty()) {
+ throw new IllegalArgumentException("List of generators must not be empty");
+ }
+ /* Calculate the total unadjusted weights, and the largest common factor
+ * between all the weights and the total, so they can be reduced to the smallest.
+ * It ignores non-positive weights to make it easy to disable generators while developing
+ * properties.
+ */
+ long unadjustedTotalWeights = 0;
+ long commonFactor = 0;
+ for (Pair<Integer, SolrGen<T>> pair : ts) {
+ int weight = pair._1;
+ if (weight <= 0) continue;
+
+ if (unadjustedTotalWeights == 0) {
+ commonFactor = weight;
+ } else {
+ commonFactor = gcd(commonFactor, weight);
+ }
+ unadjustedTotalWeights += weight;
+ }
+ if (unadjustedTotalWeights == 0) {
+ throw new IllegalArgumentException("At least one generator must have a positive weight");
+ }
+ commonFactor = gcd(commonFactor, unadjustedTotalWeights);
+
+ /* Build a tree map with the key as the first integer assigned in the range,
+ * floorEntry will pick the 'the greatest key less than or equal to the given key',
+ * which will find the right generator for the range.
+ */
+ NavigableMap<Integer, SolrGen<T>> weightedMap = new TreeMap<>();
+ int nextStart = 0;
+ for (Pair<Integer, SolrGen<T>> pair : ts) {
+ int weight = pair._1;
+ if (weight <= 0) continue;
+ try {
+ weightedMap.put(nextStart, pair._2);
+ } catch (ClassCastException e) {
+ throw new ClassCastException(
+ "Class cast exception for " + pair._1 + ',' + pair._2 + ' ' + e.getMessage());
+ }
+ nextStart += weight / commonFactor;
+ }
+ final int upperRange = (int) (unadjustedTotalWeights / commonFactor) - 1;
+
+ SolrGen<Integer> indexGen = range(0, upperRange);
+
+ return new SolrGenerate.FrequencyGen<>(indexGen, weightedMap);
+ }
+
+ @Override
+ public T generate(SolrRandomnessSource prng) {
+ return weightedMap.floorEntry(indexGen.generate(prng)).getValue().generate(prng);
+ }
+
+ @Override
+ public String asString(T t) {
+ return weightedMap.get(0).asString(t);
+ }
+
+ private static long gcd(long a, long b) {
+ return BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).longValue();
+ }
+ }
+}
+
+/**
+ * The type Retry.
+ *
+ * @param <T> the type parameter
+ */
+class Retry<T> extends SolrGen<T> {
+
+ private final Predicate<T> assumption;
+
+ /**
+ * Instantiates a new Retry.
+ *
+ * @param child the child
+ * @param assumption the assumption
+ */
+ Retry(SolrGen<T> child, Predicate<T> assumption) {
+ super(child);
+ this.assumption = assumption;
+ }
+
+ @Override
+ public T generate(RandomnessSource in) {
+ // danger of infinite loop here but space is densely populated enough for
+ // this to be unlikely
+ while (true) {
+ DetatchedRandomnessSource detatched = in.detach();
+ T t = Objects.requireNonNull(child).generate(detatched);
+ if (assumption.test(t)) {
+ detatched.commit();
+ return t;
+ }
+ }
+ }
+
+ @Override
+ public String asString(T t) {
+ return Objects.requireNonNull(child).asString(t);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/SolrRandomnessSource.java b/solr/benchmark/src/java/org/apache/solr/bench/SolrRandomnessSource.java
new file mode 100644
index 0000000..7fb4959
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/SolrRandomnessSource.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench;
+
+import org.apache.solr.bench.generators.Distribution;
+import org.quicktheories.core.RandomnessSource;
+
+/** The interface Solr randomness source. */
+public interface SolrRandomnessSource extends RandomnessSource {
+ /**
+ * Next long.
+ *
+ * @param min the min
+ * @param max the max
+ * @return the long
+ */
+ long next(long min, long max);
+
+ /**
+ * With distribution solr randomness source.
+ *
+ * @param distribution the distribution
+ * @return the solr randomness source
+ */
+ SolrRandomnessSource withDistribution(Distribution distribution);
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/SplittableRandomGenerator.java b/solr/benchmark/src/java/org/apache/solr/bench/SplittableRandomGenerator.java
new file mode 100644
index 0000000..6e60f6a
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/SplittableRandomGenerator.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench;
+
+import java.util.SplittableRandom;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.RandomGeneratorFactory;
+import org.apache.commons.math3.util.FastMath;
+
+/** Extension of {@code java.util.SplittableRandom} to implement {@link RandomGenerator}. */
+public class SplittableRandomGenerator implements RandomGenerator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7745277476784028798L;
+
+ private SplittableRandom random;
+ private double nextGaussian;
+
+ /**
+ * Instantiates a new Splittable random generator.
+ *
+ * @param random the random
+ */
+ public SplittableRandomGenerator(SplittableRandom random) {
+ this.random = random;
+ }
+
+ /**
+ * Instantiates a new Splittable random generator.
+ *
+ * @param seed the seed
+ */
+ public SplittableRandomGenerator(long seed) {
+ this.random = new SplittableRandom(seed);
+ }
+
+ public void setSeed(int seed) {
+ setSeed((long) seed);
+ }
+
+ public void setSeed(int[] seed) {
+ setSeed(RandomGeneratorFactory.convertToLong(seed));
+ }
+
+ @Override
+ public void setSeed(long seed) {
+ this.random = new SplittableRandom(seed);
+ }
+
+ @Override
+ public void nextBytes(byte[] bytes) {
+ random.nextBytes(bytes);
+ }
+
+ @Override
+ public int nextInt() {
+ return random.nextInt();
+ }
+
+ @Override
+ public int nextInt(int n) {
+ return random.nextInt(n);
+ }
+
+ @Override
+ public long nextLong() {
+ return random.nextLong();
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ return random.nextBoolean();
+ }
+
+ @Override
+ public float nextFloat() {
+ return (float) random.nextDouble();
+ }
+
+ @Override
+ public double nextDouble() {
+ return random.nextDouble();
+ }
+
+ @Override
+ public double nextGaussian() {
+
+ final double rnd;
+ if (Double.isNaN(nextGaussian)) {
+ // generate a new pair of gaussian numbers
+ final double x = nextDouble();
+ final double y = nextDouble();
+ final double alpha = 2 * FastMath.PI * x;
+ final double r = FastMath.sqrt(-2 * FastMath.log(y));
+ rnd = r * FastMath.cos(alpha);
+ nextGaussian = r * FastMath.sin(alpha);
+ } else {
+ // use the second element of the pair already generated
+ rnd = nextGaussian;
+ nextGaussian = Double.NaN;
+ }
+
+ return rnd;
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/ArraysDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/ArraysDSL.java
new file mode 100644
index 0000000..4d9ebd3
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/ArraysDSL.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.lang.reflect.Array;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.solr.bench.SolrGenerate;
+import org.quicktheories.api.AsString;
+import org.quicktheories.core.Gen;
+
+public class ArraysDSL {
+
+ /**
+ * Creates an ArrayGeneratorBuilder of Integers that can be used to create an array Source
+ *
+ * @param source a Source of type Integer
+ * @return an ArrayGeneratorBuilder of type Integer
+ */
+ public ArrayGeneratorBuilder<Integer> ofIntegers(Gen<Integer> source) {
+ return new ArrayGeneratorBuilder<>(source, Integer.class);
+ }
+
+ /**
+ * Creates an ArrayGeneratorBuilder of Characters that can be used to create an array Source
+ *
+ * @param source a Source of type Character
+ * @return an ArrayGeneratorBuilder of type Character
+ */
+ public ArrayGeneratorBuilder<Character> ofCharacters(Gen<Character> source) {
+ return new ArrayGeneratorBuilder<>(source, Character.class);
+ }
+
+ /**
+ * Creates an ArrayGeneratorBuilder of Strings that can be used to create an array Source
+ *
+ * @param source a Source of type String
+ * @return an ArrayGeneratorBuilder of type String
+ */
+ public ArrayGeneratorBuilder<String> ofStrings(Gen<String> source) {
+ return new ArrayGeneratorBuilder<>(source, String.class);
+ }
+
+ /**
+ * Creates an ArrayGeneratorBuilder of the given class that can be used to create an array Source
+ *
+ * @param <T> type of value to generate
+ * @param source a Source of type T
+ * @param c a Class of type T
+ * @return an ArrayGeneratorBuilder of type T
+ */
+ public <T> ArrayGeneratorBuilder<T> ofClass(Gen<T> source, Class<T> c) {
+ return new ArrayGeneratorBuilder<>(source, c);
+ }
+
+ public static class ArrayGeneratorBuilder<T> {
+
+ private final Gen<T> source;
+ private final Class<T> c;
+
+ ArrayGeneratorBuilder(Gen<T> source, Class<T> c) {
+ this.source = source;
+ this.c = c;
+ }
+
+ /**
+ * Generates arrays of specified type T of fixed length
+ *
+ * @param length - fixed length
+ * @return a Source of type T[]
+ */
+ public Gen<T[]> withLength(int length) {
+ return withLengthBetween(length, length);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Gen<T[]> withLengths(Gen<Integer> lengths) {
+ return Lists.listsOf(source, Lists.arrayList(), lengths)
+ .map(
+ l -> l.toArray((T[]) Array.newInstance(c, 0)) // will generate
+ // correct size if
+ // zero is less than
+ // the length of the
+ // array
+ )
+ .describedAs(arrayDescriber(source::asString));
+ }
+
+ /**
+ * Generates arrays of specified type T of length bounded inclusively between minimumSize and
+ * maximumSize
+ *
+ * @param minLength - the inclusive minimum size of the array
+ * @param maxLength - the inclusive maximum size of the array
+ * @return a Source of type T[]
+ */
+ public Gen<T[]> withLengthBetween(int minLength, int maxLength) {
+ checkArguments(
+ minLength <= maxLength,
+ "The minLength (%s) is longer than the maxLength(%s)",
+ minLength,
+ maxLength);
+ checkArguments(
+ minLength >= 0,
+ "The length of an array cannot be negative; %s is not an accepted argument",
+ minLength);
+ return withLengths(SolrGenerate.range(minLength, maxLength));
+ }
+
+ private static <T> AsString<T[]> arrayDescriber(Function<T, String> valueDescriber) {
+ return a ->
+ java.util.Arrays.stream(a)
+ .map(valueDescriber)
+ .collect(Collectors.joining(", ", "[", "]"));
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/BooleansDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/BooleansDSL.java
new file mode 100644
index 0000000..0bfd759
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/BooleansDSL.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import org.apache.solr.bench.SolrGenerate;
+
+public class BooleansDSL {
+
+ /**
+ * Generates either true or false randomly.
+ *
+ * @return a Source of booleans
+ */
+ public SolrGen<Boolean> all() {
+ return new SolrGen<>(SolrGenerate.booleans(), Boolean.class);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/DatesDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/DatesDSL.java
new file mode 100644
index 0000000..a63f7b6
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/DatesDSL.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.util.Date;
+import org.apache.solr.bench.SolrGenerate;
+
+/**
+ * A Class for creating Date Sources that will produce Dates based on the number of milliseconds
+ * since epoch
+ */
+public class DatesDSL {
+
+ public SolrGen<Date> all() {
+ return Dates.withMilliSecondsBetween(0, Long.MAX_VALUE);
+ }
+
+ /**
+ * Generates Dates inclusively bounded between January 1, 1970, 00:00:00 GMT and new
+ * Date(milliSecondsFromEpoch). The Source restricts Date generation, so that no Dates before 1970
+ * can be created. The Source is weighted, so it is likely to produce new
+ * Date(millisecondsFromEpoch) one or more times.
+ *
+ * @param millisecondsFromEpoch the number of milliseconds from the epoch such that Dates are
+ * generated within this interval.
+ * @return a Source of type Date
+ */
+ public SolrGen<Date> withMilliseconds(long millisecondsFromEpoch) {
+ lowerBoundGEQZero(millisecondsFromEpoch);
+ return Dates.withMilliSeconds(millisecondsFromEpoch);
+ }
+
+ /**
+ * Generates Dates inclusively bounded between new Date(millisecondsFromEpochStartInclusive) and
+ * new Date(millisecondsFromEpochEndInclusive).
+ *
+ * @param millisecondsFromEpochStartInclusive the number of milliseconds from epoch for the
+ * desired older Date
+ * @param millisecondsFromEpochEndInclusive the number of milliseconds from epoch for the desired
+ * more recent Date
+ * @return a source of Dates
+ */
+ public SolrGen<Date> withMillisecondsBetween(
+ long millisecondsFromEpochStartInclusive, long millisecondsFromEpochEndInclusive) {
+ lowerBoundGEQZero(millisecondsFromEpochStartInclusive);
+ maxGEQMin(millisecondsFromEpochStartInclusive, millisecondsFromEpochEndInclusive);
+ return Dates.withMilliSecondsBetween(
+ millisecondsFromEpochStartInclusive, millisecondsFromEpochEndInclusive);
+ }
+
+ private void lowerBoundGEQZero(long milliSecondsFromEpoch) {
+ checkArguments(
+ milliSecondsFromEpoch >= 0,
+ "A negative long (%s) is not an accepted number of milliseconds",
+ milliSecondsFromEpoch);
+ }
+
+ private void maxGEQMin(long startInclusive, long endInclusive) {
+ checkArguments(
+ startInclusive <= endInclusive,
+ "Cannot have the maximum long (%s) smaller than the minimum long value (%s)",
+ endInclusive,
+ startInclusive);
+ }
+
+ static class Dates {
+
+ private Dates() {}
+
+ static SolrGen<Date> withMilliSeconds(long milliSecondsFromEpoch) {
+ return withMilliSecondsBetween(0, milliSecondsFromEpoch);
+ }
+
+ static SolrGen<Date> withMilliSecondsBetween(
+ long milliSecondsFromEpochStartInclusive, long milliSecondsFromEpochEndInclusive) {
+ return SolrGenerate.longRange(
+ milliSecondsFromEpochStartInclusive, milliSecondsFromEpochEndInclusive)
+ .map(Date::new, Date.class);
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Distribution.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Distribution.java
new file mode 100644
index 0000000..48cf980
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Distribution.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+/** The enum Distribution. */
+public enum Distribution {
+ /** Gaussian distribution. */
+ GAUSSIAN,
+ /** Uniform distribution. */
+ UNIFORM,
+ /** Zipfian distribution. */
+ ZIPFIAN
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Doubles.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Doubles.java
new file mode 100644
index 0000000..519b326
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Doubles.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.util.function.Function;
+import org.apache.solr.bench.SolrGenerate;
+
+final class Doubles {
+
+ private static final long POSITIVE_INFINITY_CORRESPONDING_LONG = 0x7ff0000000000000L;
+ private static final long NEGATIVE_INFINITY_CORRESPONDING_LONG = 0xfff0000000000000L;
+ // fraction portion of double, last 52 bits
+ private static final long FRACTION_BITS = 1L << 53;
+ private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
+ private static final long NEGATIVE_ZERO_CORRESPONDING_LONG = Long.MIN_VALUE;
+
+ private Doubles() {}
+
+ static SolrGen<Double> fromNegativeInfinityToPositiveInfinity() {
+ return negative().mix(positive(), Double.class);
+ }
+
+ static SolrGen<Double> negative() {
+ return range(NEGATIVE_ZERO_CORRESPONDING_LONG, NEGATIVE_INFINITY_CORRESPONDING_LONG);
+ }
+
+ static SolrGen<Double> positive() {
+ return range(0, POSITIVE_INFINITY_CORRESPONDING_LONG);
+ }
+
+ static SolrGen<Double> fromZeroToOne() {
+ return range(0, FRACTION_BITS, l -> l * DOUBLE_UNIT);
+ }
+
+ static SolrGen<Double> between(double min, double max) {
+ checkArguments(min <= max, "Cannot have the maximum (%s) smaller than the min (%s)", max, min);
+ double adjustedMax = max - min;
+ return (SolrGen<Double>) fromZeroToOne().map(d -> (d * adjustedMax) + min);
+ }
+
+ static SolrGen<Double> range(long startInclusive, long endInclusive) {
+ return range(startInclusive, endInclusive, Double::longBitsToDouble);
+ }
+
+ static SolrGen<Double> range(
+ long startInclusive, long endInclusive, Function<Long, Double> conversion) {
+ return (SolrGen<Double>)
+ SolrGenerate.longRange(startInclusive, endInclusive).map(conversion, Double.class);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/DoublesDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/DoublesDSL.java
new file mode 100644
index 0000000..bbd2043
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/DoublesDSL.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+/**
+ * A Class for creating Double Sources that will produce doubles within a set interval and will
+ * shrink within this domain.
+ */
+public class DoublesDSL {
+
+ /**
+ * Generates Doubles inclusively bounded below by Double.NEGATIVE_INFINITY and above by
+ * Double.POSITIVE_INFINITY.
+ *
+ * @return a Source of type Double
+ */
+ public SolrGen<Double> all() {
+ return Doubles.fromNegativeInfinityToPositiveInfinity();
+ }
+
+ /**
+ * Generates Doubles inclusively bounded below by Double.NEGATIVE_INFINITY and above by a value
+ * very close to zero on the negative side.
+ *
+ * @return a Source of type Double
+ */
+ public SolrGen<Double> negative() {
+ return Doubles.negative();
+ }
+
+ /**
+ * Generates Doubles inclusively bounded below by a value very close to zero on the positive side
+ * and above by Double.POSITIVE_INFINITY.
+ *
+ * @return a Source of type Double
+ */
+ public SolrGen<Double> positive() {
+ return Doubles.positive();
+ }
+
+ /**
+ * Generates Doubles inclusively bounded below by zero and above by one.
+ *
+ * @return a Source of type Double
+ */
+ public SolrGen<Double> fromZeroToOne() {
+ return Doubles.fromZeroToOne();
+ }
+
+ /**
+ * Generates Doubles inclusively between two bounds
+ *
+ * @param minInclusive minimum value to generate
+ * @param maxInclusive maximum value to generate
+ * @return a Gen of Doubles between minInclusive and maxInclusive
+ */
+ public SolrGen<Double> between(double minInclusive, double maxInclusive) {
+ return Doubles.between(minInclusive, maxInclusive);
+ }
+
+ /**
+ * Starts a range
+ *
+ * @param startInclusive - lower bound of domain
+ * @return start of range
+ */
+ public DoubleDomainBuilder from(final double startInclusive) {
+ return new DoubleDomainBuilder(startInclusive);
+ }
+
+ public class DoubleDomainBuilder {
+
+ private final double startInclusive;
+
+ private DoubleDomainBuilder(double startInclusive) {
+ this.startInclusive = startInclusive;
+ }
+
+ /**
+ * Generates within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Double
+ */
+ public SolrGen<Double> upToAndIncluding(final double endInclusive) {
+ return between(startInclusive, endInclusive);
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Floats.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Floats.java
new file mode 100644
index 0000000..7b5a25a
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Floats.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import org.apache.solr.bench.SolrGenerate;
+
+final class Floats {
+
+ private static final int POSITIVE_INFINITY_CORRESPONDING_INT = 0x7f800000;
+ private static final int NEGATIVE_INFINITY_CORRESPONDING_INT = 0xff800000;
+ private static final int NEGATIVE_ZERO_CORRESPONDING_INT = Integer.MIN_VALUE;
+
+ private Floats() {}
+
+ static SolrGen<Float> fromNegativeInfinityToPositiveInfinity() {
+ return fromNegativeInfinityToNegativeZero().mix(fromZeroToPositiveInfinity(), Float.class);
+ }
+
+ static SolrGen<Float> fromNegativeInfinityToNegativeZero() {
+ return range(NEGATIVE_ZERO_CORRESPONDING_INT, NEGATIVE_INFINITY_CORRESPONDING_INT);
+ }
+
+ static SolrGen<Float> fromZeroToPositiveInfinity() {
+ return range(0, POSITIVE_INFINITY_CORRESPONDING_INT);
+ }
+
+ static SolrGen<Float> fromZeroToOne() {
+ return (SolrGen<Float>)
+ SolrGenerate.range(0, 1 << 24).map(i -> i / (float) (1 << 24), Float.class);
+ }
+
+ static SolrGen<Float> between(float min, float max) {
+ checkArguments(min <= max, "Cannot have the maximum (%s) smaller than the min (%s)", max, min);
+ float adjustedMax = max - min;
+ return (SolrGen<Float>) fromZeroToOne().map(f -> (f * adjustedMax) + min, Float.class);
+ }
+
+ private static SolrGen<Float> range(int startInclusive, int endInclusive) {
+ return (SolrGen<Float>)
+ SolrGenerate.range(startInclusive, endInclusive).map(Float::intBitsToFloat, Float.class);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/FloatsDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/FloatsDSL.java
new file mode 100644
index 0000000..5d6f333
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/FloatsDSL.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import org.quicktheories.core.Gen;
+
+/**
+ * A Class for creating Float Sources that will produce floats within a set interval and will shrink
+ * within this domain.
+ */
+public class FloatsDSL {
+
+ /**
+ * Generates Floats inclusively bounded below by Float.NEGATIVE_INFINITY and above by
+ * Float.POSITIVE_INFINITY.
+ *
+ * @return a Source of type Float
+ */
+ public SolrGen<Float> all() {
+ return Floats.fromNegativeInfinityToPositiveInfinity();
+ }
+
+ /**
+ * Generates Floats inclusively bounded below by Float.NEGATIVE_INFINITY and above by a value very
+ * close to zero on the negative side.
+ *
+ * @return a Source of type Float
+ */
+ public Gen<Float> negative() {
+ return Floats.fromNegativeInfinityToNegativeZero();
+ }
+
+ /**
+ * Generates Floats inclusively bounded below by a value very close to zero on the positive side
+ * and above by Float.POSITIVE_INFINITY.
+ *
+ * @return a Source of type Float
+ */
+ public Gen<Float> positive() {
+ return Floats.fromZeroToPositiveInfinity();
+ }
+
+ /**
+ * Generates Floats inclusively bounded below by zero and above by one.
+ *
+ * @return a Source of type Float
+ */
+ public Gen<Float> fromZeroToOne() {
+ return Floats.fromZeroToOne();
+ }
+
+ /**
+ * Generates Floats inclusively between two bounds
+ *
+ * @param minInclusive minimum value to generate
+ * @param maxInclusive maximum value to generate
+ * @return a Gen of Floats between minInclusive and maxInclusive
+ */
+ public Gen<Float> between(float minInclusive, float maxInclusive) {
+ return Floats.between(minInclusive, maxInclusive);
+ }
+
+ /**
+ * Starts a range
+ *
+ * @param startInclusive - lower bound of domain
+ * @return start of range
+ */
+ public FloatDomainBuilder from(final float startInclusive) {
+ return new FloatDomainBuilder(startInclusive);
+ }
+
+ public class FloatDomainBuilder {
+
+ private final float startInclusive;
+
+ private FloatDomainBuilder(float startInclusive) {
+ this.startInclusive = startInclusive;
+ }
+
+ /**
+ * Generates within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Float
+ */
+ public Gen<Float> upToAndIncluding(final float endInclusive) {
+ return between(startInclusive, endInclusive);
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java
new file mode 100644
index 0000000..b3ffc58
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/IntegersDSL.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.util.SplittableRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.solr.bench.SolrGenerate;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.quicktheories.core.Gen;
+import org.quicktheories.core.RandomnessSource;
+import org.quicktheories.impl.SplittableRandomSource;
+
+/** The type Integers dsl. */
+public class IntegersDSL {
+
+ /**
+ * Constructs a IntegerDomainBuilder object with an inclusive lower bound
+ *
+ * @param startInclusive - lower bound of domain
+ * @return an IntegerDomainBuilder
+ */
+ public IntegerDomainBuilder from(final int startInclusive) {
+ return new IntegerDomainBuilder(startInclusive);
+ }
+
+ /**
+ * Generates all possible integers in Java bounded below by Integer.MIN_VALUE and above by
+ * Integer.MAX_VALUE.
+ *
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> all() {
+ return between(Integer.MIN_VALUE, Integer.MAX_VALUE).describedAs("All Ints");
+ }
+
+ /**
+ * All with max cardinality solr gen.
+ *
+ * @param maxCardinality the max cardinality
+ * @return the solr gen
+ */
+ public SolrGen<Integer> allWithMaxCardinality(int maxCardinality) {
+ return between(Integer.MIN_VALUE, Integer.MAX_VALUE, maxCardinality)
+ .describedAs("All Ints w/ Max Cardinality");
+ }
+
+ /**
+ * Generates all possible positive integers in Java, bounded above by Integer.MAX_VALUE.
+ *
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> allPositive() {
+ return between(1, Integer.MAX_VALUE).describedAs("All Positive Ints");
+ }
+
+ /**
+ * All positive with max cardinality solr gen.
+ *
+ * @param maxCardinality the max cardinality
+ * @return the solr gen
+ */
+ public SolrGen<Integer> allPositiveWithMaxCardinality(int maxCardinality) {
+ return between(1, Integer.MAX_VALUE, maxCardinality)
+ .describedAs("All Positive Ints with Max Cardinality");
+ }
+
+ /**
+ * Incrementing SolrGen. Always returns an integer greater than the previous one. You cannot count
+ * on the increment being 1.
+ *
+ * @return a SolrGen that returns an int greater than the previous
+ */
+ public SolrGen<Integer> incrementing() {
+ return new IncrementingIntegerSolrGen();
+ }
+
+ private static class IncrementingIntegerSolrGen extends SolrGen<Integer> {
+ /** The Increment. */
+ AtomicInteger increment = new AtomicInteger();
+
+ /** Instantiates a new Incrementing integer solr gen. */
+ public IncrementingIntegerSolrGen() {
+ super(Integer.class);
+ describedAs("Incrementing Int");
+ }
+
+ @Override
+ public Integer generate(RandomnessSource in) {
+ return increment.getAndIncrement();
+ }
+
+ @Override
+ public Integer generate(SolrRandomnessSource in) {
+ return increment.getAndIncrement();
+ }
+ }
+
+ private static class IntegerMaxCardinalitySolrGen extends SolrGen<Integer> {
+ private final int maxCardinality;
+ private final Gen<Integer> integers;
+ /** The Cardinality start. */
+ Integer cardinalityStart;
+
+ /**
+ * Instantiates a new Integer max cardinality solr gen.
+ *
+ * @param maxCardinality the max cardinality
+ * @param integers the integers
+ */
+ public IntegerMaxCardinalitySolrGen(int maxCardinality, Gen<Integer> integers) {
+ this.maxCardinality = maxCardinality;
+ this.integers = integers;
+ this.describedAs("Max Cardinality Int");
+ }
+
+ @Override
+ public Integer generate(SolrRandomnessSource in) {
+ if (cardinalityStart == null) {
+ cardinalityStart =
+ SolrGenerate.range(0, Integer.MAX_VALUE - maxCardinality - 1).generate(in);
+ }
+
+ long seed =
+ SolrGenerate.range(cardinalityStart, cardinalityStart + maxCardinality - 1).generate(in);
+ return integers.generate(new SplittableRandomSource(new SplittableRandom(seed)));
+ }
+ }
+
+ /** The type Integer domain builder. */
+ public class IntegerDomainBuilder {
+
+ private final int startInclusive;
+
+ private int maxCardinality;
+
+ private IntegerDomainBuilder(int startInclusive) {
+ this.startInclusive = startInclusive;
+ }
+
+ /**
+ * Max cardinality integer domain builder.
+ *
+ * @param max the max
+ * @return the integer domain builder
+ */
+ public IntegerDomainBuilder maxCardinality(int max) {
+ maxCardinality = max;
+ return this;
+ }
+
+ /**
+ * Generates integers within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> upToAndIncluding(final int endInclusive) {
+ return between(startInclusive, endInclusive, maxCardinality)
+ .describedAs("Int UpToAndIncluding");
+ }
+
+ /**
+ * Generates integers within the interval specified with an inclusive lower bound and exclusive
+ * upper bound.
+ *
+ * @param endExclusive - exclusive upper bound of domain
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> upTo(final int endExclusive) {
+ return between(startInclusive, endExclusive - 1, maxCardinality).describedAs("Int UpTo");
+ }
+ }
+
+ /**
+ * Generates Integers within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param startInclusive - inclusive lower bound of domain
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> between(final int startInclusive, final int endInclusive) {
+ return between(startInclusive, endInclusive, 0).describedAs("Int Between");
+ }
+
+ /**
+ * Generates Integers within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param startInclusive - inclusive lower bound of domain
+ * @param endInclusive - inclusive upper bound of domain
+ * @param maxCardinality the max cardinality
+ * @return a Source of type Integer
+ */
+ public SolrGen<Integer> between(
+ final int startInclusive, final int endInclusive, int maxCardinality) {
+ checkArguments(
+ startInclusive <= endInclusive,
+ "There are no Integer values to be generated between (%s) and (%s)",
+ startInclusive,
+ endInclusive);
+ Gen<Integer> integers = SolrGenerate.range(startInclusive, endInclusive);
+ if (maxCardinality > 0) {
+ return new SolrGen<>(
+ new IntegerMaxCardinalitySolrGen(maxCardinality, integers), Integer.class)
+ .describedAs("Int Between w/ MaxCardinality");
+ } else {
+ return new SolrGen<>(integers, Integer.class).describedAs("Int Between");
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/LazyGen.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/LazyGen.java
new file mode 100644
index 0000000..971dd04
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/LazyGen.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.function.Supplier;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.quicktheories.core.RandomnessSource;
+
+/**
+ * The type Lazy gen.
+ *
+ * @param <T> the type parameter
+ */
+public class LazyGen<T> extends SolrGen<T> {
+
+ private final Supplier<SolrGen<T>> genSupplier;
+ private SolrGen<T> gen;
+
+ /**
+ * Instantiates a new Lazy gen.
+ *
+ * @param genSupplier the gen supplier
+ */
+ public LazyGen(Supplier<SolrGen<T>> genSupplier) {
+ this.genSupplier = genSupplier;
+ }
+
+ @Override
+ public T generate(RandomnessSource in) {
+ if (this.gen == null) {
+ this.gen = genSupplier.get();
+ }
+
+ return gen.generate(in);
+ }
+
+ @Override
+ public T generate(SolrRandomnessSource in) {
+ if (this.gen == null) {
+ this.gen = genSupplier.get();
+ }
+
+ return gen.generate(in);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Lists.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Lists.java
new file mode 100644
index 0000000..a341882
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Lists.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.quicktheories.api.AsString;
+import org.quicktheories.core.Gen;
+
+public final class Lists {
+
+ private Lists() {}
+
+ static <T> Gen<List<T>> listsOf(Gen<T> generator, Gen<Integer> sizes) {
+ return listsOf(generator, arrayList(), sizes).mix(listsOf(generator, linkedList(), sizes));
+ }
+
+ public static <T, A extends List<T>> Collector<T, List<T>, List<T>> arrayList() {
+ return toList(ArrayList::new);
+ }
+
+ public static <T, A extends List<T>> Collector<T, List<T>, List<T>> linkedList() {
+ return toList(LinkedList::new);
+ }
+
+ public static <T, A extends List<T>> Collector<T, A, A> toList(Supplier<A> collectionFactory) {
+ return Collector.of(
+ collectionFactory,
+ List::add,
+ (left, right) -> {
+ left.addAll(right);
+ return left;
+ });
+ }
+
+ static <T> Gen<List<T>> listsOf(
+ Gen<T> values, Collector<T, List<T>, List<T>> collector, Gen<Integer> sizes) {
+
+ Gen<List<T>> gen =
+ prng -> {
+ int size = sizes.generate(prng);
+ return Stream.generate(() -> values.generate(prng)).limit(size).collect(collector);
+ };
+ return gen.describedAs(listDescriber(values::asString));
+ }
+
+ private static <T> AsString<List<T>> listDescriber(Function<T, String> valueDescriber) {
+ return list -> list.stream().map(valueDescriber).collect(Collectors.joining(", ", "[", "]"));
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/ListsDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/ListsDSL.java
new file mode 100644
index 0000000..a8692e8
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/ListsDSL.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.Lists.listsOf;
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import org.apache.solr.bench.SolrGenerate;
+import org.quicktheories.core.Gen;
+import org.quicktheories.generators.Lists;
+
+/**
+ * A Class for creating List Sources that will produce List objects of either fixed or bounded size.
+ */
+public class ListsDSL {
+
+ /**
+ * Creates an appropriate Collector for a type of List by specifying the Supplier used as a
+ * parameter
+ *
+ * @param <T> type to generate
+ * @param <A> list type
+ * @param collectionFactory a supplier of A
+ * @return a Collector
+ */
+ public <T, A extends List<T>> Collector<T, A, A> createListCollector(
+ Supplier<A> collectionFactory) {
+ return Lists.toList(collectionFactory);
+ }
+
+ /**
+ * Creates a ListGeneratorBuilder.
+ *
+ * @param <T> type to generate
+ * @param source a Source of type T for the items in the list
+ * @return a ListGeneratorBuilder of type T
+ */
+ public <T> ListGeneratorBuilder<T> of(Gen<T> source) {
+ return new ListGeneratorBuilder<>(source);
+ }
+
+ /**
+ * ListGeneratorBuilder enables the creation of Sources for Lists of fixed and bounded size, where
+ * no Collector is specified. A ListGeneratorBuilder can be used to create a
+ * TypedListGeneratorBuilder, where the Collector is specified.
+ *
+ * @param <T> type to generate
+ */
+ public static class ListGeneratorBuilder<T> {
+
+ protected final Gen<T> source;
+
+ ListGeneratorBuilder(Gen<T> source) {
+ this.source = source;
+ }
+
+ /**
+ * Generates a List of objects, where the size of the List is fixed
+ *
+ * @param size size of lists to generate
+ * @return a Source of Lists of type T
+ */
+ public Gen<List<T>> ofSize(int size) {
+ return ofSizeBetween(size, size);
+ }
+
+ /**
+ * Generates a List of objects, where the size of the List is bounded by minimumSize and
+ * maximumSize
+ *
+ * @param minimumSize - inclusive minimum size of List
+ * @param maximumSize - inclusive maximum size of List
+ * @return a Source of Lists of type T
+ */
+ public Gen<List<T>> ofSizeBetween(int minimumSize, int maximumSize) {
+ checkBoundedListArguments(minimumSize, maximumSize);
+ return ofSizes(SolrGenerate.range(minimumSize, maximumSize));
+ }
+
+ public Gen<List<T>> ofSizes(Gen<Integer> sizes) {
+ return listsOf(source, sizes);
+ }
+
+ /**
+ * Determines how the Lists will be collected and returns an TypedListGeneratorBuilder with the
+ * Collector specified
+ *
+ * @param collector collector to use to construct list
+ * @return a TypedListGeneratorBuilder
+ */
+ public TypedListGeneratorBuilder<T> ofType(Collector<T, List<T>, List<T>> collector) {
+ return new TypedListGeneratorBuilder<>(source, collector);
+ }
+ }
+
+ /**
+ * TypedListGeneratorBuilder enables the creation of Sources for Lists of fixed and bounded size,
+ * where the Collector is fixed.
+ */
+ public static class TypedListGeneratorBuilder<T> {
+
+ private final Gen<T> source;
+ private final Collector<T, List<T>, List<T>> collector;
+
+ TypedListGeneratorBuilder(Gen<T> source, Collector<T, List<T>, List<T>> collector) {
+ this.source = source;
+ this.collector = collector;
+ }
+
+ /**
+ * Generates a List of objects, where the size of the List is fixed
+ *
+ * @param size size of lists to generate
+ * @return a Source of Lists of type T
+ */
+ public Gen<List<T>> ofSize(int size) {
+ return ofSizeBetween(size, size);
+ }
+
+ /**
+ * Generates a List of objects, where the size of the List is bounded by minimumSize and
+ * maximumSize
+ *
+ * @param minimumSize - inclusive minimum size of List
+ * @param maximumSize - inclusive maximum size of List
+ * @return a Source of Lists of type T
+ */
+ public Gen<List<T>> ofSizeBetween(int minimumSize, int maximumSize) {
+ checkBoundedListArguments(minimumSize, maximumSize);
+ return listsOf(source, collector, SolrGenerate.range(minimumSize, maximumSize));
+ }
+ }
+
+ private static void checkBoundedListArguments(int minimumSize, int maximumSize) {
+ checkArguments(
+ minimumSize <= maximumSize,
+ "The minimumSize (%s) is longer than the maximumSize(%s)",
+ minimumSize,
+ maximumSize);
+ checkSizeNotNegative(minimumSize);
+ }
+
+ private static void checkSizeNotNegative(int size) {
+ checkArguments(
+ size >= 0, "The size of a List cannot be negative; %s is not an accepted argument", size);
+ }
+
+ public <T, A extends List<T>> Collector<T, List<T>, List<T>> arrayList() {
+ return Lists.arrayList();
+ }
+
+ public <T, A extends List<T>> Collector<T, List<T>, List<T>> linkedList() {
+ return Lists.linkedList();
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/LongsDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/LongsDSL.java
new file mode 100644
index 0000000..5f14a2c
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/LongsDSL.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import org.apache.solr.bench.SolrGenerate;
+
+/**
+ * A Class for creating Long Sources that will produce Longs within a set interval and will shrink
+ * within this domain.
+ */
+public class LongsDSL {
+
+ /**
+ * Constructs a LongDomainBuilder object with an inclusive lower bound
+ *
+ * @param startInclusive - lower bound of domain
+ * @return a LongDomainBuilder
+ */
+ public LongDomainBuilder from(final long startInclusive) {
+ return new LongDomainBuilder(startInclusive);
+ }
+
+ /**
+ * Generates all possible Longs in Java bounded below by Long.MIN_VALUE and above by
+ * Long.MAX_VALUE.
+ *
+ * @return a Source of type Long
+ */
+ public SolrGen<Long> all() {
+ return between(Long.MIN_VALUE, Long.MAX_VALUE);
+ }
+
+ public class LongDomainBuilder {
+
+ private final long startInclusive;
+
+ private LongDomainBuilder(long startInclusive) {
+ this.startInclusive = startInclusive;
+ }
+
+ /**
+ * Generates Longs within the interval specified with an inclusive lower and upper bound.
+ *
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Long
+ */
+ public SolrGen<Long> upToAndIncluding(final long endInclusive) {
+ return between(startInclusive, endInclusive);
+ }
+
+ /**
+ * Generates Longs within the interval specified with an inclusive lower bound and exclusive
+ * upper bound.
+ *
+ * @param endExclusive - exclusive upper bound of domain
+ * @return a Source of type Long
+ */
+ public SolrGen<Long> upTo(final long endExclusive) {
+ return between(startInclusive, endExclusive - 1);
+ }
+ }
+
+ /**
+ * Generates Longs within the interval specified with an inclusive lower and upper bound.
+ *
+ * <p>The Source is weighted, so it is likely to generate the upper and lower limits of the domain
+ * one or more times.
+ *
+ * @param startInclusive - inclusive lower bound of domain
+ * @param endInclusive - inclusive upper bound of domain
+ * @return a Source of type Long
+ */
+ public SolrGen<Long> between(final long startInclusive, final long endInclusive) {
+ checkArguments(
+ startInclusive <= endInclusive,
+ "There are no Long values to be generated between (%s) and (%s)",
+ startInclusive,
+ endInclusive);
+ return new SolrGen<>(SolrGenerate.longRange(startInclusive, endInclusive), Long.class);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Maps.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Maps.java
new file mode 100644
index 0000000..9ab4327
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Maps.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.quicktheories.api.AsString;
+import org.quicktheories.core.Gen;
+
+/** The type Maps. */
+public class Maps {
+
+ private Maps() {}
+
+ /**
+ * Bounded maps of gen.
+ *
+ * @param <K> the type parameter
+ * @param <V> the type parameter
+ * @param kg the kg
+ * @param vg the vg
+ * @param sizes the sizes
+ * @return the gen
+ */
+ static <K, V> Gen<Map<K, V>> boundedMapsOf(Gen<K> kg, Gen<V> vg, Gen<Integer> sizes) {
+ return mapsOf(kg, vg, defaultMap(), sizes);
+ }
+
+ /**
+ * Default map collector.
+ *
+ * @param <K> the type parameter
+ * @param <V> the type parameter
+ * @return the collector
+ */
+ public static <K, V> Collector<Map.Entry<K, V>, ?, Map<K, V>> defaultMap() {
+ return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue);
+ }
+
+ /**
+ * Maps of gen.
+ *
+ * @param <K> the type parameter
+ * @param <V> the type parameter
+ * @param kg the kg
+ * @param vg the vg
+ * @param collector the collector
+ * @param sizes the sizes
+ * @return the gen
+ */
+ static <K, V> Gen<Map<K, V>> mapsOf(
+ Gen<K> kg,
+ Gen<V> vg,
+ Collector<Map.Entry<K, V>, ?, Map<K, V>> collector,
+ Gen<Integer> sizes) {
+ SolrGen<Map<K, V>> gen =
+ new SolrGen<>() {
+ @Override
+ public Map<K, V> generate(SolrRandomnessSource prng) {
+ int size = sizes.generate(prng);
+ return Stream.generate(() -> kg.generate(prng))
+ .distinct()
+ .map(k -> mapEntry(k, vg.generate(prng)))
+ .limit(size)
+ .collect(collector);
+ }
+ };
+ return gen.describedAs("Maps Of").describedAs(mapDescriber(kg::asString, vg::asString));
+ }
+
+ private static <K, V> AsString<Map<K, V>> mapDescriber(
+ Function<K, String> kd, Function<V, String> vd) {
+ return list ->
+ list.entrySet().stream()
+ .map(e -> '(' + kd.apply(e.getKey()) + ',' + vd.apply(e.getValue()) + ')')
+ .collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ /**
+ * Map entry map . entry.
+ *
+ * @param <K> the type parameter
+ * @param <V> the type parameter
+ * @param k the k
+ * @param v the v
+ * @return the map . entry
+ */
+ static <K, V> Map.Entry<K, V> mapEntry(K k, V v) {
+ return Collections.singletonMap(k, v).entrySet().iterator().next();
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/MapsDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/MapsDSL.java
new file mode 100644
index 0000000..530a9c3
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/MapsDSL.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+
+import java.util.Map;
+import org.apache.solr.bench.SolrGenerate;
+import org.quicktheories.core.Gen;
+
+/**
+ * A Class for creating Map Sources that will produce Map objects of either fixed or bounded size.
+ */
+public class MapsDSL {
+
+ /**
+ * Creates a ListGeneratorBuilder.
+ *
+ * @param keys Gen of keys
+ * @param values Gen of values
+ * @param <K> key type to generate
+ * @param <V> value type to generate
+ * @return a MapGeneratorBuilder of type K,V
+ */
+ public <K, V> MapGeneratorBuilder<K, V> of(SolrGen<K> keys, SolrGen<V> values) {
+ return new MapGeneratorBuilder<>(keys, values);
+ }
+
+ /**
+ * MapGeneratorBuilder enables the creation of Sources for Maps of fixed and bounded size, where
+ * no Collector is specified. A MapGeneratorBuilder can be used to create a
+ * TypedListGeneratorBuilder, where the Collector is specified.
+ *
+ * @param <K> key type to generate
+ * @param <V> value type to generate
+ */
+ public static class MapGeneratorBuilder<K, V> {
+ final Gen<K> kg;
+ final Gen<V> vg;
+
+ public MapGeneratorBuilder(final Gen<K> kg, final Gen<V> vg) {
+ this.kg = kg;
+ this.vg = vg;
+ }
+
+ /**
+ * Generates a Map of objects, where the size of the Map is fixed
+ *
+ * @param size size of lists to generate
+ * @return a Source of Maps of type K,V
+ */
+ public Gen<Map<K, V>> ofSize(int size) {
+ return ofSizeBetween(size, size);
+ }
+
+ /**
+ * Generates a Map of objects, where the size of the Map is bounded by minimumSize and
+ * maximumSize
+ *
+ * @param minSize inclusive minimum size of Map
+ * @param maxSize inclusive maximum size of Map
+ * @return a Source of Maps of type T
+ */
+ public Gen<Map<K, V>> ofSizeBetween(int minSize, int maxSize) {
+ checkBoundedArguments(minSize, maxSize);
+ return ofSizes(SolrGenerate.range(minSize, maxSize));
+ }
+
+ /**
+ * Generates a Map of objects with sizes drawn from sizes gen
+ *
+ * @param sizes Sizes of maps to generate
+ * @return A Source of Maps of Type T
+ */
+ public Gen<Map<K, V>> ofSizes(Gen<Integer> sizes) {
+ return Maps.boundedMapsOf(kg, vg, sizes);
+ }
+
+ private static void checkBoundedArguments(int minSize, int maxSize) {
+ checkArguments(
+ minSize <= maxSize, "The minSize (%s) is longer than the maxSize(%s)", minSize, maxSize);
+ checkSizeNotNegative(minSize);
+ }
+ }
+
+ private static void checkSizeNotNegative(int size) {
+ checkArguments(
+ size >= 0, "The size of a Map cannot be negative; %s is not an accepted argument", size);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/MultiString.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/MultiString.java
new file mode 100644
index 0000000..c05f0df
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/MultiString.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+/** The interface Multi string. */
+public interface MultiString {}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/NamedListGen.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/NamedListGen.java
new file mode 100644
index 0000000..6c1f6c3
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/NamedListGen.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.Map;
+import org.apache.solr.common.util.NamedList;
+import org.quicktheories.core.Gen;
+import org.quicktheories.core.RandomnessSource;
+
+public class NamedListGen extends SolrGen<NamedList<?>> {
+
+ private final Gen<Map<String, Object>> map;
+
+ public NamedListGen(Gen<Map<String, Object>> map) {
+ this.map = map;
+ }
+
+ /** * Generate random NamedLists. */
+ @Override
+ public NamedList<?> generate(RandomnessSource in) {
+
+ return new NamedList<>(map.generate(in));
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/RandomDataHistogram.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/RandomDataHistogram.java
new file mode 100644
index 0000000..947db5a
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/RandomDataHistogram.java
@@ -0,0 +1,544 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.lang.invoke.MethodHandles;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import org.quicktheories.api.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** The type Random data histogram. */
+public class RandomDataHistogram {
+
+ /** The constant MAX_TYPES_TO_COLLECT. */
+ public static final int MAX_TYPES_TO_COLLECT = 500;
+
+ /** The constant MAX_ITEMS_TO_PRINT. */
+ public static final int MAX_ITEMS_TO_PRINT = 100;
+
+ /** The constant MAX_LABEL_LENGTH. */
+ public static final int MAX_LABEL_LENGTH = 100;
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ /** The constant VERTICAL_BAR. */
+ public static final char VERTICAL_BAR = '│';
+
+ /** The constant COUNT. */
+ public static final String COUNT = "Count";
+
+ /** The constant MAX_WIDTH. */
+ public static final int MAX_WIDTH = 120;
+
+ /** The constant LABEL. */
+ public static final String LABEL = "Label";
+
+ /** The constant HISTO_DOT. */
+ public static final char HISTO_DOT = '⦿';
+
+ private final int bucketCnt;
+
+ private static final MathContext MATH_CONTEXT = new MathContext(4); // 4 precision
+
+ /**
+ * Instantiates a new Random data histogram.
+ *
+ * @param bucketCnt the bucket cnt
+ */
+ public RandomDataHistogram(int bucketCnt) {
+ this.bucketCnt = bucketCnt;
+ }
+
+ /**
+ * Generate random data historgram report.
+ *
+ * @param entries the list of Trackers maintaining the counts
+ * @param label the label for counted objects
+ * @return the string
+ */
+ public String print(List<Tracker> entries, String label) {
+ return print(entries, true, label);
+ }
+
+ /**
+ * Generate random data historgram report.
+ *
+ * @param entries the entries
+ * @param countsComparator the counts comparator
+ * @param label the label
+ * @return the string
+ */
+ public String print(List<Tracker> entries, boolean countsComparator, String label) {
+ entries.sort(
+ countsComparator
+ ? (left, right) -> {
+ try {
+ Integer leftFirst = left.count();
+ Integer rightFirst = right.count();
+ return rightFirst.compareTo(leftFirst);
+ } catch (ClassCastException castException) {
+ return -Integer.compare(left.count(), right.count());
+ }
+ }
+ : comparator());
+ if (bucketCnt > -1) {
+ return print(label, bucket(bucketCnt, entries), countsComparator);
+ } else {
+ List<Bucket> buckets = new ArrayList<>(32);
+ for (Tracker entry : entries) {
+ Bucket bucket = new Bucket(label(entry), entry.count());
+ buckets.add(bucket);
+ }
+ return print(label, buckets, countsComparator);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Comparator<? super Tracker> comparator() {
+ if (bucketCnt > -1) {
+ return (left, right) -> 0;
+ }
+ return (left, right) -> {
+ try {
+ Comparable<Object> leftFirst = (Comparable<Object>) left.values().get(0);
+ Comparable<Object> rightFirst = (Comparable<Object>) right.values().get(0);
+ return leftFirst.compareTo(rightFirst);
+ } catch (ClassCastException castException) {
+ return -Integer.compare(left.count(), right.count());
+ }
+ };
+ }
+
+ /** Returns the label of the tracker. */
+ private String label(final Tracker tracker) {
+ if (bucketCnt > -1) {
+ return "<--->";
+ }
+ return tracker.name();
+ }
+
+ /**
+ * Bucket list.
+ *
+ * @param bucketCnt the bucket cnt
+ * @param entries the list of Trackers maintaining the counts
+ * @return the list
+ */
+ protected static List<Bucket> bucket(int bucketCnt, final List<Tracker> entries) {
+ Pair<BigInteger, BigInteger> minMax = minMax(entries);
+ BigInteger min = minMax._1;
+ BigInteger max = minMax._2;
+
+ List<Pair<BigInteger, Bucket>> topsAndBuckets = bucketsAndMax(bucketCnt, min, max);
+
+ for (Tracker entry : entries) {
+ Bucket bucket = bucket(topsAndBuckets, new BigDecimal(entry.values().get(0).toString()));
+ bucket.addCount(entry.count());
+ }
+
+ return topsAndBuckets.stream()
+ .map(bigIntegerBucketPair -> bigIntegerBucketPair._2)
+ .collect(Collectors.toList());
+ }
+
+ private String print(String label, final List<Bucket> buckets, boolean countComparator) {
+ int labelWidth =
+ Math.max(
+ label.length(),
+ buckets.stream().mapToInt(bucket2 -> bucket2.label.length()).max().orElse(0));
+ int maxCnt = buckets.stream().mapToInt(bucket1 -> bucket1.count).max().orElse(0);
+ int cntWidth = Math.max(COUNT.length(), (int) Math.max(1, Math.floor(Math.log10(maxCnt)) + 1));
+
+ StringBuilder report = new StringBuilder(32);
+ String header =
+ "%1$4s "
+ + VERTICAL_BAR
+ + " %2$"
+ + labelWidth
+ + "s "
+ + VERTICAL_BAR
+ + " %3$"
+ + cntWidth
+ + "s "
+ + VERTICAL_BAR
+ + " %4$s";
+ String bucket =
+ "%1$4s "
+ + VERTICAL_BAR
+ + " %2$"
+ + labelWidth
+ + "s "
+ + VERTICAL_BAR
+ + " %3$"
+ + cntWidth
+ + "d "
+ + VERTICAL_BAR
+ + " %4$s";
+
+ report.append("\n\n").append(this.getClass().getSimpleName()).append("\n\n");
+ report.append(String.format(Locale.ROOT, header, "", label, COUNT, "")).append('\n');
+ String barRuler = String.valueOf('―').repeat(Math.max(0, maxCnt + 10));
+ report
+ .append(String.format(Locale.ROOT, header, "", "", "", barRuler).replace(" ", "―"))
+ .append('\n');
+
+ int size = buckets.size();
+
+ for (int i = 0; i < size; i++) {
+ report
+ .append(
+ String.format(
+ Locale.ROOT,
+ bucket,
+ i,
+ buckets.get(i).label,
+ buckets.get(i).count,
+ String.valueOf(HISTO_DOT)
+ .repeat(
+ Math.max(
+ 0,
+ (int)
+ (buckets.get(i).count
+ / Math.max(1.0, maxCnt / (double) MAX_WIDTH))))))
+ .append('\n');
+ }
+ report.append('\n');
+ return report.toString();
+ }
+
+ private static RandomDataHistogram.Bucket bucket(
+ List<Pair<BigInteger, RandomDataHistogram.Bucket>> bucketsAndMax, BigDecimal value) {
+ int size = bucketsAndMax.size();
+ for (int i = 0; i < size; i++) {
+ Pair<BigInteger, RandomDataHistogram.Bucket> bucketAndMax = bucketsAndMax.get(i);
+ BigInteger top = bucketAndMax._1;
+ if (value.compareTo(new BigDecimal(top)) < 0) {
+ return bucketAndMax._2;
+ }
+ if (i == size - 1) {
+ return bucketAndMax._2;
+ }
+ }
+ throw new IllegalArgumentException("No bucket found for value: " + value);
+ }
+
+ private static List<Pair<BigInteger, RandomDataHistogram.Bucket>> bucketsAndMax(
+ int bucketCnt, final BigInteger min, final BigInteger max) {
+ BigInteger range = max.subtract(min);
+ BigInteger numberOfBuckets = BigInteger.valueOf(bucketCnt);
+ BigInteger step = range.divide(numberOfBuckets);
+ BigInteger remainder = range.remainder(numberOfBuckets);
+ if (remainder.compareTo(BigInteger.ZERO) != 0) {
+ step = step.add(BigInteger.ONE);
+ }
+
+ List<Pair<BigInteger, RandomDataHistogram.Bucket>> bucketsAndMax = new ArrayList<>(32);
+ BigInteger left = min;
+ for (BigInteger index = min.add(step); index.compareTo(max) < 0; index = index.add(step)) {
+ String label = String.format(Locale.ROOT, "[%s..%s", left, index) + '[';
+ bucketsAndMax.add(Pair.of(index, new RandomDataHistogram.Bucket(label)));
+ left = index;
+ }
+ String label = String.format(Locale.ROOT, "[%s..%s", left, max) + ']';
+ bucketsAndMax.add(Pair.of(max, new RandomDataHistogram.Bucket(label)));
+ return bucketsAndMax;
+ }
+
+ private static Pair<BigInteger, BigInteger> minMax(final List<Tracker> entries) {
+ BigDecimal min = null;
+ BigDecimal max = null;
+ for (Tracker entry : entries) {
+ try {
+ BigDecimal value = new BigDecimal(entry.values().get(0).toString());
+ if (min == null || value.compareTo(min) < 0) {
+ min = value;
+ }
+ if (max == null || value.compareTo(max) > 0) {
+ max = value;
+ }
+ } catch (NumberFormatException e) {
+ throw new ParseNumberException("Could not parse " + entry.values().get(0), e);
+ }
+ }
+ if (max == null) max = new BigDecimal(0);
+ if (min == null) min = new BigDecimal(0);
+ // TODO: rounds to 4 decimals but leaves tons of trailing 0's
+ // max = max.round(MATH_CONTEXT);
+ // max = max.round(MATH_CONTEXT);
+ BigInteger maxBigInteger = max.setScale(0, RoundingMode.UP).toBigInteger();
+ return Pair.of(min.toBigInteger(), maxBigInteger);
+ }
+
+ /** The type Bucket. */
+ public static class Bucket {
+ private final String label;
+ private int count;
+
+ /**
+ * Instantiates a new Bucket.
+ *
+ * @param label the label
+ */
+ public Bucket(String label) {
+ this(label, 0);
+ }
+
+ /**
+ * Instantiates a new Bucket.
+ *
+ * @param label the label
+ * @param count the count
+ */
+ public Bucket(String label, final int count) {
+ Objects.requireNonNull(label);
+ this.label = label;
+ this.count = count;
+ }
+
+ /**
+ * Add count.
+ *
+ * @param count the count
+ */
+ void addCount(int count) {
+ this.count += count;
+ }
+ }
+
+ /** The type Counts. */
+ public static class Counts {
+
+ /** The constant ITEMS_ONLY_PRINTING. */
+ public static final String ITEMS_ONLY_PRINTING = " items, only printing ";
+
+ /** The constant THERE_ARE_OVER. */
+ public static final String THERE_ARE_OVER = "\n\nThere are over ";
+
+ /** The constant ELIPSE. */
+ public static final String ELIPSE = " ... ";
+
+ private final Map<List<Surrogate>, AtomicInteger> objectToCount = new ConcurrentHashMap<>(64);
+ private final String label;
+ private List<Tracker> entries = null;
+ private volatile boolean limitReached;
+
+ private final boolean bucketed;
+
+ /**
+ * Instantiates a new Counts.
+ *
+ * @param label the label
+ * @param bucketed the bucketed
+ */
+ public Counts(String label, boolean bucketed) {
+ this.label = label;
+ this.bucketed = bucketed;
+ }
+
+ /**
+ * Collect counts.
+ *
+ * @param values the values
+ * @return the counts
+ */
+ public Counts collect(Object... values) {
+ if (values.length == 0) {
+ throw new IllegalArgumentException("values length must be greater than 0: " + label);
+ }
+
+ if (objectToCount.size() > MAX_ITEMS_TO_PRINT) {
+ limitReached = true;
+ return this;
+ }
+
+ List<Surrogate> key;
+ key = surrogates(values);
+
+ AtomicInteger count = objectToCount.computeIfAbsent(key, any -> new AtomicInteger(0));
+ count.incrementAndGet();
+ entries = null;
+ return this;
+ }
+
+ private List<Surrogate> surrogates(Object[] values) {
+ List<Surrogate> surrogates = new ArrayList<>(values.length);
+ for (Object value : values) {
+ String toString = value.toString();
+ surrogates.add(
+ new Surrogate(
+ value.hashCode(),
+ System.identityHashCode(value),
+ toString.length() > MAX_LABEL_LENGTH
+ ? toString.substring(0, MAX_LABEL_LENGTH) + " ..."
+ : toString));
+ }
+ return surrogates;
+ }
+
+ /** The type Surrogate. */
+ static class Surrogate {
+ /** The Hash code. */
+ final Integer hashCode;
+
+ /** The Identity hashcode. */
+ final Integer identityHashcode;
+
+ /** The Value. */
+ final String value;
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ /**
+ * Instantiates a new Surrogate.
+ *
+ * @param hash the hash
+ * @param identity the identity
+ * @param displayValue the display value
+ */
+ Surrogate(Integer hash, Integer identity, String displayValue) {
+ this.hashCode = hash;
+ this.identityHashcode = identity;
+ this.value = displayValue;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Surrogate surrogate = (Surrogate) o;
+ return hashCode.equals(surrogate.hashCode)
+ && identityHashcode.equals(surrogate.identityHashcode);
+ }
+
+ @Override
+ public int hashCode() {
+
+ if (hashCode == null && identityHashcode == null) {
+ return 0;
+ }
+
+ if (hashCode == null) {
+ return 0;
+ }
+
+ int result = 1;
+
+ result = 31 * result + hashCode.hashCode();
+
+ result = 31 * result + (identityHashcode == null ? 0 : identityHashcode.hashCode());
+
+ return result;
+ }
+ }
+
+ /** Print a string representation of the random data histogram. @return the string */
+ public String print() {
+ return print(true, !this.bucketed ? -1 : 15);
+ }
+
+ /**
+ * Print a string representation of the random data histogram.
+ *
+ * @param bucketed -1 for no bucketing or the number of buckets
+ * @return the string
+ */
+ public String print(int bucketed) {
+ return print(true, bucketed);
+ }
+
+ /**
+ * Print string.
+ *
+ * @param sortByLabel the sort by label
+ * @param bucketed the bucketed
+ * @return the string
+ */
+ public String print(boolean sortByLabel, int bucketed) {
+ String msg = "";
+ if (entries == null) {
+ Collection<AtomicInteger> values = objectToCount.values();
+ if (limitReached) {
+ msg =
+ THERE_ARE_OVER
+ + MAX_ITEMS_TO_PRINT
+ + ITEMS_ONLY_PRINTING
+ + MAX_ITEMS_TO_PRINT
+ + ELIPSE;
+ }
+ int sum = values.stream().mapToInt(AtomicInteger::get).sum();
+ entries =
+ objectToCount.entrySet().stream()
+ .sorted(
+ (e1, e2) -> {
+ List<Surrogate> k1 = e1.getKey();
+ List<Surrogate> k2 = e2.getKey();
+
+ if (k1.size() != k2.size()) {
+ return Integer.compare(k1.size(), k2.size());
+ }
+ return Integer.compare(e2.getValue().get(), e1.getValue().get());
+ })
+ .filter(entry -> !entry.getKey().equals(Collections.emptyList()))
+ .map(
+ entry -> {
+ double percentage = entry.getValue().get() * 100.0 / sum;
+ return new Tracker(
+ entry.getKey(),
+ entry.getKey().stream()
+ .map(Objects::toString)
+ .collect(Collectors.joining(" ")),
+ entry.getValue().get(),
+ percentage);
+ })
+ .collect(Collectors.toList());
+ }
+
+ RandomDataHistogram randomDataHistogram = new RandomDataHistogram(bucketed);
+ return msg + '\n' + randomDataHistogram.print(entries, sortByLabel, label);
+ }
+ }
+
+ private static class ParseNumberException extends RuntimeException {
+ /**
+ * Instantiates a new Parse number exception.
+ *
+ * @param s the s
+ * @param e the e
+ */
+ public ParseNumberException(String s, NumberFormatException e) {
+ super(s, e);
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/SolrGen.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/SolrGen.java
new file mode 100644
index 0000000..1661283
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/SolrGen.java
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.jctools.maps.NonBlockingHashMap;
+import org.quicktheories.api.AsString;
+import org.quicktheories.core.Gen;
+import org.quicktheories.core.RandomnessSource;
+import org.quicktheories.impl.BenchmarkRandomSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The type Solr gen.
+ *
+ * @param <T> the type parameter
+ */
+public class SolrGen<T> implements Gen<T> {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ /** The constant OPEN_PAREN. */
+ public static final String OPEN_PAREN = " (";
+
+ /** The constant CLOSE_PAREN. */
+ public static final char CLOSE_PAREN = ')';
+
+ /** The constant COUNT_TYPES_ARE_TRACKED_LIMIT_WAS_REACHED. */
+ public static final String COUNT_TYPES_ARE_TRACKED_LIMIT_WAS_REACHED =
+ " Count types are tracked, limit was reached.\n\n";
+
+ /** The constant RANDOM_DATA_GEN_REPORTS. */
+ public static final String RANDOM_DATA_GEN_REPORTS =
+ "\n\n\n***** Random Data Gen Reports *****\n\n";
+
+ /** The constant ONLY. */
+ public static final String ONLY = "\n\nOnly ";
+
+ /** The Child. */
+ protected final Gen<T> child;
+
+ private final Class<?> type;
+ private Distribution distribution = Distribution.UNIFORM;
+
+ /** The Start. */
+ protected long start;
+
+ /** The End. */
+ protected long end;
+
+ private static final boolean COLLECT_COUNTS = Boolean.getBoolean("random.counts");
+
+ static {
+ log.info("random.counts is set to {}", COLLECT_COUNTS);
+ }
+
+ private String description;
+
+ private String collectKey;
+ /** The constant COUNTS. */
+ public static final Map<String, RandomDataHistogram.Counts> COUNTS = new NonBlockingHashMap<>(64);
+
+ /**
+ * Counts report list.
+ *
+ * @return the list
+ */
+ public static List<String> countsReport() {
+ List<String> reports = new ArrayList<>(COUNTS.size());
+ reports.add(RANDOM_DATA_GEN_REPORTS);
+ if (COUNTS.size() >= RandomDataHistogram.MAX_TYPES_TO_COLLECT) {
+ reports.add(
+ ONLY
+ + RandomDataHistogram.MAX_TYPES_TO_COLLECT
+ + COUNT_TYPES_ARE_TRACKED_LIMIT_WAS_REACHED);
+ }
+ COUNTS.forEach(
+ (k, v) -> {
+ reports.add(v.print());
+ });
+ return reports;
+ }
+
+ /** Instantiates a new Solr gen. */
+ protected SolrGen() {
+ child = null;
+ this.type = null;
+ }
+
+ /**
+ * Instantiates a new Solr gen.
+ *
+ * @param child the child
+ * @param type the type
+ */
+ public SolrGen(Gen<T> child, Class<?> type) {
+ this.child = child;
+ this.type = type;
+ if (type != null) {
+ this.description = type.getSimpleName();
+ } else if (child instanceof SolrGen) {
+ this.description = ((SolrGen<?>) child).description;
+ }
+ if (COLLECT_COUNTS && description != null) {
+ this.collectKey = description + OPEN_PAREN + distribution + CLOSE_PAREN;
+ }
+ }
+
+ /**
+ * Instantiates a new Solr gen.
+ *
+ * @param type the type
+ */
+ SolrGen(Class<?> type) {
+ child = null;
+ this.type = type;
+ this.description = type.getSimpleName();
+ if (COLLECT_COUNTS) {
+ this.collectKey = description + OPEN_PAREN + distribution + CLOSE_PAREN;
+ }
+ }
+
+ /**
+ * Instantiates a new Solr gen.
+ *
+ * @param child the child
+ */
+ protected SolrGen(Gen<T> child) {
+ this.child = child;
+ this.type = null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T generate(RandomnessSource in) {
+ T val;
+ if (child == null && start == end) {
+ val = generate((SolrRandomnessSource) in);
+ processCounts(val);
+ return val;
+ }
+
+ if (child == null) {
+ val = (T) Integer.valueOf((int) ((SolrRandomnessSource) in).next(start, end));
+ processCounts(val);
+ return val;
+ }
+
+ if (child instanceof SolrGen && in instanceof SolrRandomnessSource) {
+ val =
+ (T)
+ ((SolrGen<Object>) child)
+ .generate(((SolrRandomnessSource) in).withDistribution(distribution));
+ processCounts(val);
+ return val;
+ }
+
+ if (in instanceof BenchmarkRandomSource) {
+ val = child.generate(((BenchmarkRandomSource) in).withDistribution(distribution));
+ processCounts(val);
+ return val;
+ }
+ val = child.generate(in);
+ processCounts(val);
+ return val;
+ }
+
+ private void processCounts(T val) {
+ if (COLLECT_COUNTS) {
+
+ if (collectKey == null || COUNTS.size() > RandomDataHistogram.MAX_TYPES_TO_COLLECT) {
+ return;
+ }
+
+ // System.out.println("Add key " + key);
+ RandomDataHistogram.Counts newCounts = null;
+ RandomDataHistogram.Counts counts;
+ counts = COUNTS.get(collectKey);
+
+ if (counts == null) {
+ newCounts =
+ new RandomDataHistogram.Counts(
+ collectKey,
+ type != null
+ && Number.class.isAssignableFrom(type)
+ && (type != Double.class && type != Float.class));
+ counts = COUNTS.putIfAbsent(collectKey, newCounts);
+ }
+
+ if (counts == null) {
+ newCounts.collect(val);
+ } else {
+ counts.collect(val);
+ }
+ }
+ }
+
+ /**
+ * Generate t.
+ *
+ * @param in the in
+ * @return the t
+ */
+ @SuppressWarnings("unchecked")
+ public T generate(SolrRandomnessSource in) {
+ if (child == null && start == end) {
+ T val = generate((RandomnessSource) in);
+ processCounts(val);
+ return val;
+ }
+
+ if (child == null) {
+ T val = (T) Integer.valueOf((int) in.next(start, end));
+ processCounts(val);
+ return val;
+ }
+
+ T val = child.generate(in);
+ processCounts(val);
+ return val;
+ }
+
+ /**
+ * Type class.
+ *
+ * @return the class
+ */
+ @SuppressWarnings("unchecked")
+ public Class<?> type() {
+ SolrGen<?> other = this;
+ while (true) {
+ if (other.type == null && other.child instanceof SolrGen) {
+ other = ((SolrGen<Object>) other.child);
+ continue;
+ }
+ return other.type;
+ }
+ }
+
+ /**
+ * Described as solr gen.
+ *
+ * @param description the description
+ * @return the solr gen
+ */
+ public SolrGen<T> describedAs(String description) {
+ this.description = description;
+ if (COLLECT_COUNTS) {
+ this.collectKey = description + OPEN_PAREN + distribution + CLOSE_PAREN;
+ }
+ return this;
+ }
+
+ @Override
+ public SolrGen<T> describedAs(AsString<T> asString) {
+ return new SolrDescribingGenerator<>(this, asString);
+ }
+
+ /**
+ * Mix solr gen.
+ *
+ * @param rhs the rhs
+ * @param type the type
+ * @return the solr gen
+ */
+ public SolrGen<T> mix(Gen<T> rhs, Class<?> type) {
+ return mix(rhs, 50, type);
+ }
+
+ @Override
+ public Gen<T> mix(Gen<T> rhs, int weight) {
+ return mix(rhs, weight, null);
+ }
+
+ /**
+ * Mix solr gen.
+ *
+ * @param rhs the rhs
+ * @param weight the weight
+ * @param type the type
+ * @return the solr gen
+ */
+ public SolrGen<T> mix(Gen<T> rhs, int weight, Class<?> type) {
+ return new SolrGen<>(type) {
+ @Override
+ public T generate(SolrRandomnessSource in) {
+ while (true) {
+ long picked = in.next(0, 99);
+ if (picked >= weight) {
+ continue;
+ }
+ return ((SolrGen<T>) rhs).generate(in);
+ }
+ }
+ };
+ }
+
+ /**
+ * Flat maps generated values with supplied function
+ *
+ * @param mapper function to map with
+ * @param <R> Type to map to
+ * @return A Gen of R
+ */
+ @Override
+ public <R> Gen<R> flatMap(Function<? super T, Gen<? extends R>> mapper) {
+ return new SolrGen<>(in -> mapper.apply(generate(in)).generate(in), null);
+ }
+
+ @Override
+ public <R> Gen<R> map(Function<? super T, ? extends R> mapper) {
+ return map(mapper, null);
+ }
+
+ /**
+ * Map solr gen.
+ *
+ * @param <R> the type parameter
+ * @param mapper the mapper
+ * @param type the type
+ * @return the solr gen
+ */
+ public <R> SolrGen<R> map(Function<? super T, ? extends R> mapper, Class<?> type) {
+ return new SolrGen<>(in -> mapper.apply(generate(in)), type);
+ }
+
+ /**
+ * With distribution solr gen.
+ *
+ * @param distribution the distribution
+ * @return the solr gen
+ */
+ public SolrGen<T> withDistribution(Distribution distribution) {
+ this.distribution = distribution;
+ if (this.child instanceof SolrGen) {
+ ((SolrGen<?>) this.child).distribution = distribution;
+ }
+ if (COLLECT_COUNTS) {
+ this.collectKey = description + OPEN_PAREN + distribution + CLOSE_PAREN;
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "SolrGen{"
+ + "child="
+ + child
+ + ", type="
+ + type
+ + ", distribution="
+ + distribution
+ + '}';
+ }
+
+ /**
+ * Gets distribution.
+ *
+ * @return the distribution
+ */
+ protected Distribution getDistribution() {
+ return this.distribution;
+ }
+}
+
+/**
+ * The type Solr describing generator.
+ *
+ * @param <G> the type parameter
+ */
+class SolrDescribingGenerator<G> extends SolrGen<G> {
+
+ private final AsString<G> toString;
+
+ /**
+ * Instantiates a new Solr describing generator.
+ *
+ * @param child the child
+ * @param toString the to string
+ */
+ public SolrDescribingGenerator(Gen<G> child, AsString<G> toString) {
+ super(child);
+ this.toString = toString;
+ }
+
+ @Override
+ public G generate(RandomnessSource in) {
+ G val;
+ val = Objects.requireNonNull(child).generate(in);
+ return val;
+ }
+
+ @Override
+ public G generate(SolrRandomnessSource in) {
+ G val;
+ val = Objects.requireNonNull(child).generate(in);
+ return val;
+ }
+
+ @Override
+ public String asString(G t) {
+ return toString.asString(t);
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/SourceDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/SourceDSL.java
new file mode 100644
index 0000000..9b54991
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/SourceDSL.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.Locale;
+import org.quicktheories.generators.ArbitraryDSL;
+import org.quicktheories.generators.BigDecimalsDSL;
+import org.quicktheories.generators.BigIntegersDSL;
+import org.quicktheories.generators.CharactersDSL;
+import org.quicktheories.generators.LocalDatesDSL;
+
+/** The type Source dsl. */
+public class SourceDSL {
+
+ private SourceDSL() {}
+
+ /**
+ * Longs longs dsl.
+ *
+ * @return the longs dsl
+ */
+ public static LongsDSL longs() {
+ return new LongsDSL();
+ }
+
+ /**
+ * Integers integers dsl.
+ *
+ * @return the integers dsl
+ */
+ public static IntegersDSL integers() {
+ return new IntegersDSL();
+ }
+
+ /**
+ * Doubles doubles dsl.
+ *
+ * @return the doubles dsl
+ */
+ public static DoublesDSL doubles() {
+ return new DoublesDSL();
+ }
+
+ /**
+ * Floats floats dsl.
+ *
+ * @return the floats dsl
+ */
+ public static FloatsDSL floats() {
+ return new FloatsDSL();
+ }
+
+ /**
+ * Characters characters dsl.
+ *
+ * @return the characters dsl
+ */
+ public static CharactersDSL characters() {
+ return new CharactersDSL();
+ }
+
+ /**
+ * Strings strings dsl.
+ *
+ * @return the strings dsl
+ */
+ public static StringsDSL strings() {
+ return new StringsDSL();
+ }
+
+ /**
+ * Lists lists dsl.
+ *
+ * @return the lists dsl
+ */
+ public static ListsDSL lists() {
+ return new ListsDSL();
+ }
+
+ /**
+ * Maps maps dsl.
+ *
+ * @return the maps dsl
+ */
+ public static MapsDSL maps() {
+ return new MapsDSL();
+ }
+
+ /**
+ * Arrays arrays dsl.
+ *
+ * @return the arrays dsl
+ */
+ public static ArraysDSL arrays() {
+ return new ArraysDSL();
+ }
+
+ /**
+ * Big integers big integers dsl.
+ *
+ * @return the big integers dsl
+ */
+ public static BigIntegersDSL bigIntegers() {
+ return new BigIntegersDSL();
+ }
+
+ /**
+ * Big decimals big decimals dsl.
+ *
+ * @return the big decimals dsl
+ */
+ public static BigDecimalsDSL bigDecimals() {
+ return new BigDecimalsDSL();
+ }
+
+ /**
+ * Arbitrary arbitrary dsl.
+ *
+ * @return the arbitrary dsl
+ */
+ public static ArbitraryDSL arbitrary() {
+ return new ArbitraryDSL();
+ }
+
+ /**
+ * Dates dates dsl.
+ *
+ * @return the dates dsl
+ */
+ public static DatesDSL dates() {
+ return new DatesDSL();
+ }
+
+ /**
+ * Local dates local dates dsl.
+ *
+ * @return the local dates dsl
+ */
+ public static LocalDatesDSL localDates() {
+ return new LocalDatesDSL();
+ }
+
+ /**
+ * Booleans booleans dsl.
+ *
+ * @return the booleans dsl
+ */
+ public static BooleansDSL booleans() {
+ return new BooleansDSL();
+ }
+
+ /**
+ * Check arguments.
+ *
+ * @param expression the expression
+ * @param errorMessageTemplate the error message template
+ * @param errorMessageArgs the error message args
+ */
+ public static void checkArguments(
+ boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
+ if (!expression) {
+ throw new IllegalArgumentException(
+ String.format(Locale.ENGLISH, errorMessageTemplate, errorMessageArgs));
+ }
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Strings.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Strings.java
new file mode 100644
index 0000000..779741e
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Strings.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.function.Function;
+import org.apache.solr.bench.SolrGenerate;
+
+/** The type Strings. */
+final class Strings {
+
+ /** The constant BOUNDED_NUMERIC_STRING. */
+ public static final String BOUNDED_NUMERIC_STRING = "Bounded Numeric String";
+
+ /** The constant POINTS_FROM_CODE_POINT_RANGE. */
+ public static final String POINTS_FROM_CODE_POINT_RANGE = "PointsFromCodePointRange";
+
+ /** The constant BOUNDED_LENGTH_STRING. */
+ public static final String BOUNDED_LENGTH_STRING = "Bounded Length String";
+
+ private Strings() {}
+
+ /**
+ * Bounded numeric strings solr gen.
+ *
+ * @param startInclusive the start inclusive
+ * @param endInclusive the end inclusive
+ * @return the solr gen
+ */
+ static SolrGen<String> boundedNumericStrings(int startInclusive, int endInclusive) {
+ return new SolrGen<>(
+ SolrGenerate.range(startInclusive, endInclusive).map(Object::toString), String.class)
+ .describedAs(BOUNDED_NUMERIC_STRING);
+ }
+
+ /**
+ * With code points solr gen.
+ *
+ * @param minCodePoint the min code point
+ * @param maxCodePoint the max code point
+ * @param numberOfCodePoints the number of code points
+ * @return the solr gen
+ */
+ static SolrGen<String> withCodePoints(
+ int minCodePoint, int maxCodePoint, SolrGen<Integer> numberOfCodePoints) {
+
+ return new SolrGen<>(
+ SolrGenerate.intArrays(
+ numberOfCodePoints, SolrGenerate.codePoints(minCodePoint, maxCodePoint))
+ .map(is -> new String(is, 0, is.length)),
+ String.class)
+ .describedAs(POINTS_FROM_CODE_POINT_RANGE);
+ }
+
+ /**
+ * Of bounded length strings solr gen.
+ *
+ * @param minCodePoint the min code point
+ * @param maxCodePoint the max code point
+ * @param minLength the min length
+ * @param maxLength the max length
+ * @return the solr gen
+ */
+ static SolrGen<String> ofBoundedLengthStrings(
+ int minCodePoint, int maxCodePoint, int minLength, int maxLength) {
+
+ // generate strings of fixed number of code points then modify any that exceed max length
+ return new SolrGen<>(
+ withCodePoints(minCodePoint, maxCodePoint, SolrGenerate.range(minLength, maxLength))
+ .map(reduceToSize(maxLength)),
+ String.class)
+ .describedAs(BOUNDED_LENGTH_STRING);
+ }
+
+ private static Function<String, String> reduceToSize(int maxLength) {
+ // Reduce size of string by removing characters from start
+ return s -> {
+ if (s.length() <= maxLength) {
+ return s;
+ }
+ String t = s;
+ while (true) {
+ final int length = t.length();
+ if (!(length > maxLength)) break;
+ t = t.substring(1);
+ }
+ return t;
+ };
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/StringsDSL.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/StringsDSL.java
new file mode 100644
index 0000000..33d573c
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/StringsDSL.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import static org.apache.solr.bench.generators.SourceDSL.checkArguments;
+import static org.apache.solr.bench.generators.SourceDSL.integers;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.SplittableRandom;
+import org.apache.solr.bench.BaseBenchState;
+import org.apache.solr.bench.SolrGenerate;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.quicktheories.core.Gen;
+import org.quicktheories.core.RandomnessSource;
+import org.quicktheories.impl.SplittableRandomSource;
+
+/** The type Strings dsl. */
+public class StringsDSL {
+
+ private static final int BASIC_LATIN_LAST_CODEPOINT = 0x007E;
+ private static final int BASIC_LATIN_FIRST_CODEPOINT = 0x0020;
+ private static final int ASCII_LAST_CODEPOINT = 0x007F;
+ private static final int LARGEST_DEFINED_BMP_CODEPOINT = 65533;
+
+ private static final List<String> words;
+
+ private static final int WORD_SIZE;
+
+ static {
+ // english word list via https://github.com/dwyl/english-words
+
+ words = new ArrayList<>(1000);
+ InputStream inputStream = StringsDSL.class.getClassLoader().getResourceAsStream("words.txt");
+ try (Scanner scanner =
+ new Scanner(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8.name())) {
+ while (scanner.hasNextLine()) {
+ words.add(scanner.nextLine());
+ }
+ }
+ Collections.shuffle(words, new Random(BaseBenchState.getRandomSeed()));
+ WORD_SIZE = words.size();
+ }
+
+ /**
+ * Word list word list generator builder.
+ *
+ * @return the word list generator builder
+ */
+ public WordListGeneratorBuilder wordList() {
+ return new WordListGeneratorBuilder(
+ new SolrGen<>(new WordListStringSolrGen(), String.class).describedAs("WordList Word"));
+ }
+
+ /**
+ * Generates integers as Strings, and shrinks towards "0".
+ *
+ * @return a Source of type String
+ */
+ public SolrGen<String> numeric() {
+ return new SolrGen<>(numericBetween(Integer.MIN_VALUE, Integer.MAX_VALUE), String.class);
+ }
+
+ /**
+ * Generates integers within the interval as Strings.
+ *
+ * @param startInclusive - lower inclusive bound of integer domain
+ * @param endInclusive - upper inclusive bound of integer domain
+ * @return a Source of type String
+ */
+ public SolrGen<String> numericBetween(int startInclusive, int endInclusive) {
+ checkArguments(
+ startInclusive <= endInclusive,
+ "There are no Integer values to be generated between startInclusive (%s) and endInclusive (%s)",
+ startInclusive,
+ endInclusive);
+ return new SolrGen<>(Strings.boundedNumericStrings(startInclusive, endInclusive), String.class);
+ }
+
+ /**
+ * Constructs a StringGeneratorBuilder which will build Strings composed of all defined code
+ * points
+ *
+ * @return a StringGeneratorBuilder
+ */
+ public StringGeneratorBuilder allPossible() {
+ return betweenCodePoints(Character.MIN_CODE_POINT, Character.MAX_CODE_POINT);
+ }
+
+ /**
+ * Realistic unicode realistic unicode generator builder.
+ *
+ * @param minLength the min length
+ * @param maxLength the max length
+ * @return the realistic unicode generator builder
+ */
+ public RealisticUnicodeGeneratorBuilder realisticUnicode(int minLength, int maxLength) {
+ return new RealisticUnicodeGeneratorBuilder(
+ new SolrGen<>() {
+ @Override
+ public String generate(SolrRandomnessSource in) {
+
+ int block =
+ integers()
+ .between(0, blockStarts.length - 1)
+ .describedAs("Realistic Unicode BLock Index")
+ .generate(in);
+
+ return Strings.ofBoundedLengthStrings(
+ blockStarts[block], blockEnds[block], minLength, maxLength)
+ .describedAs("Realistic Unicode")
+ .generate(in);
+ }
+ });
+ }
+
+ /**
+ * Constructs a StringGeneratorBuilder which will build Strings composed of all defined code
+ * points in the Basic Multilingual Plane
+ *
+ * @return a StringGeneratorBuilder
+ */
+ public StringGeneratorBuilder basicMultilingualPlaneAlphabet() {
+ return betweenCodePoints(Character.MIN_CODE_POINT, LARGEST_DEFINED_BMP_CODEPOINT);
+ }
+
+ /**
+ * Constructs a StringGeneratorBuilder which will build Strings composed of Unicode Basic Latin
+ * Alphabet
+ *
+ * @return a StringGeneratorBuilder
+ */
+ public StringGeneratorBuilder basicLatinAlphabet() {
+ return betweenCodePoints(BASIC_LATIN_FIRST_CODEPOINT, BASIC_LATIN_LAST_CODEPOINT);
+ }
+
+ /**
+ * Alpha string generator builder.
+ *
+ * @return the string generator builder
+ */
+ public StringGeneratorBuilder alpha() {
+ return betweenCodePoints('a', 'z' + 1);
+ }
+
+ /**
+ * Alpha numeric string generator builder.
+ *
+ * @return the string generator builder
+ */
+ public StringGeneratorBuilder alphaNumeric() {
+ return betweenCodePoints(' ', 'z' + 1);
+ }
+
+ /**
+ * Constructs a StringGeneratorBuilder which will build Strings composed of Unicode Ascii Alphabet
+ *
+ * @return a StringGeneratorBuilder
+ */
+ public StringGeneratorBuilder ascii() {
+ return betweenCodePoints(Character.MIN_CODE_POINT, ASCII_LAST_CODEPOINT);
+ }
+
+ /**
+ * Strings with characters between two (inclusive) code points
+ *
+ * @param minInclusive minimum code point
+ * @param maxInclusive max code point
+ * @return Builder for strings
+ */
+ public StringGeneratorBuilder betweenCodePoints(int minInclusive, int maxInclusive) {
+ return new StringGeneratorBuilder(minInclusive, maxInclusive);
+ }
+
+ /** The type Word list generator builder. */
+ public static class WordListGeneratorBuilder {
+ private final SolrGen<String> strings;
+
+ /**
+ * Instantiates a new Word list generator builder.
+ *
+ * @param strings the strings
+ */
+ WordListGeneratorBuilder(SolrGen<String> strings) {
+ this.strings = strings;
+ }
+
+ /**
+ * Of one solr gen.
+ *
+ * @return the solr gen
+ */
+ public SolrGen<String> ofOne() {
+ return strings;
+ }
+
+ /**
+ * Multi solr gen.
+ *
+ * @param count the count
+ * @return the solr gen
+ */
+ public SolrGen<String> multi(int count) {
+ return multiStringGen(strings, count);
+ }
+
+ /**
+ * With distribution word list generator builder.
+ *
+ * @param distribution the distribution
+ * @return the word list generator builder
+ */
+ public WordListGeneratorBuilder withDistribution(Distribution distribution) {
+ this.strings.withDistribution(distribution);
+ return this;
+ }
+ }
+
+ /** The type Realistic unicode generator builder. */
+ public static class RealisticUnicodeGeneratorBuilder {
+ private final SolrGen<String> strings;
+
+ /**
+ * Instantiates a new Realistic unicode generator builder.
+ *
+ * @param strings the strings
+ */
+ RealisticUnicodeGeneratorBuilder(SolrGen<String> strings) {
+ this.strings = strings;
+ }
+
+ /**
+ * Of one solr gen.
+ *
+ * @return the solr gen
+ */
+ public SolrGen<String> ofOne() {
+ return strings;
+ }
+
+ /**
+ * Multi solr gen.
+ *
+ * @param count the count
+ * @return the solr gen
+ */
+ public SolrGen<String> multi(int count) {
+ return multiStringGen(strings, count);
+ }
+ }
+
+ /** The type String generator builder. */
+ public static class StringGeneratorBuilder {
+
+ private final int minCodePoint;
+ private final int maxCodePoint;
+ private Integer cardinalityStart;
+ private Gen<Integer> maxCardinality;
+ private int multi;
+
+ private StringGeneratorBuilder(int minCodePoint, int maxCodePoint) {
+ this.minCodePoint = minCodePoint;
+ this.maxCodePoint = maxCodePoint;
+ }
+
+ /**
+ * Generates Strings of a fixed number of code points.
+ *
+ * @param codePoints - the fixed number of code points for the String
+ * @return a a Source of type String
+ */
+ public SolrGen<String> ofFixedNumberOfCodePoints(int codePoints) {
+ checkArguments(
+ codePoints >= 0,
+ "The number of codepoints cannot be negative; %s is not an accepted argument",
+ codePoints);
+ return new SolrGen<>(
+ Strings.withCodePoints(minCodePoint, maxCodePoint, SolrGenerate.constant(codePoints)),
+ String.class);
+ }
+
+ /**
+ * Generates Strings of a fixed length.
+ *
+ * @param fixedLength - the fixed length for the Strings
+ * @return a Source of type String
+ */
+ public SolrGen<String> ofLength(int fixedLength) {
+ return ofLengthBetween(fixedLength, fixedLength);
+ }
+
+ /**
+ * Max cardinality string generator builder.
+ *
+ * @param max the max
+ * @return the string generator builder
+ */
+ public StringGeneratorBuilder maxCardinality(int max) {
+ maxCardinality = SolrGenerate.constant(max);
+ return this;
+ }
+
+ /**
+ * Max cardinality string generator builder.
+ *
+ * @param max the max
+ * @return the string generator builder
+ */
+ public StringGeneratorBuilder maxCardinality(Gen<Integer> max) {
+ maxCardinality = max;
+ return this;
+ }
+
+ /**
+ * Multi string generator builder.
+ *
+ * @param count the count
+ * @return the string generator builder
+ */
+ public StringGeneratorBuilder multi(int count) {
+ this.multi = count;
+ return this;
+ }
+
+ /**
+ * Generates Strings of length bounded between minLength and maxLength inclusively.
+ *
+ * @param minLength - minimum inclusive length of String
+ * @param maxLength - maximum inclusive length of String
+ * @return a Source of type String
+ */
+ public SolrGen<String> ofLengthBetween(int minLength, int maxLength) {
+ checkArguments(
+ minLength <= maxLength,
+ "The minLength (%s) is longer than the maxLength(%s)",
+ minLength,
+ maxLength);
+ checkArguments(
+ minLength >= 0,
+ "The length of a String cannot be negative; %s is not an accepted argument",
+ minLength);
+ SolrGen<String> strings =
+ Strings.ofBoundedLengthStrings(minCodePoint, maxCodePoint, minLength, maxLength);
+
+ if (maxCardinality != null) {
+ SolrGen<String> gen =
+ new SolrGen<>(
+ new SolrGen<>() {
+ @Override
+ public String generate(SolrRandomnessSource in) {
+ Integer maxCard = maxCardinality.generate(in);
+
+ if (cardinalityStart == null) {
+ cardinalityStart =
+ SolrGenerate.range(0, Integer.MAX_VALUE - maxCard - 1).generate(in);
+ }
+
+ long seed =
+ SolrGenerate.range(cardinalityStart, cardinalityStart + maxCard - 1)
+ .generate(in);
+ return strings.generate(
+ (RandomnessSource) new SplittableRandomSource(new SplittableRandom(seed)));
+ }
+
+ @Override
+ public String generate(RandomnessSource in) {
+ return generate((SolrRandomnessSource) in);
+ }
+ },
+ String.class);
+ if (multi > 1) {
+ return multiStringGen(gen, multi);
+ }
+ return new SolrGen<>(gen, String.class);
+ } else {
+ if (multi > 1) {
+ return multiStringGen(strings, multi);
+ }
+ return new SolrGen<>(strings, String.class);
+ }
+ }
+ }
+
+ private static SolrGen<String> multiStringGen(SolrGen<String> strings, int multi) {
+ return new SolrGen<>(MultiString.class) {
+
+ {
+ describedAs("MultiString");
+ }
+
+ @Override
+ public String generate(SolrRandomnessSource in) {
+ StringBuilder sb = new StringBuilder(64);
+ for (int i = 0; i < multi; i++) {
+ sb.append(strings.generate(in));
+ if (i < multi - 1) {
+ sb.append(' ');
+ }
+ }
+ return sb.toString();
+ }
+ };
+ }
+
+ private static class WordListStringSolrGen extends SolrGen<String> {
+ /** Instantiates a new Word list string solr gen. */
+ public WordListStringSolrGen() {
+ super(String.class);
+ }
+
+ @Override
+ public String generate(RandomnessSource in) {
+ return words.get(
+ integers()
+ .between(0, WORD_SIZE - 1)
+ .describedAs("WordList Index")
+ .withDistribution(this.getDistribution())
+ .generate(in));
+ }
+ }
+
+ private static final int[] blockStarts = {
+ 0x0000, 0x0080, 0x0100, 0x0180, 0x0250, 0x02B0, 0x0300, 0x0370, 0x0400, 0x0500, 0x0530, 0x0590,
+ 0x0600, 0x0700, 0x0750, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0980, 0x0A00, 0x0A80, 0x0B00, 0x0B80,
+ 0x0C00, 0x0C80, 0x0D00, 0x0D80, 0x0E00, 0x0E80, 0x0F00, 0x1000, 0x10A0, 0x1100, 0x1200, 0x1380,
+ 0x13A0, 0x1400, 0x1680, 0x16A0, 0x1700, 0x1720, 0x1740, 0x1760, 0x1780, 0x1800, 0x18B0, 0x1900,
+ 0x1950, 0x1980, 0x19E0, 0x1A00, 0x1A20, 0x1B00, 0x1B80, 0x1C00, 0x1C50, 0x1CD0, 0x1D00, 0x1D80,
+ 0x1DC0, 0x1E00, 0x1F00, 0x2000, 0x2070, 0x20A0, 0x20D0, 0x2100, 0x2150, 0x2190, 0x2200, 0x2300,
+ 0x2400, 0x2440, 0x2460, 0x2500, 0x2580, 0x25A0, 0x2600, 0x2700, 0x27C0, 0x27F0, 0x2800, 0x2900,
+ 0x2980, 0x2A00, 0x2B00, 0x2C00, 0x2C60, 0x2C80, 0x2D00, 0x2D30, 0x2D80, 0x2DE0, 0x2E00, 0x2E80,
+ 0x2F00, 0x2FF0, 0x3000, 0x3040, 0x30A0, 0x3100, 0x3130, 0x3190, 0x31A0, 0x31C0, 0x31F0, 0x3200,
+ 0x3300, 0x3400, 0x4DC0, 0x4E00, 0xA000, 0xA490, 0xA4D0, 0xA500, 0xA640, 0xA6A0, 0xA700, 0xA720,
+ 0xA800, 0xA830, 0xA840, 0xA880, 0xA8E0, 0xA900, 0xA930, 0xA960, 0xA980, 0xAA00, 0xAA60, 0xAA80,
+ 0xABC0, 0xAC00, 0xD7B0, 0xE000, 0xF900, 0xFB00, 0xFB50, 0xFE00, 0xFE10, 0xFE20, 0xFE30, 0xFE50,
+ 0xFE70, 0xFF00, 0xFFF0, 0x10000, 0x10080, 0x10100, 0x10140, 0x10190, 0x101D0, 0x10280, 0x102A0,
+ 0x10300, 0x10330, 0x10380, 0x103A0, 0x10400, 0x10450, 0x10480, 0x10800, 0x10840, 0x10900,
+ 0x10920, 0x10A00, 0x10A60, 0x10B00, 0x10B40, 0x10B60, 0x10C00, 0x10E60, 0x11080, 0x12000,
+ 0x12400, 0x13000, 0x1D000, 0x1D100, 0x1D200, 0x1D300, 0x1D360, 0x1D400, 0x1F000, 0x1F030,
+ 0x1F100, 0x1F200, 0x20000, 0x2A700, 0x2F800, 0xE0000, 0xE0100, 0xF0000, 0x100000
+ };
+
+ private static final int[] blockEnds = {
+ 0x007F, 0x00FF, 0x017F, 0x024F, 0x02AF, 0x02FF, 0x036F, 0x03FF, 0x04FF, 0x052F, 0x058F, 0x05FF,
+ 0x06FF, 0x074F, 0x077F, 0x07BF, 0x07FF, 0x083F, 0x097F, 0x09FF, 0x0A7F, 0x0AFF, 0x0B7F, 0x0BFF,
+ 0x0C7F, 0x0CFF, 0x0D7F, 0x0DFF, 0x0E7F, 0x0EFF, 0x0FFF, 0x109F, 0x10FF, 0x11FF, 0x137F, 0x139F,
+ 0x13FF, 0x167F, 0x169F, 0x16FF, 0x171F, 0x173F, 0x175F, 0x177F, 0x17FF, 0x18AF, 0x18FF, 0x194F,
+ 0x197F, 0x19DF, 0x19FF, 0x1A1F, 0x1AAF, 0x1B7F, 0x1BBF, 0x1C4F, 0x1C7F, 0x1CFF, 0x1D7F, 0x1DBF,
+ 0x1DFF, 0x1EFF, 0x1FFF, 0x206F, 0x209F, 0x20CF, 0x20FF, 0x214F, 0x218F, 0x21FF, 0x22FF, 0x23FF,
+ 0x243F, 0x245F, 0x24FF, 0x257F, 0x259F, 0x25FF, 0x26FF, 0x27BF, 0x27EF, 0x27FF, 0x28FF, 0x297F,
+ 0x29FF, 0x2AFF, 0x2BFF, 0x2C5F, 0x2C7F, 0x2CFF, 0x2D2F, 0x2D7F, 0x2DDF, 0x2DFF, 0x2E7F, 0x2EFF,
+ 0x2FDF, 0x2FFF, 0x303F, 0x309F, 0x30FF, 0x312F, 0x318F, 0x319F, 0x31BF, 0x31EF, 0x31FF, 0x32FF,
+ 0x33FF, 0x4DBF, 0x4DFF, 0x9FFF, 0xA48F, 0xA4CF, 0xA4FF, 0xA63F, 0xA69F, 0xA6FF, 0xA71F, 0xA7FF,
+ 0xA82F, 0xA83F, 0xA87F, 0xA8DF, 0xA8FF, 0xA92F, 0xA95F, 0xA97F, 0xA9DF, 0xAA5F, 0xAA7F, 0xAADF,
+ 0xABFF, 0xD7AF, 0xD7FF, 0xF8FF, 0xFAFF, 0xFB4F, 0xFDFF, 0xFE0F, 0xFE1F, 0xFE2F, 0xFE4F, 0xFE6F,
+ 0xFEFF, 0xFFEF, 0xFFFF, 0x1007F, 0x100FF, 0x1013F, 0x1018F, 0x101CF, 0x101FF, 0x1029F, 0x102DF,
+ 0x1032F, 0x1034F, 0x1039F, 0x103DF, 0x1044F, 0x1047F, 0x104AF, 0x1083F, 0x1085F, 0x1091F,
+ 0x1093F, 0x10A5F, 0x10A7F, 0x10B3F, 0x10B5F, 0x10B7F, 0x10C4F, 0x10E7F, 0x110CF, 0x123FF,
+ 0x1247F, 0x1342F, 0x1D0FF, 0x1D1FF, 0x1D24F, 0x1D35F, 0x1D37F, 0x1D7FF, 0x1F02F, 0x1F09F,
+ 0x1F1FF, 0x1F2FF, 0x2A6DF, 0x2B73F, 0x2FA1F, 0xE007F, 0xE01EF, 0xFFFFF, 0x10FFFF
+ };
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/Tracker.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/Tracker.java
new file mode 100644
index 0000000..e28a73a
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/Tracker.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.bench.generators;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+/** The type Tracker. */
+public class Tracker {
+ private final List<RandomDataHistogram.Counts.Surrogate> values;
+ private final String name;
+ private final int count;
+ private final double percentage;
+
+ /**
+ * Instantiates a new Tracker.
+ *
+ * @param values the values
+ * @param name the name
+ * @param count the count
+ * @param percentage the percentage
+ */
+ public Tracker(
+ List<RandomDataHistogram.Counts.Surrogate> values,
+ String name,
+ int count,
+ double percentage) {
+ this.values = values;
+ this.name = name == null ? "None" : name;
+ this.count = count;
+ this.percentage = percentage;
+ }
+
+ /**
+ * Display string.
+ *
+ * @param object the object
+ * @return the string
+ */
+ public static String display(Object object) {
+ if (object == null) return "None";
+ if (object instanceof Class) {
+ return ((Class<?>) object).getName();
+ }
+ if (object instanceof Collection) {
+ @SuppressWarnings("unchecked")
+ Collection<Object> collection = (Collection<Object>) object;
+ String elements = collection.stream().map(Tracker::display).collect(Collectors.joining(", "));
+ return String.format(Locale.ENGLISH, "[%s]", elements);
+ }
+ if (object.getClass().isArray()) {
+ if (object.getClass().getComponentType().isPrimitive()) {
+ return getString(object);
+ }
+ Object[] array = (Object[]) object;
+ String elements =
+ Arrays.stream(array).map(Tracker::display).collect(Collectors.joining(", "));
+ return String.format(Locale.ENGLISH, "%s{%s}", object.getClass().getSimpleName(), elements);
+ }
+ if (String.class.isAssignableFrom(object.getClass())) {
+ return String.format(Locale.ENGLISH, "\"%s\"", object.toString().replace('\u0000', '\ufffd'));
+ }
+ return object.toString().replace('\u0000', '\ufffd');
+ }
+
+ private static String getString(Object obj) {
+ if (obj == null) {
+ return "None";
+ }
+
+ try {
+ if (obj.getClass().isArray()) {
+ if (obj.getClass().getComponentType().isPrimitive()) {
+ if (obj instanceof boolean[]) {
+ return Arrays.toString((boolean[]) obj);
+ }
+ if (obj instanceof char[]) {
+ return Arrays.toString((char[]) obj);
+ }
+ if (obj instanceof short[]) {
+ return Arrays.toString((short[]) obj);
+ }
+ if (obj instanceof byte[]) {
+ return Arrays.toString((byte[]) obj);
+ }
+ if (obj instanceof int[]) {
+ return Arrays.toString((int[]) obj);
+ }
+ if (obj instanceof long[]) {
+ return Arrays.toString((long[]) obj);
+ }
+ if (obj instanceof float[]) {
+ return Arrays.toString((float[]) obj);
+ }
+ if (obj instanceof double[]) {
+ return Arrays.toString((double[]) obj);
+ }
+ }
+ return Arrays.deepToString((Object[]) obj);
+ }
+
+ return obj.toString();
+ } catch (Throwable t) {
+ if (t instanceof Error) {
+ throw t;
+ }
+
+ return obj.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(obj));
+ }
+ }
+
+ /**
+ * Name string.
+ *
+ * @return the string
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Count int.
+ *
+ * @return the int
+ */
+ public int count() {
+ return count;
+ }
+
+ /**
+ * Percentage double.
+ *
+ * @return the double
+ */
+ public double percentage() {
+ return percentage;
+ }
+
+ /**
+ * Values list.
+ *
+ * @return the list
+ */
+ public List<RandomDataHistogram.Counts.Surrogate> values() {
+ return Collections.unmodifiableList(values);
+ }
+
+ public String toString() {
+ return String.format(
+ Locale.ENGLISH, "%s (%s, %s%%): %s", name, count, percentage, Tracker.display(values));
+ }
+}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/generators/package-info.java b/solr/benchmark/src/java/org/apache/solr/bench/generators/package-info.java
new file mode 100644
index 0000000..0b1c740
--- /dev/null
+++ b/solr/benchmark/src/java/org/apache/solr/bench/generators/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+/** Type random generators that are built upon and maintain compatibility with QuickTheories */
+package org.apache.solr.bench.generators;
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java b/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
index bac8406..981ce0d 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
@@ -16,11 +16,13 @@
*/
package org.apache.solr.bench.index;
+import static org.apache.solr.bench.Docs.docs;
+import static org.apache.solr.bench.generators.SourceDSL.integers;
+import static org.apache.solr.bench.generators.SourceDSL.longs;
+import static org.apache.solr.bench.generators.SourceDSL.strings;
+
import java.util.Iterator;
-import java.util.SplittableRandom;
import java.util.concurrent.TimeUnit;
-import org.apache.solr.bench.DocMaker;
-import org.apache.solr.bench.FieldDef;
import org.apache.solr.bench.MiniClusterState;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.common.SolrInputDocument;
@@ -39,21 +41,25 @@ import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Timeout;
import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.BenchmarkParams;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
-@Threads(6)
-@Warmup(time = 3, iterations = 5)
-@Measurement(time = 15, iterations = 5)
+@Threads(4)
+@Warmup(time = 10, iterations = 3)
+@Measurement(time = 20, iterations = 4)
@Fork(value = 1)
@Timeout(time = 60)
-/** A benchmark to experiment with the performance of distributed indexing. */
+// A benchmark to experiment with the performance of distributed indexing.
public class CloudIndexing {
@State(Scope.Benchmark)
public static class BenchState {
- String collection = "testCollection";
+ static final String COLLECTION = "testCollection";
+
+ @Param({"1"})
+ public int scale;
@Param("4")
int nodeCount;
@@ -61,117 +67,108 @@ public class CloudIndexing {
@Param("5")
int numShards;
- @Param({"1", "3", "9"})
+ @Param({"1", "3"})
int numReplicas;
- @Param({"100000"})
- public int docCount;
-
- @Setup(Level.Iteration)
- public void doSetup(MiniClusterState.MiniClusterBenchState miniClusterState) throws Exception {
-
- miniClusterState.startMiniCluster(nodeCount);
-
- miniClusterState.createCollection(collection, numShards, numReplicas);
+ @Param({"0", "15", "30", "70", "100", "500", "1000"})
+ int useStringUtf8Over;
+
+ @Param({"true", "false"})
+ boolean directBuffer;
+
+ private final org.apache.solr.bench.Docs largeDocs;
+ private Iterator<SolrInputDocument> largeDocIterator;
+
+ private final org.apache.solr.bench.Docs smallDocs;
+ private Iterator<SolrInputDocument> smallDocIterator;
+
+ public BenchState() {
+
+ largeDocs =
+ docs()
+ .field("id", integers().incrementing())
+ .field(strings().basicLatinAlphabet().multi(312).ofLengthBetween(30, 64))
+ .field(strings().basicLatinAlphabet().multi(312).ofLengthBetween(30, 64))
+ .field(integers().all())
+ .field(integers().all())
+ .field(integers().all())
+ .field(longs().all())
+ .field(longs().all());
+
+ try {
+ largeDocIterator = largeDocs.preGenerate(50000);
+
+ smallDocs =
+ docs()
+ .field("id", integers().incrementing())
+ .field("text", strings().basicLatinAlphabet().multi(2).ofLengthBetween(20, 32))
+ .field("int1_i", integers().all())
+ .field("int2_i", integers().all())
+ .field("long1_l", longs().all());
+
+ smallDocIterator = smallDocs.preGenerate(50000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
}
- @TearDown(Level.Iteration)
- public void doTearDown(MiniClusterState.MiniClusterBenchState miniClusterState)
- throws Exception {
- miniClusterState.shutdownMiniCluster();
+ public SolrInputDocument getLargeDoc() {
+ if (!largeDocIterator.hasNext()) {
+ largeDocIterator = largeDocs.generatedDocsIterator();
+ }
+ return largeDocIterator.next();
}
- @State(Scope.Thread)
- public static class Docs {
-
- private DocMaker largeDocMaker;
- private Iterator<SolrInputDocument> largeDocIterator;
-
- private DocMaker smallDocMaker;
- private Iterator<SolrInputDocument> smallDocIterator;
-
- private SplittableRandom random;
-
- @Setup(Level.Trial)
- public void setupDoc(BenchState state) throws Exception {
- Long seed = Long.getLong("randomSeed");
-
- random = new SplittableRandom(seed);
-
- largeDocMaker = new DocMaker();
- largeDocMaker.addField(
- "id", FieldDef.FieldDefBuilder.aFieldDef().withContent(DocMaker.Content.UNIQUE_INT));
- largeDocMaker.addField(
- "text",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.ALPHEBETIC)
- .withMaxLength(64)
- .withTokenCount(random.nextInt(350, 512)));
-
- largeDocMaker.preGenerateDocs(state.docCount, random);
-
- largeDocIterator = largeDocMaker.getGeneratedDocsIterator();
-
- smallDocMaker = new DocMaker();
- smallDocMaker.addField(
- "id", FieldDef.FieldDefBuilder.aFieldDef().withContent(DocMaker.Content.UNIQUE_INT));
- smallDocMaker.addField(
- "text",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.ALPHEBETIC)
- .withMaxLength(32)
- .withTokenCount(1));
+ public SolrInputDocument getSmallDoc() {
+ if (!smallDocIterator.hasNext()) {
+ smallDocIterator = smallDocs.generatedDocsIterator();
+ }
+ return smallDocIterator.next();
+ }
- smallDocMaker.preGenerateDocs(state.docCount, random);
+ @Setup(Level.Trial)
+ public void doSetup(MiniClusterState.MiniClusterBenchState miniClusterState) throws Exception {
+ System.setProperty("useStringUtf8Over", Integer.toString(useStringUtf8Over));
+ System.setProperty("httpClientDirectBuffer", Boolean.toString(directBuffer));
- smallDocIterator = smallDocMaker.getGeneratedDocsIterator();
- }
+ System.setProperty("mergePolicyFactory", "org.apache.solr.index.NoMergePolicyFactory");
+ miniClusterState.startMiniCluster(nodeCount);
+ miniClusterState.createCollection(COLLECTION, numShards, numReplicas);
+ }
- public SolrInputDocument getLargeDoc() {
- if (!largeDocIterator.hasNext()) {
- largeDocIterator = largeDocMaker.getGeneratedDocsIterator();
- }
- return largeDocIterator.next();
- }
+ @TearDown(Level.Trial)
+ public void doTearDown(
+ MiniClusterState.MiniClusterBenchState miniClusterState, BenchmarkParams benchmarkParams)
+ throws Exception {
- public SolrInputDocument getSmallDoc() {
- if (!smallDocIterator.hasNext()) {
- smallDocIterator = smallDocMaker.getGeneratedDocsIterator();
- }
- return smallDocIterator.next();
- }
+ // miniClusterState.shutdownMiniCluster(benchmarkParams);
}
}
@Benchmark
@Timeout(time = 300)
public Object indexLargeDoc(
- MiniClusterState.MiniClusterBenchState miniClusterState,
- BenchState state,
- BenchState.Docs docState)
- throws Exception {
+ MiniClusterState.MiniClusterBenchState miniClusterState, BenchState state) throws Exception {
UpdateRequest updateRequest = new UpdateRequest();
- updateRequest.setBasePath(miniClusterState.nodes.get(docState.random.nextInt(state.nodeCount)));
- SolrInputDocument doc = docState.getLargeDoc();
-
+ updateRequest.setBasePath(
+ miniClusterState.nodes.get(miniClusterState.getRandom().nextInt(state.nodeCount)));
+ SolrInputDocument doc = state.getLargeDoc();
updateRequest.add(doc);
- return miniClusterState.client.request(updateRequest, state.collection);
+ return miniClusterState.client.request(updateRequest, BenchState.COLLECTION);
}
@Benchmark
@Timeout(time = 300)
public Object indexSmallDoc(
- MiniClusterState.MiniClusterBenchState miniClusterState,
- BenchState state,
- BenchState.Docs docState)
- throws Exception {
+ MiniClusterState.MiniClusterBenchState miniClusterState, BenchState state) throws Exception {
UpdateRequest updateRequest = new UpdateRequest();
- updateRequest.setBasePath(miniClusterState.nodes.get(docState.random.nextInt(state.nodeCount)));
- SolrInputDocument doc = docState.getSmallDoc();
-
+ updateRequest.setBasePath(
+ miniClusterState.nodes.get(miniClusterState.getRandom().nextInt(state.nodeCount)));
+ SolrInputDocument doc = state.getSmallDoc();
updateRequest.add(doc);
- return miniClusterState.client.request(updateRequest, state.collection);
+ return miniClusterState.client.request(updateRequest, BenchState.COLLECTION);
}
}
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java b/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
index 088c703..61e4b87 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
@@ -16,10 +16,14 @@
*/
package org.apache.solr.bench.search;
+import static org.apache.solr.bench.Docs.docs;
+import static org.apache.solr.bench.generators.SourceDSL.integers;
+import static org.apache.solr.bench.generators.SourceDSL.strings;
+
import java.util.SplittableRandom;
import java.util.concurrent.TimeUnit;
-import org.apache.solr.bench.DocMaker;
-import org.apache.solr.bench.FieldDef;
+import org.apache.solr.bench.BaseBenchState;
+import org.apache.solr.bench.Docs;
import org.apache.solr.bench.MiniClusterState;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.common.params.ModifiableSolrParams;
@@ -53,7 +57,7 @@ public class JsonFaceting {
@State(Scope.Benchmark)
public static class BenchState {
- public String collection = "testCollection";
+ public static final String collection = "testCollection";
@Param({"100000"})
public int docCount;
@@ -95,8 +99,6 @@ public class JsonFaceting {
BenchmarkParams benchmarkParams, MiniClusterState.MiniClusterBenchState miniClusterState)
throws Exception {
- SplittableRandom random = miniClusterState.getRandom();
-
System.setProperty("maxMergeAtOnce", "20");
System.setProperty("segmentsPerTier", "20");
@@ -105,52 +107,28 @@ public class JsonFaceting {
miniClusterState.createCollection(collection, numShards, numReplicas);
// Define random documents
- DocMaker docMaker = new DocMaker();
- docMaker.addField(
- "id", FieldDef.FieldDefBuilder.aFieldDef().withContent(DocMaker.Content.UNIQUE_INT));
- docMaker.addField(
- "facet_s",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.ALPHEBETIC)
- .withMaxLength(64)
- .withMaxCardinality(facetCard, random));
- docMaker.addField(
- "facet2_s",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.ALPHEBETIC)
- .withMaxLength(16)
- .withMaxCardinality(facetCard, random));
- docMaker.addField(
- "facet3_s",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.UNICODE)
- .withMaxLength(128)
- .withMaxCardinality(facetCard2, random));
- docMaker.addField(
- "text",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.ALPHEBETIC)
- .withMaxLength(64)
- .withTokenCount(random.nextInt(350, 512)));
- docMaker.addField(
- "int_i", FieldDef.FieldDefBuilder.aFieldDef().withContent(DocMaker.Content.INTEGER));
- docMaker.addField(
- "int2_i",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.INTEGER)
- .withMaxCardinality(facetCard2, random));
- docMaker.addField(
- "int3_i",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.INTEGER)
- .withMaxCardinality(facetCard2, random));
- docMaker.addField(
- "int4_i",
- FieldDef.FieldDefBuilder.aFieldDef()
- .withContent(DocMaker.Content.INTEGER)
- .withMaxCardinality(facetCard2, random));
-
- miniClusterState.index(collection, docMaker, docCount);
+ Docs docs =
+ docs()
+ .field("id", integers().incrementing())
+ .field(
+ "facet_s",
+ strings().basicLatinAlphabet().maxCardinality(facetCard).ofLengthBetween(1, 64))
+ .field(
+ "facet2_s",
+ strings().basicLatinAlphabet().maxCardinality(facetCard).ofLengthBetween(1, 16))
+ .field(
+ "facet3_s",
+ strings()
+ .basicMultilingualPlaneAlphabet()
+ .maxCardinality(facetCard2)
+ .ofLengthBetween(1, 128))
+ .field(strings().basicLatinAlphabet().multi(512).ofLengthBetween(4, 16))
+ .field(integers().all())
+ .field(integers().allWithMaxCardinality(facetCard2))
+ .field(integers().allWithMaxCardinality(facetCard2))
+ .field(integers().allWithMaxCardinality(facetCard2));
+
+ miniClusterState.index(collection, docs, docCount);
miniClusterState.forceMerge(collection, 15);
params = new ModifiableSolrParams();
@@ -178,7 +156,7 @@ public class JsonFaceting {
+ " , f6:{type:terms, field:'int3_i', limit:1, sort:'x desc', facet:{x:'hll(int2_i)'} } "
+ " , f7:{type:terms, field:'facet_s', limit:2, sort:'x desc', facet:{x:'missing(int4_i)'} } "
+ " , f8:{type:terms, field:'facet_s', limit:2, sort:'x desc', facet:{x:'countvals(int4_i)'} } "
- + "}");
+ + '}');
// MiniClusterState.log("params: " + params + "\n");
}
@@ -189,10 +167,8 @@ public class JsonFaceting {
private SplittableRandom random;
@Setup(Level.Trial)
- public void setup() throws Exception {
- Long seed = Long.getLong("randomSeed");
-
- this.random = new SplittableRandom(seed);
+ public void setup() {
+ this.random = new SplittableRandom(BaseBenchState.getRandomSeed());
}
}
}
diff --git a/solr/benchmark/src/java/org/quicktheories/impl/BenchmarkRandomSource.java b/solr/benchmark/src/java/org/quicktheories/impl/BenchmarkRandomSource.java
new file mode 100644
index 0000000..8eefcdc
--- /dev/null
+++ b/solr/benchmark/src/java/org/quicktheories/impl/BenchmarkRandomSource.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.quicktheories.impl;
+
+import org.apache.commons.math3.random.RandomDataGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.apache.solr.bench.generators.Distribution;
+import org.quicktheories.core.DetatchedRandomnessSource;
+
+/** The type Benchmark random source. */
+public class BenchmarkRandomSource
+ implements DetatchedRandomnessSource, ExtendedRandomnessSource, SolrRandomnessSource {
+ private final RandomGenerator random;
+ private final RandomDataGenerator rdg;
+
+ private org.apache.solr.bench.generators.Distribution distribution = Distribution.UNIFORM;
+
+ /**
+ * Instantiates a new Benchmark random source.
+ *
+ * @param random the random
+ */
+ public BenchmarkRandomSource(RandomGenerator random) {
+ this.random = random;
+ rdg = new RandomDataGenerator(random);
+ }
+
+ private BenchmarkRandomSource(
+ RandomGenerator random,
+ RandomDataGenerator rdg,
+ org.apache.solr.bench.generators.Distribution distribution) {
+ this.random = random;
+ this.rdg = rdg;
+ this.distribution = distribution;
+ }
+
+ public BenchmarkRandomSource withDistribution(Distribution distribution) {
+ if (this.distribution == distribution) {
+ return this;
+ }
+ return new BenchmarkRandomSource(random, rdg, distribution);
+ }
+
+ @Override
+ public long next(long min, long max) {
+ switch (distribution) {
+ case UNIFORM:
+ return rdg.nextLong(min, max);
+ case ZIPFIAN:
+ return rdg.nextZipf((int) (max - min), 2) + min - 1;
+ case GAUSSIAN:
+ return (int) BenchmarkRandomSource.normalize(rdg.nextGaussian(.5, .125), min, max - 1.0d);
+ default:
+ throw new IllegalStateException("Unknown distribution: " + distribution);
+ }
+ }
+
+ @Override
+ public long next(Constraint constraints) {
+ if (constraints.min() == constraints.max()) {
+ throw new RuntimeException(constraints.min() + " " + constraints.max());
+ }
+ return next(constraints.min(), constraints.max());
+ }
+
+ /**
+ * Normalize double.
+ *
+ * @param value the value
+ * @param normalizationLowerBound the normalization lower bound
+ * @param normalizationUpperBound the normalization upper bound
+ * @return the double
+ */
+ public static double normalize(
+ double value, double normalizationLowerBound, double normalizationUpperBound) {
+ double boundedValue = boundValue(value);
+ // normalize boundedValue to new range
+ double normalizedRange = normalizationUpperBound - normalizationLowerBound;
+ return ((boundedValue - 0) * normalizedRange) + normalizationLowerBound;
+ }
+
+ private static double boundValue(double value) {
+ double boundedValue = value;
+ if (value < 0) {
+ boundedValue = 0;
+ }
+ if (value > 1) {
+ boundedValue = 1;
+ }
+ return boundedValue;
+ }
+
+ @Override
+ public DetatchedRandomnessSource detach() {
+ return this;
+ }
+
+ @Override
+ public void registerFailedAssumption() {}
+
+ @Override
+ public long tryNext(Constraint constraints) {
+ return next(constraints);
+ }
+
+ @Override
+ public void add(Precursor other) {}
+
+ @Override
+ public void commit() {}
+}
diff --git a/solr/benchmark/src/java/org/quicktheories/impl/SplittableRandomSource.java b/solr/benchmark/src/java/org/quicktheories/impl/SplittableRandomSource.java
new file mode 100644
index 0000000..df6f9b0
--- /dev/null
+++ b/solr/benchmark/src/java/org/quicktheories/impl/SplittableRandomSource.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.quicktheories.impl;
+
+import java.util.SplittableRandom;
+import org.apache.solr.bench.SolrRandomnessSource;
+import org.apache.solr.bench.generators.Distribution;
+import org.quicktheories.core.DetatchedRandomnessSource;
+
+/** The type Splittable random source. */
+public class SplittableRandomSource
+ implements DetatchedRandomnessSource, ExtendedRandomnessSource, SolrRandomnessSource {
+ /** The Random. */
+ final SplittableRandom random;
+
+ /**
+ * Instantiates a new Splittable random source.
+ *
+ * @param random the random
+ */
+ public SplittableRandomSource(SplittableRandom random) {
+ this.random = random;
+ }
+
+ @Override
+ public long next(Constraint constraints) {
+ if (constraints.min() == constraints.max()) {
+ throw new RuntimeException(constraints.min() + " " + constraints.max());
+ }
+ return next(constraints.min(), constraints.max());
+ }
+
+ @Override
+ public long next(long min, long max) {
+ return random.nextLong(min, max);
+ }
+
+ @Override
+ public DetatchedRandomnessSource detach() {
+ return this;
+ }
+
+ @Override
+ public void registerFailedAssumption() {}
+
+ @Override
+ public long tryNext(Constraint constraints) {
+ return next(constraints);
+ }
+
+ @Override
+ public void add(Precursor other) {}
+
+ @Override
+ public void commit() {}
+
+ @Override
+ public SolrRandomnessSource withDistribution(Distribution distribution) {
+ return this;
+ }
+}
diff --git a/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml b/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
index 727f5e2..bc4676c 100644
--- a/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
+++ b/solr/benchmark/src/resources/configs/cloud-minimal/conf/schema.xml
@@ -16,22 +16,39 @@
limitations under the License.
-->
<schema name="minimal" version="1.1">
- <fieldType name="string" class="solr.StrField" docValues="true"/>
- <fieldType name="int" class="org.apache.solr.schema.IntPointField" docValues="true" omitNorms="true" positionIncrementGap="0"/>
- <fieldType name="long" class="org.apache.solr.schema.LongPointField" docValues="true" omitNorms="true" positionIncrementGap="0"/>
- <fieldType name="text" class="solr.TextField">
- <analyzer>
- <tokenizer class="solr.StandardTokenizerFactory"/>
- <filter class="solr.LowerCaseFilterFactory"/>
- </analyzer>
- </fieldType>
+ <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
+ <fieldType name="string" class="solr.StrField" docValues="true"/>
+ <fieldType name="int" class="org.apache.solr.schema.IntPointField" docValues="true" omitNorms="true"
+ positionIncrementGap="0"/>
+ <fieldType name="long" class="org.apache.solr.schema.LongPointField" docValues="true" omitNorms="true"
+ positionIncrementGap="0"/>
+ <fieldType name="float" class="org.apache.solr.schema.FloatPointField" docValues="true" omitNorms="true"
+ positionIncrementGap="0"/>
+ <fieldType name="double" class="org.apache.solr.schema.DoublePointField" docValues="true" omitNorms="true"
+ positionIncrementGap="0"/>
+ <fieldType name="date" class="org.apache.solr.schema.DatePointField" docValues="true" omitNorms="true"
+ positionIncrementGap="0"/>
+ <fieldType name="text" class="solr.TextField">
+ <analyzer>
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.LowerCaseFilterFactory"/>
+ </analyzer>
+ </fieldType>
- <!-- for versioning -->
- <field name="_version_" type="long" indexed="true" stored="true"/>
- <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
- <field name="id" type="string" indexed="true" stored="true"/>
- <field name="text" type="text" indexed="true" stored="false"/>
- <dynamicField name="*_s" type="string" indexed="true" stored="true" />
- <dynamicField name="*_i" type="int" indexed="false" stored="false" />
- <uniqueKey>id</uniqueKey>
+ <!-- for versioning -->
+ <field name="_version_" type="long" indexed="true" stored="true"/>
+ <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
+ <field name="id" type="string" indexed="true" stored="true"/>
+ <field name="text" type="text" indexed="true" stored="false"/>
+
+ <dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
+ <dynamicField name="*_s" type="string" indexed="true" stored="false"/>
+ <dynamicField name="*_t" type="text" indexed="true" stored="false"/>
+ <dynamicField name="*_i" type="int" indexed="false" stored="false"/>
+ <dynamicField name="*_l" type="long" indexed="false" stored="false"/>
+ <dynamicField name="*_f" type="float" indexed="false" stored="false"/>
+ <dynamicField name="*_d" type="double" indexed="false" stored="false"/>
+ <dynamicField name="*_dt" type="date" indexed="false" stored="false"/>
+
+ <uniqueKey>id</uniqueKey>
</schema>
diff --git a/solr/benchmark/src/resources/configs/cloud-minimal/conf/solrconfig.xml b/solr/benchmark/src/resources/configs/cloud-minimal/conf/solrconfig.xml
index 9ddb072..2b8090f 100644
--- a/solr/benchmark/src/resources/configs/cloud-minimal/conf/solrconfig.xml
+++ b/solr/benchmark/src/resources/configs/cloud-minimal/conf/solrconfig.xml
@@ -24,13 +24,13 @@
<dataDir>${solr.data.dir:}</dataDir>
<directoryFactory name="DirectoryFactory"
- class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
+ class="${directoryFactory:solr.NRTCachingDirectoryFactory}"/>
<schemaFactory class="ClassicIndexSchemaFactory"/>
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
<indexConfig>
- <mergePolicyFactory class="org.apache.solr.index.TieredMergePolicyFactory">
+ <mergePolicyFactory class="${mergePolicyFactory:org.apache.solr.index.TieredMergePolicyFactory}">
<int name="maxMergeAtOnce">${maxMergeAtOnce:10}</int>
<int name="segmentsPerTier">${segmentsPerTier:10}</int>
<double name="noCFSRatio">${noCFSRatio:.1}</double>
@@ -38,7 +38,7 @@
<useCompoundFile>${useCompoundFile:true}</useCompoundFile>
- <ramBufferSizeMB>${solr.ramBufferSizeMB:100}</ramBufferSizeMB>
+ <ramBufferSizeMB>${ramBufferSizeMB:100}</ramBufferSizeMB>
<!-- <ramPerThreadHardLimitMB>60</ramPerThreadHardLimitMB> -->
<!-- <mergeScheduler class="org.apache.lucene.index.ConcurrentMergeScheduler">
@@ -54,20 +54,20 @@
use the single process lockType for speed - but tests that explicitly need
to vary the lockType canset it as needed.
-->
- <lockType>${solr.lockType:single}</lockType>
+ <lockType>${lockType:single}</lockType>
- <infoStream>${solr.tests.infostream:false}</infoStream>
+ <infoStream>${infostream:false}</infoStream>
</indexConfig>
<updateHandler class="solr.DirectUpdateHandler2">
<commitWithin>
- <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
+ <softCommit>${commitwithin.softcommit:true}</softCommit>
</commitWithin>
<autoCommit>
- <maxTime>${solr.autoCommit.maxTime:60000}</maxTime>
+ <maxTime>${autoCommit.maxTime:60000}</maxTime>
</autoCommit>
- <updateLog class="${solr.ulog:solr.UpdateLog}" enable="${enable.update.log:true}"/>
+ <updateLog class="${ulog:solr.UpdateLog}" enable="${enable.update.log:true}"/>
</updateHandler>
<requestHandler name="/select" class="solr.SearchHandler">
@@ -81,25 +81,25 @@
<query>
<queryResultCache
- enabled ="${queryResultCache.enabled:false}"
+ enabled="${queryResultCache.enabled:false}"
class="${queryResultCache.class:solr.LRUCache}"
size="${queryResultCache.size:0}"
initialSize="${queryResultCache.initialSize:0}"
autowarmCount="${queryResultCache.autowarmCount:0}"/>
- <documentCache
- enabled ="${documentCache.enabled:false}"
- class="${documentCache.class:solr.LRUCache}"
- size="${documentCache.size:0}"
- initialSize="${documentCache.initialSize:0}"
- autowarmCount="${documentCache.autowarmCount:0}"/>
- <filterCache
- enabled ="${filterCache.enabled:false}"
- class="${filterCache.class:solr.LRUCache}"
- size="${filterCache.size:0}"
- initialSize="${filterCache.initialSize:0}"
- autowarmCount="${filterCache.autowarmCount:0}"/>
+ <documentCache
+ enabled="${documentCache.enabled:false}"
+ class="${documentCache.class:solr.LRUCache}"
+ size="${documentCache.size:0}"
+ initialSize="${documentCache.initialSize:0}"
+ autowarmCount="${documentCache.autowarmCount:0}"/>
+ <filterCache
+ enabled="${filterCache.enabled:false}"
+ class="${filterCache.class:solr.LRUCache}"
+ size="${filterCache.size:0}"
+ initialSize="${filterCache.initialSize:0}"
+ autowarmCount="${filterCache.autowarmCount:0}"/>
<cache name="myPerSegmentCache"
- enabled ="${myPerSegmentCache.enabled:false}"
+ enabled="${myPerSegmentCache.enabled:false}"
class="${myPerSegmentCache.class:solr.LRUCache}"
size="${myPerSegmentCache.size:0}"
initialSize="${myPerSegmentCache.initialSize:0}"
diff --git a/solr/benchmark/src/resources/words.txt b/solr/benchmark/src/resources/words.txt
new file mode 100644
index 0000000..235a020
--- /dev/null
+++ b/solr/benchmark/src/resources/words.txt
@@ -0,0 +1,466550 @@
+2
+1080
+&c
+10-point
+10th
+11-point
+12-point
+16-point
+18-point
+1st
+2,4,5-t
+2,4-d
+20-point
+2D
+2nd
+30-30
+3D
+3-D
+3M
+3rd
+48-point
+4-D
+4GL
+4H
+4th
+5-point
+5-T
+5th
+6-point
+6th
+7-point
+7th
+8-point
+8th
+9-point
+9th
+a
+a'
+a-
+A&M
+A&P
+A.
+A.A.A.
+A.B.
+A.B.A.
+A.C.
+A.D.
+A.D.C.
+A.F.
+A.F.A.M.
+A.G.
+A.H.
+A.I.
+A.I.A.
+A.I.D.
+A.L.
+A.L.P.
+A.M.
+A.M.A.
+A.M.D.G.
+A.N.
+a.p.
+a.r.
+A.R.C.S.
+A.U.
+A.U.C.
+A.V.
+a.w.
+A.W.O.L.
+A/C
+A/F
+A/O
+A/P
+A/V
+A1
+A-1
+A4
+A5
+AA
+AAA
+AAAA
+AAAAAA
+AAAL
+AAAS
+Aaberg
+Aachen
+AAE
+AAEE
+AAF
+AAG
+aah
+aahed
+aahing
+aahs
+AAII
+aal
+Aalborg
+Aalesund
+aalii
+aaliis
+aals
+Aalst
+Aalto
+AAM
+AAMSI
+Aandahl
+A-and-R
+Aani
+AAO
+AAP
+AAPSS
+Aaqbiye
+Aar
+Aara
+Aarau
+AARC
+aardvark
+aardvarks
+aardwolf
+aardwolves
+Aaren
+Aargau
+aargh
+Aarhus
+Aarika
+Aaron
+Aaronic
+Aaronical
+Aaronite
+Aaronitic
+Aaron's-beard
+Aaronsburg
+Aaronson
+AARP
+aarrgh
+aarrghh
+Aaru
+AAS
+A'asia
+aasvogel
+aasvogels
+AAU
+AAUP
+AAUW
+AAVSO
+AAX
+A-axes
+A-axis
+AB
+ab-
+ABA
+Ababa
+Ababdeh
+Ababua
+abac
+abaca
+abacay
+abacas
+abacate
+abacaxi
+abaci
+abacinate
+abacination
+abacisci
+abaciscus
+abacist
+aback
+abacli
+Abaco
+abacot
+abacterial
+abactinal
+abactinally
+abaction
+abactor
+abaculi
+abaculus
+abacus
+abacuses
+Abad
+abada
+Abadan
+Abaddon
+abadejo
+abadengo
+abadia
+Abadite
+abaff
+abaft
+Abagael
+Abagail
+Abagtha
+abay
+abayah
+Abailard
+abaisance
+abaised
+abaiser
+abaisse
+abaissed
+abaka
+Abakan
+abakas
+Abakumov
+abalation
+abalienate
+abalienated
+abalienating
+abalienation
+abalone
+abalones
+Abama
+abamp
+abampere
+abamperes
+abamps
+Abana
+aband
+abandon
+abandonable
+abandoned
+abandonedly
+abandonee
+abandoner
+abandoners
+abandoning
+abandonment
+abandonments
+abandons
+abandum
+abanet
+abanga
+Abanic
+abannition
+Abantes
+abapical
+abaptiston
+abaptistum
+Abarambo
+Abarbarea
+Abaris
+abarthrosis
+abarticular
+abarticulation
+Abas
+abase
+abased
+abasedly
+abasedness
+abasement
+abasements
+abaser
+abasers
+abases
+Abasgi
+abash
+abashed
+abashedly
+abashedness
+abashes
+abashing
+abashless
+abashlessly
+abashment
+abashments
+abasia
+abasias
+abasic
+abasing
+abasio
+abask
+abassi
+Abassieh
+Abassin
+abastard
+abastardize
+abastral
+abatable
+abatage
+Abate
+abated
+abatement
+abatements
+abater
+abaters
+abates
+abatic
+abating
+abatis
+abatised
+abatises
+abatjour
+abatjours
+abaton
+abator
+abators
+ABATS
+abattage
+abattis
+abattised
+abattises
+abattoir
+abattoirs
+abattu
+abattue
+Abatua
+abature
+abaue
+abave
+abaxial
+abaxile
+abaze
+abb
+Abba
+abbacy
+abbacies
+abbacomes
+Abbadide
+Abbai
+abbaye
+abbandono
+abbas
+abbasi
+Abbasid
+abbassi
+Abbassid
+Abbasside
+Abbate
+abbatial
+abbatical
+abbatie
+Abbe
+Abbey
+abbeys
+abbey's
+abbeystead
+abbeystede
+abbes
+abbess
+abbesses
+abbest
+Abbevilean
+Abbeville
+Abbevillian
+Abbi
+Abby
+Abbie
+Abbye
+Abbyville
+abboccato
+abbogada
+Abbot
+abbotcy
+abbotcies
+abbotnullius
+abbotric
+abbots
+abbot's
+Abbotsen
+Abbotsford
+abbotship
+abbotships
+Abbotson
+Abbotsun
+Abbott
+Abbottson
+Abbottstown
+Abboud
+abbozzo
+ABBR
+abbrev
+abbreviatable
+abbreviate
+abbreviated
+abbreviately
+abbreviates
+abbreviating
+abbreviation
+abbreviations
+abbreviator
+abbreviatory
+abbreviators
+abbreviature
+abbroachment
+ABC
+abcess
+abcissa
+abcoulomb
+ABCs
+abd
+abdal
+abdali
+abdaria
+abdat
+Abdel
+Abd-el-Kadir
+Abd-el-Krim
+Abdella
+Abderhalden
+Abderian
+Abderite
+Abderus
+abdest
+Abdias
+abdicable
+abdicant
+abdicate
+abdicated
+abdicates
+abdicating
+abdication
+abdications
+abdicative
+abdicator
+Abdiel
+abditive
+abditory
+abdom
+abdomen
+abdomens
+abdomen's
+abdomina
+abdominal
+Abdominales
+abdominalia
+abdominalian
+abdominally
+abdominals
+abdominoanterior
+abdominocardiac
+abdominocentesis
+abdominocystic
+abdominogenital
+abdominohysterectomy
+abdominohysterotomy
+abdominoposterior
+abdominoscope
+abdominoscopy
+abdominothoracic
+abdominous
+abdomino-uterotomy
+abdominovaginal
+abdominovesical
+Abdon
+Abdu
+abduce
+abduced
+abducens
+abducent
+abducentes
+abduces
+abducing
+abduct
+abducted
+abducting
+abduction
+abductions
+abduction's
+abductor
+abductores
+abductors
+abductor's
+abducts
+Abdul
+Abdul-Aziz
+Abdul-baha
+Abdulla
+Abe
+a-be
+abeam
+abear
+abearance
+Abebi
+abecedaire
+abecedary
+abecedaria
+abecedarian
+abecedarians
+abecedaries
+abecedarium
+abecedarius
+abed
+abede
+abedge
+Abednego
+abegge
+Abey
+abeyance
+abeyances
+abeyancy
+abeyancies
+abeyant
+abeigh
+ABEL
+Abelard
+abele
+abeles
+Abelia
+Abelian
+Abelicea
+Abelite
+Abell
+Abelmoschus
+abelmosk
+abelmosks
+abelmusk
+Abelonian
+Abelson
+abeltree
+Abencerrages
+abend
+abends
+Abenezra
+abenteric
+Abeokuta
+abepithymia
+ABEPP
+Abercromby
+Abercrombie
+Aberdare
+aberdavine
+Aberdeen
+Aberdeenshire
+aberdevine
+Aberdonian
+aberduvine
+Aberfan
+Aberglaube
+Aberia
+Aberystwyth
+Abernant
+Abernathy
+abernethy
+Abernon
+aberr
+aberrance
+aberrancy
+aberrancies
+aberrant
+aberrantly
+aberrants
+aberrate
+aberrated
+aberrating
+aberration
+aberrational
+aberrations
+aberrative
+aberrator
+aberrometer
+aberroscope
+Abert
+aberuncate
+aberuncator
+abesse
+abessive
+abet
+abetment
+abetments
+abets
+abettal
+abettals
+abetted
+abetter
+abetters
+abetting
+abettor
+abettors
+Abeu
+abevacuation
+abfarad
+abfarads
+ABFM
+Abgatha
+ABHC
+abhenry
+abhenries
+abhenrys
+abhinaya
+abhiseka
+abhominable
+abhor
+abhorred
+abhorrence
+abhorrences
+abhorrency
+abhorrent
+abhorrently
+abhorrer
+abhorrers
+abhorrible
+abhorring
+abhors
+Abhorson
+ABI
+aby
+Abia
+Abiathar
+Abib
+abichite
+abidal
+abidance
+abidances
+abidden
+abide
+abided
+abider
+abiders
+abides
+abidi
+abiding
+abidingly
+abidingness
+Abidjan
+Abydos
+Abie
+abye
+abied
+abyed
+abiegh
+abience
+abient
+Abies
+abyes
+abietate
+abietene
+abietic
+abietin
+Abietineae
+abietineous
+abietinic
+abietite
+Abiezer
+Abigael
+Abigail
+abigails
+abigailship
+Abigale
+abigeat
+abigei
+abigeus
+Abihu
+abying
+Abijah
+Abyla
+abilao
+Abilene
+abiliment
+Abilyne
+abilitable
+ability
+abilities
+ability's
+abilla
+abilo
+abime
+Abimelech
+Abineri
+Abingdon
+Abinger
+Abington
+Abinoam
+Abinoem
+abintestate
+abiogeneses
+abiogenesis
+abiogenesist
+abiogenetic
+abiogenetical
+abiogenetically
+abiogeny
+abiogenist
+abiogenous
+abiology
+abiological
+abiologically
+abioses
+abiosis
+abiotic
+abiotical
+abiotically
+abiotrophy
+abiotrophic
+Abipon
+Abiquiu
+abir
+abirritant
+abirritate
+abirritated
+abirritating
+abirritation
+abirritative
+abys
+Abisag
+Abisha
+Abishag
+Abisia
+abysm
+abysmal
+abysmally
+abysms
+Abyss
+abyssa
+abyssal
+abysses
+Abyssinia
+Abyssinian
+abyssinians
+abyssobenthonic
+abyssolith
+abyssopelagic
+abyss's
+abyssus
+abiston
+abit
+Abitibi
+Abiu
+abiuret
+Abixah
+abject
+abjectedness
+abjection
+abjections
+abjective
+abjectly
+abjectness
+abjectnesses
+abjoint
+abjudge
+abjudged
+abjudging
+abjudicate
+abjudicated
+abjudicating
+abjudication
+abjudicator
+abjugate
+abjunct
+abjunction
+abjunctive
+abjuration
+abjurations
+abjuratory
+abjure
+abjured
+abjurement
+abjurer
+abjurers
+abjures
+abjuring
+abkar
+abkari
+abkary
+Abkhas
+Abkhasia
+Abkhasian
+Abkhaz
+Abkhazia
+Abkhazian
+abl
+abl.
+ablach
+ablactate
+ablactated
+ablactating
+ablactation
+ablaqueate
+ablare
+A-blast
+ablastemic
+ablastin
+ablastous
+ablate
+ablated
+ablates
+ablating
+ablation
+ablations
+ablatitious
+ablatival
+ablative
+ablatively
+ablatives
+ablator
+ablaut
+ablauts
+ablaze
+able
+able-bodied
+able-bodiedness
+ableeze
+ablegate
+ablegates
+ablegation
+able-minded
+able-mindedness
+ablend
+ableness
+ablepharia
+ablepharon
+ablepharous
+Ablepharus
+ablepsy
+ablepsia
+ableptical
+ableptically
+abler
+ables
+ablesse
+ablest
+ablet
+ablewhackets
+ably
+ablings
+ablins
+ablock
+abloom
+ablow
+ABLS
+ablude
+abluent
+abluents
+ablush
+ablute
+abluted
+ablution
+ablutionary
+ablutions
+abluvion
+ABM
+abmho
+abmhos
+abmodality
+abmodalities
+abn
+Abnaki
+Abnakis
+abnegate
+abnegated
+abnegates
+abnegating
+abnegation
+abnegations
+abnegative
+abnegator
+abnegators
+Abner
+abnerval
+abnet
+abneural
+abnormal
+abnormalcy
+abnormalcies
+abnormalise
+abnormalised
+abnormalising
+abnormalism
+abnormalist
+abnormality
+abnormalities
+abnormalize
+abnormalized
+abnormalizing
+abnormally
+abnormalness
+abnormals
+abnormity
+abnormities
+abnormous
+abnumerable
+Abo
+aboard
+aboardage
+Abobra
+abococket
+abodah
+abode
+aboded
+abodement
+abodes
+abode's
+abody
+aboding
+abogado
+abogados
+abohm
+abohms
+aboideau
+aboideaus
+aboideaux
+aboil
+aboiteau
+aboiteaus
+aboiteaux
+abolete
+abolish
+abolishable
+abolished
+abolisher
+abolishers
+abolishes
+abolishing
+abolishment
+abolishments
+abolishment's
+abolition
+abolitionary
+abolitionise
+abolitionised
+abolitionising
+abolitionism
+abolitionist
+abolitionists
+abolitionize
+abolitionized
+abolitionizing
+abolitions
+abolla
+abollae
+aboma
+abomas
+abomasa
+abomasal
+abomasi
+abomasum
+abomasus
+abomasusi
+A-bomb
+abominability
+abominable
+abominableness
+abominably
+abominate
+abominated
+abominates
+abominating
+abomination
+abominations
+abominator
+abominators
+abomine
+abondance
+Abongo
+abonne
+abonnement
+aboon
+aborad
+aboral
+aborally
+abord
+Aboriginal
+aboriginality
+aboriginally
+aboriginals
+aboriginary
+Aborigine
+aborigines
+aborigine's
+Abor-miri
+Aborn
+aborning
+a-borning
+aborsement
+aborsive
+abort
+aborted
+aborter
+aborters
+aborticide
+abortient
+abortifacient
+abortin
+aborting
+abortion
+abortional
+abortionist
+abortionists
+abortions
+abortion's
+abortive
+abortively
+abortiveness
+abortogenic
+aborts
+abortus
+abortuses
+abos
+abote
+Abott
+abouchement
+aboudikro
+abought
+Aboukir
+aboulia
+aboulias
+aboulic
+abound
+abounded
+abounder
+abounding
+aboundingly
+abounds
+Abourezk
+about
+about-face
+about-faced
+about-facing
+abouts
+about-ship
+about-shipped
+about-shipping
+about-sledge
+about-turn
+above
+aboveboard
+above-board
+above-cited
+abovedeck
+above-found
+above-given
+aboveground
+abovementioned
+above-mentioned
+above-named
+aboveproof
+above-quoted
+above-reported
+aboves
+abovesaid
+above-said
+abovestairs
+above-water
+above-written
+abow
+abox
+Abp
+ABPC
+Abqaiq
+abr
+abr.
+Abra
+abracadabra
+abrachia
+abrachias
+abradable
+abradant
+abradants
+abrade
+abraded
+abrader
+abraders
+abrades
+abrading
+Abraham
+Abrahamic
+Abrahamidae
+Abrahamite
+Abrahamitic
+Abraham-man
+Abrahams
+Abrahamsen
+Abrahan
+abray
+abraid
+Abram
+Abramis
+Abramo
+Abrams
+Abramson
+Abran
+abranchial
+abranchialism
+abranchian
+Abranchiata
+abranchiate
+abranchious
+abrasax
+abrase
+abrased
+abraser
+abrash
+abrasing
+abrasiometer
+abrasion
+abrasions
+abrasion's
+abrasive
+abrasively
+abrasiveness
+abrasivenesses
+abrasives
+abrastol
+abraum
+abraxas
+abrazite
+abrazitic
+abrazo
+abrazos
+abreact
+abreacted
+abreacting
+abreaction
+abreactions
+abreacts
+abreast
+abreed
+abrege
+abreid
+abrenounce
+abrenunciate
+abrenunciation
+abreption
+abret
+abreuvoir
+abri
+abrico
+abricock
+abricot
+abridgable
+abridge
+abridgeable
+abridged
+abridgedly
+abridgement
+abridgements
+abridger
+abridgers
+abridges
+abridging
+abridgment
+abridgments
+abrim
+abrin
+abrine
+abris
+abristle
+abroach
+abroad
+Abrocoma
+abrocome
+abrogable
+abrogate
+abrogated
+abrogates
+abrogating
+abrogation
+abrogations
+abrogative
+abrogator
+abrogators
+Abroma
+Abroms
+Abronia
+abrood
+abrook
+abrosia
+abrosias
+abrotanum
+abrotin
+abrotine
+abrupt
+abruptedly
+abrupter
+abruptest
+abruptio
+abruption
+abruptiones
+abruptly
+abruptness
+Abrus
+Abruzzi
+ABS
+abs-
+Absa
+Absalom
+absampere
+Absaraka
+Absaroka
+Absarokee
+absarokite
+ABSBH
+abscam
+abscess
+abscessed
+abscesses
+abscessing
+abscession
+abscessroot
+abscind
+abscise
+abscised
+abscises
+abscisin
+abscising
+abscisins
+abscision
+absciss
+abscissa
+abscissae
+abscissas
+abscissa's
+abscisse
+abscissin
+abscission
+abscissions
+absconce
+abscond
+absconded
+abscondedly
+abscondence
+absconder
+absconders
+absconding
+absconds
+absconsa
+abscoulomb
+abscound
+Absecon
+absee
+absey
+abseil
+abseiled
+abseiling
+abseils
+absence
+absences
+absence's
+absent
+absentation
+absented
+absentee
+absenteeism
+absentees
+absentee's
+absenteeship
+absenter
+absenters
+absentia
+absenting
+absently
+absentment
+absentminded
+absent-minded
+absentmindedly
+absent-mindedly
+absentmindedness
+absent-mindedness
+absentmindednesses
+absentness
+absents
+absfarad
+abshenry
+Abshier
+Absi
+absinth
+absinthe
+absinthes
+absinthial
+absinthian
+absinthiate
+absinthiated
+absinthiating
+absinthic
+absinthiin
+absinthin
+absinthine
+absinthism
+absinthismic
+absinthium
+absinthol
+absinthole
+absinths
+Absyrtus
+absis
+absist
+absistos
+absit
+absmho
+absohm
+absoil
+absolent
+Absolute
+absolutely
+absoluteness
+absoluter
+absolutes
+absolutest
+absolution
+absolutions
+absolutism
+absolutist
+absolutista
+absolutistic
+absolutistically
+absolutists
+absolutive
+absolutization
+absolutize
+absolutory
+absolvable
+absolvatory
+absolve
+absolved
+absolvent
+absolver
+absolvers
+absolves
+absolving
+absolvitor
+absolvitory
+absonant
+absonous
+absorb
+absorbability
+absorbable
+absorbance
+absorbancy
+absorbant
+absorbed
+absorbedly
+absorbedness
+absorbefacient
+absorbency
+absorbencies
+absorbent
+absorbents
+absorber
+absorbers
+absorbing
+absorbingly
+absorbition
+absorbs
+absorbtion
+absorpt
+absorptance
+absorptiometer
+absorptiometric
+absorption
+absorptional
+absorptions
+absorption's
+absorptive
+absorptively
+absorptiveness
+absorptivity
+absquatulate
+absquatulation
+abstain
+abstained
+abstainer
+abstainers
+abstaining
+abstainment
+abstains
+abstemious
+abstemiously
+abstemiousness
+abstention
+abstentionism
+abstentionist
+abstentions
+abstentious
+absterge
+absterged
+abstergent
+absterges
+absterging
+absterse
+abstersion
+abstersive
+abstersiveness
+abstertion
+abstinence
+abstinences
+abstinency
+abstinent
+abstinential
+abstinently
+abstort
+abstr
+abstract
+abstractable
+abstracted
+abstractedly
+abstractedness
+abstracter
+abstracters
+abstractest
+abstracting
+abstraction
+abstractional
+abstractionism
+abstractionist
+abstractionists
+abstractions
+abstraction's
+abstractitious
+abstractive
+abstractively
+abstractiveness
+abstractly
+abstractness
+abstractnesses
+abstractor
+abstractors
+abstractor's
+abstracts
+abstrahent
+abstrict
+abstricted
+abstricting
+abstriction
+abstricts
+abstrude
+abstruse
+abstrusely
+abstruseness
+abstrusenesses
+abstruser
+abstrusest
+abstrusion
+abstrusity
+abstrusities
+absume
+absumption
+absurd
+absurder
+absurdest
+absurdism
+absurdist
+absurdity
+absurdities
+absurdity's
+absurdly
+absurdness
+absurds
+absurdum
+absvolt
+abt
+abterminal
+abthain
+abthainry
+abthainrie
+abthanage
+abtruse
+Abu
+abubble
+Abu-Bekr
+Abucay
+abucco
+abuilding
+Abukir
+abuleia
+Abulfeda
+abulia
+abulias
+abulic
+abulyeit
+abulomania
+abumbral
+abumbrellar
+Abuna
+abundance
+abundances
+abundancy
+abundant
+Abundantia
+abundantly
+abune
+abura
+aburabozu
+aburagiri
+aburban
+Abury
+aburst
+aburton
+abusable
+abusage
+abuse
+abused
+abusedly
+abusee
+abuseful
+abusefully
+abusefulness
+abuser
+abusers
+abuses
+abush
+abusing
+abusion
+abusious
+abusive
+abusively
+abusiveness
+abusivenesses
+abut
+Abuta
+Abutilon
+abutilons
+abutment
+abutments
+abuts
+abuttal
+abuttals
+abutted
+abutter
+abutters
+abutter's
+abutting
+abuzz
+abv
+abvolt
+abvolts
+abwab
+abwatt
+abwatts
+ac
+ac-
+a-c
+AC/DC
+ACAA
+Acacallis
+acacatechin
+acacatechol
+Acacea
+Acaceae
+acacetin
+Acacia
+Acacian
+acacias
+acaciin
+acacin
+acacine
+acad
+academe
+academes
+Academy
+academia
+academial
+academian
+academias
+Academic
+academical
+academically
+academicals
+academician
+academicians
+academicianship
+academicism
+academics
+academie
+academies
+academy's
+academise
+academised
+academising
+academism
+academist
+academite
+academization
+academize
+academized
+academizing
+Academus
+Acadia
+acadialite
+Acadian
+Acadie
+Acaena
+acajou
+acajous
+acal
+acalculia
+acale
+acaleph
+Acalepha
+Acalephae
+acalephan
+acalephe
+acalephes
+acalephoid
+acalephs
+Acalia
+acalycal
+acalycine
+acalycinous
+acalyculate
+Acalypha
+Acalypterae
+Acalyptrata
+Acalyptratae
+acalyptrate
+Acamar
+Acamas
+Acampo
+acampsia
+acana
+acanaceous
+acanonical
+acanth
+acanth-
+acantha
+Acanthaceae
+acanthaceous
+acanthad
+Acantharia
+acanthi
+Acanthia
+acanthial
+acanthin
+acanthine
+acanthion
+acanthite
+acantho-
+acanthocarpous
+Acanthocephala
+acanthocephalan
+Acanthocephali
+acanthocephalous
+Acanthocereus
+acanthocladous
+Acanthodea
+acanthodean
+Acanthodei
+Acanthodes
+acanthodian
+Acanthodidae
+Acanthodii
+Acanthodini
+acanthoid
+Acantholimon
+acantholysis
+acanthology
+acanthological
+acanthoma
+acanthomas
+Acanthomeridae
+acanthon
+Acanthopanax
+Acanthophis
+acanthophorous
+acanthopod
+acanthopodous
+acanthopomatous
+acanthopore
+acanthopteran
+Acanthopteri
+acanthopterygian
+Acanthopterygii
+acanthopterous
+acanthoses
+acanthosis
+acanthotic
+acanthous
+Acanthuridae
+Acanthurus
+acanthus
+acanthuses
+acanthuthi
+acapnia
+acapnial
+acapnias
+acappella
+acapsular
+acapu
+Acapulco
+acara
+Acarapis
+acarari
+acardia
+acardiac
+acardite
+acari
+acarian
+acariasis
+acariatre
+acaricidal
+acaricide
+acarid
+Acarida
+acaridae
+acaridan
+acaridans
+Acaridea
+acaridean
+acaridomatia
+acaridomatium
+acarids
+acariform
+Acarina
+acarine
+acarines
+acarinosis
+Acarnan
+acarocecidia
+acarocecidium
+acarodermatitis
+acaroid
+acarol
+acarology
+acarologist
+acarophilous
+acarophobia
+acarotoxic
+acarpellous
+acarpelous
+acarpous
+Acarus
+ACAS
+acast
+Acastus
+acatalectic
+acatalepsy
+acatalepsia
+acataleptic
+acatallactic
+acatamathesia
+acataphasia
+acataposis
+acatastasia
+acatastatic
+acate
+acategorical
+acater
+acatery
+acates
+acatharsy
+acatharsia
+acatholic
+acaudal
+acaudate
+acaudelescent
+acaulescence
+acaulescent
+acauline
+acaulose
+acaulous
+ACAWS
+ACB
+ACBL
+ACC
+acc.
+acca
+accable
+Accad
+accademia
+Accadian
+Accalia
+acce
+accede
+acceded
+accedence
+acceder
+acceders
+accedes
+acceding
+accel
+accel.
+accelerable
+accelerando
+accelerant
+accelerate
+accelerated
+acceleratedly
+accelerates
+accelerating
+acceleratingly
+acceleration
+accelerations
+accelerative
+accelerator
+acceleratory
+accelerators
+accelerograph
+accelerometer
+accelerometers
+accelerometer's
+accend
+accendibility
+accendible
+accensed
+accension
+accensor
+accent
+accented
+accenting
+accentless
+accentor
+accentors
+accents
+accentuable
+accentual
+accentuality
+accentually
+accentuate
+accentuated
+accentuates
+accentuating
+accentuation
+accentuations
+accentuator
+accentus
+accept
+acceptability
+acceptabilities
+acceptable
+acceptableness
+acceptably
+acceptance
+acceptances
+acceptance's
+acceptancy
+acceptancies
+acceptant
+acceptation
+acceptavit
+accepted
+acceptedly
+acceptee
+acceptees
+accepter
+accepters
+acceptilate
+acceptilated
+acceptilating
+acceptilation
+accepting
+acceptingly
+acceptingness
+acception
+acceptive
+acceptor
+acceptors
+acceptor's
+acceptress
+accepts
+accerse
+accersition
+accersitor
+access
+accessability
+accessable
+accessary
+accessaries
+accessarily
+accessariness
+accessaryship
+accessed
+accesses
+accessibility
+accessibilities
+accessible
+accessibleness
+accessibly
+accessing
+accession
+accessional
+accessioned
+accessioner
+accessioning
+accessions
+accession's
+accessit
+accessive
+accessively
+accessless
+accessor
+accessory
+accessorial
+accessories
+accessorii
+accessorily
+accessoriness
+accessory's
+accessorius
+accessoriusorii
+accessorize
+accessorized
+accessorizing
+accessors
+accessor's
+acciaccatura
+acciaccaturas
+acciaccature
+accidence
+accidency
+accidencies
+accident
+accidental
+accidentalism
+accidentalist
+accidentality
+accidentally
+accidentalness
+accidentals
+accidentary
+accidentarily
+accidented
+accidential
+accidentiality
+accidently
+accident-prone
+accidents
+accidia
+accidias
+accidie
+accidies
+accinge
+accinged
+accinging
+accipenser
+accipient
+Accipiter
+accipitral
+accipitrary
+Accipitres
+accipitrine
+accipter
+accise
+accismus
+accite
+Accius
+acclaim
+acclaimable
+acclaimed
+acclaimer
+acclaimers
+acclaiming
+acclaims
+acclamation
+acclamations
+acclamator
+acclamatory
+acclimatable
+acclimatation
+acclimate
+acclimated
+acclimatement
+acclimates
+acclimating
+acclimation
+acclimations
+acclimatisable
+acclimatisation
+acclimatise
+acclimatised
+acclimatiser
+acclimatising
+acclimatizable
+acclimatization
+acclimatizations
+acclimatize
+acclimatized
+acclimatizer
+acclimatizes
+acclimatizing
+acclimature
+acclinal
+acclinate
+acclivity
+acclivities
+acclivitous
+acclivous
+accloy
+accoast
+accoy
+accoyed
+accoying
+accoil
+Accokeek
+accolade
+accoladed
+accolades
+accolated
+accolent
+accoll
+accolle
+accolled
+accollee
+Accomac
+accombination
+accommodable
+accommodableness
+accommodate
+accommodated
+accommodately
+accommodateness
+accommodates
+accommodating
+accommodatingly
+accommodatingness
+accommodation
+accommodational
+accommodationist
+accommodations
+accommodative
+accommodatively
+accommodativeness
+accommodator
+accommodators
+accomodate
+accompanable
+accompany
+accompanied
+accompanier
+accompanies
+accompanying
+accompanyist
+accompaniment
+accompanimental
+accompaniments
+accompaniment's
+accompanist
+accompanists
+accompanist's
+accomplement
+accompletive
+accompli
+accomplice
+accomplices
+accompliceship
+accomplicity
+accomplis
+accomplish
+accomplishable
+accomplished
+accomplisher
+accomplishers
+accomplishes
+accomplishing
+accomplishment
+accomplishments
+accomplishment's
+accomplisht
+accompt
+accord
+accordable
+accordance
+accordances
+accordancy
+accordant
+accordantly
+accordatura
+accordaturas
+accordature
+accorded
+accorder
+accorders
+according
+accordingly
+accordion
+accordionist
+accordionists
+accordions
+accordion's
+accords
+accorporate
+accorporation
+accost
+accostable
+accosted
+accosting
+accosts
+accouche
+accouchement
+accouchements
+accoucheur
+accoucheurs
+accoucheuse
+accoucheuses
+accounsel
+account
+accountability
+accountabilities
+accountable
+accountableness
+accountably
+accountancy
+accountancies
+accountant
+accountants
+accountant's
+accountantship
+accounted
+accounter
+accounters
+accounting
+accountings
+accountment
+accountrement
+accounts
+accouple
+accouplement
+accourage
+accourt
+accouter
+accoutered
+accoutering
+accouterment
+accouterments
+accouters
+accoutre
+accoutred
+accoutrement
+accoutrements
+accoutres
+accoutring
+Accoville
+ACCRA
+accrease
+accredit
+accreditable
+accreditate
+accreditation
+accreditations
+accredited
+accreditee
+accrediting
+accreditment
+accredits
+accrementitial
+accrementition
+accresce
+accrescence
+accrescendi
+accrescendo
+accrescent
+accretal
+accrete
+accreted
+accretes
+accreting
+accretion
+accretionary
+accretions
+accretion's
+accretive
+accriminate
+Accrington
+accroach
+accroached
+accroaching
+accroachment
+accroides
+accruable
+accrual
+accruals
+accrue
+accrued
+accruement
+accruer
+accrues
+accruing
+ACCS
+ACCT
+acct.
+accts
+accubation
+accubita
+accubitum
+accubitus
+accueil
+accultural
+acculturate
+acculturated
+acculturates
+acculturating
+acculturation
+acculturational
+acculturationist
+acculturative
+acculturize
+acculturized
+acculturizing
+accum
+accumb
+accumbency
+accumbent
+accumber
+accumulable
+accumulate
+accumulated
+accumulates
+accumulating
+accumulation
+accumulations
+accumulativ
+accumulative
+accumulatively
+accumulativeness
+accumulator
+accumulators
+accumulator's
+accupy
+accur
+accuracy
+accuracies
+accurate
+accurately
+accurateness
+accuratenesses
+accurre
+accurse
+accursed
+accursedly
+accursedness
+accursing
+accurst
+accurtation
+accus
+accusable
+accusably
+accusal
+accusals
+accusant
+accusants
+accusation
+accusations
+accusation's
+accusatival
+accusative
+accusative-dative
+accusatively
+accusativeness
+accusatives
+accusator
+accusatory
+accusatorial
+accusatorially
+accusatrix
+accusatrixes
+accuse
+accused
+accuser
+accusers
+accuses
+accusing
+accusingly
+accusive
+accusor
+accustom
+accustomation
+accustomed
+accustomedly
+accustomedness
+accustoming
+accustomize
+accustomized
+accustomizing
+accustoms
+Accutron
+ACD
+ACDA
+AC-DC
+ACE
+acea
+aceacenaphthene
+aceae
+acean
+aceanthrene
+aceanthrenequinone
+acecaffin
+acecaffine
+aceconitic
+aced
+acedy
+acedia
+acediamin
+acediamine
+acedias
+acediast
+ace-high
+Acey
+acey-deucy
+aceite
+aceituna
+Aceldama
+aceldamas
+acellular
+Acemetae
+Acemetic
+acemila
+acenaphthene
+acenaphthenyl
+acenaphthylene
+acenesthesia
+acensuada
+acensuador
+acentric
+acentrous
+aceology
+aceologic
+aceous
+acephal
+Acephala
+acephalan
+Acephali
+acephalia
+Acephalina
+acephaline
+acephalism
+acephalist
+Acephalite
+acephalocyst
+acephalous
+acephalus
+acepots
+acequia
+acequiador
+acequias
+Acer
+Aceraceae
+aceraceous
+Acerae
+Acerata
+acerate
+acerated
+Acerates
+acerathere
+Aceratherium
+aceratosis
+acerb
+Acerbas
+acerbate
+acerbated
+acerbates
+acerbating
+acerber
+acerbest
+acerbic
+acerbically
+acerbity
+acerbityacerose
+acerbities
+acerbitude
+acerbly
+acerbophobia
+acerdol
+aceric
+acerin
+acerli
+acerola
+acerolas
+acerose
+acerous
+acerra
+acers
+acertannin
+acerval
+acervate
+acervately
+acervatim
+acervation
+acervative
+acervose
+acervuli
+acervuline
+acervulus
+aces
+ace's
+acescence
+acescency
+acescent
+acescents
+aceship
+Acesius
+acesodyne
+acesodynous
+Acessamenus
+Acestes
+acestoma
+acet-
+aceta
+acetable
+acetabula
+acetabular
+Acetabularia
+acetabuliferous
+acetabuliform
+acetabulous
+acetabulum
+acetabulums
+acetacetic
+acetal
+acetaldehydase
+acetaldehyde
+acetaldehydrase
+acetaldol
+acetalization
+acetalize
+acetals
+acetamid
+acetamide
+acetamidin
+acetamidine
+acetamido
+acetamids
+acetaminol
+Acetaminophen
+acetanilid
+acetanilide
+acetanion
+acetaniside
+acetanisidide
+acetanisidine
+acetannin
+acetary
+acetarious
+acetars
+acetarsone
+acetate
+acetated
+acetates
+acetation
+acetazolamide
+acetbromamide
+acetenyl
+Acetes
+acethydrazide
+acetiam
+acetic
+acetify
+acetification
+acetified
+acetifier
+acetifies
+acetifying
+acetyl
+acetylacetonates
+acetylacetone
+acetylamine
+acetylaminobenzene
+acetylaniline
+acetylasalicylic
+acetylate
+acetylated
+acetylating
+acetylation
+acetylative
+acetylator
+acetylbenzene
+acetylbenzoate
+acetylbenzoic
+acetylbiuret
+acetylcarbazole
+acetylcellulose
+acetylcholine
+acetylcholinesterase
+acetylcholinic
+acetylcyanide
+acetylenation
+acetylene
+acetylenediurein
+acetylenes
+acetylenic
+acetylenyl
+acetylenogen
+acetylfluoride
+acetylglycin
+acetylglycine
+acetylhydrazine
+acetylic
+acetylid
+acetylide
+acetyliodide
+acetylizable
+acetylization
+acetylize
+acetylized
+acetylizer
+acetylizing
+acetylmethylcarbinol
+acetylperoxide
+acetylphenylhydrazine
+acetylphenol
+acetylrosaniline
+acetyls
+acetylsalicylate
+acetylsalicylic
+acetylsalol
+acetyltannin
+acetylthymol
+acetyltropeine
+acetylurea
+acetimeter
+acetimetry
+acetimetric
+acetin
+acetine
+acetins
+acetite
+acetize
+acetla
+acetmethylanilide
+acetnaphthalide
+aceto-
+acetoacetanilide
+acetoacetate
+acetoacetic
+acetoamidophenol
+acetoarsenite
+Acetobacter
+acetobenzoic
+acetobromanilide
+acetochloral
+acetocinnamene
+acetoin
+acetol
+acetolysis
+acetolytic
+acetometer
+acetometry
+acetometric
+acetometrical
+acetometrically
+acetomorphin
+acetomorphine
+acetonaemia
+acetonaemic
+acetonaphthone
+acetonate
+acetonation
+acetone
+acetonemia
+acetonemic
+acetones
+acetonic
+acetonyl
+acetonylacetone
+acetonylidene
+acetonitrile
+acetonization
+acetonize
+acetonuria
+acetonurometer
+acetophenetide
+acetophenetidin
+acetophenetidine
+acetophenin
+acetophenine
+acetophenone
+acetopiperone
+acetopyrin
+acetopyrine
+acetosalicylic
+acetose
+acetosity
+acetosoluble
+acetostearin
+acetothienone
+acetotoluid
+acetotoluide
+acetotoluidine
+acetous
+acetoveratrone
+acetoxyl
+acetoxyls
+acetoxim
+acetoxime
+acetoxyphthalide
+acetphenetid
+acetphenetidin
+acetract
+acettoluide
+acetum
+aceturic
+ACF
+ACGI
+ac-globulin
+ACH
+Achab
+Achad
+Achaea
+Achaean
+Achaemenes
+Achaemenian
+Achaemenid
+Achaemenidae
+Achaemenides
+Achaemenidian
+Achaemenids
+achaenocarp
+Achaenodon
+Achaeta
+achaetous
+Achaeus
+achafe
+achage
+Achagua
+Achaia
+Achaian
+Achakzai
+achalasia
+Achamoth
+Achan
+Achango
+achape
+achaque
+achar
+acharya
+Achariaceae
+Achariaceous
+acharne
+acharnement
+Acharnians
+achate
+Achates
+Achatina
+Achatinella
+Achatinidae
+achatour
+Achaz
+ache
+acheat
+achech
+acheck
+ached
+acheer
+ACHEFT
+acheilary
+acheilia
+acheilous
+acheiria
+acheirous
+acheirus
+Achelous
+Achen
+achene
+achenes
+achenia
+achenial
+achenium
+achenocarp
+achenodia
+achenodium
+acher
+Acherman
+Achernar
+Acheron
+Acheronian
+Acherontic
+Acherontical
+aches
+Acheson
+achesoun
+achete
+Achetidae
+Acheulean
+Acheulian
+acheweed
+achy
+achier
+achiest
+achievability
+achievable
+achieve
+achieved
+achievement
+achievements
+achievement's
+achiever
+achievers
+achieves
+achieving
+ach-y-fi
+achigan
+achilary
+achylia
+Achill
+Achille
+Achillea
+Achillean
+achilleas
+Achilleid
+achillein
+achilleine
+Achilles
+Achillize
+achillobursitis
+achillodynia
+achilous
+achylous
+Achimaas
+achime
+Achimelech
+Achimenes
+achymia
+achymous
+Achinese
+achiness
+achinesses
+aching
+achingly
+achiote
+achiotes
+achira
+Achyranthes
+achirite
+Achyrodes
+Achish
+Achitophel
+achkan
+achlamydate
+Achlamydeae
+achlamydeous
+achlorhydria
+achlorhydric
+achlorophyllous
+achloropsia
+achluophobia
+Achmed
+Achmetha
+achoke
+acholia
+acholias
+acholic
+Acholoe
+acholous
+acholuria
+acholuric
+Achomawi
+achondrite
+achondritic
+achondroplasia
+achondroplastic
+achoo
+achor
+achordal
+Achordata
+achordate
+Achorion
+Achorn
+Achras
+achree
+achroacyte
+Achroanthes
+achrodextrin
+achrodextrinase
+achroglobin
+achroiocythaemia
+achroiocythemia
+achroite
+achroma
+achromacyte
+achromasia
+achromat
+achromat-
+achromate
+Achromatiaceae
+achromatic
+achromatically
+achromaticity
+achromatin
+achromatinic
+achromatisation
+achromatise
+achromatised
+achromatising
+achromatism
+Achromatium
+achromatizable
+achromatization
+achromatize
+achromatized
+achromatizing
+achromatocyte
+achromatolysis
+achromatope
+achromatophil
+achromatophile
+achromatophilia
+achromatophilic
+achromatopia
+achromatopsy
+achromatopsia
+achromatosis
+achromatous
+achromats
+achromaturia
+achromia
+achromic
+Achromycin
+Achromobacter
+Achromobacterieae
+achromoderma
+achromophilous
+achromotrichia
+achromous
+achronical
+achronychous
+achronism
+achroo-
+achroodextrin
+achroodextrinase
+achroous
+achropsia
+Achsah
+achtehalber
+achtel
+achtelthaler
+achter
+achterveld
+Achuas
+achuete
+acy
+acyanoblepsia
+acyanopsia
+acichlorid
+acichloride
+acyclic
+acyclically
+acicula
+aciculae
+acicular
+acicularity
+acicularly
+aciculas
+aciculate
+aciculated
+aciculum
+aciculums
+acid
+acidaemia
+Acidalium
+Acidanthera
+Acidaspis
+acid-binding
+acidemia
+acidemias
+acider
+acid-fast
+acid-fastness
+acid-forming
+acidhead
+acid-head
+acidheads
+acidy
+acidic
+acidiferous
+acidify
+acidifiable
+acidifiant
+acidific
+acidification
+acidified
+acidifier
+acidifiers
+acidifies
+acidifying
+acidyl
+acidimeter
+acidimetry
+acidimetric
+acidimetrical
+acidimetrically
+acidite
+acidity
+acidities
+acidize
+acidized
+acidizing
+acidly
+acidness
+acidnesses
+acidogenic
+acidoid
+acidolysis
+acidology
+acidometer
+acidometry
+acidophil
+acidophile
+acidophilic
+acidophilous
+acidophilus
+acidoproteolytic
+acidoses
+acidosis
+acidosteophyte
+acidotic
+acidproof
+acids
+acid-treat
+acidulant
+acidulate
+acidulated
+acidulates
+acidulating
+acidulation
+acidulent
+acidulous
+acidulously
+acidulousness
+aciduria
+acidurias
+aciduric
+Acie
+acier
+acierage
+Acieral
+acierate
+acierated
+acierates
+acierating
+acieration
+acies
+acyesis
+acyetic
+aciform
+acyl
+acylal
+acylamido
+acylamidobenzene
+acylamino
+acylase
+acylate
+acylated
+acylates
+acylating
+acylation
+aciliate
+aciliated
+Acilius
+acylogen
+acyloin
+acyloins
+acyloxy
+acyloxymethane
+acyls
+Acima
+acinaceous
+acinaces
+acinacifoliate
+acinacifolious
+acinaciform
+acinacious
+acinacity
+acinar
+acinary
+acinarious
+Acineta
+Acinetae
+acinetan
+Acinetaria
+acinetarian
+acinetic
+acinetiform
+Acinetina
+acinetinan
+acing
+acini
+acinic
+aciniform
+acinose
+acinotubular
+acinous
+acinuni
+acinus
+acious
+Acipenser
+Acipenseres
+acipenserid
+Acipenseridae
+acipenserine
+acipenseroid
+Acipenseroidei
+acyrology
+acyrological
+Acis
+acystia
+acitate
+acity
+aciurgy
+ACK
+ack-ack
+ackee
+ackees
+ackey
+ackeys
+Acker
+Ackerley
+Ackerly
+Ackerman
+Ackermanville
+Ackley
+Ackler
+ackman
+ackmen
+acknew
+acknow
+acknowing
+acknowledge
+acknowledgeable
+acknowledged
+acknowledgedly
+acknowledgement
+acknowledgements
+acknowledger
+acknowledgers
+acknowledges
+acknowledging
+acknowledgment
+acknowledgments
+acknowledgment's
+acknown
+ack-pirate
+ackton
+Ackworth
+ACL
+aclastic
+acle
+acleidian
+acleistocardia
+acleistous
+Aclemon
+aclydes
+aclidian
+aclinal
+aclinic
+aclys
+a-clock
+acloud
+ACLS
+ACLU
+ACM
+Acmaea
+Acmaeidae
+acmaesthesia
+acmatic
+acme
+acmes
+acmesthesia
+acmic
+Acmispon
+acmite
+Acmon
+acne
+acned
+acneform
+acneiform
+acnemia
+acnes
+Acnida
+acnodal
+acnode
+acnodes
+ACO
+acoasm
+acoasma
+a-coast
+Acocanthera
+acocantherin
+acock
+acockbill
+a-cock-bill
+a-cock-horse
+acocotl
+Acoela
+Acoelomata
+acoelomate
+acoelomatous
+Acoelomi
+acoelomous
+acoelous
+Acoemetae
+Acoemeti
+Acoemetic
+acoenaesthesia
+ACOF
... 464798 lines suppressed ...