Aller au contenu
quest'n'click

Clever CloudGuidePour adminsS'applique de 4.2.3

Sauvegardes

This page in EnglishCette page sur le wiki GitHub

Un Studio hébergé protège son jeu deux fois : le journal de chaque modification confirmée est expédié vers le bucket à mesure qu’il s’écrit, et une sauvegarde quotidienne des tables du projet est écrite dans le même bucket. Cette page met en place la sauvegarde quotidienne, dit ce qu’une sauvegarde contient et ne contient pas, et combien de temps les sauvegardes sont gardées.

Ce que vous obtenez

Une sauvegarde backups/<date>-r<révision>.json dans le bucket de l’instance chaque jour à 03:17 UTC, les 14 plus récentes gardées (ou STUDIO_BACKUP_KEEP), sa ligne "event":"backup" dans les journaux de l’application studio, et la façon d’en faire une à la main avant une montée de version. Avec le journal expédié depuis, une sauvegarde restaure le jeu jusqu’à sa dernière révision expédiée (exercice de restauration).

Contexte et versions

  • Un Studio hébergé sur Clever Cloud. backup <fichier> et restore existent depuis la 4.2.2 ; backup --to-bucket --keep et la tâche quotidienne depuis la 4.2.3.
  • Vérifié sur : 19 octobre 2026, par le déploiement de référence (moteur au commit c66fbf56, package.json 4.2.2 avant le tag 4.2.3) : la tâche installée sur l’application studio (crontab -l la montrait), le script lancé à la main sur l’instance 0 sans --config, et --keep supprimant la plus ancienne. Non observés : le passage de 03:17 UTC lui-même, et qu’une seule sauvegarde soit faite par jour.

Prérequis

  • Un Studio déployé (Premier déploiement) dont la configuration a blobs.kind: "s3".
  • Pour une copie hors du bucket : un outil S3 (la console Cellar, s3cmd, aws s3) et les identifiants Cellar.

Où lancer les commandes

  • clever env set, clever ssh, clever logs : sur votre machine, dans le dépôt du moteur où l’application studio est liée.
  • npm run studio-server -- backup … : dans un shell sur l’application studio (clever ssh --alias my-game-studio, puis cd "$APP_HOME"), où la configuration et les identifiants des add-ons sont déjà dans l’environnement.

Étapes

  1. Activez la sauvegarde quotidienne, sur l’application studio seulement, jamais sur le worker :

    # sur votre machine, dans le dépôt du moteur
    clever env set --alias my-game-studio STUDIO_BACKUP_CRON 1
    clever env set --alias my-game-studio STUDIO_BACKUP_KEEP 14      # facultatif ; 14 en son absence

    Le clevercloud/cron.json du dépôt lance ops/studio/backup-cron.sh chaque jour à 03:17 UTC. Clever Cloud installe les tâches d’un dépôt sur chaque application et chaque instance déployée depuis lui : le script ne fait donc rien si l’application ne définit pas STUDIO_BACKUP_CRON=1, et rien sur une autre instance que la numéro 0 (INSTANCE_NUMBER). Il lance alors npm run -s studio-server -- backup --to-bucket --keep "${STUDIO_BACKUP_KEEP:-14}". Un changement de variable s’applique au déploiement ou au redémarrage suivant.

  1. Le lendemain matin, trouvez la ligne de la sauvegarde dans les journaux (clever logs --alias my-game-studio --since 12h, arrêté par son numéro de processus : sinon il continue de suivre), ou lancez le script à la main pour la voir tout de suite :

    # dans un shell sur l'application studio (clever ssh --alias my-game-studio)
    cd "$APP_HOME" && ops/studio/backup-cron.sh

    Sa ligne est {"event":"backup","key":"backups/2026-10-19T19-38-41Z-r63.json","revision":63,"digest":"…","removed":[…]}.

  1. Avant une montée de version, ou toute opération risquée, faites-en une à la main et notez sa révision. Sans --keep, rien n’est supprimé :

    # dans un shell sur l'application studio (clever ssh --alias my-game-studio)
    cd "$APP_HOME" && npm run -s studio-server -- backup --to-bucket

    backup <fichier> écrit la même sauvegarde dans un nouveau fichier (mode 0600 ; un fichier existant est refusé), sur le disque de l’instance, qui ne survit pas à un redémarrage : sur Clever Cloud, préférez le bucket.

  1. Copiez vous-même le bucket hors de Cellar, avec un outil S3, vers un stockage où seuls les opérateurs écrivent : backups/, mais aussi sha256/ (le contenu des fichiers) et journal/ (les opérations expédiées). Une sauvegarde nomme ses blobs sans les contenir, et une restauration refuse une sauvegarde dont des blobs manquent : une copie de backups/ seule ne restaure rien. Le bucket contient les trois ensemble : une sauvegarde survit à la perte de la base, pas à celle du bucket ou de ses identifiants.

Ce qu’une sauvegarde contient, et ce qu’elle ne contient pas

ContientNe contient pas
Les tables du projet, lues en un seul instantané en lecture seule : la tête (studio_files, studio_meta), les opérations confirmées (studio_ops), les membres, utilisateurs, invitations, le journal d’audit, l’annulation personnelle, les verdicts de santé, les prévisualisations, les builds publiés, les ensembles préparés, les commentairesLe contenu des fichiers : elle liste les blobs qu’elle nomme, par leur hash ; ils restent dans le bucket
Un condensé (sha256 de ses tables), vérifié à la restauration : une sauvegarde altérée ou tronquée est refuséeLes sessions et les connexions en cours : une restauration déconnecte tout le monde
La version du schéma où elle a été faite : une restauration demande la mêmeLes baux (celui de l’écrivain, celui du service, ceux des pans) : personne ne tient de pan après une restauration
Les opérations en cours ou abandonnées à l’instantané ; les réponses gardées pour la nouvelle tentative d’un client (7 jours) ; les tickets et sessions de l’origine du jeu

