perf sentineldocs
ENGitHub
Documentation / Architecture

Architecture

perf-sentinel est un détecteur polyglotte d'anti-patterns de performance, construit sous forme de workspace Rust avec deux crates :

  • sentinel-core : bibliothèque contenant toute la logique du pipeline
  • sentinel-cli : binaire fournissant le point d'entrée CLI (perf-sentinel)

Glossaire

Quelques termes récurrents dans la doc et le code, dont les distinctions comptent :

  • Event — un SpanEvent normalisé. L'unité pré-détection (entrée).
  • Finding — un anti-pattern détecté (sortie de detect). Le concept produit, exposé dans la sortie CLI, JSON, SARIF, HTML, l'API daemon et les acks.
  • Pattern — le template SQL/HTTP normalisé qui regroupe les events d'un finding (ex. SELECT * FROM users WHERE id = ?).
  • Detection — l'action, ou la famille de détecteurs (n_plus_one, chatty, ...). N'est pas synonyme de finding.

Modes opérationnels :

  • Mode batch (perf-sentinel analyze) — traite un jeu complet de traces et produit un seul Report. Utilisé pour les tests d'intégration CI, l'analyse ponctuelle, et perf-sentinel report (HTML).
  • Mode CI (perf-sentinel analyze --ci) — identique au batch, mais une violation du quality gate fait sortir le process en code 1. Le champ Confidence de chaque finding est tagué ci_batch (confiance la plus faible : variété de trafic limitée).
  • Mode daemon / streaming / watch (perf-sentinel watch) — process long-running qui ingère les events OTLP/JSON en temps réel, avec éviction TTL et détection per-trace. Les findings sont tagués daemon_staging ou daemon_production selon [daemon] daemon_environment (booste la sévérité chez les consommateurs downstream comme perf-lint).

L'axe Confidence (ci_batch < daemon_staging < daemon_production) est tagué automatiquement par le runtime et exposé dans JSON, SARIF, HTML et l'API de requête du daemon.

Vue d'ensemble du pipeline

Modes de fonctionnement

Mode batch (perf-sentinel analyze)

Traite un ensemble complet d'événements et produit un rapport unique avec évaluation du quality gate.

Vec<SpanEvent>
  -> normalize::normalize_all()        -> Vec<NormalizedEvent>
  -> correlate::correlate()            -> Vec<Trace>
  -> detect::detect()                  -> Vec<Finding>
  -> score::score_green()              -> (Vec<Finding>, GreenSummary)
  -> quality_gate::evaluate()          -> QualityGate
  -> Report { analysis, findings, green_summary, quality_gate }

En mode CI (--ci), le processus se termine avec le code 1 si le quality gate échoue.

Mode streaming (perf-sentinel watch)

Fonctionne comme un daemon, recevant les événements en temps réel et émettant les findings au fur et à mesure de leur détection.

OTLP gRPC (port 4317)  \
OTLP HTTP (port 4318)   +---> canal mpsc ---> TraceWindow (LRU + TTL)
Socket unix JSON       /                               |
                                              +--------+--------+
                                              |                 |
                                        Éviction LRU    Éviction TTL
                                              |                 |
                                              +--------+--------+
                                                       |
                                          normalize -> detect -> score
                                                       |
                                              NDJSON findings (stdout)
                                              Prometheus /metrics
  • Les événements sont normalisés en dehors du verrou TraceWindow pour minimiser le temps de détention du verrou.
  • Les traces sont évincées lorsque le cache LRU est plein (max_active_traces) ou lorsque le TTL expire (trace_ttl_ms).
  • À l'éviction, la trace est analysée via les étapes detect et score.
  • Les findings sont émis en JSON délimité par des sauts de ligne sur stdout.
  • Les métriques Prometheus sont exposées sur le même port HTTP (4318) à /metrics.

API de requête du daemon

En mode watch, le daemon expose son état interne via des endpoints HTTP sur le port 4318 aux côtés de /v1/traces et /metrics :

  • GET /api/findings (filtrable, plafonné à 1000)
  • GET /api/findings/{trace_id}
  • GET /api/explain/{trace_id} (arbre de trace avec findings en ligne, depuis la mémoire du daemon)
  • GET /api/correlations (corrélations cross-trace, plafonné à 1000)
  • GET /api/status (uptime, traces actives, nombre de findings stockés)

Un FindingsStore (ring buffer) retient les findings récents pour l'API (dimensionné par [daemon] max_retained_findings, défaut 10k). La sous-commande compagnon perf-sentinel query rend ces endpoints en sortie coloré terminal. Gated par [daemon] api_enabled (défaut true). Voir Limites pour le threat model sans authentification.

Responsabilités des modules

