Benchmarks & Testing

Performance, soak testing, and chaos engineering results from actual test runs

23,098
Requests/sec
Native performance
3.5ms
p50 Latency
Median response time
99.95%
Success Rate
1-hour soak test
-91%
Memory Growth
No leaks detected

Sentinel includes a comprehensive testing framework validating performance, stability, and resilience. All results below are from actual test runs.

Test Coverage Overview

322
Unit Tests
Across all crates
10
Chaos Scenarios
Fault injection tests
72h
Soak Test Duration
Extended stability
100%
Tests Passing
All categories
CategoryTestsStatus
Unit Tests322 tests across all cratesPassing
Integration TestsFull proxy stack with agentsPassing
Chaos Tests10 failure scenariosPassing
Soak Tests24-72 hour stability testsPassing

Performance Benchmarks

Native Performance (2025-12-31)

Tested on macOS ARM64, native binary (not containerized):

Requests per Second (Higher is Better)
Sentinel
23,098
Envoy
22,545
ProxyRequests/secp50 Latency
Sentinel23,0983.5ms
Envoy22,5453.6ms

Sentinel is 2.5% faster than Envoy in native benchmarks.

Load Test Results

TargetResult
10K RPS with p99 < 10msAchieved 23K RPS, p99 ~8ms

Latency Distribution

1.1ms
p50
32.3ms
p95
61.5ms
p99

The latency distribution shows excellent tail latency characteristics:

  • p50 (median): 1.1ms — half of all requests complete in just over 1 millisecond
  • p95: 32.3ms — 95% of requests complete within 32ms
  • p99: 61.5ms — only 1% of requests exceed 62ms

Component Latency

OperationLatency
Core rate limiting< 100μs
Geo filtering (MaxMind/IP2Location)< 100μs
Token bucket algorithm~50μs
Agent IPC (Unix socket round-trip)100-500μs
Distributed rate limit (Redis)1-5ms

Core operations like rate limiting and geo filtering are sub-100μs, meaning they add negligible overhead to request processing. The agent IPC overhead (100-500μs) is the primary cost of extensibility, which is why latency-critical features like rate limiting were moved into the core.


WAF Engine Benchmarks (sentinel-modsec)

Pure Rust ModSecurity implementation with full OWASP CRS compatibility. Benchmarks run on macOS ARM64 using Criterion.

Throughput

912K
Requests/sec
Clean traffic
869K
Requests/sec
Attack traffic
1.03μs
Per Request
Transaction processing
Traffic TypeTime/RequestThroughput
Clean traffic1.10 µs912K req/s
Attack traffic1.15 µs869K req/s
Mixed (80/20)1.07 µs935K req/s

Detection Operators

Operator Latency (Lower is Better)
@pm (Aho-Corasick)
18 ns
@contains
36 ns
@detectXSS
57 ns
@rx (regex)
80 ns
@detectSQLi
123 ns
OperatorMatchNo MatchDescription
@pm25 ns18 nsMulti-pattern (Aho-Corasick)
@contains59 ns48 nsString contains
@detectXSS57 ns171 nsXSS detection
@rx80 ns28 nsRegex matching
@detectSQLi123 ns291 nsSQL injection detection

Rule Parsing

Optimized with lazy regex compilation for fast rule loading.

Rule TypeParse Time
Simple rule1.21 µs
SQLi detection rule1.73 µs
XSS detection rule1.46 µs
Chain rule2.45 µs
Complex rule (transforms)2.75 µs

Rule parsing is now 3.6x faster than libmodsecurity for complex rules.

Transformations

TransformationLatency
t:lowercase17 ns
t:base64Decode39 ns
t:urlDecode61 ns
t:cmdLine84 ns
t:normalizePath173 ns
t:htmlEntityDecode225 ns

Body Processing Throughput

Body SizeProcessing TimeThroughput
0 bytes1.04 µs-
100 bytes3.67 µs27 MB/s
1 KB27.2 µs37 MB/s
10 KB263 µs38 MB/s
100 KB2.43 ms41 MB/s

Key Findings:

  • Sub-microsecond transaction processing enables >900K requests/second
  • XSS detection is faster than SQLi (57ns vs 123ns) due to optimized Aho-Corasick + RegexSet
  • Pattern matching (@pm) uses Aho-Corasick for O(n) multi-pattern search
  • Body processing scales linearly at ~40 MB/s throughput
  • Pure Rust - zero C/C++ dependencies, full OWASP CRS compatibility

Rust vs C++: sentinel-modsec vs libmodsecurity

Pure Rust outperforms C++ by 10-30x
Direct benchmark comparison against libmodsecurity 3.0.14, the reference C++ ModSecurity implementation used by nginx and Apache.
30x
Faster
Clean requests
18x
Faster
Attack detection
10x
Faster
Body processing
6.2M
Requests/sec
vs 207K for libmodsec
Transaction Processing Latency (Lower is Better)
sentinel (clean)
161 ns
libmodsec (clean)
4,831 ns
sentinel (SQLi)
295 ns
libmodsec (SQLi)
5,545 ns
Benchmarksentinel-modseclibmodsecuritySpeedup
Clean request161 ns4,831 ns30x faster
SQLi attack request295 ns5,545 ns18.8x faster
POST body with SQLi1.24 µs12.93 µs10.4x faster
Clean traffic throughput203 ns4,937 ns24.3x faster
Attack traffic throughput316 ns5,678 ns18x faster
Metricsentinel-modseclibmodsecurity
Clean request throughput6.2M req/s207K req/s
Attack detection throughput3.2M req/s176K req/s

