Skip to content

test: Add JMH benchmark suite for PubSub implementations#390

Open
iamabhilaksh wants to merge 4 commits intosalesforce:mainfrom
iamabhilaksh:feat/pubsub-benchmarks
Open

test: Add JMH benchmark suite for PubSub implementations#390
iamabhilaksh wants to merge 4 commits intosalesforce:mainfrom
iamabhilaksh:feat/pubsub-benchmarks

Conversation

@iamabhilaksh
Copy link
Copy Markdown
Contributor

📊 Overview

This PR introduces a comprehensive JMH (Java Microbenchmark Harness) benchmark suite for PubSub implementations across AWS SNS/SQS and GCP Pub/Sub. The benchmarks establish performance baselines and enable continuous performance monitoring.

✨ Features

Benchmark Coverage

  • AWS SNS/SQS: 13 benchmarks (26 results with both modes)
  • GCP Pub/Sub: 14 benchmarks (27 results with both modes)
  • Measurement Modes:
    • Throughput - Operations per microsecond
    • SampleTime - Latency distribution with percentiles (p50, p90, p99, p99.9)

Benchmark Categories

Publishing (7 benchmarks)

  • benchmarkSingleMessagePublish - Concurrent publishing (4 threads)
  • benchmarkSequentialPublish - Single-threaded baseline
  • benchmarkPublishTinyMessages - 256 bytes payload
  • benchmarkPublishMediumMessages - 10KB payload
  • benchmarkPublishLargeMessages - 100KB payload
  • benchmarkPublishWithAttributes - Metadata overhead measurement
  • benchmarkMaxSizeMessages - Max size (100KB) stress test

Consuming (3 benchmarks)

  • benchmarkSingleMessageReceive - Receive + ack latency
  • benchmarkBatchAck - Batch acknowledgment efficiency (10 messages)
  • benchmarkLargeBatchAck - Large batch scaling (AWS: 10, GCP: 100)

End-to-End (3 benchmarks)

  • benchmarkPublishConsumeAck - Full pipeline latency
  • benchmarkHighThroughputPipeline - Max sustainable throughput (8 threads)
  • benchmarkMessageBacklog - Backlog recovery performance (5000 messages)

Provider-Specific (1 benchmark)

  • benchmarkNackAndRedelivery - GCP NACK/redelivery latency (GCP only)

📈 Benchmark Results

AWS SNS/SQS Performance

Benchmark Throughput (ops/μs) Latency p50 (ms) Latency p99 (ms)
Single Message Publish 0.0000084 517.5 638.5
Sequential Publish 0.0000019 549.4 649.7
Tiny Messages (256B) 0.0000035 534.4 656.9
Medium Messages (10KB) 0.0000031 587.6 716.8
Large Messages (100KB) 0.0000021 878.6 1064.2
With Attributes 0.0000055 553.0 677.8
Max Size Messages 0.0000214 94.8 119.5
Publish-Consume-Ack 0.0000066 619.5 760.2
High Throughput Pipeline 0.0235 119.4 147.2
Message Backlog 0.0000056 564.3 690.4
Batch Ack (10 msgs) 0.0000072 580.0 710.7
Large Batch Ack 0.0000059 592.9 726.0
Single Message Receive 0.0000064 547.1 670.2

Duration: 56 minutes | Status: ✅ Complete (26/26 results)

GCP Pub/Sub Performance

Benchmark Throughput (ops/μs) Latency p50 (ms) Latency p99 (ms)
Single Message Publish 0.0000042 974.2 1193.0
Sequential Publish 0.0000015 675.1 826.4
Tiny Messages (256B) 0.0000032 594.0 727.3
Medium Messages (10KB) 0.0000029 692.5 847.9
Large Messages (100KB) 0.0000019 1200.2 1469.5
With Attributes 0.0000041 960.5 1176.6
Max Size Messages 0.0000122 109.5 134.1
Publish-Consume-Ack 0.0000038 1063.7 1303.0
High Throughput Pipeline 0.6271 266.4 326.5
Message Backlog 0.0000030 N/A* N/A*
Batch Ack (10 msgs) 0.0000039 935.0 1145.3
Large Batch Ack (100 msgs) 0.0000003 19748.9 24192.2
Single Message Receive 0.0000040 961.3 1177.6
NACK & Redelivery 0.0000045 309.3 378.8

