runbook: n8n index unique degrade insights_metadata (collation) — restore litteral impossible, laisse en etat
This commit is contained in:
@@ -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`.
|
||||||
Reference in New Issue
Block a user