# 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.
