runbook: n8n index unique degrade insights_metadata (collation) — restore litteral impossible, laisse en etat

This commit is contained in:
Nabil Derouiche
2026-07-23 21:46:32 +00:00
parent d976a92857
commit 117c61b056
@@ -0,0 +1,92 @@
# n8n — index unique dégradé sur `insights_metadata` (collation Postgres)
**Date** : 2026-07-23 · **Tags** : n8n, postgres, collation, index, insights · **Statut** : clos — laissé en l'état (décision assumée)
## Contexte
Stack n8n NAS (Portainer stack **254**, DB `n8n-DB` / postgres 17-alpine, bind-mount `/volume1/docker/n8n/db`).
Après la MAJ n8n 2.23.0 → 2.31.5, les tables `insights_*` (analytique interne n8n) présentaient des chiffres surestimés.
## Symptôme
Toute session psql sur la base affiche :
```
WARNING: database "n8n" has no actual collation version, but a version was recorded
```
Conséquence : l'index **unique** `IDX_1d8ab99d5861c9388d2dc1cf73` sur `insights_metadata(workflowId)` est **dégradé** — il a laissé entrer des doublons.
État constaté : **8 doublons de metadata**, **~13 workflows légèrement surcomptés** dans les insights. 69 lignes dans `insights_metadata`.
Cause : la version de collation enregistrée dans le catalogue ne correspond plus à celle de la libc du conteneur (bascule d'image / de libc). Les index B-tree sur colonnes texte construits sous l'ancienne collation deviennent incohérents → l'unicité n'est plus garantie.
## Ce qui NE MARCHE PAS
### ❌ Restore littéral du dump `insights_*`
Le réflexe « TRUNCATE + COPY depuis le backup » **échoue** :
```
BEGIN
TRUNCATE TABLE
ERROR: duplicate key value violates unique constraint "IDX_1d8ab99d5861c9388d2dc1cf73"
DETAIL: Key ("workflowId")=(XNppr5IcEMfJ73Ep) already exists.
CONTEXT: COPY insights_metadata, line 31
```
**Pourquoi** : le `TRUNCATE` vide l'index, qui se **reconstruit au fil du COPY sous la collation courante** → il redevient strict. Or le backup contient les doublons d'origine (entrés à l'époque où l'index était dégradé). Le restore se mord la queue.
**Le garde-fou a fonctionné** : `psql -v ON_ERROR_STOP=1` + `BEGIN` explicite → rollback complet, table inchangée (69 lignes avant/après). **Toujours encadrer un restore partiel par ON_ERROR_STOP + transaction.**
### ❌ Comptages naïfs
Avec un index dégradé, les `SELECT count(*)` peuvent passer par l'index et renvoyer des chiffres incohérents.
Forcer le parcours séquentiel pour un état fiable :
```sql
SET enable_indexscan = off;
SET enable_bitmapscan = off;
SELECT count(*) FROM insights_metadata;
```
## Ce qui marche
### Option A — restore dédupliqué (non retenue)
Dédupliquer les données **hors ligne** (sur le dump, données statiques → fiable, insensible à l'index dégradé), puis `TRUNCATE` + `COPY` du fichier propre. L'index unique reconstruit l'accepte. Atomique, faible risque, mais perd le churn depuis le dump.
### Option B — ne rien faire (RETENUE le 23/07/2026)
Les insights sont de l'**analytique bas-enjeu**, et n8n **churne la table en continu** : observé en direct, `insights_raw` 49 → 0 et `insights_by_period` 19347 → 19363 pendant la session (compaction en tâche de fond). Le surcomptage s'atténue seul avec la rétention.
→ Aucune chirurgie. Aucune perte de données, aucun impact fonctionnel sur les workflows.
## ⚠️ Bombe à retardement (à connaître avant tout geste futur)
Tant que les doublons sont présents, **échoueront** :
- `REINDEX` (index ou base)
- `ALTER DATABASE n8n REFRESH COLLATION VERSION`
- `pg_upgrade`
- tout dump/restore **complet** de la base
Si l'un de ces gestes devient nécessaire (montée de version postgres, changement d'image), **dédupliquer `insights_metadata` AVANT** :
```sql
-- inspection préalable
SELECT "workflowId", count(*) FROM insights_metadata GROUP BY 1 HAVING count(*) > 1;
```
## Prévention
- **Pin le patch postgres exact** dans le compose du stack (ex. `postgres:17.10-alpine`), jamais `postgres:17` flottant — c'est une bascule mineure/libc implicite qui produit ce type de désynchro de collation. Cf. `RUNBOOK-HERMES-UPDATE.md` et le piège 2 du stack n8n.
- Après toute bascule d'image postgres, guetter le `WARNING ... collation version` en tête de session psql.
## Accès rapide
```bash
ssh nas 'export PATH=$PATH:/usr/local/bin; docker exec -i n8n-DB psql -U n8nuser -d n8n' <<'SQL' 2>&1 | grep -vi "collation version"
SELECT count(*) FROM insights_metadata;
SQL
```
Voir aussi : `common/n8n-credentials-corruption-veille-consolidation.md`, `common/ports-registry.md`.