Duration: 117 minutes | Status: ✅ Excellent (27/28 results)
Note: benchmarkMessageBacklog SampleTime mode data missing due to timeout

Key Insights

  1. AWS vs GCP Throughput: AWS shows ~2x better throughput for most operations
  2. GCP Batch Advantage: GCP supports 100-message batches vs AWS's 10-message limit
  3. High Throughput: GCP excels in concurrent pipeline scenarios (0.63 ops/μs vs 0.024)
  4. Latency: AWS generally shows lower p50/p99 latencies for single operations

🏗️ Implementation Details

Code Structure

pubsub/
├── pubsub-client/
│ ├── pom.xml # Added JMH dependencies
│ └── src/test/java/.../client/
│ └── AbstractPubsubBenchmarkTest.java # Base benchmark class (670 lines)
├── pubsub-aws/
│ ├── pom.xml # Added JMH dependencies
│ └── src/test/java/.../aws/
│ └── AwsSnsPubsubBenchmarkTest.java # AWS implementation (96 lines)
└── pubsub-gcp/
├── pom.xml # Added JMH dependencies
└── src/test/java/.../gcp/
└── GcpPubsubBenchmarkTest.java # GCP implementation (136 lines)

Code Quality Improvements

Eliminated Code Duplication

  • Created ensurePrePopulated() helper method
  • Applies DRY principle across 6 benchmarks
  • Thread-safe with double-checked locking pattern

Extracted Magic Numbers to Constants

ITERATIONS_PER_BENCHMARK = 10
PREPOPULATE_SMALL = 100
PREPOPULATE_BATCH = 200
PREPOPULATE_HIGH_THROUGHPUT = 500
PREPOPULATE_BACKLOG = 5000
PROPAGATION_DELAY_MS = 10000
MESSAGE_AVAILABILITY_DELAY_MS = 1000

✅ Fixed AWS SNS Batch Limit Issue

  • Changed benchmarkMaxSizeMessages from 256KB to 100KB
  • AWS SNS 256KB limit includes payload + attributes + envelope overhead

✅ Consistent Pre-Population Pattern

  • GCP NACK benchmark refactored to use parent helper
  • Removed static mutable state

🧪 Running Benchmarks

Run Full AWS Benchmark Suite

  mvn clean install -DskipTests
  mvn test -pl pubsub/pubsub-aws -Dtest=AwsSnsPubsubBenchmarkTest#runBenchmarks

Run Full GCP Benchmark Suite

  mvn clean install -DskipTests
  mvn test -pl pubsub/pubsub-gcp -Dtest=GcpPubsubBenchmarkTest#runBenchmarks

Results Location

AWS Results

pubsub/pubsub-aws/target/jmh-pubsub-results-aws-sns.json

GCP Results

pubsub/pubsub-gcp/target/jmh-pubsub-results-gcp.json

📦 Full Benchmark Results

Due to size limitations (AWS: 221KB, GCP: 197KB), full JMH JSON results are available:

AWS SNS/SQS Results (221KB, 26 results)

  • File: jmh-pubsub-results-aws-sns.json
  • Benchmarks: 13 × 2 modes = 26 results
  • Duration: 56 minutes
  • Status: Perfect (0 failures)

GCP Pub/Sub Results (197KB, 27 results)

  • File: jmh-pubsub-results-gcp-FINAL.json
  • Benchmarks: 14 × ~2 modes = 27 results
  • Duration: 117 minutes
  • Status: Excellent (1 minor timeout)

Contact maintainer for full JSON files or generate them by running the benchmarks.

⚠️ Known Limitations

GCP Timeouts (Minor, Non-Blocking)

  1. benchmarkMessageBacklog - SampleTime mode data missing
    - Throughput mode data complete
    - Root cause: Consumer timeout in long-running backlog test

Disabled Benchmarks (Future Work)

