perf sentinelperf sentineldocs
FRENGitHub
Documentation / Architecture

Architecture

Ce sujet existe en version animée : voir le moteur dans le synoptique →

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 reviennent dans la doc et le code, chacun avec un sens distinct :

  • 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 ou messaging 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 processus avec le code 1.
  • Mode daemon / streaming / watch (perf-sentinel watch) : processus de longue durée qui ingère les events OTLP/JSON en temps réel, avec éviction TTL et détection par trace. Les findings sont étiquetés daemon_staging ou daemon_production selon [daemon] environment (augmente la sévérité chez les consommateurs en aval comme perf-lint).

L'axe Confidence (local_batch < ci_batch < daemon_staging < daemon_production) est étiqueté automatiquement par le runtime et exposé dans JSON, SARIF, HTML et l'API de requête du daemon. Un run batch est étiqueté ci_batch quand il détecte un environnement de CI (CI défini à autre chose que false ou 0, ou JENKINS_URL présent) et local_batch sinon, que --ci soit passé ou non.

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, Vec<PerEndpointIoOps>)
  -> 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 routes, liens parent et destinations de consumer OTLP valides et échantillonnés de services nommés sont conservés avec les événements, afin que la fenêtre puisse résoudre une route d'entrée externe du même service, ou un span consumer, arrivé dans un export ultérieur. Les contextes d'endpoint, destinations de consumer et entrées d'ascendance sont plafonnés séparément par max_events_per_trace, partagent le LRU et le TTL de la trace et ne comptent jamais comme événements I/O.
  • 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, sauf avec [daemon] findings_stdout = false.
  • 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 inter-traces, plafonné à 1000)
  • GET /api/status (uptime, traces actives, nombre de findings stockés)
  • GET /api/config (configuration [daemon] effective, secrets résumés)
  • GET /api/energy (santé live des backends énergie et intensité)
  • GET /api/export/report (état live en JSON Report, pipe-compatible avec report --input -)
  • POST et DELETE /api/findings/{signature}/ack, GET /api/acks (acquittements au runtime)
  • POST et GET /api/incidents (incidents Alertmanager avec leurs findings figés)

Un FindingsStore (buffer circulaire) retient les findings récents pour l'API (dimensionné par [daemon] max_retained_findings, défaut 10k). La sous-commande compagnon perf-sentinel query restitue ces endpoints en sortie colorée dans le terminal. Conditionné par [daemon] api_enabled (défaut true). Voir Limites pour le modèle de menace sans authentification.

Responsabilités des modules

ModuleCheminResponsabilité
eventevent.rsType central SpanEvent (variantes SQL, HTTP et messaging) avec horodatage, 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/), dont la conversion recoud les requêtes SQL qu'une instrumentation en couches a scindées entre les spans qui portent le statement et ceux qui portent la durée (PHP Doctrine + PDO). Implémente le trait IngestSource. Parseur PostgreSQL pg_stat_statements (pg_stat.rs) et parseur des digests MySQL Performance Schema (mysql_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 frères séquentiels indépendants potentiellement parallélisables. Passages sous le seuil (below_threshold.rs) : quand le daemon tient une liste de surveillance, les détecteurs à seuil comptent dans leur propre boucle les exécutions des signatures à seuil surveillées restées au niveau de leur seuil ou en dessous, pour le registre de détections du daemon. Corrélateur inter-traces pour le mode daemon (correlate_cross.rs). Pistes de remédiation attachées aux findings (suggestions/). 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é du réseau embarquée, constantes SCI), carbon_compute.rs (boucle d'accumulation par span), carbon_profiles.rs (profils horaires du réseau) et region_breakdown.rs (regroupement par région, sélection du model-tag, finalisation du CarbonReport). Six backends de puissance et d'énergie opt-in, chacun un sous-module structuré : alumet/, scaphandre/ et kepler/ (RAPL), redfish/ (télémétrie BMC), cloud_energy/ (CPU% + interpolation SPECpower embarquée), electricity_maps/ (intensité du réseau en direct, uniquement si configuré), grid_feed/ (huit flux gratuits de gestionnaires de réseau pour l'intensité en direct, chacun en opt-in, Electricity Maps l'emportant là où les deux s'appliquent). Plomberie partagée des scrapers : energy_state.rs (cache de coefficients par service, et le partage d'une source : les services qui lisent une même source se partagent son énergie au prorata de leurs opérations), ops_snapshot_diff.rs (deltas d'opérations par service et par fenêtre de scrape) et prom_parser.rs (parseur d'exposition Prometheus partagé par les scrapers Kepler et Alumet). Hôtes déclarés : host_static.rs (les sections [green.database_static] et [green.broker_static] typées, leur placement et les noms base de données et broker) et broker_static.rs (le plafond provisionné qu'elles facturent). 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 du réseau pour 22 régions (mensuel x horaire complet pour FR/DE/GB/US-East, journalier représentatif pour 18 de plus). Équivalences Base Carbone des cartes à partager : equivalence.rs (recherche, choix, nombre calculé sur le CO2 imprimé) sur le fichier généré equivalence_data.rs.
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/ + html_template.html embarqué) avec onglets Findings, Explain, pg_stat, mysql_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) periodic/pdf/ rend une divulgation en PDF/A-3b et en relit les pièces jointes (feature pdf). Cartes à partager (card/, feature card) : l'entrée card.json et les charges utiles signées complète et compacte (payload.rs), clés Ed25519, empreintes et enveloppes (keys.rs), la signature du contenu des fichiers (content.rs), les textes (copy.rs) et la mise en page (layout.rs), le rendu PNG sur tiny-skia (raster.rs), le rendu SVG (svg.rs), le rendu JPEG XL (jxl.rs) et le carrousel PDF (pdf.rs), la vérification des charges utiles signées et du contenu des fichiers (verify.rs), le lien de la clé au domaine (binding.rs) et ses lectures --online (online.rs). Voir docs/FR/SHARE-CARD-FR.md.
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 plafond 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/ (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), detection_ledger.rs (comptes de détections par jour UTC et signature de finding, servis sur /api/detections), hub_detection_export.rs (pousse vers PerfSentinelHub les jours du registre qui ont changé)
configconfig/Parse .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 partagés de conversion d'horodatage (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 en ligne (suggestion, correctif par framework, emplacement dans le code). 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
capturecapture.rsRécepteur OTLP côté CI : monte les mêmes écouteurs OTLP gRPC/HTTP que le daemon sur un sink brut et écrit chaque requête reçue sous forme d'une ligne NDJSON pour analyze --ci. Aucune corrélation, aucune détection ni aucun scoring, et aucune conversion non plus, si bien que le fichier décrit ce que l'application a envoyé. Sous-tend la sous-commande capture, qui enveloppe aussi une commande de test et propage son code de sortie
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, horodatages, 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/TTL de traces actives avec contexte OTLP d'endpoint et de parent borné pour l'attribution entre exports

Frontières des crates

sentinel-cli (binaire)
  |
  +-- CLI clap : sous-commandes analyze / explain / diff / report / watch / query / ack / inspect / tempo / jaeger-query / pg-stat / mysql-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 reste 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)
pdfcore et cliactivée dans cli, désactivée dans coredisclose-pdf et verify-hash --report report.pdf (rendu PDF/A-3b et lecture des pièces jointes)
cardcore et cliactivée dans cli, désactivée dans corecard keygen, card render et verify-card (cartes à partager, implique pdf)

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