opensearch-project / opensearch-project/opensearch-java

[FEATURE] Add JMH Benchmark Module

Open
#2,110 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement untriaged
Dominant language
Java
Stars
165
Forks
250
Avg merge
1d 18h
Merged PRs (30d)
26

Description

Is your feature request related to a problem?

The opensearch-java client has no performance benchmarking infrastructure. There is currently no way to:

  • Measure the throughput or latency of BulkIngester under different configurations
  • Detect performance regressions introduced by code changes
  • Compare the cost of different serialization strategies (e.g. raw POJO vs BinaryData)
  • Validate that optimizations like increased maxConcurrentRequests or batch sizing actually improve end-to-end throughput

Without a benchmark module, performance work relies entirely on ad-hoc scripts written outside the repository, making results hard to reproduce and compare across contributors.

What solution would you like?

A benchmarks/ submodule using JMH (Java Microbenchmark Harness) (https://github.com/openjdk/jmh) via the me.champeau.jmh(https://github.com/melix/jmh-gradle-plugin) Gradle plugin.

Proposed module structure

benchmarks/
├── build.gradle.kts # JMH plugin + dependencies
└── src/jmh/java/org/opensearch/client/benchmarks/
└── BulkIngesterBenchmark.java # end-to-end throughput benchmark

BulkIngesterBenchmark — initial benchmark

Measures end-to-end BulkIngester throughput (ops/s) against a live OpenSearch cluster, parameterized on:

  • maxOperations — operations per bulk flush (e.g. 1000, 5000, 10000)
  • maxSize — max bytes size per bulk flush (e.g. 5MB, 10MB)
  • maxConcurrentRequests — in-flight bulk request limit (e.g. 1, 2, 4, 8)

Key design decisions:

  • Uses @Threads(Threads.MAX) to simulate concurrent producers and reach steady-state throughput
  • Pre-serializes documents to BinaryData once at @Setup to isolate transport cost from serialization cost
  • Creates a fresh timestamped index per trial and deletes it in @TearDown
  • Supports HTTPS + basic auth via system properties for testing against remote clusters
Run against a local cluster

./gradlew :benchmarks:jmh -Pbenchmark=BulkIngesterBenchmark

Run against a remote cluster with HTTPS + basic auth

./gradlew :benchmarks:jmh -Pbenchmark=BulkIngesterBenchmark
-Dbenchmark.scheme=https
-Dbenchmark.host=my-cluster.example.com
-Dbenchmark.port=443
-Dbenchmark.user=admin
-Dbenchmark.password=secret

Sample output:

Benchmark (batchSize) (maxConcurrentRequests) Mode Cnt Score Error Units
BulkIngesterBenchmark.bulkIndexThroughput 1000 1 thrpt 10 28,776 ± 1,839 ops/s
BulkIngesterBenchmark.bulkIndexThroughput 5000 1 thrpt 10 35,395 ± 1,423 ops/s
BulkIngesterBenchmark.bulkIndexThroughput 10000 1 thrpt 10 35,507 ± 2,873 ops/s
BulkIngesterBenchmark.bulkIndexThroughput N/A 1 thrpt 10 35,241 ± 1,102 ops/s

What alternatives have you considered?

OpenSearch Benchmark (OSB) maybe an option, but OSB drives OpenSearch directly using its own internal HTTP client. It measures what the cluster can handle — indexing throughput, query latency, segment merge cost. It has no knowledge of the Java client's internal architecture: BulkIngester's buffer management, the maxConcurrentRequests semaphore, the ReentrantLock contention, serialization cost, or connection pool behavior.

If a code change in BulkIngester introduces a lock contention bug that halves throughput, OSB would never detect it — because OSB never calls BulkIngester.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the proposed benchmarks/build.gradle.kts module and src/jmh/java/org/opensearch/client/benchmarks/BulkIngesterBenchmark.java, then review the existing Gradle project structure and BulkIngester API. Run ./gradlew :benchmarks:jmh -Pbenchmark=BulkIngesterBenchmark against a local cluster. Done means the JMH benchmark supports the requested parameters, measures throughput, and cleans up its timestamped index.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.