ModuleCheminResponsabilité
eventevent.rsType central SpanEvent (variantes SQL et HTTP) avec timestamp, IDs trace/span, service, opération, cible, durée
ingestingest/Sources d'entrée : parseur JSON avec auto-détection du format (json.rs), import Jaeger JSON (jaeger.rs), import Zipkin JSON v2 (zipkin.rs), récepteur OTLP gRPC+HTTP (otlp.rs). Implémente le trait IngestSource. Parseur PostgreSQL pg_stat_statements (pg_stat.rs) pour l'analyse de hotspots. Récupérateurs de traces distants pour Grafana Tempo (tempo.rs) et l'API de requête Jaeger (jaeger_query.rs), avec helpers partagés pour l'en-tête d'authentification (auth_header.rs), les fenêtres de recherche en arrière (lookback.rs) et l'encodage d'URL (url_enc.rs)
normalizenormalize/Produit des NormalizedEvent avec template + paramètres extraits. Tokenizer SQL (sql.rs) : remplace les littéraux, UUIDs, listes IN. Normaliseur HTTP (http.rs) : remplace les segments numériques/UUID, supprime les paramètres de requête
correlatecorrelate/Regroupe les événements par trace_id. Mode batch (mod.rs) : agrégation par HashMap. Mode streaming (window.rs) : cache LRU avec buffer circulaire par trace et éviction TTL
detectdetect/Détection de patterns sur les traces corrélées. N+1 (n_plus_one.rs) : même template, paramètres différents, dans une fenêtre. Redondant (redundant.rs) : même template et paramètres. Lent (slow.rs) : durée au-dessus du seuil avec template récurrent. Fanout (fanout.rs) : span parent avec trop de spans enfants. Service bavard (chatty.rs) : trop d'appels HTTP sortants par trace. Saturation du pool (pool_saturation.rs) : spans SQL concurrents dépassant le seuil via algorithme de balayage. Appels sérialisés (serialized.rs) : appels siblings séquentiels indépendants potentiellement parallélisables. Corrélateur inter-traces pour le mode daemon (correlate_cross.rs). Pistes de remédiation attachées aux findings (suggestions.rs). Classification sensible aux sanitizers (sanitizer_aware.rs) : reconnaît le SQL déjà paramétré pour que la détection N+1 écarte les cas sûrs (SQL uniquement aujourd'hui)
scorescore/Scoring GreenOps (mod.rs) : IIS par endpoint, ratio de gaspillage, top offenders, green_impact par finding. Pipeline carbone réparti entre carbon.rs (table d'intensité grid embarquée, constantes SCI), carbon_compute.rs (boucle d'accumulation par span), carbon_profiles.rs (profils horaires de grid) et region_breakdown.rs (fold par région, sélection du model-tag, finalisation du CarbonReport). Cinq backends de puissance et d'énergie opt-in, chacun un sous-module structuré : scaphandre/ et kepler/ (RAPL), redfish/ (télémétrie BMC), cloud_energy/ (CPU% + interpolation SPECpower embarquée), electricity_maps/ (intensité de grid en direct, uniquement si configuré). Plomberie partagée des scrapers : energy_state.rs (cache de coefficients par service) et ops_snapshot_diff.rs (deltas d'opérations par service et par fenêtre de scrape). Calcul de l'évitable à seuil canonique fixe contre la manipulation des divulgations (canonical.rs). Multi-région SCI v1.0 via attribut OTel cloud.region, intervalles de confiance, profils horaires pour FR/DE/GB/US-East
reportreport/Formatage de sortie. Rapport JSON (json.rs), export SARIF v2.1.0 (sarif.rs), sortie CLI colorée (mod.rs), métriques Prometheus avec exemplars OpenMetrics (metrics.rs), dashboard HTML autonome en un fichier (html.rs + html_template.html embarqué) avec onglets Findings, Explain, pg_stat, Diff, Correlations et Green. Interprétations en langage clair des findings (interpret.rs), avertissements de rapport (warnings.rs), et la pile de divulgation publique périodique (periodic/ : schéma, agrégateur, validateur, configuration d'organisation, hacheur de contenu, attestation)
quality_gatequality_gate.rsÉvalue des règles de seuils configurables par rapport aux findings et au résumé green
pipelinepipeline.rsConnecte toutes les étapes pour le mode batch : normalize -> correlate -> detect -> score -> quality_gate -> Report
daemondaemon/Mode streaming : mod.rs héberge DaemonError et l'orchestrateur run(). Responsabilités réparties entre event_loop.rs (boucle tokio::select, éviction TraceWindow, worker d'analyse unique exécutant detect/score hors de la boucle), listeners.rs (spawn OTLP gRPC/HTTP, scrapers énergie optionnels), tls.rs (chargement certs, MaybeTlsStream, boucle HTTPS avec cap de concurrence sur les handshakes), json_socket.rs (ingestion NDJSON Unix, cfg(unix)), sampling.rs (échantillonnage par hash de trace-id), findings_store.rs + query_api.rs (findings retenus et endpoints HTTP de requête), ack.rs (store d'acquittements et endpoints HTTP d'ack), health.rs (liveness et readiness), archive.rs (archive NDJSON Report par fenêtre consommée par disclose)
configconfig.rsParse .perf-sentinel.toml avec le format sectionné ([thresholds], [detection], [green], [daemon]). Les 8 clés top-level legacy acceptées en 0.5.x ont été retirées en 0.6.0 (voir Configuration pour la table de migration)
timetime.rsHelpers de conversion timestamp partagés (nanos_to_iso8601, micros_to_iso8601). Utilisé par l'ingestion OTLP, Jaeger et Zipkin
explainexplain.rsVisualiseur d'arbre de trace : construit l'arbre des spans à partir de parent_span_id, annote les findings inline (suggestion, fix par framework, code location). Sortie texte et JSON
diffdiff.rsDelta de régression entre deux jeux de traces. Sous-tend la sous-commande diff utilisée en CI de PR
calibratecalibrate.rsAjuste les coefficients I/O vers énergie à partir de traces de référence et d'un CSV de puissance mesurée. Sous-tend la sous-commande calibrate
acknowledgmentsacknowledgments.rsFiltrage .perf-sentinel-acknowledgments.toml pour la CI, avec une signature canonique sha2 par finding
http_clienthttp_client.rsLe client HTTP sortant unique compatible TLS (limites de taille de corps, caviardage des endpoints) partagé par les scrapers d'énergie, l'ingestion Tempo et Jaeger, et query
shutdownshutdown.rsFuture de signal d'arrêt (SIGINT partout, SIGTERM sous Unix) partagée par la boucle d'événements du daemon et la boucle de récupération Tempo
text_safetytext_safety.rsHelpers purs partagés par tous les renderers texte qui impriment des chaînes contrôlées par l'attaquant vers un terminal : sanitize_for_terminal retire les caractères de contrôle ASCII pour neutraliser les injections ANSI / OSC 8 / curseur ; safe_url filtre suggested_fix.reference_url aux seules URL HTTPS sans caractères de contrôle

