ZFS a la réputation d’être « difficile à casser », ce qui est vrai la plupart du temps — jusqu’à ce que vous rencontriez la commande qui peut faire disparaître l’histoire : la destruction de snapshots. Les snapshots sont peu coûteux, rapides, et dangereusement faciles à supprimer en masse — surtout quand quelqu’un court après l’espace libre et que le pager hurle déjà.
zfs hold est le petit mécanisme qui transforme les snapshots de « oups » en « pas aujourd’hui ». Il n’encrypte rien, il ne déplace pas de données, et il ne vous protégera pas de toutes les défaillances possibles. Mais il fait une chose exceptionnellement bien : rendre un snapshot indestructible tant que le hold n’est pas volontairement libéré. En production, c’est la différence entre un incident récupérable et une mise à jour de carrière.
Ce que zfs hold fait réellement (et ce qu’il ne fait pas)
Un snapshot ZFS est immuable, mais pas indestructible. Vous pouvez le détruire, et s’il était la seule chose référencant d’anciens blocs, ces blocs deviennent libres et réutilisables. Dans un pool occupé, « réutilisé » peut rapidement signifier « perdu ».
zfs hold attache un ou plusieurs « holds » (pensez : étiquettes) à un snapshot. Tant qu’au moins un hold existe, le snapshot ne peut pas être détruit. Ni par vous, ni par un script, ni par un collègue bien intentionné avec un wildcard et une échéance. La commande destroy échouera avec un message pointant vers les holds.
Ce qu’il ne fait pas :
- Il n’empêche pas le snapshot de consommer de l’espace. En fait, il peut garder de l’espace occupé plus longtemps (c’est le but).
- Il n’empêche pas quelqu’un de détruire l’ensemble du dataset ou du pool par d’autres moyens si cette personne a suffisamment de privilèges et est déterminée.
- Il ne remplace pas une véritable politique de rétention, des sauvegardes, la vérification de la réplication ou le contrôle d’accès.
Voici le modèle mental que j’utilise :
- Snapshot = une vue figée des blocs d’un dataset à un instant donné.
- Hold = un post-it sur ce snapshot indiquant « ne pas supprimer tant que ces conditions ne sont pas remplies ».
- Release = enlever un post-it ; une fois tous les post-it retirés, la suppression redevient possible.
Première plaisanterie (restez bref, comme votre budget d’indisponibilité) : un hold est la seule chose « collante » dans le stockage que vous voulez réellement voir collante.
Comment fonctionnent les holds en interne
Les holds sont implémentés comme des métadonnées sur le snapshot. Chaque hold a un nom (souvent appelé tag) et est associé à ce snapshot. Plusieurs holds peuvent exister simultanément — courant dans les organisations où la sauvegarde, la réplication et la conformité veulent chacun avoir leur mot à dire.
La sémantique des tags : pourquoi le nom importe
Le tag n’est pas décoratif. Il devient votre poignée opérationnelle pour les audits, le dépannage et l’automatisation sûre. Si vous nommez des holds « keep », vous le regretterez quand vous regarderez 40 To de snapshots épinglés et que personne ne se souviendra de l’objet de chaque « keep ».
Un bon tag inclut :
- Propriétaire/système :
replication,backup,legal,migration - Portée ou cible :
to-dr,to-s3-gw,pre-upgrade - Optionnel : ticket ou ID de changement :
chg12345(si votre environnement en utilise)
Holds vs. propriétés vs. « ne fais pas ça »
Oui, vous pouvez limiter qui peut exécuter zfs destroy. Vous devriez le faire. Mais les frontières de privilèges s’estompent sous la pression : accès d’urgence, comptes break-glass, automatisation exécutée en root, ingénieurs on-call avec droits élevés. Un hold ajoute une étape intentionnelle en plus : « Je dois d’abord libérer le hold. » Cette étape supplémentaire est là où beaucoup de catastrophes s’éteignent discrètement.
Holds et réplication
Les holds sont particulièrement utiles avec la réplication parce que la réplication crée une chaîne de dépendance : vous avez souvent besoin d’un snapshot spécifique qui doit exister suffisamment longtemps pour terminer les envois incrémentaux, pour amorcer une nouvelle cible, ou pour garantir une fenêtre de restauration cohérente. L’automatisation qui épingle « le dernier snapshot répliqué avec succès » est une pratique classique, ennuyeuse et correcte.
Que se passe-t-il quand on tente de détruire un snapshot retenu
ZFS n’argumente pas ; il refuse. La destruction échoue avec une erreur faisant référence aux holds. C’est une bonne chose. C’est aussi une source fréquente de confusion quand quelqu’un s’attend à ce que son job de nettoyage récupère immédiatement de l’espace.
Deuxième plaisanterie : zfs hold c’est comme mettre une étiquette « Ne pas débrancher » sur le câble d’alimentation d’un serveur — agaçant jusqu’au jour où ça ne l’est plus.
Faits intéressants et contexte
Quelques points concrets et brefs qui aident à comprendre pourquoi les holds existent et pourquoi ils sont utilisés ainsi :
- Les snapshots ZFS ne sont pas des copies. Ce sont un ensemble de pointeurs de blocs ; les « anciennes données » restent référencées tant que quelque chose y pointe.
- Les holds sont par snapshot et basés sur des tags. Plusieurs équipes peuvent épingler indépendamment le même snapshot sans coordination — jusqu’au moment de la suppression.
- La pression d’espace rend les humains dangereux. En exploitation réelle, la plupart des suppressions catastrophiques surviennent pendant une urgence de stockage, pas pendant une planification calme.
- La réplication dépend de la lignée. Les envois incrémentaux exigent une base commune ; supprimez la mauvaise base et votre prochaine réplication devient un renvoi complet.
- Les outils d’auto-snapshot peuvent créer des « tempêtes de snapshots ». Si la rétention dysfonctionne, vous obtenez des milliers de snapshots — puis quelqu’un utilise un wildcard pour tout détruire. Les holds sont la ceinture de sécurité.
- Les holds ne sont pas identiques aux bookmarks. Les bookmarks peuvent préserver des points d’envoi incrémental sans conserver tous les blocs comme le font les snapshots ; les holds maintiennent le snapshot lui-même en vie.
- Les holds sont bon marché jusqu’à ce qu’ils ne le soient plus. Les métadonnées sont minuscules ; le coût est constitué par les blocs référencés que vous ne pouvez pas libérer tant que le snapshot est épinglé.
- « Cannot destroy » est une fonctionnalité, pas un bug. La plupart des systèmes considèrent la suppression comme finale ; ZFS vous donne un mécanisme délibéré « êtes-vous vraiment sûr » autour duquel vous pouvez automatiser.
Tâches pratiques : commandes que vous utiliserez réellement
L’objectif ici n’est pas d’exhiber la syntaxe. C’est de construire une mémoire musculaire opérationnelle : comment appliquer des holds, les trouver, interpréter les erreurs et intégrer les holds dans la rétention et la réplication sans immobiliser votre pool.
Tâche 1 : Créer un snapshot avec un nom opérationnellement signifiant
cr0x@server:~$ sudo zfs snapshot tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,refer,mountpoint -r tank/app | tail -n 3
NAME USED REFER MOUNTPOINT
tank/app@auto-2025-12-25_0000 12M 48G -
tank/app@auto-2025-12-25_0030 8M 48G -
tank/app@pre-upgrade-2025-12-25_0100 0B 48G -
Interprétation : Le snapshot est créé instantanément. USED peut afficher 0B à la création parce qu’aucun bloc n’a encore divergé.
Tâche 2 : Mettre un hold sur ce snapshot
cr0x@server:~$ sudo zfs hold change:pre-upgrade tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME TAG TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100 change:pre-upgrade Fri Dec 25 01:00 2025
Interprétation : Le snapshot a maintenant un tag. Il ne peut pas être détruit tant que ce tag n’est pas libéré (et que les autres tags éventuels le sont aussi).
Tâche 3 : Prouver que le hold bloque la destruction
cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cannot destroy snapshot tank/app@pre-upgrade-2025-12-25_0100: snapshot has holds
Interprétation : Cette erreur est votre épingle de sûreté qui fait son travail. Votre automatisation de nettoyage doit considérer cela comme « ignorer » et non « réessayer indéfiniment ».
Tâche 4 : Ajouter un second hold (stakeholders multiples)
cr0x@server:~$ sudo zfs hold replication:to-dr tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME TAG TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100 change:pre-upgrade Fri Dec 25 01:00 2025
tank/app@pre-upgrade-2025-12-25_0100 replication:to-dr Fri Dec 25 01:02 2025
Interprétation : La destruction est bloquée jusqu’à ce que les deux tags soient libérés. C’est ainsi que les équipes évitent de se marcher sur les pieds.
Tâche 5 : Libérer exactement un hold et confirmer que la protection reste
cr0x@server:~$ sudo zfs release change:pre-upgrade tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs holds tank/app@pre-upgrade-2025-12-25_0100
NAME TAG TIMESTAMP
tank/app@pre-upgrade-2025-12-25_0100 replication:to-dr Fri Dec 25 01:02 2025
cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cannot destroy snapshot tank/app@pre-upgrade-2025-12-25_0100: snapshot has holds
Interprétation : Libérer un tag ne supprime pas la protection si un autre tag reste. C’est le mécanisme qui rend la rétention partagée raisonnable.
Tâche 6 : Libérer le hold restant et détruire le snapshot
cr0x@server:~$ sudo zfs release replication:to-dr tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs destroy tank/app@pre-upgrade-2025-12-25_0100
cr0x@server:~$ sudo zfs list -t snapshot -r tank/app | grep pre-upgrade || echo "snapshot removed"
snapshot removed
Interprétation : Une fois le dernier hold libéré, la suppression se comporte normalement.
Tâche 7 : Appliquer des holds de façon récursive à un arbre de datasets (avec précaution)
cr0x@server:~$ sudo zfs snapshot -r tank/projects@quarterly-freeze-2025Q4
cr0x@server:~$ sudo zfs hold -r legal:q4-retention tank/projects@quarterly-freeze-2025Q4
cr0x@server:~$ sudo zfs holds -r tank/projects@quarterly-freeze-2025Q4 | head
NAME TAG TIMESTAMP
tank/projects@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
tank/projects/alpha@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
tank/projects/beta@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
tank/projects/beta/builds@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
Interprétation : Cela épingle des snapshots à travers l’arbre. C’est puissant et potentiellement coûteux en rétention d’espace. On le fait volontairement, pas par accident.
Tâche 8 : Trouver quels holds bloquent la suppression (la commande « pourquoi ça ne meurt pas ? »)
cr0x@server:~$ sudo zfs destroy tank/projects/alpha@quarterly-freeze-2025Q4
cannot destroy snapshot tank/projects/alpha@quarterly-freeze-2025Q4: snapshot has holds
cr0x@server:~$ sudo zfs holds tank/projects/alpha@quarterly-freeze-2025Q4
NAME TAG TIMESTAMP
tank/projects/alpha@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
Interprétation : C’est le premier arrêt dans tout incident de nettoyage : identifiez le tag exact et décidez si vous êtes autorisé à le supprimer.
Tâche 9 : Auditer les holds à travers un arbre de datasets
cr0x@server:~$ sudo zfs holds -r tank/projects | awk 'NR==1 || $2 ~ /legal:|replication:|backup:/ {print}'
NAME TAG TIMESTAMP
tank/projects@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
tank/projects/alpha@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
tank/projects/beta@quarterly-freeze-2025Q4 legal:q4-retention Fri Dec 25 02:00 2025
Interprétation : Vous construisez un inventaire des snapshots « épinglés ». Dans les grands environnements, c’est la différence entre une politique de rétention gérable et un grenier hanté.
Tâche 10 : Estimer l’impact en espace : trouver les deltas de snapshot lourds
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,creation -s used -r tank/projects | tail -n 10
tank/projects/beta@auto-2025-12-20_0000 18G Sat Dec 20 00:00 2025
tank/projects/beta@auto-2025-12-21_0000 22G Sun Dec 21 00:00 2025
tank/projects/beta@quarterly-freeze-2025Q4 35G Fri Dec 25 02:00 2025
Interprétation : Les snapshots avec un USED élevé épinglent beaucoup de blocs uniques. S’ils sont retenus, cet espace est probablement non négociable tant que le hold n’est pas libéré.
Tâche 11 : Utiliser des holds pour protéger les snapshots « dernière base de réplication valide »
cr0x@server:~$ sudo zfs snapshot tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs hold replication:last-good tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs holds tank/app@replica-base-2025-12-25_0300
NAME TAG TIMESTAMP
tank/app@replica-base-2025-12-25_0300 replication:last-good Fri Dec 25 03:00 2025
Interprétation : C’est un schéma de réplication courant : conservez toujours une base connue bonne épinglée jusqu’à ce que la réplication suivante confirme le succès.
Tâche 12 : Faire tourner proprement un snapshot « dernière bonne base » après validation
cr0x@server:~$ sudo zfs holds -r tank/app | grep replication:last-good
tank/app@replica-base-2025-12-25_0300 replication:last-good Fri Dec 25 03:00 2025
cr0x@server:~$ sudo zfs snapshot tank/app@replica-base-2025-12-25_0400
cr0x@server:~$ sudo zfs hold replication:last-good tank/app@replica-base-2025-12-25_0400
cr0x@server:~$ sudo zfs release replication:last-good tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs destroy tank/app@replica-base-2025-12-25_0300
cr0x@server:~$ sudo zfs holds tank/app@replica-base-2025-12-25_0400
NAME TAG TIMESTAMP
tank/app@replica-base-2025-12-25_0400 replication:last-good Fri Dec 25 04:00 2025
Interprétation : Notez l’ordre : créer la nouvelle base → la retenir → libérer la vieille base → détruire l’ancienne base. Si vous inversez cet ordre en présence de perte de paquets, vous finirez par payer le prix d’un renvoi complet.
Tâche 13 : Détruire des snapshots en ignorant ceux qui sont retenus (patron de nettoyage sûr)
cr0x@server:~$ for s in $(sudo zfs list -H -t snapshot -o name -r tank/app | head -n 5); do
> sudo zfs destroy "$s" 2>&1 | sed "s/^/[$s] /"
> done
[tank/app@auto-2025-12-25_0000] cannot destroy snapshot tank/app@auto-2025-12-25_0000: snapshot has holds
[tank/app@auto-2025-12-25_0030] destroyed
[tank/app@auto-2025-12-25_0100] destroyed
[tank/app@auto-2025-12-25_0130] destroyed
[tank/app@auto-2025-12-25_0200] destroyed
Interprétation : Les scripts de nettoyage doivent traiter « has holds » comme un état attendu. Logguez-le, reportez-le, passez à autre chose. Ne faites pas échouer tout le job et surtout n’essayez pas de « corriger » automatiquement.
Tâche 14 : Trouver les snapshots avec holds et trier par date de création
cr0x@server:~$ sudo zfs holds -r tank | awk 'NR==1{next} {print $1}' | sort -u | while read snap; do
> sudo zfs get -H -o value creation "$snap" | awk -v s="$snap" '{print $0 " " s}'
> done | sort | head
Fri Dec 20 00:00 2025 tank/projects/beta@auto-2025-12-20_0000
Fri Dec 25 02:00 2025 tank/projects@quarterly-freeze-2025Q4
Fri Dec 25 02:00 2025 tank/projects/alpha@quarterly-freeze-2025Q4
Interprétation : Cela aide à répondre à la question de conformité : « Qu’avons-nous épinglé, et depuis combien de temps ? » L’outil ne le fait pas sous forme d’un tableau propre, donc vous le construisez.
Trois micro-histoires du monde de l’entreprise
1) L’incident causé par une mauvaise hypothèse
L’équipe avait une règle simple : « Nous gardons 48 heures de snapshots, tout ce qui est plus vieux est détruit. » Elle était appliquée par un job cron écrit il y a des années. Personne ne l’aimait, mais cela évitait que le pool ne devienne un musée.
Puis ils ont migré une charge bruyante — artefacts CI et caches de build — sur le même pool que quelques bases de données critiques. Le pool a commencé à se remplir plus vite que prévu. Sous pression, quelqu’un a exécuté le job de nettoyage manuellement, deux fois, puis a décidé de « l’accélérer » en élargissant le pattern de destroy. Des wildcards ont été utilisés. Rien n’a explosé immédiatement, et c’est ainsi que les incidents de stockage vous attirent dans une fausse confiance.
Plus tard dans la journée, la réplication vers le site DR a échoué. Pas un échec temporaire — « incremental base not found ». L’hypothèse était que si un snapshot avait plus de 48 heures, il était sûr de le supprimer. Mais le calendrier de réplication avait dérivé durant une fenêtre de maintenance et avait maintenant plusieurs jours de retard. La chaîne incrémentale dépendait d’un snapshot que le job de nettoyage considérait comme « expiré ».
Ils ont dû re-sender en full sur un lien dimensionné pour des incrémentaux, pas pour des datasets complets. L’impact business n’était pas seulement la posture DR retardée. Le renvoi a concurrencé le trafic de production et a poussé la latence à un niveau visible par les utilisateurs.
La correction post-mortem n’a pas été « dire aux gens de ne pas utiliser de wildcards » (bonne chance). Elle a été opérationnalisée : le job de réplication applique un tag hold au dernier snapshot confirmé sur la cible. Le job de rétention supprime librement, mais les snapshots retenus sont ignorés. La mauvaise hypothèse a été remplacée par un mécanisme qui rend le comportement sûr par défaut.
2) L’optimisation qui s’est retournée contre eux
Une autre société a eu une idée astucieuse : « Retenons tous les snapshots pendant sept jours, puis libérons-les en une seule fois. Comme ça, on ne pense à la rétention qu’une fois par semaine. » Cela semblait efficace : moins de pièces mobiles, moins de risques d’erreur.
Ça a marché — jusqu’au changement de la charge. Un nouveau pipeline d’analytics a commencé à réécrire de gros datasets quotidiennement. Les snapshots ont commencé à accumuler de gros deltas. Les holds ont épinglé ces deltas pendant une semaine entière, et l’espace libre du pool a commencé à osciller violemment. Chaque semaine, ils libéraient les holds et effectuaient une destruction massive, obtenant un bref afflux d’espace libre… suivi d’une pression d’allocation ressemblant à de la fragmentation et de chutes de performances alors que le pool se débattait pour réécrire des blocs chauds tout en libérant les anciens.
L’optimisation avait rendu la rétention « simple », mais elle avait concentré l’amplification d’écriture et le travail de suppression en une tempête hebdomadaire prévisible. Pire, quand la tempête arrivait pendant une période chargée pour l’entreprise, des pics de latence réguliers apparaissaient. Ce n’était pas un mystère ; c’était une douleur programmée.
La correction a été ennuyeuse : rétention étalée. Ils ont gardé les holds, mais seulement pour les snapshots qui avaient réellement besoin de protection (bases de réplication, points avant changement, conformité). Les autres ont suivi une fenêtre roulante avec des suppressions graduelles. Ils ont aussi surveillé l’espace libre du pool de façon plus conservatrice, car les holds transforment « espace libre » en une promesse que vous ne pouvez pas forcément honorer immédiatement.
La leçon : les holds sont un mécanisme de sécurité, pas une politique globale. Si vous optimisez pour moins de décisions, vous pouvez accidentellement optimiser pour le chaos périodique.
3) La pratique ennuyeuse mais correcte qui a sauvé la mise
Celle-ci est ma préférée parce qu’elle n’implique pas d’héroïsme, juste de la discipline.
Une application liée à la finance avait des procédures de clôture trimestrielle toujours très stressantes. L’équipe de stockage avait une étape dans le runbook : avant le début de la clôture, prendre un snapshot récursif d’un sous-arbre de datasets spécifique et appliquer un tag hold légal. Tout le monde levait les yeux au ciel parce que cela prenait cinq minutes de plus et ne semblait jamais « faire » quoi que ce soit.
Lors d’une clôture, un outil de déploiement a mal lu une valeur de configuration et a exécuté un pas de nettoyage contre le mauvais point de montage. Ce n’était pas malveillant ; c’était le genre de bug d’automatisation qui n’apparaît que lorsqu’une variable est vide et que quelqu’un ne l’a pas entre guillemets. Des fichiers ont disparu. L’équipe applicative a essayé de revenir en arrière au niveau de l’application, mais la suppression avait déjà eu lieu sur le disque.
Ils se sont tournés vers les snapshots, comme prévu — sauf que la première tentative de restauration a échoué parce que l’ingénieur on-call (fatigué mais bien intentionné) a commencé à supprimer des « snapshots plus anciens » pour créer de l’espace pour la restauration. Le pool était serré. C’est là que les organisations normales perdent le fil : vous finissez par détruire les snapshots dont vous avez besoin parce que le système vous pousse à agir.
Les snapshots trimestriels étaient retenus. Ils ne pouvaient pas être détruits dans l’urgence. Cela a forcé l’équipe à choisir une voie plus sûre : étendre temporairement la capacité et restaurer à partir des snapshots épinglés. L’incident a quand même fait mal, mais il est resté dans la catégorie « coûteux et pénible », pas « irréversible ».
La pratique n’était pas glamour. C’était une étape mécanique qui a empêché une personne paniquée de faire une erreur permanente.
Méthode de diagnostic rapide
Voici la méthode pour le moment de production courant : « Nous avons besoin d’espace, nous avons essayé de supprimer des snapshots, ZFS refuse, et les performances deviennent étranges. » Le but est de trouver rapidement le goulot d’étranglement et le point de décision, pas d’admirer le système de fichiers.
1) Premier contrôle : qu’est-ce qui bloque exactement la suppression ?
Choisissez un snapshot précis qui refuse de disparaître et inspectez les holds.
cr0x@server:~$ sudo zfs destroy tank/app@auto-2025-12-24_0000
cannot destroy snapshot tank/app@auto-2025-12-24_0000: snapshot has holds
cr0x@server:~$ sudo zfs holds tank/app@auto-2025-12-24_0000
NAME TAG TIMESTAMP
tank/app@auto-2025-12-24_0000 replication:last-good Thu Dec 24 00:05 2025
Décision : Si le tag est attendu (réplication/conformité), ne le supprimez pas sous la pression sans confirmer la dépendance en aval.
2) Deuxième contrôle : les holds sont-ils répandus ou isolés ?
Inventoriez les holds de façon récursive pour voir si vous avez affaire à quelques snapshots épinglés ou à tout un sous-arbre.
cr0x@server:~$ sudo zfs holds -r tank/app | wc -l
128
Interprétation : Grand nombre ? Vous avez probablement un problème de politique ou d’automatisation, pas un cas isolé.
3) Troisième contrôle : où va réellement l’espace ?
Regardez l’utilisation d’espace des datasets et des snapshots, en vous concentrant sur ce qui est épinglé.
cr0x@server:~$ sudo zfs list -o name,used,avail,refer,compressratio,mounted tank
NAME USED AVAIL REFER RATIO MOUNTED
tank 68T 1.2T 256K 1.42x yes
cr0x@server:~$ sudo zfs list -t snapshot -o name,used -s used -r tank/app | tail -n 5
tank/app@auto-2025-12-23_0000 210G
tank/app@auto-2025-12-24_0000 260G
tank/app@auto-2025-12-25_0000 300G
tank/app@quarterly-freeze 420G
tank/app@legal-freeze 1.1T
Interprétation : Les snapshots avec un USED important sont des suspects de premier plan. S’ils sont retenus, vous ne pourrez pas récupérer cet espace rapidement sans changer la décision métier.
4) Quatrième contrôle : atteignez-vous un goulot de performance au niveau du pool ?
Les problèmes d’espace et les tentatives de suppression peuvent coïncider avec une mauvaise latence. Vérifiez rapidement la santé du pool et l’I/O.
cr0x@server:~$ sudo zpool status -x
all pools are healthy
cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0 (server) 12/25/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.40 0.00 4.10 8.20 0.00 75.30
Device r/s w/s rkB/s wkB/s await %util
nvme0n1 220.0 180.0 18000 26000 6.20 92.0
nvme1n1 210.0 170.0 17500 24000 6.50 89.0
Interprétation : Un %util élevé et une await en hausse suggèrent que vous êtes lié par l’I/O. Les suppressions de snapshots ne sont peut-être pas la cause directe, mais la même charge d’écriture qui a créé de gros deltas l’est souvent.
5) Cinquième contrôle : confirmez que vous n’êtes pas bloqué par un send/receive ou un scrub de longue durée
cr0x@server:~$ sudo zpool status tank | sed -n '1,25p'
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:10:21 with 0 errors on Fri Dec 25 01:30:11 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
Interprétation : Si un scrub/resilver est actif, le pool sera occupé. Il ne bloque pas directement les destructions, mais cela change l’évaluation du risque lorsque vous manquez aussi d’espace.
Erreurs courantes : symptômes et corrections
Erreur 1 : Traiter « snapshot has holds » comme un état d’erreur à corriger automatiquement
Symptôme : Le job de nettoyage échoue chaque nuit, réessaie agressivement, envoie une alerte, ou (pire) libère automatiquement des holds pour « corriger ».
Correction : Faites de « has holds » un état de première classe : loggez et rapportez les snapshots retenus séparément. La libération des holds doit exiger des vérifications de propriété explicites (succès de la réplication, fermeture de la fenêtre de changement, approbation légale).
Erreur 2 : Utiliser des tags de hold vagues qui n’identifient pas un propriétaire
Symptôme : Vous trouvez des tags comme keep, hold ou important. Personne ne peut les supprimer en toute sécurité ; ils traînent pendant des années.
Correction : Adoptez une convention de nommage : system:purpose (optionnellement avec un ID de changement). Exemple : replication:last-good, change:pre-upgrade, legal:q4-retention.
Erreur 3 : Garder des snapshots « au cas où » sans mesurer l’impact sur l’espace
Symptôme : L’espace libre du pool diminue régulièrement, et la suppression de snapshots non retenus bouge à peine l’aiguille.
Correction : Identifiez les snapshots retenus à fort USED. Décidez s’ils sont vraiment nécessaires. Si la conformité exige la rétention, planifiez la capacité en conséquence ; ne prétendez pas pouvoir nettoyer le problème.
Erreur 4 : Holds récursifs appliqués à la mauvaise portée
Symptôme : Soudainement des milliers de snapshots à travers de nombreux datasets enfants sont retenus ; la consommation de stockage explose ; la rétention s’arrête de fonctionner.
Correction : Avant d’utiliser -r, listez l’arbre des datasets et confirmez la racine voulue. En automatisation, whitelisttez les préfixes de dataset. Envisagez de retenir seulement des noms de snapshots spécifiques plutôt que d’appliquer à chaque enfant automatiquement.
Erreur 5 : Confondre holds et permissions / contrôle d’accès
Symptôme : Les gens pensent que les holds empêchent les utilisateurs privilégiés d’exécuter des actions destructrices sur tout le pool.
Correction : Utilisez la délégation des permissions ZFS et des contrôles opérationnels. Les holds protègent les snapshots de la destruction, pas tout votre patrimoine de stockage contre root.
Erreur 6 : Scripts de réplication qui mettent des holds mais ne les libèrent jamais
Symptôme : replication:last-good existe sur des dizaines ou des centaines de snapshots ; vous ne vouliez en fait qu’un seul.
Correction : Sur une réplication réussie, faites tourner le hold : appliquez-le à la nouvelle base, libérez-le sur l’ancienne base, puis supprimez l’ancienne si la politique le permet. Ajoutez une surveillance qui alerte si plus de N snapshots sont taggés replication:last-good.
Erreur 7 : Supposer que les holds sont la seule raison pour laquelle un snapshot ne peut pas être détruit
Symptôme : La destruction échoue et les gens se focalisent uniquement sur les holds, alors que le problème est ailleurs (typo, clones dépendants, problèmes de permissions).
Correction : Lisez le message d’erreur complet et vérifiez aussi l’existence de clones et les permissions. Les holds sont fréquents, mais pas exclusifs.
Checklists / plan étape par étape
Checklist A : Introduire les holds en sécurité dans un environnement existant
- Définir des tags de propriété. Décidez quels systèmes sont autorisés à placer des holds (réplication, sauvegarde, gestion des changements, légal).
- Choisir une convention de nommage. Préférez
owner:purposeet ajoutez éventuellement un identifiant court dans le nom du snapshot lui-même. - Définir les règles de portée. Quand les holds récursifs sont-ils autorisés ? Sur quelles racines de dataset ? Documentez les préfixes autorisés.
- Mettre à jour les jobs de nettoyage. Traitez « has holds » comme une condition d’ignorance. Produisez un rapport séparé des snapshots retenus.
- Ajouter de la visibilité. Créez un job d’inventaire planifié qui compte les holds par tag et liste le snapshot retenu le plus ancien par tag.
- Faire un exercice. Exécutez la procédure d’urgence « manque d’espace » sans être réellement en manque, pour ne pas apprendre sous stress.
Checklist B : Snapshot de sécurité pré-changement utilisant des holds
- Prenez le(s) snapshot(s) des dataset(s) impliqués, idéalement de façon récursive si le changement couvre plusieurs datasets enfants.
- Appliquez un tag hold spécifique au changement.
- Vérifiez que les holds existent.
- Procédez au changement.
- Après la fenêtre de validation, libérez le tag hold.
- Laissez la rétention normale supprimer le snapshot plus tard (ou détruisez-le explicitement si la politique l’exige).
cr0x@server:~$ sudo zfs snapshot -r tank/app@chg-pre-2025-12-25_0500
cr0x@server:~$ sudo zfs hold -r change:chg-pre tank/app@chg-pre-2025-12-25_0500
cr0x@server:~$ sudo zfs holds -r tank/app@chg-pre-2025-12-25_0500 | head -n 5
NAME TAG TIMESTAMP
tank/app@chg-pre-2025-12-25_0500 change:chg-pre Fri Dec 25 05:00 2025
tank/app/db@chg-pre-2025-12-25_0500 change:chg-pre Fri Dec 25 05:00 2025
tank/app/uploads@chg-pre-2025-12-25_0500 change:chg-pre Fri Dec 25 05:00 2025
Checklist C : Rétention de réplication avec holds « dernière base bonne »
- Créer un nouveau snapshot de réplication.
- Envoyer/recevoir (ou quel que soit votre mécanisme de réplication).
- Vérifier le succès côté cible.
- Retenir la nouvelle base sur la source.
- Libérer le hold sur la base précédente.
- Éventuellement détruire les anciennes bases si elles sont hors rétention.
FAQ
1) Un hold est-il la même chose que définir une propriété de dataset comme readonly=on ?
Non. readonly=on affecte le comportement d’écriture sur un dataset. Un hold n’affecte que la possibilité de détruire un snapshot précis.
2) Puis-je retenir un dataset, pas un snapshot ?
Les holds s’appliquent aux snapshots. Vous pouvez snapshotter récursivement puis retenir ces snapshots, ce que les gens entendent souvent opérationnellement par « retenir un arbre de datasets ».
3) Pourquoi ZFS autorise-t-il plusieurs holds sur un même snapshot ?
Parce que les environnements réels ont plusieurs parties prenantes. La réplication peut avoir besoin d’un snapshot pour les chaînes incrémentales, tandis que la conformité peut avoir besoin du même snapshot pour la gouvernance. Les holds multiples permettent à chaque système d’exprimer sa nécessité indépendamment.
4) Si un snapshot est retenu, continue-t-il à consommer plus d’espace au fil du temps ?
Le snapshot lui-même ne change pas, mais il peut garder d’anciens blocs référencés pendant que le dataset vivant change. Plus il y a de churn dans le dataset, plus d’anciens blocs uniques restent épinglés par ce snapshot.
5) Comment trouver ce qui empêche la suppression d’un snapshot ?
Exécutez zfs holds <snapshot>. Si vous avez beaucoup de datasets, utilisez zfs holds -r pour inventorier puis affinez sur les snapshots spécifiques qui bloquent votre politique.
6) Puis-je utiliser les holds comme mécanisme de rétention pour la conformité ?
Vous pouvez utiliser les holds pour imposer « ne peut pas être supprimé tant que libéré explicitement », ce qui est un primitif utile. S’il satisfait la conformité dépend de vos contrôles autour de qui peut libérer les holds, de l’auditabilité et de la possibilité pour les utilisateurs privilégiés de contourner votre processus.
7) Quelle est la différence entre un hold et un bookmark ?
Un hold épingle un snapshot (et donc tous les blocs référencés). Un bookmark préserve un point d’envoi incrémental sans retenir les blocs du snapshot de la même façon. Les holds concernent l’immutabilité de suppression ; les bookmarks concernent la lignée de réplication sans conserver tout le snapshot.
8) Mon outil de rétention supprime des snapshots mais ne récupère pas d’espace. Les holds en sont-ils la cause ?
Possiblement, mais pas toujours. Les holds empêchent la suppression de snapshots spécifiques ; ils n’expliquent pas directement pourquoi la suppression d’autres snapshots ne libère pas beaucoup. Causes courantes : les snapshots restants (retenus ou non) référencent toujours les anciens blocs, le modèle de churn du dataset maintient des blocs référencés par de nouveaux snapshots, ou vous regardez la mauvaise hiérarchie de dataset.
9) Dois-je mettre des holds sur chaque snapshot ?
Généralement non. C’est ainsi que vous transformez une politique de rétention en crise de capacité. Utilisez les holds de façon sélective : bases de réplication « last-good », points de sécurité pré-changement et snapshots de conformité explicites.
10) Qui devrait être autorisé à libérer les holds ?
Dans des environnements matures : seulement l’automatisation propriétaire ou un petit ensemble de rôles privilégiés avec une piste d’audit. Opérationnellement, « libérer un hold » équivaut à « autoriser la suppression permanente », et cela doit être traité avec le même sérieux.
Conclusion
zfs hold est une petite fonctionnalité aux conséquences disproportionnées. Elle ne rend pas les snapshots « plus snapshottés ». Elle les rend plus difficiles à détruire accidentellement, et c’est une superpuissance étonnamment rare dans les systèmes de production — où la plupart des échecs ne sont pas des bugs exotiques du noyau mais des humains qui agissent rapidement.
Si vous adoptez les holds avec une propriété de tag claire, que vous les intégrez dans la rétention et la réplication, et que vous prenez l’habitude d’auditer ce qui est épinglé, vous obtenez un système qui échoue de façon plus sûre sous la pression. Et le jour où quelqu’un essaiera de supprimer la mauvaise chose pour faire taire l’alerte rouge, ZFS fera ce que vous l’avez entraîné à faire : refuser.