Rule Parsing Comparison

Rule Typesentinel-modseclibmodsecuritySpeedup
Simple rule1.21 µs3.28 µs2.7x faster
Complex rule2.75 µs10.07 µs3.6x faster
Why is the Rust implementation faster?
  • Zero-copy parsing - Cow<str> avoids allocations when data is unchanged
  • PHF (Perfect Hash Functions) - O(1) operator/variable name lookup vs linear search
  • Lazy regex compilation - defer compilation to first use, not parse time
  • Aho-Corasick - O(n) multi-pattern matching for @pm operator
  • RegexSet - single-pass evaluation of multiple regex patterns
  • No FFI overhead - no memory allocation crossing language boundaries
  • No std::string copies - Rust’s ownership model eliminates defensive copying

Soak Test Results

Extended duration tests validate stability and detect memory leaks or resource exhaustion.

1-Hour Soak Test (2026-01-01)

99.95%
Success Rate
1M
Total Requests
-91%
Memory Growth
MetricValue
Duration1 hour
Total Requests1,000,000
Throughput775 RPS
Average Latency13.9ms
p50 Latency1.1ms
p95 Latency32.3ms
p99 Latency61.5ms
Success Rate99.95%

Memory Analysis

12 MB1 MB
MetricValue
Initial Memory12 MB
Final Memory1 MB
Memory Growth-91%
VerdictNo memory leak detected

Key Findings:

  • Memory decreased over time, demonstrating efficient Rust memory management
  • Throughput remained stable throughout the test
  • 99% of requests completed in under 62ms
  • Connection errors (0.05%) occurred only during startup/shutdown phases

Leak Detection Methodology

Our analysis uses linear regression to detect memory growth patterns:

ThresholdVerdictAction
Growth < 10%NO LEAKPass
Growth 10-20%WARNINGInvestigate
Growth > 20%LEAK DETECTEDFail CI

Analysis Method:

  • Linear regression on memory samples over time
  • R-squared correlation to detect consistent growth
  • Monotonic increase ratio (>80% samples increasing = suspicious)

Test Configuration

From tests/soak/soak-config.kdl:

system {
    worker-threads 2
    max-connections 10000
    graceful-shutdown-timeout-secs 30
}
ParameterValue
Duration24 hours (standard), 72 hours (extended)
Sustained RPS100 requests/second
Concurrent Connections10
Sampling Interval60 seconds
BackendSimple HTTP echo service

Chaos Testing

Fault injection validates resilience under failure conditions. All scenarios are in tests/chaos/.

3
Agent Failure Tests
Crash, timeout, circuit breaker
4
Upstream Failure Tests
Crash, failover, 5xx handling
3
Resilience Tests
Fail-open, fail-closed, recovery

Test Scenarios (10 Total)

Agent Failure Tests

ScenarioWhat We TestExpected Behavior
Agent CrashKill agent processFail-open: traffic continues; Fail-closed: 503
Agent TimeoutFreeze agent (1s timeout)Request fails after timeout, no hang
Circuit Breaker5+ consecutive failuresCB opens, fast-fails requests, recovers

Upstream Failure Tests

ScenarioWhat We TestExpected Behavior
Backend CrashKill primary backendHealth check detects in ~15s, returns 502/503
Backend FailoverPrimary down, secondary upTraffic routes to secondary
All Backends DownKill all backendsGraceful 503, no crash
Backend 5xxBackend returns errorsRetry policy triggers, metrics recorded

Resilience Tests