Types principaux

TypeModuleDescription
SpanEventeventÉvénement I/O brut (requête SQL ou appel HTTP) avec métadonnées et parent_span_id optionnel
NormalizedEventnormalizeSpanEvent enrichi d'un template normalisé et de paramètres extraits
TracecorrelateCollection de NormalizedEvents partageant le même trace_id
FindingdetectAnti-pattern détecté avec type, sévérité, détails du pattern, timestamps, green_impact et confidence (CI batch / daemon staging / daemon production)
GreenSummaryscoreStatistiques I/O agrégées : total ops, ops évitables, ratio de gaspillage, top offenders, CO2 optionnel
QualityGatequality_gateRésultat pass/fail avec évaluations individuelles des règles
ReportreportSortie d'analyse complète : métadonnées d'analyse, findings, résumé green, quality gate
ConfigconfigConfiguration parsée avec toutes les sections et champs validés
TraceWindowcorrelate/windowCache LRU de traces actives pour le mode streaming avec éviction TTL

Frontières des crates

sentinel-cli (binaire)
  |
  +-- CLI clap : sous-commandes analyze / explain / diff / report / watch / query / ack / inspect / tempo / jaeger-query / pg-stat / calibrate / disclose / verify-hash / hash-bake / demo / bench / man / completions
  |
  +-- dépend de sentinel-core (bibliothèque)
        |
        +-- Toute la logique du pipeline
        +-- Traits uniquement aux frontières : IngestSource, ReportSink
        +-- Fonctions pures entre les étapes

Le crate CLI est intentionnellement léger : il parse les arguments, charge la configuration et délègue aux fonctions de sentinel-core. Toute la logique métier réside dans sentinel-core.

Features Cargo

Des sous-commandes entières sont conditionnées à la compilation par des features Cargo :

FeatureDéfinie dansDéfautConditionne
daemoncore et cliactivée dans cli, désactivée dans corewatch, query, ack, la boucle d'événements du daemon et les scrapers d'énergie
tuicli uniquementactivéeinspect et les prévisualisations interactives --tui
tempocore et cliactivée dans cli, désactivée dans corela sous-commande tempo (ingestion via l'API Grafana Tempo)
jaeger-querycore et cliactivée dans cli, désactivée dans corela sous-commande jaeger-query (ingestion via l'API de requête Jaeger)

Le binaire publié active les quatre. Les consommateurs de la bibliothèque sentinel-core partent de tout désactivé et activent au besoin. À noter que tonic, axum, tower, prometheus et opentelemetry-proto restent des dépendances inconditionnelles de sentinel-core car report/metrics.rs et ingest/otlp.rs utilisent leurs types même quand aucun écouteur ne tourne.