Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-15428: Integrate the OpenJDK JMH micro benchmark framework for m… #214

Merged
merged 1 commit into from
Aug 10, 2021

Conversation

markrmiller
Copy link
Member

Draft stripped down PR

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 2 times, most recently from fd9a36a to 16b22d2 Compare July 13, 2021 16:31
@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 2 times, most recently from 611102e to 9297745 Compare July 14, 2021 16:18
@madrob
Copy link
Contributor

madrob commented Jul 14, 2021

Can you add a task for gradlew helpJmh describing the various flags and usages that developers should consider? Should be straightforward as adding a line to help.gradle and a new file under help/ directory.

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 5 times, most recently from 6b7735b to 24b086f Compare July 15, 2021 00:07
Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a review but have yet to try this, which I am anxious to do.

It seems there is an overarching issue relating to the benchmark running classes in test-framework yet not having a RandomizedTesting unit test runner and thus no random seed? Let's ping Dawid about this use-case; ehh? It'd be nice to explicitly initialize that framework without using the runner.

Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this capability embedded into the test-framework module instead of being a new module because we want to publish the foundation so other modules (including 3rd party) can use them? test-framework is about publishing utilities for other modules, so that's the only reason I can think of why this was added there. Otherwise, it seems a new module would be more appropriate.

@markrmiller
Copy link
Member Author

bq. Otherwise, it seems a new module would be more appropriate.

You don't back up that statement with any support, other than:

bq. test-framework is about publishing utilities for other modules

Which I'm not sure I agree with.

Initially, I will say its here for:

  • initial ease
  • overlap in code/use/idea - often a benchmark will share much with how it's unit test is created, use some of the same supporting classes. The unit tests help test correctness, the jmh benchmarks help test performance. The test-framwork modules has support / base classes for 'testing' (correctness, performance, etc).

I like to stack up reasons for going one way or another, but I see the test-framework module as the home of the testing framework, not "about publishing utilities for other modules", so not really adding to my calculus in that form.

I also thought there would be more that was shared initially though. And I've sense realized this could use more separation from the randomized testing framework layers and requirements, and the different needs do make reuse of items between benchmark and junit tests a bit less simple and with more of a need to make sure mini clusters and jettyrunners are happy outside of randomized runner stuff.

Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the move to its own module! It'll be nice to see them all easily as we learn to explore this capability -- it's more discoverable. It also appears to fit in better from a build/Gradle perspective than when it was embedded in solr-test-framework.

Comment on lines 224 to 226
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.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use Objects.checkIndex for this

Comment on lines 83 to 96
args '-jvmArgsPrepend', '-Dlog4j2.is.webapp=false'
args '-jvmArgsPrepend', '-Dlog4j2.garbagefreeThreadContextMap=true'
args '-jvmArgsPrepend', '-Dlog4j2.enableDirectEncoders=true'
args '-jvmArgsPrepend', '-Dlog4j2.enable.threadlocals=true'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these make their way into our standard deployment as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 11 times, most recently from 1ba7f6c to eb9339d Compare July 29, 2021 17:22

echo "gradle build done"

echo "running JMH with args: $@"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ShellCheck: Argument mixes string and array. Use * or separate argument.
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 3 times, most recently from 0e96672 to d4974fd Compare August 5, 2021 01:09