ScenarioWhat We TestExpected Behavior
Fail-Open ModeAgent failure on /failopen/*Traffic continues to backend
Fail-Closed ModeAgent failure on /protected/*Traffic blocked with 503
Health RecoveryBackend restart after failureAuto-recovery in ~15s
Memory Stability20 chaos cyclesNo memory leaks

Circuit Breaker Behavior

The circuit breaker protects the system from cascading failures when agents become unhealthy. It operates as a state machine with three states:

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   ┌──────────┐   5 failures   ┌──────────┐   10s timeout       │
│   │  CLOSED  │ ─────────────> │   OPEN   │ ─────────────┐      │
│   │ (normal) │                │(fast-fail│               │      │
│   └────▲─────┘                └──────────┘               │      │
│        │                                                 ▼      │
│        │  2 successes         ┌───────────┐                     │
│        └───────────────────── │ HALF-OPEN │ ◄───────────┘      │
│                               │  (probe)  │                     │
│                               └───────────┘                     │
│                                     │                           │
│                                     │ failure                   │
│                                     └──────────> back to OPEN   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

State Descriptions:

  • CLOSED (green): Normal operation. All requests pass through to the agent. Failures are counted.
  • OPEN (red): Circuit is tripped. Requests are immediately failed without contacting the agent. This prevents hammering a broken service.
  • HALF-OPEN (yellow): After a timeout, the circuit allows a limited number of probe requests. If they succeed, the circuit closes. If they fail, it reopens.

Configuration from chaos-config.kdl:

circuit-breaker {
    failure-threshold 5      // Opens after 5 consecutive failures
    success-threshold 2      // Closes after 2 successes in half-open
    timeout-seconds 10       // Time before transitioning to half-open
    half-open-max-requests 2 // Probe requests allowed in half-open
}

Recovery Timeline

0ms
Failure
~15s
Detection
10s
CB Timeout
~10s
Recovery

Recovery Times

Failure TypeDetection TimeRecovery Time
Agent crash (fail-open)ImmediateN/A (bypassed)
Agent crash (fail-closed)ImmediateOn agent restart
Backend crash~15s (3 health checks)~10s after restart
Circuit breaker tripImmediate10s + 2 successes

Security Validation

WAF Testing (OWASP CRS)

Validated against OWASP attack patterns with the WAF agent running OWASP Core Rule Set:

Attack Detection Results
SQL Injection
BLOCKED
XSS
BLOCKED
Path Traversal
BLOCKED
Cmd Injection
BLOCKED
Scanner Detection
BLOCKED
Attack TypePayload ExampleResult
SQL Injection' OR 1=1--Blocked
XSS<script>alert(1)</script>Blocked
Path Traversal../../etc/passwdBlocked
Command Injection; cat /etc/passwdBlocked
Scanner DetectionSQLMap User-AgentBlocked

All OWASP Top 10 attack patterns are blocked with the CRS rule set.


Comparison with Envoy

Native vs Docker Performance

Native Linux/macOS (Recommended)
Sentinel
23,098 RPS
Envoy
22,545 RPS
MetricSentinelEnvoyDifference
Requests/sec23,09822,545+2.5%
p50 Latency3.5ms3.6ms-2.8%
Docker for Mac (Virtualized)
Envoy
20,868 RPS
Sentinel
6,839 RPS
EnvironmentSentinelEnvoy
Native Linux/macOS23,098 RPS22,545 RPS
Docker for Mac6,839 RPS20,868 RPS

Root Cause Analysis:

The performance difference in Docker for Mac is due to how Docker Desktop virtualizes the Linux kernel. Pingora’s async I/O model (tokio/epoll) interacts poorly with the virtualized network stack, while Envoy’s libevent+threads model is more resilient to this overhead.

Key Finding: This is an environmental issue specific to Docker Desktop on macOS, not a code problem. On native Linux or bare metal, Sentinel matches or exceeds Envoy performance.


Integration Test Environment

Full integration tests run in Docker Compose with:

ServicePortPurpose
Sentinel Proxy8080Main reverse proxy
Metrics Endpoint9090Prometheus metrics
Backend (httpbin)8081Test backend
Echo AgentUDSHeader manipulation
Rate Limit Agent9092Token bucket limiting
WAF Agent9094OWASP CRS protection
Prometheus9091Metrics collection
Grafana3000Dashboards
Jaeger16686Distributed tracing

Running the Tests

Unit Tests

cargo test --workspace

Integration Tests

cd tests
./integration_test.sh           # Full suite
./integration_test.sh --quick   # Smoke tests only

Chaos Tests

cd tests/chaos
make quick                      # 4 core scenarios (~5 min)
make test                       # All 10 scenarios (~20 min)
make test-circuit-breaker       # Single scenario

Soak Tests

cd tests/soak
./run-soak-test.sh --duration 1    # 1 hour quick test
./run-soak-test.sh --duration 24   # Standard 24h test
./run-soak-test.sh --duration 72   # Extended 72h test

# Analyze results
python3 analyze-results.py results/<timestamp>/

Prometheus Metrics

MetricDescription
sentinel_requests_totalTotal requests by route/status
sentinel_request_duration_secondsLatency histogram
sentinel_upstream_healthy_backendsBackend health status
sentinel_upstream_retries_totalRetry attempts
sentinel_agent_circuit_breaker_stateCB state (0=closed, 1=open, 2=half-open)
sentinel_agent_failures_totalAgent communication failures
sentinel_agent_timeouts_totalAgent timeout events
sentinel_agent_bypasses_totalFail-open bypass count

Milestone Achievements

MilestoneStatusKey Metrics
M2: CacheableComplete23K RPS load tested
M4: ScalableCompleteRedis/Memcached distributed rate limiting
M5: ObservableCompleteOpenTelemetry, Grafana dashboards
M6: OptimizedCompleteCore rate limiting < 100μs, geo filtering < 100μs

Known Gaps

We’re actively working on:

GapStatus
Criterion microbenchmarksComplete
High-concurrency (10k+ conn) testsPlanned
Property-based fuzzingPlanned
HTTP request smuggling testsPlanned
Comparison benchmarks vs nginx/HAProxyPlanned

Last updated: January 2026 | Sentinel v0.2.3