Dump -Fc avec vérification de relecture et rotation, test de restauration qui compare les comptages table par table, extraction par instance, contrôle de fraîcheur, units systemd et procédure de restauration écrite. Le dump Mongo est volontairement en lecture seule : aucun mongorestore, pour qu'aucune erreur de manipulation ne puisse écrire dans la prod. Ce qui manque pour que ce soit une sauvegarde : BACKUP_DEST n'est pas tranché. Tant qu'il est vide, le dump reste sur la machine qui héberge la base et ne protège de rien — le script le dit à chaque exécution. Et notify.sh ne fait qu'un logger, donc les trois surveillances écrivent dans un journal que personne ne lit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.0 KiB
Markdown
100 lines
4.0 KiB
Markdown
# 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/<fichier>.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 < <fichier>.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-global>.dump <instanceId>
|
|
```
|
|
|
|
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 <fichier>.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 "<base>";
|
|
ALTER DATABASE "<base>" 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.
|