Latency benchmarks, payload sizes, protocol comparisons, and optimization strategies
Performance
Flaggr is designed for sub-millisecond flag evaluations at the edge and single-digit millisecond evaluations over the network.
Latency Benchmarks
| Evaluation Mode | P50 | P95 | P99 |
|---|---|---|---|
| In-process (local eval) | 0.01ms | 0.05ms | 0.1ms |
| L1 cache hit | 0.05ms | 0.1ms | 0.2ms |
| L2 cache hit (Redis) | 1ms | 3ms | 5ms |
| gRPC evaluation | 8ms | 25ms | 45ms |
| REST evaluation | 15ms | 55ms | 150ms |
| OFREP evaluation | 20ms | 65ms | 180ms |
For latency-sensitive paths, use the gRPC provider with localEvaluation: true. After initial sync, evaluations happen in-process in under 0.1ms.
Payload Size Comparison
| Protocol | Typical Request | Typical Response | Total |
|---|---|---|---|
| REST (JSON) | ~320 bytes | ~410 bytes | ~730 bytes |
| OFREP (JSON) | ~280 bytes | ~350 bytes | ~630 bytes |
| Connect (JSON) | ~300 bytes | ~380 bytes | ~680 bytes |
| gRPC (Protobuf) | ~95 bytes | ~120 bytes | ~215 bytes |
gRPC with protobuf is approximately 70% smaller than JSON-based protocols.
Optimization Strategies
1. Use Local Evaluation
The fastest evaluation is one that doesn't hit the network. Local evaluation providers download the flag configuration once and evaluate in-process.
// gRPC with local evaluation
const provider = new GrpcProvider({
grpcAddress: 'flaggr.dev:50051',
serviceId: 'my-service',
localEvaluation: true,
})
// In-process provider (no gRPC needed)
const provider = new InProcessProvider({
apiUrl: 'https://flaggr.dev',
serviceId: 'my-service',
pollIntervalMs: 30000,
})2. Use Bulk Evaluation
Instead of N individual requests, fetch all flags in a single call:
POST /api/connect/bulk-evaluate
{
"serviceId": "web-app",
"flagKeys": ["flag-a", "flag-b", "flag-c"],
"context": { "targetingKey": "user-123" }
}This reduces N round-trips to 1.
3. Enable Streaming
Instead of polling, use SSE or gRPC streaming to receive flag changes instantly:
const provider = new FlaggrWebProvider({
enableStreaming: true,
serviceId: 'web-app',
})4. Bootstrap Server-Rendered Pages
For SSR/SSG, evaluate flags on the server and pass to the client as bootstrap data:
// Server-side
const flags = await evaluateForUser(userId)
// Client-side (zero network calls on load)
const provider = new FlaggrWebProvider({
bootstrap: flags,
})5. Cache Configuration
Tune cache TTLs based on your consistency requirements:
const client = createFlaggr({
serviceId: 'web-app',
cacheTtl: 30000, // 30s cache for less-critical flags
})Longer cache TTLs reduce API calls but delay flag change propagation. For real-time updates, use streaming instead of aggressive caching.
Connection Overhead
| Connection Type | Handshake | Persistent | Multiplexed |
|---|---|---|---|
| REST (HTTP/1.1) | Per request | Keep-alive | No |
| Connect (HTTP/2) | Once | Yes | Yes |
| gRPC (HTTP/2) | Once | Yes | Yes |
| SSE (HTTP/1.1) | Once | Yes | No |
gRPC and Connect reuse a single HTTP/2 connection for all requests, eliminating per-request handshake overhead.
Monitoring Performance
Track evaluation performance via Prometheus:
# P95 evaluation latency
histogram_quantile(0.95, sum(rate(flaggr_http_request_duration_seconds_bucket[5m])) by (le))
# Evaluation throughput
sum(rate(flaggr_evaluations_total[5m]))
# Cache hit rate
sum(rate(flaggr_cache_operations_total{result="hit"}[5m]))
/ sum(rate(flaggr_cache_operations_total[5m]))Related
- Provider Architecture — Provider selection guide
- Caching Strategy — Cache tiers and configuration
- Edge & Serverless — Edge-optimized evaluation