Reality BridgeGuidePour adminsS'applique de 4.2.5
Sauvegarde et restauration
This page in EnglishCette page sur le wiki GitHub
Comment sauvegarder le stockage d’un Reality Bridge dans un seul fichier, le restaurer tout ou rien, et remettre un
Bridge en route après la perte d’une instance ou de sa base. Il s’agit de la sauvegarde du Bridge
(npm run bridge -- backup), pas de celle du Studio hébergé (studio-server backup,
Sauvegardes sur Clever Cloud).
Ce que vous obtenez
Un fichier de sauvegarde du stockage entier, pris en un seul instantané, avec une empreinte qui détecte un fichier modifié ou tronqué par accident ; et, le jour où il le faut, un stockage restauré depuis ce fichier en une seule transaction, vérifié avant et après sa validation, dans un mode que vous nommez.
Contexte et versions
- Un Bridge sur un stockage SQL (SQLite ou Postgres). Le journal JSON-lines peut être sauvegardé, mais pas restauré : migrez-le d’abord vers SQLite (Stockages).
- À partir de la 4.1.20 : le schéma 4 des sauvegardes (ADR 0023), les deux modes de restauration et les baux de
maintenance. Le schéma 3 est venu avec la 4.1.19 (ADR 0021). Une restauration lit les schémas 1 à 4. Cette page
s’applique à partir de la 4.2.5 (les noms
quest-n-click-bridge). - Vérifié sur :
backup,restore --mode=merge(refusé, puis avec--force) etrestore --mode=replace-storeont été lancés le 23 octobre 2026 sur un stockage SQLite du jeusignals(un tenant, un signal), macOS, Node 22.14, moteur 4.4.0. Un gros stockage ou un stockage Postgres n’ont pas été rejoués pour cette page ; les répétitions en CI sont nommées dans les Références.
Prérequis
- Le dossier du Bridge (
--dir, avecconfig.json) et l’accès à son stockage (le fichier SQLite, ou l’URL Postgres dansBRIDGE_STOREouBRIDGE_STORE_FILE). - De la mémoire pour la taille du fichier, environ 10 Mo par Mio sauvegardé et 20 Mo par Mio restauré (la mesure plus bas).
- Pour une restauration : toutes les instances du Bridge sur ce stockage arrêtées, et toute instance antérieure à la 4.1.20 arrêtée avant la première restauration (elles ne prennent pas de bail de maintenance).
Où lancer les commandes
Sur une machine qui atteint le stockage, avec le dossier du Bridge : la machine du Bridge, ou une machine avec plus de
mémoire (une restauration n’a besoin que d’atteindre le stockage). npm run bridge -- … dans le dépôt du moteur ;
npx quest-n-click-bridge … avec le paquet autonome.
Étapes
-
Sauvegardez le stockage :
# sur la machine du Bridge, dans le dépôt du moteur npm run bridge -- backup --out=bridge-2026-10-23.jsonElle affiche
✔ 1 tenant(s), 1 signal(s), 0 run(s), 0 daily record(s) backed up to …/bridge-2026-10-23.json (schema 4, one snapshot, sha256 1edef3c0f48b…). Le stockage entier est lu dans une seule transaction en lecture seule : tenants, runs, enregistrements du jour et quota des speedruns. Le fichier est en mode 600, écrit dans un fichier temporaire puis renommé. Il contient les empreintes des capacités, jamais une capacité ni une clé.Son enveloppe donne le backend, les versions du Bridge et du schéma SQL, ce qu’elle contient (
completeness:whole-storepour un stockage SQL,tenants-onlypour le journal ou la mémoire), le début et la fin, les comptes par famille, et un SHA-256 de l’ensemble (scope: envelope).
- Pour restaurer, arrêtez d’abord chaque
servesur ce stockage. Depuis la 4.1.20, le stockage refuse une restauration tant que quelque chose tient un bail vivant, en nommant qui le tient et jusqu’à quand ; une restauration ne tourne jamais à côté d’écrivains vivants.
-
Restaurez avec
--mode=merge: dans un stockage vide, ou avec--forcepar-dessus les tenants que contient le fichier.# sur une machine qui atteint le stockage, dans le dépôt du moteur npm run bridge -- restore --from=bridge-2026-10-23.json --mode=merge # dans un stockage vide, sinon refusé npm run bridge -- restore --from=bridge-2026-10-23.json --mode=merge --force # les tenants du fichier remplacés, les autres gardésUn conflit (un tenant, un run ou un quota de ses tenants, une clé du jour) la refuse sans
--force:✖ the store already holds tenant(s) default: restore into an empty store, or --force; nothing restored. Avec--force, les familles du fichier remplacent ce avec quoi elles entrent en conflit, et ce dont le fichier ne parle pas reste (un autre tenant, une autre clé du jour). Tout schéma, de 1 à 4.
-
Ou restaurez avec
--mode=replace-store: le stockage devient le fichier.# sur une machine qui atteint le stockage, dans le dépôt du moteur npm run bridge -- restore --from=bridge-2026-10-23.json --mode=replace-store --yes-replace-store --safety-out=before-restore.jsonToutes les tables sauf celles de contrôle (
schema_migrations) sont vidées dans la même transaction, puis le fichier est écrit et relu, rien de plus n’est gardé. Ce mode n’accepte qu’une sauvegarde de schéma 4 d’un stockage SQL entier, au schéma SQL de ce stockage. D’abord, une sauvegarde du stockage remplacé est écrite dans--safety-out, un chemin qui ne doit pas exister (pas de lien non plus), relue et vérifiée ; si cela échoue, rien n’est supprimé. Son chemin est rappelé dans chaque issue.
-
Vérifiez le stockage, puis relancez le Bridge :
# dans le dépôt du moteur npm run bridge -- doctor
Résultat attendu, et comment le vérifier
restore se termine avec le code 0 et une ligne : ✔ 1 tenant(s), 0 run(s), 0 daily record(s), 0 quota row(s) restored from <date> (schema 4, merge), checked before and after the commit (avec replace-store, suivie de (the store before: <fichier de sécurité>)). Le fichier a été lu de façon bornée et vérifié en entier avant l’ouverture du
stockage (schémas 1 à 4 ; chaque ligne, les doublons, les relations, les comptes et l’empreinte de la charge du schéma
3, l’empreinte de l’enveloppe du schéma 4, les dates, la version et la complétude), écrit dans une transaction, relu
et comparé avant la validation, puis audité après. doctor liste ensuite les joueurs et les signaux de chaque tenant,
sans trou.
Erreurs fréquentes
| Message ou code de sortie | Cause | Correction |
|---|---|---|
code 2, say the mode: --mode=merge … --mode=replace-store …; nothing restored | Pas de --mode | Nommer le mode |
the store already holds tenant(s) …: restore into an empty store, or --force; nothing restored | Un conflit en merge | Un nouveau stockage, ou --force |
replace-store needs a schema 4 backup of a whole SQL store (…) | Un fichier plus ancien, ou venu du journal (tenants-only) | --mode=merge, ou une nouvelle sauvegarde |
the backup comes from a store at schema N, newer than this one (M): upgrade the Bridge | Le moteur est plus ancien que la sauvegarde | Mettre le Bridge à jour |
the JSON-lines journal cannot be restored all or nothing: migrate it to SQLite first … | Le stockage est le journal | migrate --from=jsonl --to=sqlite, puis restaurer avec --store=sqlite |
the backup would be N bytes, more than M (--max-bytes=): no file written | Un stockage trop gros pour un fichier | Les outils de la base elle-même |
Un refus qui nomme le détenteur du bail, ; nothing restored | Un Bridge sert encore le stockage | L’arrêter |
code 3, P0: the restore committed but … | Une différence trouvée après la validation | Arrêter toutes les instances, garder ce stockage pour examen ; un écrivain pendant la restauration peut l’expliquer |
code 3, the commit's answer was lost … | La connexion est tombée à la validation | doctor, et une sauvegarde, pour voir de quel côté c’est tombé |
Fichiers ou données modifiés
backup: le fichier--outseulement (mode 600). Elle lit le stockage.restore --mode=merge: les tenants, runs, enregistrements du jour et quotas que contient le fichier (avec--force, ceux avec lesquels il entre en conflit sont remplacés). Le reste du stockage reste.restore --mode=replace-store: toutes les tables saufschema_migrations; et le fichier--safety-out, écrit en premier.
Reprise ou retour en arrière
- Une restauration ratée dit « nothing restored » et laisse le stockage tel qu’il était (une seule transaction).
- Après un
replace-store, le chemin du retour est son fichier de sécurité :restore --from=<fichier de sécurité> --mode=replace-store …avec un nouveau--safety-out. - Une instance tuée ou plantée : en lancer une autre ; rien n’est perdu (une transaction est validée ou absente ;
tests/bridge-fanout.test.tsen tue une sur trois pendant 1 000 propositions). - La base perdue : restaurer la dernière sauvegarde dans une nouvelle, puis
doctor. Les connecteurs renvoient ce qu’ils ont envoyé depuis (leurdedupeKeyrend l’envoi idempotent), et les joueurs gardent leurs curseurs. - Un journal qui ne démarre pas :
doctornomme la ligne. - Une clé ou une racine Biscuit compromise, ou l’hôte d’une instance : Sécurité.
Santé et mesures
GET /livez: le processus répond.GET /readyz: le stockage répond (503 sinon).GET /healthz: les deux, avec les tenants, le type de stockage et les flux ouverts. Sans jeton, sans donnée d’un tenant.- Mesures : acceptations, doublons, refus par code, accusés de réception, nouvelles tentatives du stockage,
quarantaine, flux refusés, l’arriéré et la latence d’une proposition. Toujours gardées dans le processus ; par l’API
de métriques d’OpenTelemetry quand
@opentelemetry/apiest installé à côté du Bridge, exportées en OTLP quandOTEL_EXPORTER_OTLP_ENDPOINTest défini et@opentelemetry/sdk-nodeinstallé. Attributs : le tenant et un code, jamais un joueur ni une charge. - Une ligne gardée qui ne se vérifie plus est mise en quarantaine, jamais livrée, et
doctorla liste.
Mémoire : dimensionner la machine à la sauvegarde
backup, tenant export et restore tiennent le fichier entier en mémoire, lu et canonique ; une restauration le
tient deux fois (le fichier, puis le stockage relu pour comparer).
Dans un conteneur de 512 Mio, une restauration tient jusqu’à environ 20 Mio de sauvegarde. Lancez une plus grosse
depuis une machine qui a la mémoire, ou abaissez --max-bytes et --max-rows à ce que la machine tient, pour qu’un
fichier trop gros soit refusé avant d’être lu.
Les baux de maintenance (4.1.20)
À partir du schéma 5 (migration 0005), chaque écrivain tient un bail dans le stockage lui-même (ADR 0023) :
serveet chaque commande qui écrit (migrate,tenant delete,backupettenant export, qui montent le schéma) tiennent un bailserving, côte à côte.restore, etmigrate --schema=Nvers le bas, prennent le bailexclusive. Ils sont refusés, en nommant qui tient le stockage et jusqu’à quand, tant que quoi que ce soit d’autre tient un bail vivant : aussitôt sur SQLite ; sur Postgres après le--lock-timeoutdu verrou consultatif au plus (30 s par défaut, jamais attendu plus longtemps).- Un bail expiré (un processus tué) est supprimé, jamais gênant. Toutes les heures sont celles de la base.
- Un
servequi perd son bail (non renouvelé, ou la connexion de son verrou tombée) retient ses écritures, qui répondent 503, et échoue à/readyzjusqu’à ce qu’il reprenne un bail ; de même pour un processus qui se fige au-delà des deux tiers de son bail, sur sa propre horloge. Une restauration qui perd son bail avant la validation est annulée.
Les lignes d’un seul tenant
tenant export --tenant=<id> [--out=<fichier>] écrit les lignes d’un tenant, dans chaque table (sur la sortie
standard sans --out) ; tenant delete --tenant=<id> --yes les supprime. Sans --yes, rien n’est supprimé. La
sauvegarde est logique, portable et bornée ; pour un gros déploiement, pg_dump ou les instantanés du fournisseur
restent la sauvegarde de la base elle-même.
Références
- Le code :
bridge/src/backup/,bridge/src/cli-store.ts,bridge/src/maintenance.ts. - La conception : ADR 0021 (un instantané, tout ou rien), ADR 0023 (la complétude, les modes, les baux).
- Répété par
tests/bridge-backup.test.ts,tests/bridge-backup-v4.test.ts,tests/bridge-maintenance.test.tsettests/bridge-ops.test.ts; la topologie entière détruite et restaurée parnpm run ops:qualify(Le déploiement de référence). - La ligne de commande du Bridge · Dépannage.