# Diagnostic — Features manquantes dans `view_ai_features_1h` > Généré le 2026-03-17 — Mis à jour le 2026-03-17 (corrections appliquées) — À destination de l'administrateur ClickHouse ## ✅ Statut des corrections (2026-03-17 13:05) | Problème | Correction appliquée | Résultat | |----------|---------------------|----------| | **1** — MV `mv_agg_header_fingerprint_1h` absente | MV recréée + backfill 25h | ✅ 10 features header actives | | **2** — `header_order_shared_count` / `distinct_header_orders` globales | Se corrige avec Problème 1 | ✅ Résolu automatiquement | | **3** — `orphan_ratio` = 0 pour `correlated=1` | Comportement normal (by design) | ℹ️ Pas d'action requise | | **4** — 4 vues dashboard absentes | Vues créées | ✅ | | **5** — `view_dashboard_variability` référence `header_user_agent` inexistant | Colonne remplacée par `reason` | ✅ Bug corrigé | | **6** — Anciennes vues heuristiques orphelines | Droppées | ✅ | Cycle post-correction (13:05) — features dans les warnings : - `Complet` : seulement `orphan_ratio` (by design) - `Applicatif` : `request_size_variance`, `mss_mobile_mismatch`, `is_rare_ja4` (see §4 below) - Header features **disparues des warnings** → pipeline opérationnel ✅ --- --- ## Résumé Le service Bot Detector signale des **features non-discriminantes** à chaque cycle. Ce document en explique les causes exactes et les corrections nécessaires côté ClickHouse. Ces avertissements **n'empêchent pas le service de fonctionner** — les features invalides sont automatiquement exclues du modèle (A7). Mais leur absence réduit la qualité de la détection. --- ## Problème 1 — Pipeline `agg_header_fingerprint_1h` arrêté ⚠️ CRITIQUE ### Symptôme Les features suivantes sont toujours à **0** dans `view_ai_features_1h` : - `header_count` - `has_accept_language` - `has_cookie` - `has_referer` - `modern_browser_score` - `ua_ch_mismatch` - `mss_mobile_mismatch` *(dépend de `modern_browser_score`)* ### Cause La table `ja4_processing.agg_header_fingerprint_1h` (AggregatingMergeTree) n'a plus reçu de données depuis le **2026-03-13 23:00** : ```sql SELECT max(window_start), count() FROM ja4_processing.agg_header_fingerprint_1h; -- Résultat : 2026-03-13 23:00:00, 73024 lignes ``` La vue fait un `LEFT JOIN` avec condition `window_start >= now() - INTERVAL 24 HOUR`, et comme aucune ligne récente n'existe dans `agg_header_fingerprint_1h`, **toutes les colonnes issues de ce JOIN retournent NULL** (→ 0 après coalesce). ### Recherche de la MV source La liste des Materialized Views ne montre aucune MV dédiée à `agg_header_fingerprint_1h` : ```sql SELECT name FROM system.tables WHERE database = 'ja4_processing' AND engine = 'MaterializedView'; -- mv_agg_host_ip_ja4_1h -- mv_http_logs -- view_dashboard_entities_mv -- view_dashboard_user_agents_mv ``` Aucune MV ne cible `agg_header_fingerprint_1h`. Elle est probablement alimentée par un **processus externe** (ETL, script, pipeline Kafka, etc.) qui s'est arrêté. ### Correction appliquée ✅ La MV `mv_agg_header_fingerprint_1h` était **définie dans `deploy_views.sql`** mais n'avait jamais été créée en base. Elle a été recréée le 2026-03-17 : ```sql -- Recréation de la MV (déjà appliquée) CREATE MATERIALIZED VIEW ja4_processing.mv_agg_header_fingerprint_1h TO ja4_processing.agg_header_fingerprint_1h AS SELECT toStartOfHour(src.time) AS window_start, toIPv6(src.src_ip) AS src_ip, any(toString(cityHash64(src.client_headers))) AS header_order_hash, max(toUInt16(length(src.client_headers) - length(replaceAll(src.client_headers, ',', '')) + 1)) AS header_count, -- ... (voir deploy_views.sql §5) FROM ja4_logs.http_logs AS src GROUP BY window_start, src.src_ip; ``` Un **backfill de 25 heures** a été effectué depuis `http_logs` pour alimenter la table avec des données historiques (377 689 lignes insérées). Les nouvelles données sont désormais alimentées en temps réel par la MV. ### Cause historique La MV avait été omise lors du déploiement initial. La table `agg_header_fingerprint_1h` contenait 73 024 lignes datant du 2026-03-13 (probablement issues d'un backfill manuel ponctuel), puis n'avait plus été alimentée. --- ## Problème 2 — Features non-discriminantes (agrégat global, non per-IP) ### Symptôme Les features suivantes ont une **valeur unique non-nulle identique pour toutes les IPs** : - `header_order_shared_count` (valeur ≈ 421 000 pour toutes les lignes) - `distinct_header_orders` (valeur identique pour toutes les lignes) ### Cause Ces features sont calculées via des window functions `PARTITION BY header_order_hash` : ```sql -- Dans la vue : count() OVER (PARTITION BY h.header_order_hash) AS header_order_shared_count uniqExact(h.header_order_hash) OVER (PARTITION BY a.src_ip) AS distinct_header_orders ``` Comme `h.header_order_hash` est **NULL pour toutes les lignes** (problème 1 ci-dessus), la `PARTITION BY NULL` regroupe **toutes les lignes dans une seule partition** → `count()` retourne le total de toutes les lignes pour chaque IP. ### Correction ✅ (auto-résolue avec Problème 1) Ce problème s'est résolu automatiquement une fois la MV `mv_agg_header_fingerprint_1h` recréée. `header_order_hash` est désormais non-NULL, les partitions de window functions sont correctement calculées par hash d'ordre d'en-têtes. --- ## Problème 3 — `orphan_ratio` absent pour le trafic corrélé TCP ### Symptôme `orphan_ratio` = 0 pour **toutes les lignes avec `correlated = 1`** (trafic TCP enrichi). ### Cause La colonne `orphan_count` dans `ja4_processing.agg_host_ip_ja4_1h` est calculée par la MV `mv_agg_host_ip_ja4_1h` : ```sql sum(IF(src.orphan_side = 'A' OR src.correlated = 0, 1, 0)) AS orphan_count ``` Pour les connexions `correlated=1`, `correlated = 0` est toujours faux, et `orphan_side = 'A'` n'est jamais vrai pour le trafic corrélé → `orphan_count = 0` systématiquement. **C'est un comportement intentionnel** : les connexions TCP corrélées ont une réponse confirmée, donc elles ne sont pas des requêtes orphelines par définition. ### Statut Pas d'action requise. La feature reste exclue automatiquement par A7 pour le modèle `Complet` (correlated=1). --- ## Problème 4 — Features à 0 persistantes dans le modèle Applicatif ### Symptôme (post-correction) Depuis le 2026-03-17 13:05, le modèle `Applicatif` (trafic non-corrélé) signale encore ces features à 0 : - `request_size_variance` - `mss_mobile_mismatch` - `is_rare_ja4` ### Cause Ces features sont calculées depuis des colonnes L4/TCP qui sont **absent ou non-pertinentes pour le trafic applicatif pur** (`correlated=0`) : | Feature | Cause | |---------|-------| | `request_size_variance` | `varPopMerge(total_ip_length_var)` — variance de longueur IP ; trafic non-corrélé = pas de données IP brutes fiables | | `mss_mobile_mismatch` | Dépend de `tcp_meta_mss` et `modern_browser_score` — MSS non fiable sans corrélation TCP | | `is_rare_ja4` | `sum(hits) OVER (PARTITION BY ja4) < 100` — dans la fenêtre Applicatif (1h, trafic réduit), tous les JA4 sont rares | ### Impact Faible — ces features sont exclues automatiquement (A7). Elles ne dégradent pas le modèle. --- ## Impact sur le modèle IA | Feature | Impact si absente | Statut | |---------|-------------------|--------| | `header_count` | Perte d'un signal fort : bots envoient souvent peu d'en-têtes | ✅ Corrigé | | `has_accept_language` | Perte de détection des bots sans localisation | ✅ Corrigé | | `has_cookie` | Perte de détection des sessions sans état | ✅ Corrigé | | `has_referer` | Perte du signal de navigation directe | ✅ Corrigé | | `modern_browser_score` | Perte du score composite de conformité navigateur | ✅ Corrigé | | `ua_ch_mismatch` | Perte de détection des fausses déclarations UA | ✅ Corrigé | | `header_order_shared_count` | Perte de la détection de fingerprints d'en-têtes partagés | ✅ Corrigé | | `orphan_ratio` | Signal faible pour trafic corrélé | ℹ️ By design | | `request_size_variance` | Signal L4 faible pour Applicatif | ℹ️ Normal | | `mss_mobile_mismatch` | Signal TCP faible pour Applicatif | ℹ️ Normal | --- ## Vérification post-correction Cycle du 2026-03-17 13:05 — résultat observé : ``` [Complet] Features à 0 : ['orphan_ratio'] ← by design ✅ [Applicatif] Features à 0 : ['request_size_variance', 'mss_mobile_mismatch', 'is_rare_ja4'] ← normales ✅ [Applicatif] Features non-discriminantes : ['tcp_shared_count'] ← agrégat global résiduel ``` Les **10 features header** (`header_count`, `has_accept_language`, `has_cookie`, `has_referer`, `modern_browser_score`, `ua_ch_mismatch`, `header_order_shared_count`, `distinct_header_orders`, `header_order_confidence`, `mss_mobile_mismatch` pour Complet) **ne sont plus dans les warnings**. Le pipeline est opérationnel.