# Restauration — procédure > Document à lire **sous stress**. Il ne cherche pas à expliquer, il dit quoi taper. > La procédure est rejouée automatiquement chaque mois par `myim-restore-check.timer`. **Règle unique, jamais enfreinte : la base de production n'est jamais la cible d'un `pg_restore`.** On restaure toujours dans une base neuve, on regarde, puis on décide. --- ## Scénario A — sinistre complet (la machine est perdue) 1. Récupérer le dump depuis la destination hors site : ```sh rclone lsl "$BACKUP_DEST" | sort -k2 | tail -5 # choisir, ne pas prendre le dernier à l'aveugle rclone copy "$BACKUP_DEST/.dump" . ``` ⚠️ Vérifier la **date** avant de restaurer. Le dernier dump peut être postérieur à l'incident et contenir déjà les dégâts. 2. Monter Postgres avec ses extensions (`postgis` **et** `vector`), puis appliquer le schéma : ```sh docker compose -f docker-compose.preprod.yaml up -d postgres dotnet ef database update ``` 3. Restaurer : ```sh docker exec -i myim_postgres pg_restore -U mym -d my_info_mate \ --no-owner --no-privileges < .dump ``` 4. **Relancer l'indexation des embeddings.** `ContentEmbeddings` est exclu des dumps (régénérable, et ça divise leur taille par 7). Sans cette étape le guide IA répond à côté sans le dire. 5. Vérifier avant d'ouvrir au public : login manager-app, une app visiteur avec sa clé API, un parcours, une carte, un PDF, **un quiz avec ses questions**. C'est la liste du point 20 du §1quinquies, et les deux derniers sont ceux qui échouent en silence. ## Scénario B — un seul client à récupérer Un client a supprimé son contenu, ou demande ses données (RGPD). **On ne restaure pas la prod** : on extrait depuis le dump global. ```sh ./extract-instance.sh .dump ``` Le script restaure le dump dans une base jetable, la réduit à cette instance, la re-dumpe, et affiche les comptages obtenus. Lire ces comptages : c'est là qu'on voit qu'un quiz est arrivé sans ses questions. Puis réinjecter en connaissance de cause — les ID sont conservés, donc une ligne encore présente en prod provoquera un conflit de clé. Choisir explicitement entre écraser et ignorer ; il n'y a pas de bon défaut. ### Ce que l'extraction ne contient pas - **Les médias.** Les blobs vivent dans Firebase Storage, la base n'en garde que le chemin. Une extraction seule rend un catalogue de liens morts. - **Les lignes sans `InstanceId`** (logs d'audit système) : elles n'appartiennent à aucun client, donc à aucun export. C'est volontaire — sans ça elles fuiteraient dans l'export de *chaque* client. - `SubscriptionPlans`, `spatial_ref_sys` et `__EFMigrationsHistory` repartent entiers : données de référence, pas données client. ## Vérifier une sauvegarde sans rien restaurer ```sh ./restore-check.sh .dump ``` Restaure dans une base jetable, compare les comptages table par table avec la base courante, puis supprime la base jetable. Aucun effet sur la prod. Écart attendu : `ContentEmbeddings` à 0. --- ## Pièges rencontrés pour de vrai **« collation version mismatch »** — bloque la création de toute base, donc toute restauration. Arrivé deux fois en local (09/08 et 07/09). Dans cet ordre ; le second seul masque le symptôme sans le corriger : ```sql REINDEX DATABASE ""; ALTER DATABASE "" REFRESH COLLATION VERSION; ``` Peut concerner `template1` et `postgres`, pas seulement la base applicative — et c'est `template1` qui bloque `createdb`. **`pg_restore --list /dev/stdin`** échoue (« did not find magic string ») : un pipe n'est pas seekable. Sans argument, `pg_restore` lit stdin correctement. **`docker exec -i` dans une boucle** consomme le stdin de la boucle. Ne pas passer `-i` quand la commande n'en a pas besoin. **`"InstanceId" <> 'x'` ne supprime pas les `NULL`.** En SQL `NULL <> 'x'` vaut `NULL`, pas vrai. Utiliser `is distinct from`. Repéré parce que la somme des extractions par client dépassait le global de 37 lignes.