Les snapshots sont la monnaie de la réplication ZFS. Vous les créez, les échangez sur le réseau avec zfs send/zfs receive, et parfois vous en perdez un à 2 h du matin parce qu’un job de rétention est devenu « créatif ». Quand ça arrive, votre chaîne incrémentielle se casse, la réplication suivante devient un envoi complet, et votre lien WAN commence à fumer comme un grille-pain sous la pluie.
Les signets ZFS sont la fonctionnalité discrète qui rend ces désastres ennuyeux à nouveau. Ce ne sont pas des snapshots. Ils ne contiennent pas de données. Ce sont de petites références à « l’état du système de fichiers à l’instant de ce snapshot », et cette référence peut maintenir un flux incrémentiel vivant même si le snapshot a disparu. Les signets sont ce que vous auriez voulu avoir configuré la dernière fois que quelqu’un a dit : « On peut tailler agressivement ; la réplication ira bien. »
Ce que sont réellement (et ne sont pas) les signets ZFS
Un signet ZFS est une référence nommée au GUID d’un snapshot et à sa position dans le groupe de transactions (TXG). Pensez-y comme un « point de sauvegarde » qui dit : ce dataset ressemblait à ceci au moment du snapshot X — sans conserver l’espace de noms visible du snapshot X. C’est uniquement des métadonnées : pas d’arbre de fichiers, pas de vue montable, pas de navigation de répertoires.
Les signets existent pour une raison principale : les envois incrémentiels ont besoin d’un ancêtre commun des deux côtés. Normalement, cet ancêtre est un snapshot présent sur l’émetteur et le récepteur. Avec les signets, le récepteur peut conserver l’ancêtre sous forme de signet même si le snapshot est ensuite détruit, et ZFS peut toujours accepter des incrémentiels qui font référence à ce point de l’historique.
Les signets ne sont pas magiques :
- Ils ne vous permettent pas de restaurer des fichiers ; ils ne sont pas montables.
- Ils ne préservent pas les blocs de données à eux seuls ; ils ne verrouillent pas les blocs comme le font les snapshots.
- Ils ne peuvent pas ressusciter un flux incrémentiel si l’émetteur a perdu l’ancêtre et ne peut pas générer le flux adéquat.
Mais pour les pipelines de réplication — où le récepteur doit souvent se souvenir du « dernier point sain » et où l’émetteur doit continuer à partir de là — les signets font la différence entre « l’incrémentiel continue » et « on renvoie 40 To à nouveau ».
Blague n°1 (courte et pratique) : un signet, c’est comme noter le numéro de votre chambre d’hôtel. Si vous perdez la carte (snapshot), vous pouvez toujours dire à l’accueil où vous étiez censé être.
Pourquoi la réplication incrémentielle se casse
L’envoi incrémentiel ZFS fonctionne en transmettant seulement les blocs modifiés entre deux snapshots (ou entre un snapshot et un signet, selon le cas). L’important est que les deux côtés doivent s’accorder sur le point « depuis ». S’ils ne le font pas, ZFS refuse d’appliquer le flux car il ne peut pas prouver qu’il aboutira au même état du système de fichiers.
La plupart des chaînes cassées sont auto-infligées. Causes courantes :
- Élagage des snapshots côté récepteur : quelqu’un supprime d’anciens snapshots pour gagner de l’espace, sans réaliser que la réplication nécessite au moins un snapshot commun (ou un signet) pour ancrer les incrémentiels.
- Élagage des snapshots côté émetteur : l’émetteur perd le snapshot « depuis » et ne peut plus générer l’incrémental attendu par le récepteur.
- Réplication vers le mauvais dataset : la cible a été recréée, rollbackée ou remplacée. Le « dernier snapshot » du récepteur n’est alors plus lié à l’émetteur.
- Hypothèses de nommage incohérentes : les scripts supposent que « le snapshot le plus récent est le plus récent », mais les fuseaux horaires, la dérive d’horloge ou des changements de nom font que le « plus récent » n’est pas forcément celui utilisé la dernière fois.
Quand ça casse, les opérateurs choisissent souvent la solution de moindre résistance : faire un envoi complet. Ça marche — finalement. Mais ça dévore la bande passante, les IOPS et la patience. Les signets vous permettent de conserver l’ascendance côté récepteur même quand vous supprimez des snapshots, et ça change la décision de « envoi complet maintenant » à « l’incrémentiel continue comme prévu ».
Faits intéressants et contexte
Un peu de contexte aide car les signets sont une de ces fonctionnalités que l’on suppose nouvelles, exotiques ou « réservées aux grandes entreprises ». Ce n’est pas le cas.
- Les signets ont été conçus spécifiquement pour l’hygiène de réplication : conserver la lignée incrémentielle sans garder des snapshots complets indéfiniment.
- Ils sont uniquement des métadonnées : créer un signet est rapide et peu coûteux en espace, typiquement une « marge d’arrondi » comparée à l’espace des snapshots.
- Les snapshots verrouillent les blocs ; les signets ne le font pas : un signet n’empêche pas la libération d’espace quand les blocs deviennent libres. C’est intentionnel : l’ascendance de réplication sans alourdir la rétention.
- Les envois incrémentiels peuvent référencer des signets (par ex. envoyer depuis un signet vers un nouveau snapshot), ce qui est utile opérationnellement quand vous voulez élaguer des snapshots sur le récepteur.
- Le côté réception peut conserver un signet pour le dernier snapshot répliqué, puis supprimer ce snapshot, et accepter quand même des incrémentiels faisant référence à ce point — à condition que l’émetteur puisse toujours les générer.
- Les signets ont leur propre espace de noms : on les liste avec
zfs list -t bookmarket on les gère séparément des snapshots. - Beaucoup d’outils de réplication ont ajouté le support des signets après les snapshots, donc les scripts anciens peuvent les ignorer à moins d’être explicitement configurés.
- Dans les versions modernes d’OpenZFS, le support des signets est généralisé sur les plateformes courantes, mais des flottes de versions mixtes trébuchent encore sur les flags de fonctionnalités et les options de flux.
Mécanique de base : comment les signets sauvent les incrémentiels
Voici le modèle mental qui tient en pression d’incident :
- Un snapshot est un point de contrôle complet et navigable d’un dataset. ZFS l’utilise pour calculer les différences et fournir une vue stable.
- Un signet est une référence non navigable au point exact d’un snapshot dans l’historique (son GUID et métadonnées associées).
- Un flux incrémentiel est une transformation de l’état A vers l’état B. Pour des raisons de sécurité, le récepteur doit confirmer qu’il est actuellement à l’état A avant d’appliquer la transformation.
Alors, comment les signets aident-ils quand quelque chose tourne mal ?
Supposons que le récepteur avait le snapshot pool/fs@replica_2025-12-01, et que vous avez pris des snapshots plus récents et répliqué incrémentiellement à partir de celui-ci. Si quelqu’un supprime @replica_2025-12-01 sur le récepteur, vous avez retiré l’ancre « état A ». Avec un signet, vous faites plutôt ceci :
- Créer le signet
#replica_2025-12-01qui pointe vers le GUID de ce snapshot. - Éventuellement supprimer le snapshot
@replica_2025-12-01pour réduire l’encombrement de l’espace de noms (et possiblement libérer de l’espace s’il verrouillait des blocs). - Continuer à recevoir des incrémentiels qui se réfèrent à cet état, car ZFS peut valider le « depuis » contre le signet.
Contraintes clés : l’émetteur doit encore avoir le snapshot « depuis » (ou quelque chose d’équivalent) pour calculer les différences incrémentielles. Les signets ne peuvent pas inventer des deltas. Ils résolvent le problème d’ascendance côté récepteur, pas côté émetteur.
Blague n°2 (courte et pertinente) : la réplication ZFS, c’est comme la thérapie de couple : ça ne marche que si les deux parties s’accordent sur ce qui s’est passé la dernière fois.
Tâches pratiques avec commandes (et ce que la sortie signifie)
Ci‑dessous des tâches réelles que vous pouvez faire aujourd’hui. Rédigées dans la voix « runbook » que j’aimerais voir plus d’équipes utiliser : commande, sortie d’exemple et ce que ça implique. Adaptez les noms de dataset à votre environnement.
Task 1: Confirm bookmark feature support and dataset health
cr0x@server:~$ zpool status -x
all pools are healthy
cr0x@server:~$ zfs get -H -o value -s local,received,default all pool/fs | head -n 1
on
Interprétation : Commencez par « le pool est-il malade ? » avant d’accuser la réplication. Si le pool est dégradé, vous poursuivez peut‑être des symptômes de performance dus à un resilver ou des erreurs de checksum, pas aux signets.
Task 2: List snapshots and bookmarks separately (don’t assume you’re seeing both)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,mountpoint -s creation pool/fs | tail -n 3
pool/fs@replica_2025-12-20 0B 1.20T -
pool/fs@replica_2025-12-21 0B 1.21T -
pool/fs@replica_2025-12-22 0B 1.22T -
cr0x@server:~$ zfs list -t bookmark -o name,createtxg,guid -s createtxg pool/fs | tail -n 3
pool/fs#replica_2025-12-19 19548312 17084256651437522314
pool/fs#replica_2025-12-20 19591288 5721345558355301722
pool/fs#replica_2025-12-21 19634801 15884259650518200641
Interprétation : Les snapshots et les signets vivent dans des listes différentes. Si votre outil ne regarde que les snapshots, il peut vous dire « pas de snapshot commun », alors que ZFS pourrait encore avoir un signet ancre.
Task 3: Create a bookmark from an existing snapshot
cr0x@server:~$ zfs bookmark pool/fs@replica_2025-12-22 pool/fs#replica_2025-12-22
Interprétation : C’est l’opération centrale. Vous nommez une ancre de réplication. Le nom du signet peut suivre votre convention de nommage de snapshot ; rappelez-vous simplement qu’il utilise #, pas @.
Task 4: Safely delete an old snapshot after bookmarking it (receiver-side pruning)
cr0x@server:~$ zfs destroy pool/fs@replica_2025-12-20
cr0x@server:~$ zfs list -t bookmark pool/fs#replica_2025-12-20
NAME CREATETXG GUID
pool/fs#replica_2025-12-20 19591288 5721345558355301722
Interprétation : Vous pouvez supprimer le snapshot tout en gardant le pointeur d’ascendance. C’est le mouvement « sauver les incrémentiels quand la rétention chauffe ».
Task 5: Verify you still have a common base on the receiver after pruning
cr0x@server:~$ zfs get -H -o name,value type pool/fs
pool/fs filesystem
cr0x@server:~$ zfs list -t snapshot,bookmark -o name -s name pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20
Interprétation : Votre ancre de chaîne est présente même si le snapshot ne l’est pas. C’est ce qui maintient la viabilité des incrémentiels futurs.
Task 6: Send an incremental stream using a bookmark as the “from” point
cr0x@sender:~$ zfs send -nv -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22
send from pool/fs#replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G
cr0x@sender:~$ zfs send -w -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | ssh backup1 zfs receive -u -F pool/fs
receiving full stream of pool/fs@replica_2025-12-22 into pool/fs@replica_2025-12-22
received 18.4G stream in 00:06:12 (50.7M/sec)
Interprétation : Le dry-run (-n) vous indique si ZFS reconnaît le signet comme base valide et estime la taille du transfert. Le send réel utilise -w (raw) quand c’est approprié ; si vous n’utilisez pas les flux raw/chiffrés, supprimez cette option.
Task 7: Diagnose “incremental source is not earlier than destination” (a common mismatch)
cr0x@sender:~$ zfs send -i pool/fs@replica_2025-12-22 pool/fs@replica_2025-12-21
cannot send 'pool/fs@replica_2025-12-21': incremental source (pool/fs@replica_2025-12-22) is not earlier than destination (pool/fs@replica_2025-12-21)
Interprétation : Vous essayez d’envoyer « à l’envers » dans le temps. En général c’est un bug de script qui a choisi le mauvais « plus récent ». Corrigez votre logique de sélection de snapshot, ne forcez pas une solution par envoi complet.
Task 8: Show what the receiver thinks it has (snapshots vs bookmarks)
cr0x@backup1:~$ zfs list -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-22
cr0x@backup1:~$ zfs list -t bookmark -o name -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-19
pool/fs#replica_2025-12-20
pool/fs#replica_2025-12-21
Interprétation : Ce récepteur manque les snapshots 20–21 mais a gardé des signets. C’est un schéma normal pour « garder peu de points de récupération, garder beaucoup d’ancres ».
Task 9: Confirm GUID lineage (when you suspect you’re replicating to the wrong target)
cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20 5721345558355301722
Interprétation : Des GUID qui correspondent sont une preuve solide que vous êtes alignés sur l’ascendance. Si ces GUID ne correspondent pas, vous ne parlez pas du même historique, peu importe à quel point les noms se ressemblent.
Task 10: Convert “latest replicated snapshot” into a bookmark automatically
cr0x@backup1:~$ last=$(zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 1)
cr0x@backup1:~$ echo "$last"
pool/fs@replica_2025-12-22
cr0x@backup1:~$ zfs bookmark "$last" "${last/@/#}"
Interprétation : C’est un schéma courant : après chaque receive réussi, bookmarkez le snapshot reçu, puis votre politique de rétention peut supprimer les snapshots sans rompre la chaîne.
Task 11: Destroy bookmarks safely (cleaning up anchors you no longer need)
cr0x@backup1:~$ zfs destroy pool/fs#replica_2025-12-19
cr0x@backup1:~$ zfs list -t bookmark -o name pool/fs | grep replica_2025-12-19 || echo "bookmark removed"
bookmark removed
Interprétation : Les signets peuvent s’accumuler. Si vous conservez un signet par snapshot indéfiniment, votre espace de noms finira par ressembler à un calendrier explosé. Il est acceptable d’élaguer aussi les signets — faites‑le intentionnellement.
Task 12: Estimate send size before you pull the trigger (avoid surprise full sends)
cr0x@sender:~$ zfs send -nv -i pool/fs@replica_2025-12-21 pool/fs@replica_2025-12-22
send from pool/fs@replica_2025-12-21 to pool/fs@replica_2025-12-22 estimated size is 1.7G
Interprétation : Le dry-run -n est un contrôle de sanity peu coûteux. Si vous attendez 1–3 Go et qu’il annonce 1.2 To, arrêtez‑vous et cherchez pourquoi avant de saturer votre lien de réplication.
Task 13: Check receive-side space pressure and whether deletes are actually freeing space
cr0x@backup1:~$ zfs list -o name,used,avail,refer,mountpoint pool
NAME USED AVAIL REFER MOUNTPOINT
pool 61.2T 3.8T 192K /pool
cr0x@backup1:~$ zfs get -o name,property,value -s local,received usedbysnapshots,usedbydataset pool/fs
NAME PROPERTY VALUE
pool/fs usedbysnapshots 9.4T
pool/fs usedbydataset 51.6T
Interprétation : Si vous supprimez des snapshots parce que vous manquez d’espace, mesurez si les snapshots sont réellement le coupable. Les signets ne libéreront pas d’espace (ils ne conservent pas les blocs), mais l’élagage des snapshots peut le faire — sauf si des clones, des holds ou des références actives gardent des blocs verrouillés.
Task 14: Receive with caution: understand what -F does before using it in anger
cr0x@backup1:~$ ssh sender zfs send -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -u -F pool/fs
Interprétation : zfs receive -F peut revenir en arrière sur le dataset cible pour correspondre au flux entrant, détruisant des snapshots plus récents sur le récepteur. Parfois c’est précisément ce que vous voulez pour un réplicat strict — et parfois c’est un geste limitant pour la carrière si le récepteur contient aussi des snapshots locaux pour la récupération. Décidez ce que vous exécutez : un réplicat, ou une sauvegarde avec historique local.
Trois mini-histoires du monde de l’entreprise
Mini-story 1: The incident caused by a wrong assumption
L’hypothèse était simple et fausse : « Si le récepteur a le dernier nom de snapshot, les incrémentiels fonctionneront toujours. » Le script de réplication avait été écrit par un ingénieur compétent qui n’avait jamais vécu la collision entre politiques de rétention et calendriers de réplication. Ils utilisaient des noms de snapshot avec dates, les triaient lexicographiquement, prenaient le dernier et l’appelaient la base. Ça a fonctionné pendant des mois.
Puis l’équipe de stockage a changé le nom pour inclure un suffixe de fuseau horaire parce qu’un audit demandait « explicitement UTC ». L’émetteur a commencé à créer @replica_2025-12-01Z tandis que le récepteur avait encore des snapshots plus anciens sans suffixe. Le script — toujours triant par nom — a choisi la mauvaise base. ZFS a fait ce qu’il fallait et a refusé la réception incrémentielle avec un message qui ressemblait à un argument philosophique : le flux ne correspondait pas à l’état courant du dataset.
Ops a fait ce que fait ops sous pression : tenter à nouveau avec -F, ce qui a rollbacké le récepteur et supprimé une semaine de snapshots locaux « au cas où » qui ne faisaient pas partie de la réplication. Ils se sont retrouvés avec deux problèmes : la réplication échouait encore de façon intermittente, et le support recevait des demandes de restauration impossibles à satisfaire.
La correction n’était pas héroïque. Ils ont arrêté de faire confiance aux noms et ont commencé à utiliser la lignée GUID. Le récepteur bookmarke le dernier snapshot reçu avec succès (par nom, oui, mais vérifié par GUID), et l’émetteur utilise ce signet comme base incrémentielle. Lorsque la convention de nommage a changé encore (parce que ça change toujours), la réplication s’en fichait. Elle continuait d’envoyer les deltas depuis le bon historique.
Mini-story 2: The optimization that backfired
Celle-ci commence par une bonne idée : « On peut économiser de l’espace en élaguant agressivement les snapshots sur la cible de backup. C’est un réplicat, pas un musée. » Ils ont réduit la rétention côté récepteur de 30 jours à 3 jours. Le graphe d’espace s’est amélioré. Les alertes de stockage ont arrêté de réveiller des gens. On s’est félicité en réunion qui aurait dû être un email.
Deux semaines plus tard, une fenêtre de maintenance réseau a duré plus longtemps, et la réplication a été mise en pause pendant 36 heures. Ce n’est pas dramatique en soi. Le drame est venu du job de rétention : il a supprimé le dernier snapshot commun sur le récepteur pendant la pause. Quand la réplication a repris, l’émetteur a essayé d’envoyer des incrémentiels depuis un snapshot que le récepteur n’avait plus. ZFS a refusé de recevoir, correctement, car il ne pouvait pas valider la base.
La prochaine « optimisation » de l’équipe a été de forcer des envois complets pour ce dataset « jusqu’à stabilité ». Les envois complets ont stabilisé le processus : ils l’ont stabilisé en un transfert nocturne qui a saturé le chemin d’écriture de l’array pendant la journée. Des plaintes de latence sont apparues depuis la couche base de données. Un système de backup était devenu silencieusement un problème de performance en production.
Le post-mortem était presque ennuyeux : ils auraient dû utiliser des signets côté récepteur depuis le début. Les signets auraient permis d’élaguer les snapshots sans casser la chaîne incrémentielle. Ils ont remanié le pipeline pour qu’après chaque receive réussi, le récepteur crée un signet pour ce snapshot, puis supprime les snapshots selon la rétention locale. La prochaine pause n’a posé aucun problème : la réplication a repris incrémentiellement comme si de rien n’était. L’« optimisation » est devenue une vraie optimisation au lieu d’un incident lent.
Mini-story 3: The boring but correct practice that saved the day
Une autre entreprise, ambiance différente. Leur équipe stockage était allergique à la sophistication. Ils ont écrit des runbooks. Ils pratiquaient des restaurations trimestrielles. Ils avaient une règle peu glamour : « Chaque dataset répliqué doit avoir une ancre machine‑lisible ‘dernier répliqué’ sur le récepteur, indépendante de la rétention des snapshots. » Cette ancre était un signet au nom fixe, comme pool/fs#last-received, mis à jour à chaque réplication réussie.
Un jour, un ingénieur vérifiant l’usage d’espace a lancé un script de nettoyage dans le mauvais onglet d’historique de shell. Il a supprimé un bloc de vieux snapshots sur le récepteur — exactement le type d’erreur humaine que personne n’avoue jusqu’à ce que vous montriez les logs d’audit. L’équipe a été alertée par la surveillance qui signalait « latence de réplication en hausse » et « base d’incrémentiel manquante ».
Ils n’ont pas paniqué. Le runbook indiquait : vérifier le signet au nom fixe ; valider que le GUID correspond à la base attendue ; reprendre la réplication depuis le signet. Le signet était là, pointant toujours vers le dernier état reçu. Ils n’avaient pas besoin des snapshots supprimés pour continuer les incrémentiels ; il leur suffisait du pointeur d’ascendance. La réplication a repris.
Il y a eu des conséquences, mais elles étaient contenues : moins de points de récupération sur le récepteur pour la semaine, et un petit problème de conformité parce que la rétention n’était pas respectée. Le système central est resté sain. Pas de meltdown WAN, pas de renvoi complet, pas de mystère « pourquoi la base de données est lente ». Voilà à quoi ressemble une bonne hygiène opérationnelle : ne pas empêcher toutes les erreurs, mais rendre les erreurs peu coûteuses.
Playbook de diagnostic rapide
Voici la checklist « vous avez 10 minutes avant la réunion suivante et 30 minutes avant que le lien ne sature ». Elle est ordonnée pour trouver rapidement le goulot d’étranglement et éviter les actions destructrices.
1) First: is it actually a replication-chain problem, or a system problem?
cr0x@backup1:~$ zpool status -x
all pools are healthy
cr0x@backup1:~$ zfs list -o name,used,avail pool
NAME USED AVAIL
pool 61.2T 3.8T
Interprétation : Si le pool est dégradé, presque plein ou en cours de resilver, la réplication sera lente ou échouera d’une manière qui ressemble à des problèmes send/receive. Réparez d’abord la santé du pool.
2) Second: what exact error are you getting from send/receive?
cr0x@backup1:~$ ssh sender zfs send -nv -i pool/fs@replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -nvu pool/fs
cannot receive incremental stream: incremental source (replica_2025-12-20) does not exist
Interprétation : C’est un manque de base côté récepteur. C’est là que les signets résolvent souvent le problème — si vous les aviez déjà créés.
3) Third: does the receiver have a bookmark that matches the sender’s base snapshot GUID?
cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep 5721345558355301722
pool/fs#replica_2025-12-20 5721345558355301722
Interprétation : Si vous avez un signet avec le bon GUID, vous pouvez probablement reprendre les incrémentiels sans recréer des snapshots sur le récepteur.
4) Fourth: if incrementals are valid, why is it slow?
cr0x@sender:~$ zpool iostat -v 1 3
capacity operations bandwidth
pool alloc free read write read write
---------------------------- ----- ----- ----- ----- ----- -----
pool 48.1T 12.3T 210 980 92.1M 311M
raidz2-0 48.1T 12.3T 210 980 92.1M 311M
sda - - 30 140 12.9M 42.7M
sdb - - 28 139 12.3M 42.0M
...
Interprétation : Si les écritures sont saturées sur le récepteur ou les lectures sur l’émetteur, votre goulot est le stockage, pas la logique ZFS. Si aucun n’est saturé, c’est probablement le réseau, le CPU (compression/chiffrement) ou un job de réplication sérialisé.
5) Fifth: validate the “base” and “target” selection logic in your tooling
cr0x@sender:~$ zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-20
pool/fs@replica_2025-12-21
pool/fs@replica_2025-12-22
Interprétation : La bonne base est « le dernier snapshot répliqué avec succès », pas « le snapshot le plus récent par nom » et pas « hier ». Les signets facilitent la persistance explicite de cet état.
Erreurs courantes, symptômes et corrections
Mistake 1: Treating bookmarks like snapshots
Symptôme : Quelqu’un essaie de monter ou de parcourir un signet, ou s’attend à une restauration de fichiers depuis lui.
Correction : Les signets sont uniquement des marqueurs d’ascendance. Si vous avez besoin de points de restauration, il vous faut des snapshots (ou des clones) sur le récepteur. Utilisez les signets pour maintenir les incrémentiels intacts en élaguant les snapshots, pas pour remplacer les snapshots entièrement.
Mistake 2: Only bookmarking on the receiver, while pruning on the sender
Symptôme : Le récepteur a des signets corrects, mais l’émetteur renvoie une erreur « cannot send incremental: snapshot does not exist » ou votre outil de réplication bascule quand même sur des envois complets.
Correction : L’émetteur doit conserver suffisamment de snapshots pour générer les incrémentiels. Les signets côté récepteur aident le récepteur à accepter les flux ; ils n’aident pas l’émetteur à calculer les deltas. Alignez la rétention : l’émetteur conserve au moins la base « dernier répliqué » plus ce dont votre RPO a besoin.
Mistake 3: Using zfs receive -F as a reflex
Symptôme : La réplication « marche » à nouveau, mais le récepteur a perdu des snapshots plus récents ou des points de récupération locaux. Plus tard, des demandes de restauration échouent.
Correction : Décidez si la cible est un réplicat strict (rollback acceptable) ou un dépôt de backup (historique local préservé). Si c’est le second, évitez -F ou utilisez des datasets séparés pour réplicat vs backups.
Mistake 4: Assuming snapshot names define lineage
Symptôme : Des snapshots avec des noms correspondants existent aux deux extrémités, mais les incrémentiels échouent toujours avec « does not match incremental source ».
Correction : Validez les GUIDs. Les noms peuvent se chevaucher, être recréés ou applicables à des datasets non liés après des rollbacks. Utilisez zfs get guid pour confirmer l’ascendance.
Mistake 5: Retention jobs that delete the last common base first
Symptôme : Les incrémentiels échouent juste après l’élagage, typiquement après une pause ou un backlog de réplication.
Correction : La rétention doit protéger l’ancre de base. Approche commune : maintenir un signet au nom fixe pour « last received » et/ou ne jamais supprimer les snapshots plus récents que cette ancre tant qu’une nouvelle ancre n’existe pas.
Mistake 6: Over-creating bookmarks without cleanup strategy
Symptôme : Des milliers de signets encombrent l’exploitation ; les listes prennent plus de temps ; les outils deviennent lents ou confus.
Correction : Gardez les signets pour la fenêtre nécessaire (par ex. « 60 dernières ancres ») ou conservez un signet au nom fixe « last-received » plus des signets « ancre hebdomadaire » périodiques. Élaguer le reste délibérément.
Mistake 7: Confusing “space usage improved” with “replication safety improved”
Symptôme : Vous élaguez des snapshots pour libérer de l’espace, la réplication casse ensuite, et quelqu’un propose « élaguer encore plus ».
Correction : Séparez les préoccupations : utilisez les signets pour l’ascendance de réplication, les snapshots pour les points de récupération, et surveillez usedbysnapshots pour comprendre ce qui verrouille réellement de l’espace.
Checklists / plan étape par étape
Voici un plan pragmatique que vous pouvez adopter sans réécrire tout votre système de réplication. Il suppose que vous faites déjà de la réplication basée sur snapshots et que vous voulez durcir cela avec des signets.
Checklist A: Introduce receiver-side “last replicated” bookmarks
- Après chaque receive réussi, créer/mettre à jour un signet au nom fixe pointant vers le snapshot reçu.
- Conservez votre rétention de snapshots existante pour les restaurations, mais autorisez‑la à être plus agressive car le signet protège la lignée incrémentielle.
- Assurez-vous que votre script/outils de réplication peuvent utiliser des bases de signet lors de la génération ou de la validation des incrémentiels.
cr0x@backup1:~$ snap="pool/fs@replica_2025-12-22"
cr0x@backup1:~$ zfs destroy -r pool/fs#last-received 2>/dev/null || true
cr0x@backup1:~$ zfs bookmark "$snap" pool/fs#last-received
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs#last-received
NAME GUID
pool/fs#last-received 15884259650518200641
Interprétation : Un nom de signet stable vous donne une base stable indépendamment de l’élagage des snapshots ou des changements de nommage.
Checklist B: Make retention replication-aware (avoid cutting the branch you’re sitting on)
- Identifier le snapshot correspondant à
#last-received(ou conservez‑le comme snapshot si vous préférez). - Supprimez d’abord les snapshots plus anciens ; ne supprimez jamais le snapshot ancre tant que vous n’avez pas déplacé l’ancre vers l’avant.
- Élaguer aussi les signets, mais conservez au moins le signet au nom fixe.
cr0x@backup1:~$ zfs list -t bookmark -o name,createtxg -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-20 19591288
pool/fs#replica_2025-12-21 19634801
pool/fs#replica_2025-12-22 19678110
pool/fs#last-received 19678110
Interprétation : Si #last-received suit le plus récent, votre rétention doit éviter de supprimer quoi que ce soit « plus récent que » son état TXG équivalent. En pratique, protégez le(s) snapshot(s) répliqué(s) le(s) plus récent(s) et le(s) signet(s) ancre(s).
Checklist C: Recovery procedure when a receiver snapshot was deleted
- Arrêtez les tentatives automatiques de réplication (elles peuvent déclencher des basculements vers des envois complets).
- Vérifiez si le récepteur a un signet avec le GUID de base.
- Si oui, reprenez les incrémentiels en utilisant ce signet comme base.
- Sinon, choisissez entre : recréer une base commune (rarement possible), rollback de la cible (dangereux), ou renvoi complet (coûteux mais fiable).
cr0x@sender:~$ base="pool/fs@replica_2025-12-20"
cr0x@sender:~$ next="pool/fs@replica_2025-12-22"
cr0x@sender:~$ zfs send -nv -i "$base" "$next"
send from pool/fs@replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep "$(ssh sender zfs get -H -o value guid "$base")"
pool/fs#replica_2025-12-20 5721345558355301722
Interprétation : C’est l’approche « prouve avant d’agir ». Si vous pouvez faire correspondre les GUIDs, vous pouvez généralement restaurer le flux incrémentiel sans options de receive destructrices.
Checklist D: Performance sanity for incremental streams
- Faire une estimation avec
zfs send -nv. - Vérifier le débit de lecture côté émetteur et de write côté récepteur (
zpool iostat). - Confirmer la charge CPU si vous utilisez compression ou chiffrement des flux.
- Confirmer que le dataset n’est pas thrashé par d’autres workloads pendant la réplication.
FAQ
1) Can bookmarks replace snapshots for backups?
Non. Les signets ne contiennent pas une vue navigable du système de fichiers et ne permettent pas de restaurer des fichiers directement. Ce sont des marqueurs d’ascendance pour la réplication, pas des points de récupération.
2) Do bookmarks consume space?
Ils consomment une petite quantité d’espace métadonnée. Ils ne gardent pas les blocs de données vivants comme le font les snapshots, donc ils n’entraînent pas le même comportement de rétention d’espace.
3) If I delete a snapshot but keep a bookmark, can I still do an incremental send from that point?
Côté récepteur, oui : il peut toujours valider des incrémentiels qui référencent cette base (signet). Côté émetteur, vous avez toujours besoin d’un snapshot base valide (ou d’une lignée équivalente) pour générer le delta incrémentiel.
4) What’s the operational best practice: one bookmark per snapshot or a fixed-name bookmark?
En production, un signet au nom fixe comme #last-received est la base ennuyeuse et correcte. Certaines équipes ajoutent des signets « ancre » périodiques (hebdomadaires/mensuels) pour la sécurité, mais évitez la croissance non bornée.
5) Why do I still get “does not exist” when the receiver has a bookmark?
Parce que la référence de base du flux doit correspondre à ce que le récepteur possède. Si votre commande d’envoi référence @snapname mais que le récepteur n’a que #snapname, cela peut être acceptable — mais seulement si le pair send/receive et les métadonnées du flux s’alignent. Vérifiez que le GUID correspond, et envisagez d’utiliser explicitement le signet comme base dans la commande send quand c’est supporté.
6) Are bookmarks safe across rollbacks and dataset recreation?
Ils sont sûrs dans le sens où ils restent des références exactes à l’historique du dataset depuis lequel ils ont été créés. Ils ne sont pas sûrs dans le sens de « s’appliqueront encore à un dataset détruit puis recréé avec le même nom ». Utilisez des vérifications GUID pour éviter de répliquer dans un dataset non lié.
7) Do bookmarks help if the sender lost snapshots due to pruning?
Pas directement. Si l’émetteur n’a plus le snapshot base nécessaire pour calculer un incrémentiel, vous pouvez être contraint à un envoi complet ou à une autre approche de récupération. Les signets protègent principalement la capacité du récepteur à accepter des incrémentiels sans conserver d’anciens snapshots.
8) Should I use zfs receive -F to “fix” incremental failures?
Seulement si la cible est un réplicat strict et que vous acceptez de perdre des snapshots plus récents sur le récepteur. Si le récepteur sert aussi de dépôt de backup avec rétention locale, -F peut supprimer silencieusement les points de récupération que vous voulez conserver.
9) How do bookmarks interact with encryption and raw sends?
Les signets suivent l’ascendance indépendamment de l’utilisation de flux raw, mais le chiffrement ajoute des contraintes : les clés et propriétés doivent être compatibles avec la façon dont vous recevez. Faites des dry-runs et testez un chemin de restauration, pas seulement « la réplication a réussi ».
10) What’s the simplest “I can implement this today” version?
Côté récepteur : après chaque receive réussi, créez/actualisez #last-received pointant vers le snapshot reçu. Ensuite adaptez la rétention des snapshots pour ne jamais supprimer le snapshot répliqué le plus récent tant que le signet n’a pas avancé.
Conclusion
Les signets ZFS ne sont pas glamour, et c’est le but. C’est une petite fonctionnalité de métadonnées qui transforme une catégorie d’échecs de réplication en maintenance de routine. Quand des snapshots disparaissent — par accident, par rétention ou parce que quelqu’un cherche à libérer de l’espace — les signets peuvent maintenir votre lignée incrémentielle intacte côté récepteur, ce qui signifie souvent éviter des renvois complets coûteux et les conséquences de performance en cascade qui en découlent.
Si vous exécutez de la réplication ZFS en production, traitez les signets comme des ceintures de sécurité : vous ne les installez pas parce que vous prévoyez de chuter. Vous les installez parce que vous avez rencontré des humains, des cron et des gels de changement de fin de trimestre.