Une sauvegarde est une entrée de confiance, comme les identifiants de la base : elle peut ajouter des owners. Son condensé dit qu’elle est entière, pas qui l’a faite : gardez-la là où seuls les opérateurs écrivent.

Le journal expédié vers le bucket

Chaque opération confirmée est copiée dans le bucket, dans l’ordre, en segment immuable (journal/<époque>/<ligne>/<rev>.json ; les contenus sont déjà des blobs). Un échec est consigné ship-failed et retenté sous 30 s. /readyz donne rpoMs, l’âge de la plus ancienne opération pas encore expédiée (0 quand tout l’est) : la cible est une minute (D42), surveillée, jamais promise (RPO et RTO). La ligne vaut 0, puis une nouvelle après chaque restauration : les révisions qu’une instance restaurée réécrit n’écrasent jamais les segments de la chronologie qu’elle a quittée. Faites une nouvelle sauvegarde après une restauration : les suivantes rejouent sur la nouvelle ligne.

Ce que fait une restauration

La procédure, c’est l’exercice de restauration ; voici ce que fait la commande, en bref. Elle ne restaure qu’une instance hébergée, dans une base vide (un nouvel add-on PostgreSQL, le même bucket) : npm run studio-server -- restore <fichier> --replay --config <fichier> vérifie le condensé, les tables et les colonnes, et que chaque blob de la tête et du journal est dans le bucket (une prévisualisation, un build ou un ensemble préparé dont les blobs ont été ramassés depuis est laissé de côté et compté dans dropped) ; écrit les lignes ; puis rejoue le journal expédié depuis la sauvegarde. Chaque opération rejouée doit continuer la tête (sa révision, le contenu précédent de chaque fichier, ses blobs) ; le rejeu s’arrête à la première qui ne le fait pas (stopped : gap, not-the-head, blob-missing…). S’il échoue en cours de route, le serveur refuse de démarrer jusqu’à ce que npm run studio-server -- replay --config <fichier> le reprenne. Démarrez ensuite le serveur : son dossier est refait depuis la tête. Les opérations rejouées ne gardent pas leur réponse : un client qui en retente une reçoit null. Faites une nouvelle sauvegarde après une restauration : les suivantes rejouent sur la nouvelle ligne.

Depuis les archives de la release

Les archives de la release ne portent pas ops/studio/backup-cron.sh. Copiez-le dans le dépôt de votre application en remplaçant sa dernière ligne par :

# la dernière ligne de backup-cron.sh, dans le dépôt de votre application (lancée par la tâche de Clever sur l'application studio)
exec npx quest-n-click-studio-server backup --to-bucket --keep "${STUDIO_BACKUP_KEEP:-14}"

rendez-le exécutable, et pointez votre propre clevercloud/cron.json dessus : ["17 3 * * * $ROOT/backup-cron.sh"]. Clever Cloud lance une tâche sans environnement à moins que son script commence par #!/bin/bash -l (la première ligne du script), et avec des chemins absolus seulement ($ROOT est le dossier de l’application).

Résultat attendu, et comment le vérifier

  • Un nouvel objet backups/…json chaque jour dans le bucket, et sa ligne "event":"backup" dans les journaux.
  • Pas plus de STUDIO_BACKUP_KEEP d’entre eux (les autres objets sous backups/ sont laissés tranquilles).
  • La vraie vérification, c’est une restauration : faites l’exercice de restauration au moins une fois, et après chaque montée de version.

Erreurs fréquentes

SymptômeCauseCorrection
Pas de ligne "event":"backup" un matinSTUDIO_BACKUP_CRON n’est pas 1, pas de redéploiement depuis, ou pas d’INSTANCE_NUMBER=0Dépannage
backup --to-bucket writes to the instance's bucket: blobs.kind is s3Une configuration localeLancez-la sur l’application studio
backup: --keep is the number of backups kept (1 or more)STUDIO_BACKUP_KEEP vaut 0, est négatif ou n’est pas un entierUn entier à partir de 1
backup: a new file is required (or --to-bucket [--keep n])backup sans fichier, ou avec un fichier existantUn nouveau nom de fichier, ou --to-bucket
Deux sauvegardes à la même minuteUn redéploiement à chevauchement à 03:17 : deux instances numérotées 0 un instantSans conséquence

Fichiers ou données modifiés

Des objets sous backups/ dans le bucket ; avec --keep, les plus anciens des siens supprimés. Rien dans la base.

Reprise ou retour arrière

Une sauvegarde supprimée par --keep est perdue, sauf si vous l’avez copiée ailleurs (étape 4). Restaurer une sauvegarde, c’est l’exercice de restauration, dans une nouvelle base.

Ramasser les blobs inutilisés

npm run studio-server -- gc liste les blobs qu’aucune ligne ne nomme (plus vieux d’un jour) et le staging du worker de plus d’une heure ; --delete les supprime. Un blob stocké à nouveau reçoit une nouvelle date : une écriture qui nomme un vieux blob est couverte par le jour de grâce. Les blobs de la tête et du journal ne sont jamais ramassés (le journal garde chaque opération) ; une sauvegarde plus ancienne restaurée après un ramassage ne perd que les prévisualisations, builds et ensembles préparés ramassés (comptés dans dropped). Ne lancez jamais gc --delete contre la base d’un exercice : il supprimerait des blobs que la production nomme encore.

Références