Performance Test Report
Historical measurements. The figures of this report were measured on valerter 1.0.0-rc.5: a single VictoriaLogs source and a single notification queue shared by every destination. They have not been measured again since. Later versions run one task per
(rule, source)pair (capped bydefaults.max_streams) and one queue per destination; the throttle results and the orders of magnitude remain a useful baseline. The memory bounds of the current version are given in Memory bounds (2.1.0).
Date: 2026-01-19
Version tested: valerter 1.0.0-rc.5
Load generator: Rust (tools/load-generator)
Test Environment
| Component | Specification |
|---|---|
| CPU | Intel Core i7-14700 (20 cores) |
| RAM | 32 GB DDR5 |
| Storage | Samsung 990 PRO NVMe 1TB |
| OS | Linux (CachyOS 6.18) |
| VictoriaLogs | Docker container (local) |
Understanding the Tests
Why Two Types of Tests?
Valerter is an alerting system, not a log forwarder. The goal is to send actionable notifications to humans, not to forward every single log line. This distinction is crucial for understanding the test results.
| Test Type | Purpose | Realistic? |
|---|---|---|
| With Throttle | Simulates production use | YES - This is how you should run valerter |
| Without Throttle | Stress test / robustness check | NO - Only to verify no crash under extreme load |
The throttle tests are the most important. They prove that valerter can handle massive log volumes while sending only the notifications you actually need.
Executive Summary
Production-Realistic Tests (With Throttle)
| Test | Log Rate | Logs Processed | Notifications | Dropped | Status |
|---|---|---|---|---|---|
| Throttle 10k/s | 10,000/s | 150,000 | 25 | 0 | PASS |
| Throttle 50k/s | 50,000/s | 500,000 | 25 | 0 | PASS |
| Throttle 100k/s | 100,000/s | 500,000 | 25 | 0 | PASS |
Key insight: With throttle enabled, valerter handles 100,000 logs/sec while sending only 25 actionable notifications (5 per host). Zero drops, stable memory.
Robustness Tests (Without Throttle)
| Test | Log Rate | Notifications | Dropped | Crash? | Status |
|---|---|---|---|---|---|
| T1 Baseline | 100/s | 3,000 (100%) | 0 | No | PASS |
| T2 Moderate | 1,000/s | 7,843 (26%) | 25,157 | No | PASS |
| T3 Stress | 5,000/s | 13,098 (13%) | ~87k | No | PASS |
| T4 Extreme | 10,000/s | 18,957 (13%) | ~131k | No | PASS |
Key insight: Even without throttle, valerter doesn’t crash. It gracefully drops excess notifications to protect memory.
Part 1: Production-Realistic Tests (With Throttle)
These tests simulate real-world usage where you want actionable alerts, not notification spam.
Configuration
throttle:
key: "{{ host }}" # Group by host
count: 5 # Max 5 notifications per host
window: 60s # Per minute
With 5 unique hosts in the test data, maximum expected notifications = 5 hosts × 5 = 25 per minute.
Test: 10,000 logs/sec with Throttle
| Metric | Value |
|---|---|
| Log rate | 10,000/sec |
| Duration | 15s |
| Total logs | 150,000 |
| Notifications sent | 25 |
| Throttled | 149,975 |
| Dropped | 0 |
| RSS Memory | 18.49 MB |
| Panics | 0 |
Result: Exactly 25 notifications as configured. Zero drops. System stable.
Test: 50,000 logs/sec with Throttle
| Metric | Value |
|---|---|
| Log rate | 50,000/sec |
| Duration | 10s |
| Total logs | 500,000 |
| Notifications sent | 25 |
| Throttled | 499,975 |
| Dropped | 0 |
| RSS Memory | 18.64 MB |
| Panics | 0 |
Result: Half a million logs processed, only 25 notifications. Zero drops.
Test: 100,000 logs/sec with Throttle
| Metric | Value |
|---|---|
| Log rate | 100,000/sec |
| Duration | 5s |
| Total logs | 500,000 |
| Notifications sent | 25 |
| Throttled | 499,975 |
| Dropped | 0 |
| RSS Memory | 20.11 MB |
| Panics | 0 |
Result: 100k logs/sec ingested, only 25 actionable notifications sent. Perfect throttle behavior.
Why This Matters
In production, you don’t want 100,000 Mattermost messages per second. You want:
- “server-01 has high CPU” → 1 notification
- “server-02 has high CPU” → 1 notification
- etc.
The throttle ensures your team gets actionable alerts without notification fatigue.
Part 2: Robustness Tests (Without Throttle)
These tests verify that valerter doesn’t crash under extreme load, even in misconfigured scenarios.
Note: These tests are NOT realistic. In production, you should ALWAYS enable throttle.
Why Run These Tests?
To ensure that if someone misconfigures valerter (no throttle, or throttle too high), the system:
- Doesn’t crash
- Doesn’t run out of memory
- Gracefully degrades (drops old notifications)
T1: Baseline (100 logs/sec, no throttle)
| Metric | Value |
|---|---|
| Log rate | 100/sec |
| Notifications sent | 3,000 (100%) |
| Dropped | 0 |
| RSS Memory | 16.65 MB |
Result: At low rates, 100% delivery is possible even without throttle.
T2: Moderate (1,000 logs/sec, no throttle)
| Metric | Value |
|---|---|
| Log rate | 1,000/sec |
| Notifications sent | 7,843 (26%) |
| Dropped | 25,157 |
| RSS Memory | 18.38 MB |
Result: HTTP notifiers can’t keep up. Valerter drops oldest alerts to prevent memory growth. No crash.
T3: Stress (5,000 logs/sec, no throttle)
| Metric | Value |
|---|---|
| Log rate | 5,000/sec |
| Notifications sent | 13,098 (13%) |
| Dropped | ~87,000 |
| RSS Memory | 21.18 MB |
Result: Heavy backpressure. Queue drops working as designed. No crash.
T4: Extreme (10,000 logs/sec, no throttle)
| Metric | Value |
|---|---|
| Log rate | 10,000/sec |
| Notifications sent | 18,957 (13%) |
| Dropped | ~131,000 |
| RSS Memory | 21.83 MB |
Result: Extreme load. Memory bounded, no crash, no panic.
What These Tests Prove
Even without throttle:
- No crash under any load tested
- Memory stayed at ~22 MB (the notification queue has a fixed size)
- Graceful degradation (FIFO drops, not OOM)
Part 3: VictoriaLogs Resilience
Tests that valerter recovers from VictoriaLogs outages.
Reconnection Test
Procedure:
- Valerter connected to VictoriaLogs
docker stop valerter-perf-vl- Observed exponential backoff (1s, 2s, 4s…)
docker start valerter-perf-vl- Valerter auto-reconnected
Results:
- Reconnection attempts: 5 (with exponential backoff)
- Final state: the connection gauge back to 1
- Auto-reconnect successful
Verdict: PASS
Performance Characteristics
Throughput Summary
| Component | Measured Throughput |
|---|---|
| Log ingestion | 100,000+ logs/sec |
| JSON parsing | 100,000+ logs/sec |
| Throttle evaluation | 100,000+ logs/sec |
| HTTP notification | ~50-100/sec per notifier |
Bottleneck: HTTP notification delivery (network latency). This is expected and mitigated by throttle.
Memory Usage (measured on 1.0.0-rc.5)
| Scenario | RSS Memory |
|---|---|
| Idle | ~15 MB |
| With throttle (any load) | ~18-20 MB |
| Without throttle (extreme) | ~22 MB |
Memory bounds (2.1.0)
The number of pending alerts is bounded whatever the load: each notifier has its own queue of 100 alerts, so at most 100 × number of notifiers alerts are pending. An alert is shared by its destinations (not copied). With ordinary log lines (a few hundred bytes), even dozens of notifiers keep the pending alerts within a few MB.
The size of a pending alert grows with its log line (at most 1 MiB). For a line of L bytes:
- the fields of the event (the
logvariable of notifier templates) take up to 2L: the flat keys, plus a copy of each dotted key expanded into objects; - a
body_format: markdownbody that inserts the line once takes up to 2L for its source (a backslash before each punctuation character), then, only for the formats used by its destinations: up to L forplain, 4L formarkdown(<written<), 6L forhtml("written") and 5L fortelegram_html(&written&). Each rendering is computed once and shared.
That is about 20 MiB per alert in the worst case (a 1 MiB line of such characters, every format used), times the number of times the body inserts the line. The leading blanks and empty lines of a value are the exception: in a Markdown body, a leading space becomes a no-break space (2 bytes), a leading tab four (8 bytes) and an empty line one, which multiplies these figures for a value made of them. A text body keeps its rendered text only (about L per insertion).
Key Prometheus Metrics
# The metrics that matter in production
valerter_alerts_sent_total # Notifications actually delivered
valerter_alerts_throttled_total # Logs matched but throttled (expected)
valerter_alerts_dropped_total # Queue overflow, all destinations (should be 0 with throttle)
valerter_destination_alerts_dropped_total{notifier_name} # Which destination overflows
valerter_vl_source_up{vl_source} # Connection health, per source
Recommendations
1. Always Enable Throttle
throttle:
key: "{{ host }}" # Or "{{ service }}", "{{ pod }}", etc.
count: 10 # Max notifications per key
window: 60s # Per minute
2. Monitor These Metrics
| Metric | Expected | Action if abnormal |
|---|---|---|
valerter_destination_alerts_dropped_total |
0 | Enable/tighten throttle, check the slow destination |
valerter_alerts_throttled_total |
> 0 | Normal, throttle working |
valerter_vl_source_up |
1 (per source) | Check VL connection |
3. Choose Appropriate Throttle Keys
{{ host }}- Alert per machine{{ service }}- Alert per service{{ host }}-{{ level }}- Alert per machine+severity
Test Files
| File | Description |
|---|---|
tools/load-generator/ |
Rust load generator (100k+ logs/sec) |
scripts/perf-test-config.yaml |
Test configuration |
scripts/mock-mattermost.py |
Mock Mattermost webhook |
scripts/mock-webhook.py |
Mock generic webhook |
scripts/docker-compose-perf.yaml |
Infrastructure (VL + Mailhog) |
scripts/PERF-TEST-GUIDE.md |
Test execution guide |
Conclusion
Production Use (With Throttle)
Valerter handles 100,000 logs/sec while sending only the notifications you configure:
- ✅ Exact throttle behavior (25 notifications from 500k logs)
- ✅ Zero drops
- ✅ Stable memory (~20 MB, measured on 1.0.0-rc.5)
- ✅ No crashes
Robustness (Without Throttle)
Even misconfigured, valerter doesn’t crash:
- ✅ Graceful backpressure
- ✅ Bounded memory
- ✅ No panics
Resilience
- ✅ Auto-reconnects to VictoriaLogs after outage
Overall status: PASS
The throttle feature is the key to production use. With proper throttle configuration, valerter transforms massive log volumes into actionable, human-friendly notifications.