Saved in backup for future implementation:

  1. benchmarkOrderedPublishing - Requires FIFO/ordering setup
  2. benchmarkEmptyQueuePolling - Needs receive(timeout) support
  3. benchmarkMultipleConsumers - Requires multi-consumer infrastructure

🔍 Testing Checklist

  • Code compiles successfully
  • All 13 AWS benchmarks validated (56 min runtime)
  • All 14 GCP benchmarks validated (117 min runtime)
  • Both Throughput and SampleTime modes captured
  • Results include p50, p90, p99, p99.9 latency percentiles
  • No interference between benchmarks (isolated pre-population)
  • Thread-safe implementation verified
  • Checkstyle compliance verified

🎯 Next Steps

  1. Integrate into CI/CD: Run benchmarks on performance regression PRs
  2. Performance Budgets: Set thresholds for acceptable regression
  3. Sequential Mode Runs: Separate Throughput/SampleTime runs for 20-30% speedup
  4. Dashboard: Visualize trends over time
  5. Implement Future Benchmarks: Ordered publishing, empty polling, multi-consumer

📚 Related Documentation

  • JMH Guide: https://github.com/openjdk/jmh
  • Benchmark Design Document: See BENCHMARK_READY_TO_RUN.md (not included in PR)
  • Future Benchmarks: See FUTURE_BENCHMARKS.md (saved in backup)

Performance baselines established! 🚀 Ready for continuous monitoring and optimization.

Implement comprehensive performance benchmarks for AWS SNS and GCP Pub/Sub:
- 13 benchmarks for AWS (publishing, consuming, end-to-end, edge cases)
- 14 benchmarks for GCP (includes additional NACK/redelivery test)
- Both Throughput and SampleTime modes for latency percentiles
- Concurrent execution with @threads annotation

Key changes:
- Add AbstractPubsubBenchmarkTest.java with 14 benchmark scenarios
- Add AwsSnsPubsubBenchmarkTest.java and GcpPubsubBenchmarkTest.java
- Add JMH dependencies to pubsub-client, pubsub-aws, pubsub-gcp
- Implement ensurePrePopulated() helper to eliminate code duplication
- Extract magic numbers to named constants for maintainability
- Fix benchmarkMaxSizeMessages to use 100KB (was 256KB) to stay within AWS SNS 256KB batch limit

Benchmarks cover:
- Publishing: single, sequential, tiny/medium/large messages, attributes, max size
- Consuming: single message, batch ack, large batch ack
- End-to-end: publish-consume-ack pipeline, high throughput pipeline, message backlog
- Edge cases: max size messages
- Provider-specific: NACK and redelivery (GCP only)

Code quality improvements:
- Refactored GCP NACK benchmark to use parent helper method (consistency)
- Removed unused MAX_MESSAGE constant
- All magic numbers extracted to named constants with clear purpose
- Thread-safe pre-population with double-checked locking pattern

Establishes performance baselines for both AWS and GCP implementations.
- Line 60: Shortened NACK comment from 105 to 99 chars
- Line 66: Shortened availability comment from 108 to 99 chars
- Fix line 60 checkstyle: shorten comment from 101 to 88 chars
- Change ensurePrePopulated() from private to protected (needed by GCP subclass)
- Add @disabled to AwsSnsPubsubBenchmarkTest (follows blobstore pattern)
- Add @disabled to GcpPubsubBenchmarkTest (follows blobstore pattern)

This prevents benchmarks from running in CI while keeping them compilable
and easily enableable for manual execution.
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.11%. Comparing base (d8556bc) to head (35b9b3f).

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #390   +/-   ##
=========================================
  Coverage     82.10%   82.11%           
  Complexity      641      641           
=========================================
  Files           194      194           
  Lines         11798    11798           
  Branches       1572     1572           
=========================================
+ Hits           9687     9688    +1     
+ Misses         1434     1433    -1     
  Partials        677      677           
Flag Coverage Δ
unittests 82.11% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sandeepvinayak sandeepvinayak changed the title feat: Add JMH benchmark suite for PubSub implementations test: Add JMH benchmark suite for PubSub implementations Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants