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>
4.0 KiB
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)
-
Récupérer le dump depuis la destination hors site :
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.
-
Monter Postgres avec ses extensions (
postgisetvector), puis appliquer le schéma :docker compose -f docker-compose.preprod.yaml up -d postgres dotnet ef database update -
Restaurer :
docker exec -i myim_postgres pg_restore -U mym -d my_info_mate \ --no-owner --no-privileges < <fichier>.dump -
Relancer l'indexation des embeddings.
ContentEmbeddingsest 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. -
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.
./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_syset__EFMigrationsHistoryrepartent entiers : données de référence, pas données client.
Vérifier une sauvegarde sans rien restaurer
./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 :
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.