
I built a Rust CLI that detects N+1 SQL/HTTP anti-patterns across services and scores their carbon impact
After a few years building Java/Spring microservices in enterprise settings (insurance, agriculture) I kept hitting the same N+1 queries that slip through code review because they span multiple services, redundant HTTP calls nobody notices until production latency spikes. Every project, same patterns, different stack.
I also have a background in environmental science (I founded and ran a scientific association for 3 years, organizing conferences with climate researchers from the IPCC and IPBES). So when I started thinking about an N+1 detector, I naturally wanted to quantify the environmental cost of wasteful I/O too, not just the latency impact.
Existing tools are either runtime-specific (Hypersistence Optimizer only works with JPA), heavy and proprietary (Datadog, New Relic), or don't correlate across services. So I built perf-sentinel: a lightweight Rust CLI that analyzes runtime traces and flags these patterns automatically regardless of language or ORM.
How it works:
It takes OpenTelemetry traces (or Jaeger/Zipkin exports) and runs them through a pipeline: ingest -> normalize -> correlate -> detect -> score -> report. Detection is protocol-level, it sees the SQL queries and HTTP calls your code produces, not the code itself. So it works the same whether you're using JPA, EF Core, SeaORM or raw SQL.
Quick demo (30 seconds):
cargo install perf-sentinel
perf-sentinel demo
Output:
=== perf-sentinel demo ===
Analyzed 17 events across 2 traces in 0ms
Found 3 issue(s):
[WARNING] #1 N+1 SQL
Service: order-svc
Endpoint: POST /api/orders/42/submit
Template: SELECT * FROM order_item WHERE order_id = ?
Hits: 6 occurrences, 6 distinct params, 250ms window
Suggestion: Use WHERE ... IN (?) to batch 6 queries into one
[WARNING] #2 N+1 HTTP
Template: GET /api/users/{id}
Hits: 6 occurrences
Suggestion: Use batch endpoint with ?ids=...
[CRITICAL] #3 Slow SQL
Template: SELECT * FROM order_status WHERE order_id = ?
Suggestion: Consider adding an index or optimizing query
--- GreenOps Summary ---
Total I/O ops: 17
Avoidable I/O ops: 10
I/O waste ratio: 58.8%
Est. CO₂: 0.000108 g
Quality gate: FAILED
What it detects:
- N+1 SQL queries (same template, different params, tight time window)
- N+1 HTTP calls across services
- Redundant queries (exact duplicates)
- Slow recurring queries (with p50/p95/p99 across traces)
- Excessive fanout (parent span spawning too many children)
What makes it different:
- Protocol-level, not runtime-level. Works with any language/ORM that emits OTLP traces. Unlike Hypersistence Optimizer (JPA only) or Datadog (shows queries in trace view but doesn't auto-detect N+1 patterns).
- Built-in GreenOps scoring: every finding includes an I/O Intensity Score and optional gCO2eq estimate, aligned with the SCI model (ISO/IEC 21031:2024).
- CI-native:
perf-sentinel analyze --ciwith configurable quality gate and exit codes. If someone introduces an N+1 the pipeline breaks. - SARIF export for GitHub/GitLab code scanning integration.
- Imports Jaeger and Zipkin trace exports directly, no infra changes needed.
perf-sentinel explain --trace-id abc123shows a tree view of a trace with findings annotated inline.perf-sentinel inspectopens a TUI to browse findings interactively.- Can cross-reference with
pg_stat_statementsdata for DB-side validation.
Numbers:
- Single binary: ~4 MB (macOS arm64), ~5 MB (Windows), < 10 MB (Linux static)
- < 5 MB RSS idle, < 20 MB under load (10k traces)
- > 100k events/sec throughput
I've been dogfooding it on a personal polyglot microservices project (Java Spring WebFlux + Virtual Threads, Quarkus/GraalVM Native, C# .NET NativeAOT, Rust Actix, all talking to each other) and it caught real N+1s across all stacks without any language-specific configuration.
Still early (v0.2.0) and I would really appreciate feedbacks on:
- Detection heuristics: are the defaults sane? False positive rate?
- CLI UX and output format
- Anti-patterns you'd want detected that aren't covered
cargo install perf-sentinel or grab a binary from the releases page (Linux amd64/arm64, macOS arm64, Windows amd64).