// dump Solr metrics
Path metricsResults =
Paths.get(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PATH_TRAVERSAL_IN: This API (java/nio/file/Paths.get(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;) reads a file whose location might be specified by user input (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Set<String> registryNames = metricsManager.registryNames();
for (String registryName : registryNames) {
MetricRegistry metricsRegisty = metricsManager.registry(registryName);
try (PrintStream ps = outputDirectory == null ? new NullPrintStream() : new PrintStream(new File(outputDirectory, registryName + "_" + fileName), StandardCharsets.UTF_8)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PATH_TRAVERSAL_IN: This API (java/io/File.(Ljava/io/File;Ljava/lang/String;)V) reads a file whose location might be specified by user input (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

SplittableRandom random = new SplittableRandom(seed);
if (fieldDef.getLength() > -1) {
return RandomStringUtils.random(
nextInt(fieldDef.getLength(), fieldDef.getLength(), random),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.Random) is predictable (details)
(at-me in a reply with help or ignore)

ThreadLocalRandom.current(), fieldDef.getLength(), fieldDef.getLength());
} else {
return TestUtil.randomRealisticUnicodeString(
ThreadLocalRandom.current(), 1, fieldDef.getMaxLength());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.concurrent.ThreadLocalRandom) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

}
this.maxCardinality = maxCardinality;
this.cardinalityStart =
ThreadLocalRandom.current().nextLong(0, Long.MAX_VALUE - maxCardinality);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.concurrent.ThreadLocalRandom) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

}

// set the seed used by ThreadLocalRandom
System.setProperty("randomSeed", Long.toString(new Random(seed).nextLong()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.Random) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore


QueryRequest queryRequest = new QueryRequest(new SolrQuery("q", "*:*", "rows", "1"));
queryRequest.setBasePath(
nodes.get(ThreadLocalRandom.current().nextInt(cluster.getJettySolrRunners().size())));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.concurrent.ThreadLocalRandom) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

throws Exception {
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.setBasePath(
miniClusterState.nodes.get(ThreadLocalRandom.current().nextInt(state.nodeCount)));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.concurrent.ThreadLocalRandom) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

throws Exception {
QueryRequest queryRequest = new QueryRequest(state.params);
queryRequest.setBasePath(
miniClusterState.nodes.get(ThreadLocalRandom.current().nextInt(state.nodeCount)));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PREDICTABLE_RANDOM: This random generator (java.util.concurrent.ThreadLocalRandom) is predictable (details)
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

}
}

SolrMetricManager metricsManager = getCoreContainer().getMetricManager();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NULL_DEREFERENCE: object returned by getCoreContainer() could be null and is dereferenced at line 729.
(at-me in a reply with help or ignore)

@@ -487,6 +488,14 @@ public long getIndexSize() {
return size;
}

public int getSegmentCount() {
try {
return withSearcher( solrIndexSearcher -> solrIndexSearcher.getRawReader().getIndexCommit().getSegmentCount());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THREAD_SAFETY_VIOLATION: Read/Write race. Non-private method SolrCore.getSegmentCount() indirectly reads with synchronization from this.indexReaderFactory. Potentially races with unsynchronized write in method SolrCore.initIndex(...).
Reporting because another access to the same memory occurs on a background thread, although this access may not.
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

@@ -212,7 +197,7 @@ public MiniSolrCloudCluster(int numServers, String hostContext, Path baseDir, St
* @throws Exception if there was an error starting the cluster
*/
public MiniSolrCloudCluster(int numServers, Path baseDir, String solrXml, JettyConfig jettyConfig) throws Exception {
this(numServers, baseDir, solrXml, jettyConfig, null);
this(numServers, baseDir, solrXml, jettyConfig, null, false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THREAD_SAFETY_VIOLATION: Unprotected write. Non-private method MiniSolrCloudCluster(...) indirectly mutates container util.ObjectReleaseTracker.OBJECTS via call to Map.remove(...) outside of synchronization.
Reporting because another access to the same memory occurs on a background thread, although this access may not.
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

ZkTestServer zkTestServer) throws Exception {
this(numServers, baseDir, solrXml, jettyConfig, zkTestServer, Optional.empty());
ZkTestServer zkTestServer, boolean formatZkServer) throws Exception {
this(numServers, baseDir, solrXml, jettyConfig, zkTestServer, Optional.empty(), formatZkServer);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THREAD_SAFETY_VIOLATION: Unprotected write. Non-private method MiniSolrCloudCluster(...) indirectly mutates container util.ObjectReleaseTracker.OBJECTS via call to Map.remove(...) outside of synchronization.
Reporting because another access to the same memory occurs on a background thread, although this access may not.
(at-me in a reply with help or ignore)

Copy link
Member Author

@markrmiller markrmiller Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

}
if (securityJson.isPresent()) { // configure Solr security
zkClient.makePath("/solr/security.json", securityJson.get().getBytes(Charset.defaultCharset()), true);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THREAD_SAFETY_VIOLATION: Unprotected write. Non-private method MiniSolrCloudCluster(...) indirectly mutates container util.ObjectReleaseTracker.OBJECTS via call to Map.remove(...) outside of synchronization.
Reporting because another access to the same memory occurs on a background thread, although this access may not.
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

AtomicReference<DocCollection> state = new AtomicReference<>();
AtomicReference<Set<String>> liveNodesLastSeen = new AtomicReference<>();
try {
getSolrClient().waitForState(collection, wait, unit, (n, c) -> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THREAD_SAFETY_VIOLATION: Unprotected write. Non-private method MiniSolrCloudCluster.waitForActiveCollection(...) indirectly writes to field noggit.JSONParser.devNull.buf outside of synchronization.
Reporting because another access to the same memory occurs on a background thread, although this access may not.
(at-me in a reply with help or ignore)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 2 times, most recently from eab2742 to 000e83a Compare August 5, 2021 16:46
@madrob
Copy link
Contributor

madrob commented Aug 5, 2021

Do we need to include a JFR or async-profiler recording profile?

@markrmiller markrmiller force-pushed the SOLR-15428-CORE branch 3 times, most recently from 48570ed to 9e6fa16 Compare August 9, 2021 17:54
@markrmiller
Copy link
Member Author

Do we need to include a JFR or async-profiler recording profile?

There is a sample config for JFR (that essentially turns on everything), but in general, these options are configured on the command line depending on what you are trying to look at.

…icro benchmarks and performance comparisons & investigation.
@markrmiller markrmiller merged commit 890ef78 into apache:main Aug 10, 2021
epugh pushed a commit to epugh/solr that referenced this pull request Oct 22, 2021
…icro benchmarks and performance comparisons & investigation. (apache#214)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants