A sophisticated load testing tool for the Swift Pulsar Client with integrated Prometheus metrics, designed for short or long running distributed tests across multiple devices.
- Multiple Run Modes: Consumer-only, Producer-only, or Both modes
- Configurable Message Rates: Support for messages per second/minute/hour
- Prometheus Integration: Automatic metrics export with zero configuration
- Multi-Device Support: Coordinate tests across multiple devices simultaneously
- Swift 6 Compliant: Built with modern Swift concurrency and safety features
- Comprehensive Metrics: Track send/receive rates, latencies, errors, and more
- Swift 6.1 or later
- Apache Pulsar instance running (local or remote)
- macOS 13+ or Linux (Ubuntu 20.04+)
cd PulsarTesting
swift build -c releaseThe executable will be available at .build/release/pulsar-load-test
# Copy to a location in your PATH
cp .build/release/pulsar-load-test /usr/local/bin/pulsar-load-test \
--mode producer \
--rate "10/second" \
--duration "1h" \
--service-url "pulsar://localhost:6650" \
--topic "persistent://public/default/load-test"pulsar-load-test \
--mode consumer \
--duration "1h" \
--service-url "pulsar://localhost:6650" \
--topic "persistent://public/default/load-test" \
--subscription "test-subscription"pulsar-load-test \
--mode both \
--rate "60/minute" \
--duration "48h" \
--service-url "pulsar://localhost:6650" \
--topic "persistent://public/default/load-test"| Option | Short | Description | Default |
|---|---|---|---|
--mode |
-m |
Run mode: consumer, producer, or both |
both |
--rate |
-r |
Message rate (e.g., 10/second, 60/minute, 100/hour) |
10/second |
--duration |
-d |
Test duration (e.g., 30s, 10m, 2h, 1d, 1w) |
1m |
--service-url |
-s |
Pulsar service URL | pulsar://localhost:6650 |
--topic |
-t |
Topic name | persistent://public/default/load-test |
--subscription |
Subscription name (for consumer) | load-test-subscription |
|
--prometheus-port |
-p |
Prometheus metrics port | 9090 |
--device-id |
Device identifier (auto-generated if not set) | <hostname>-<timestamp> |
|
--message-size |
Message size in bytes | 1024 |
|
--log-level |
-l |
Log level: trace, debug, info, warning, error, critical |
info |
--verbose |
Enable verbose logging | false |
N/secondorN/s- N messages per secondN/minuteorN/m- N messages per minuteN/hourorN/h- N messages per hour
Examples:
10/second- 10 messages per second600/minute- 600 messages per minute (10 per second)36000/hour- 36000 messages per hour (10 per second)
Ns- N secondsNm- N minutesNh- N hoursNd- N daysNw- N weeks
Examples:
30s- 30 seconds10m- 10 minutes2h- 2 hours1d- 1 day (24 hours)1w- 1 week (7 days)
The tool automatically exposes Prometheus metrics on the configured port (default: 9090).
curl http://localhost:9090/metricspulsar.test.start_time- Test start timestamppulsar.test.duration_seconds- Configured test durationpulsar.test.configured_rate- Configured message ratepulsar.test.actual_rate- Actual achieved message ratepulsar.test.status- Test status (1=running, 2=completed, 3=failed)pulsar.test.errors- Error count
All standard PulsarClient metrics are automatically emitted:
Client Metrics:
pulsar.client.connections.activepulsar.client.connections.totalpulsar.client.connections.failedpulsar.client.lookup.requestspulsar.client.lookup.latency
Producer Metrics:
pulsar.producer.messages.sentpulsar.producer.messages.failedpulsar.producer.messages.pendingpulsar.producer.send.latencypulsar.producer.batch.size
Consumer Metrics:
pulsar.consumer.messages.receivedpulsar.consumer.messages.acknowledgedpulsar.consumer.messages.nackedpulsar.consumer.receive.latencypulsar.consumer.process.latencypulsar.consumer.backlog
- Start Prometheus on a central monitoring server:
# prometheus.yml configuration
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'pulsar-load-test'
static_configs:
- targets: ['device1:9090', 'device2:9090', 'device3:9090']- Run tests on multiple devices with unique device IDs:
Device 1 (Producer):
pulsar-load-test \
--mode producer \
--device-id "device-1" \
--rate "100/second" \
--duration "48h"Device 2 (Consumer):
pulsar-load-test \
--mode consumer \
--device-id "device-2" \
--subscription "test-sub-1" \
--duration "48h"Device 3 (Both):
pulsar-load-test \
--mode both \
--device-id "device-3" \
--rate "50/second" \
--duration "48h"Messages contain JSON payloads with:
device_id: Source device identifiersequence: Message sequence numbertimestamp: Unix timestamp when senttest_run: Test run identifierdata: Padding to reach configured message size
Example:
{
"device_id": "device-1",
"sequence": 12345,
"timestamp": 1702934567.123,
"test_run": "1702934500.0",
"data": "xxx..."
}# Test connectivity
pulsar-load-test \
--mode producer \
--rate "1/second" \
--duration "10s" \
--log-level debugReduce batch sizes or receiver queue size in the code:
withBatchingMaxMessages(50)(default: 100)withReceiverQueueSize(500)(default: 1000)
- Check Prometheus port is accessible:
curl http://localhost:9090/metrics- Verify no port conflicts:
lsof -i :9090