Trou d’écriture (write hole) expliqué : qui est concerné et pourquoi ZFS l’évite

Cet article vous a aidé ?

Vous ne remarquez pas le write hole quand tout est vert. Vous le remarquez quand un index de base de données ne se reconstruit pas, le système de fichiers d’une VM ne monte pas,
ou l’archive d’un seul client échoue la somme de contrôle — des mois après un « bref » coupure d’alimentation.

Le write hole est l’équivalent stockage d’une déposition manquante : le système « se souvient » de certaines parties d’une écriture, en oublie d’autres,
et vous livre avec assurance une histoire qui n’est jamais arrivée. ZFS est célèbre pour ne pas faire ça. Voici pourquoi.

Le write hole : ce que c’est (et ce que ce n’est pas)

Le « write hole » classique est une défaillance de cohérence de parité RAID causée par une écriture interrompue. Pas « disque mort », pas « contrôleur explosé ».
C’est plus subtil : une coupure d’alimentation, un kernel panic, un reset de contrôleur, un câble fatigué ou un bug de firmware intervient au milieu de la mise à jour d’une stripe.
Les blocs de données et les blocs de parité ne sont plus d’accord.

La parité RAID (pensez RAID5/RAID6 et cousins) assure la redondance en calculant la parité sur un ensemble de blocs dans une stripe. Pour une stripe RAID5 simple,
la parité est un XOR des blocs de données. Si vous mettez à jour un bloc de données, vous devez aussi mettre à jour la parité. Cela signifie plusieurs écritures par écriture logique.
Si le système plante après qu’une d’elles ait atteint le disque mais avant que l’autre ne le fasse, vous créez une stripe qui semble valide structurellement mais est
mathématiquement incohérente.

Cette incohérence n’est pas toujours détectée immédiatement. En fait, c’est tout le problème : l’ensemble revient en ligne, le système de fichiers se monte,
et la corruption reste comme une mine jusqu’à ce que vous lisiez le bloc « faux » (ou que vous ayez besoin de reconstruire après une panne de disque).

Ce que le write hole n’est pas :

  • Ce n’est pas la même chose que la dégradation bit à bit (bit rot). La détérioration média peut survenir sans crash ; le write hole requiert un chemin d’écriture interrompu.
  • Ce n’est pas « votre RAID est inutile ». C’est un mode de défaillance spécifique déclenché par des mises à jour partielles dans des systèmes à parité.
  • Ce n’est pas résolu par « plus de parité » seul. RAID6 réduit le risque pendant la reconstruction, mais des mises à jour partielles de la parité peuvent toujours créer des stripes incohérentes.

La question clé est simple : quand vous écrivez, obtenez-vous parfois un mélange d’anciennes et de nouvelles données/parité que le système ne peut pas plus tard réconcilier ?
Si oui, vous avez un risque de write hole.

Qui est concerné par le write hole : niveaux RAID et piles vulnérables

Le write hole est le plus souvent associé à RAID5, mais la vraie classification est : toute redondance basée sur la parité où une seule écriture logique devient
plusieurs écritures sur disque, et où la pile ne peut pas garantir l’atomicité entre elles.

Probablement vulnérable (sauf atténuation)

  • RAID5 / RAID6 dans de nombreuses piles RAID logicielles et contrôleurs RAID matériels, surtout sans cache d’écriture persistant ou protégé par batterie.
  • mdadm RAID5/6 sur Linux peut être vulnérable si les barrières/flushes sont mal configurés, si le cache d’écriture ment, ou si un crash survient en cours de mise à jour.
  • Systèmes de fichiers classiques sur RAID parité (ext4, XFS, NTFS, etc.) ne connaissent généralement pas l’état de parité. Ils supposent que le périphérique de blocs dit la vérité.
  • Appliances de stockage qui « optimisent » en réordonnant les écritures sans intégrité bout en bout peuvent aggraver le problème.

Moins vulnérable par conception

  • Mirrors (RAID1, RAID10) : une écriture est dupliquée ; vous pouvez toujours avoir des torn writes au niveau bloc, mais en général vous n’empoisonnez pas les mathématiques de la parité.
  • ZFS sur mirrors : ajoute checksums + copy-on-write, donc il ne renverra pas un bloc déchiré comme « bon ».
  • ZFS RAIDZ : la parité fait partie d’un schéma transactionnel et copy-on-write ; il évite le comportement classique du write hole.

« Mais mon contrôleur a un cache »

Un cache aide s’il est persistant en cas de perte d’alimentation et si le contrôleur respecte correctement les flushes et l’ordre d’écriture.
Un cache d’écriture avec batterie (BBWC) ou flash-backed (FBWC) peut réduire considérablement le risque.
« Write-back cache sans persistance » n’est que de la vitesse avec un goût du risque.

Blague n°1 : Un contrôleur RAID sans vrai cache protégé contre la perte d’alimentation, c’est comme un parachute fait d’optimisme — vous découvrez qu’il est faux uniquement une fois.

Pourquoi cela arrive : stripes partielles, parité et « torn writes »

Prenez un ensemble RAID5 avec trois disques : deux blocs de données et un bloc de parité par stripe. Mettez à jour un bloc 4K dans une stripe.
L’ensemble doit mettre à jour la parité pour que la parité corresponde toujours aux deux blocs de données.

Il y a deux façons classiques de mettre à jour la parité :

  • Read-modify-write (RMW) : lire l’ancien bloc de données et l’ancienne parité, calculer la nouvelle parité = old_parity XOR old_data XOR new_data, puis écrire la nouvelle donnée et la nouvelle parité.
  • Reconstruct-write : lire tous les autres blocs de données dans la stripe, recalculer la parité de zéro, écrire la donnée mise à jour et la nouvelle parité.

Dans les deux cas, plusieurs lectures et écritures ont lieu. C’est acceptable quand la séquence s’achève. Le write hole apparaît lorsqu’elle ne s’achève pas.
Le plus désagréable est qu’un plantage peut interrompre à de nombreux moments :

  • La nouvelle donnée est écrite, la parité est ancienne.
  • La parité est écrite, la donnée est ancienne.
  • Une moitié de secteur est mise à jour (« torn write »), selon la sémantique du périphérique et le comportement du cache.
  • Les écritures sont réordonnées par le cache/le contrôleur/le firmware du disque, malgré ce que croit le système d’exploitation.

Après redémarrage, l’ensemble n’a pas de mémoire de ce qu’il faisait en vol. Une stripe est juste une stripe.
Il n’y a pas de journal de transaction durable au niveau RAID qui dise « cette mise à jour de parité était en cours ; veuillez réconcilier ».
Beaucoup de piles font simplement confiance à la parité et passent à autre chose.

Le pire moment pour découvrir une stripe incohérente est pendant une reconstruction, quand vous perdez un disque et que la parité devient votre source de vérité.
Si la parité est fausse, la reconstruction produit des données pourries, et elle les produit avec assurance.

Pourquoi ZFS l’évite : COW, checksums et commits transactionnels

ZFS ne « défait » pas la physique magiquement. Il utilise un modèle de stockage qui rend le scénario classique du write hole structurellement difficile à créer et
facile à détecter. Trois piliers comptent : copy-on-write, checksums bout en bout et sémantique de commit transactionnel.

1) Copy-on-write (COW) : ne jamais écraser les données actives en place

Les systèmes de fichiers traditionnels réécrivent souvent des métadonnées et des blocs de données en place (le journal réduit mais n’élimine pas certains risques).
ZFS adopte une approche différente : lorsqu’il modifie un bloc, il alloue un nouveau bloc ailleurs, écrit le nouveau contenu là-bas, puis
met à jour les pointeurs pour référencer le nouveau bloc.

Cette mise à jour de pointeur est elle-même réalisée via COW, en remontant l’arbre jusqu’à la métadonnée de haut niveau (uberblock) qui est mise à jour.
L’ancienne version reste intacte sur disque jusqu’à la validation de la nouvelle version. Si vous plantez en cours d’écriture, vous revenez généralement à l’arbre précédent
cohérent. Vous ne vous retrouvez pas avec des métadonnées « à moitié anciennes, à moitié nouvelles » prétendant être un système de fichiers cohérent.

2) Checksums bout en bout : détecter les mensonges et les corruptions

ZFS stocke une somme de contrôle pour chaque bloc dans sa métadonnée parente. Quand vous lisez un bloc, ZFS vérifie la checksum. Si elle ne correspond pas,
ZFS sait que le bloc est incorrect. Ce n’est pas « peut-être faux ». C’est mathématiquement ou cryptographiquement incohérent.

Sur des vdevs redondants (mirrors, RAIDZ), ZFS peut ensuite réparer : il lit une copie alternative/reconstruit la parité, trouve une version qui correspond
à la checksum attendue, renvoie les bonnes données et éventuellement répare la copie corrompue. La checksum rend la corruption détectable ; la redondance la rend réparables.

3) Groupes de transactions (TXGs) : changements d’état du système de fichiers presque atomiques

ZFS regroupe les modifications en transaction groups. En mémoire, il accumule les données et métadonnées sales, puis périodiquement synchronise un TXG sur disque.
Le « point de commit » est un nouvel uberblock écrit sur disque pointant vers le nouvel arbre. Les uberblocks sont écrits dans un ensemble rotatif afin que ZFS puisse choisir
le plus récent valide à l’import.

Cela compte pour le write hole parce que ZFS n’a pas besoin d’effectuer des mises à jour de parité en place des blocs existants pour « corriger » la vérité courante.
Il écrit de nouveaux blocs, nouvelle parité, et ne bascule la racine qu’ensuite. Si un crash survient avant le basculement, les nouveaux blocs sont orphelins
puis nettoyés plus tard ; l’ancien état cohérent est toujours présent.

Où vivait le write hole, ZFS met une porte fermée à clé

Write hole classique : « J’ai mis à jour les données mais pas la parité, maintenant la stripe est incohérente et personne ne sait. »
Modèle ZFS : « J’ai écrit nouvelles données+parité à de nouvelles emplacements. Si je n’ai pas fini, l’arbre actif pointe toujours vers les anciens blocs cohérents. »

ZFS peut toujours subir des écritures interrompues, mais il a deux avantages :

  • Consistance de l’arbre actif : l’état sur disque choisi à l’import est un snapshot cohérent du système de fichiers.
  • Détectabilité : si un bloc est corrompu, ZFS le détecte via la checksum ; il ne l’accepte pas silencieusement comme bon.

Une citation à garder sur un post-it près de votre baie :

« L’espoir n’est pas une stratégie. » — General Gordon R. Sullivan

Spécificités de RAIDZ : largeur de stripe variable et ce qui change

RAIDZ est la parité RAID de ZFS, mais ce n’est pas « RAID5 implémenté dans ZFS » au sens naïf. RAIDZ est intégré au modèle d’allocation et transactionnel de ZFS.

Dans le RAID5 classique, une écriture logique sur un petit bloc force souvent un cycle read-modify-write sur une stripe complète. C’est là que les mises à jour partielles
deviennent dangereuses, surtout si le système réécrit des blocs existants en place.

RAIDZ fait quelque chose de différent : il utilise une largeur de stripe variable. ZFS peut empaqueter les données sur les disques selon la taille réelle d’écriture et la disposition des blocs,
plutôt que d’imposer des frontières de stripe fixes. Il y a toujours de la parité, mais l’allocation est coordonnée avec les mises à jour COW.

La conséquence opérationnelle clé : la parité RAIDZ est calculée pour les blocs nouvellement alloués et écrite dans le cadre du sync TXG.
Elle n’est pas réadaptée sur des stripes vivantes existantes via des mises à jour en place comme le fait le RMW classique de RAID5.

Cela ne veut pas dire que RAIDZ est invincible. Vous pouvez toujours perdre des données si vous perdez plus de périphériques que la parité autorise, ou si votre système ment sur les flushes
et que vous perdez l(es) uberblock(s) récemment commités. Mais le mode de défaillance « mismatch de parité silencieux créé en cours d’écriture et ensuite utilisé comme vérité »
n’est pas le mode par défaut.

La quête annexe du vilain : caches, barrières et « je jure que c’était écrit »

La discussion sur le write hole traîne toujours des caches derrière elle parce que la plupart des corruptions réelles ne sont pas un « bug ZFS » ou un « bug mdadm ».
Ce sont des « la pile de stockage m’a dit que c’était sur un média stable, mais c’était en fait dans un cache volatil. »

Votre OS utilise des flushes (FUA/flush/barriers) pour dire : « engagez ceci sur un média non volatile avant de confirmer. » Si le dispositif/contrôleur ignore cela,
tout ce qui est au-dessus fonctionne sur une timeline fantaisiste.

ZFS n’est pas immunisé contre les mensonges. ZFS suppose que lorsqu’il émet un flush et reçoit un accusé, les données sont durables. Si votre hardware dit « oui »
puis perd l’alimentation, vous pouvez perdre le(s) TXG(s) les plus récents. En général, cela signifie perdre les dernières écritures, pas corrompre des données déjà commités,
mais l’ampleur dépend de l’honnêteté du périphérique.

C’est là que SLOG, les réglages de sync et la protection contre la perte d’alimentation entrent en jeu. Si vous mettez sync=disabled pour courir après des benchmarks,
vous vous exposez à réapprendre pourquoi les bases de données insistent sur fsync.

Blague n°2 : Mettre sync=disabled en production, c’est comme enlever le détecteur de fumée parce qu’il bippe quand il y a de la fumée.

Faits intéressants et contexte historique

  • Fait 1 : Le write hole a été discuté dans la littérature RAID bien avant les SSD ; il est lié à la sémantique de mise à jour de la parité, pas aux bizarreries du flash.
  • Fait 2 : Les vendeurs de RAID matériel ont poussé BBWC en partie parce que cela rend les mises à jour multi-écritures « assez atomiques » face à une coupure de courant.
  • Fait 3 : Les premières baies d’entreprise implémentaient un « parity logging » ou journalisation au niveau RAID pour réconcilier les mises à jour de stripe incomplètes après un crash.
  • Fait 4 : Le journaling des systèmes de fichiers (par ex. ext3/ext4) protège la cohérence des métadonnées, mais il ne peut généralement pas corriger une mismatch de parité en dessous.
  • Fait 5 : Le checksumming bout en bout de ZFS a été une réaction directe à la corruption silencieuse dans les piles de stockage — contrôleurs, firmware et même DMA peuvent être fautifs.
  • Fait 6 : Le concept d’uberblock de ZFS fournit plusieurs points de commit récents ; à l’import, ZFS peut choisir le plus récent valide.
  • Fait 7 : La réputation de RAID5 s’est dégradée à mesure que les disques ont grossi ; les fenêtres de reconstruction se sont allongées, augmentant la probabilité d’erreurs de lecture non récupérables.
  • Fait 8 : Le « write hole » ne concerne pas seulement la perte d’alimentation ; toute interruption en vol (panic, reset HBA, link flap) peut produire le même résultat de mise à jour partielle.
  • Fait 9 : Certains SSD ont historiquement accusé des flushes trop tôt à cause de bugs de firmware ; les ingénieurs fiabilité ont appris à se méfier des labels « enterprise » bon marché.

Trois mini-histoires du monde de l’entreprise

Mini-histoire 1 : l’incident causé par une mauvaise hypothèse

Une société SaaS de taille moyenne faisait tourner son cluster PostgreSQL principal sur un volume Linux software RAID6 avec un HBA « réputé » en mode IT.
L’hypothèse était simple : RAID6 signifie « sûr », et le journaling ext4 signifie « cohérent ». Ils avaient des UPS dans la baie, donc les événements d’alimentation
étaient considérés comme théoriques.

Puis il y a eu un événement de maintenance du bâtiment. L’alimentation n’est pas tombée complètement ; elle a flanché, rebondi, et les serveurs ont redémarré. Les logs UPS ont montré un court transfert,
puis un second transfert. Les hôtes sont revenus. Le monitoring était vert. Tout le monde est passé à autre chose.

Deux semaines plus tard, un disque a lâché — normal, remplaçable, ennuyeux. Pendant la reconstruction, mdadm a commencé à renvoyer des erreurs de lecture qui ne correspondaient pas au SMART.
Puis PostgreSQL a commencé à échouer les vérifications de checksum sur une table qui n’avait pas été touchée depuis l’événement d’alimentation. Les fichiers de données semblaient « ok »,
jusqu’à ce qu’ils ne le soient pas.

Le postmortem a trouvé une probable incohérence de stripe : certaines stripes avaient une parité reflétant des blocs de données récents, tandis que d’autres avaient une parité ancienne et des données nouvelles.
Le journal du système de fichiers avait fait son travail, mais il n’avait jamais eu sa chance : il était construit sur un périphérique de blocs qui renvoyait des blocs corrompus comme s’ils étaient valides.

La mauvaise hypothèse n’était pas « RAID6 est mauvais ». La mauvaise hypothèse était de croire que la couche de blocs préserve toujours l’ordre d’écriture et l’atomicité lors d’une mise à jour de parité.
Ils ont migré vers des mirrors ZFS pour les bases de données et réservé RAIDZ2 pour le stockage d’objets volumineux et séquentiels où les scrubs et checksums leur donnaient un comportement détectable et réparable.

Mini-histoire 2 : l’optimisation qui s’est retournée contre eux

Une autre organisation — plateforme d’analytics interne, beaucoup de tâches batch — utilisait OpenZFS sur RAIDZ2. Les performances allaient bien, jusqu’à ce qu’ils onboardent une nouvelle charge :
un service d’ingestion en file faisant de petites écritures synchrones. La latence a grimpé.

Quelqu’un a fait ce que fait toujours quelqu’un : il a googlé « ZFS slow sync writes », a vu la phrase magique sync=disabled, et l’a appliquée au dataset.
Les graphiques souriaient. Les tickets se sont arrêtés. Victoire.

Des mois plus tard, ils ont eu un kernel panic pendant une mise à jour de driver. Au reboot, le pool s’est importé proprement. Pas d’erreurs. Mais le service d’ingestion a recommencé
à rejouer des messages qu’il croyait commités mais qui n’étaient pas réellement sur un stockage stable. Le système avalant a ingéré des doublons puis a écrasé des tables dérivées.
Rien n’a explosé bruyamment ; c’est juste devenu incorrect en silence.

Le retour de bâton n’était pas une corruption ZFS. C’était une corruption sémantique : le contrat de durabilité de l’application a été rompu. En désactivant sync, ils ont transformé
« ack après commit durable » en « ack après que la RAM s’est sentie bien ». La correction fut banale : restaurer sync=standard, ajouter un SLOG approprié avec protection contre la perte d’alimentation,
et régler l’agrégation des écritures côté application pour éviter d’appeler fsync à chaque éternuement.

Mini-histoire 3 : la pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe de services financiers utilisait ZFS en mirrors pour leur datastore de VM et RAIDZ2 pour les backups. Leur ingénieur stockage était résolument terre-à-terre :
scrubs mensuels, tests SMART et alertes sur la moindre erreur de checksum. Ils refusaient aussi d’acheter des SSD sans protection claire contre la perte d’alimentation.

Un trimestre, un nouveau lot de disques a commencé à renvoyer des erreurs de checksum occasionnelles pendant les scrubs. Pas d’erreurs de lecture. Pas de SMART en échec. Juste des mismatches de checksum
que ZFS corrigeait grâce à la redondance. Les tableaux de bord montraient des événements « self-healing » et une lente hausse des erreurs corrigées.

Les achats voulaient ignorer car les charges étaient correctes. L’ingénieur stockage a insisté : le budget d’erreurs n’est pas un compte d’épargne. Ils ont vidangé le pool de ce lot sur quelques maintenances,
remplacé les disques et fait RMA.

Deux mois plus tard, une autre équipe dans le même bâtiment (pile d’un autre fournisseur) a eu un incident sévère : une corruption silencieuse est apparue lors d’un test de restauration.
L’équipe finance n’a même pas eu d’incident. Leur pratique était ennuyeuse, correcte et donc efficace : les scrubs ont transformé une corruption silencieuse en tâche de maintenance planifiée plutôt qu’en panne surprise.

Tâches pratiques : commandes, sorties et décisions

Ce sont des gestes d’opérateur réels. Chaque tâche inclut une commande, ce que signifie la sortie, et quelle décision prendre ensuite.
Les exemples supposent OpenZFS sur Linux, mais la plupart se traduisent vers illumos/FreeBSD avec de légères différences.

Task 1: Confirm pool health and whether you already have checksum errors

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Determine if the device needs to be replaced, and clear the errors
  scan: scrub repaired 0B in 02:13:44 with 2 errors on Sun Dec 22 03:10:18 2025
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
            ata-WDC_WD80EFAX-00A0  ONLINE       0     0     2
            ata-WDC_WD80EFAX-00A1  ONLINE       0     0     0
            ata-WDC_WD80EFAX-00A2  ONLINE       0     0     0
            ata-WDC_WD80EFAX-00A3  ONLINE       0     0     0

errors: Permanent errors have been detected in the following files:

        tank/vmstore:vm-102-disk-0

Ce que cela signifie : ZFS a détecté des données corrompues via la checksum et n’a pas pu entièrement les réparer (erreur permanente), ou il a réparé mais enregistre des fichiers impactés.

Décision : Traitez cela comme un incident de production. Identifiez la charge impactée, restaurez le fichier/volume spécifique depuis la sauvegarde ou un réplica,
et planifiez le remplacement du disque si les erreurs continuent d’augmenter.

Task 2: Check whether your vdev layout is parity-based (where classic write hole matters)

cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0

Ce que cela signifie : vdev parité RAIDZ2. ZFS évitera les patterns classiques du write hole, mais vous devez toujours vous soucier de l’honnêteté des flushes et des sémantiques de sync.

Décision : Si ce pool héberge des bases de données à fort usage de sync, évaluez SLOG et les settings de dataset. Si c’est du stockage en vrac, concentrez-vous sur la cadence de scrub et la santé des disques.

Task 3: Confirm sync policy and whether someone “optimized” durability away

cr0x@server:~$ zfs get -o name,property,value -s local,default sync tank tank/db tank/vmstore
NAME         PROPERTY  VALUE
tank         sync      standard
tank/db      sync      standard
tank/vmstore sync      disabled

Ce que cela signifie : tank/vmstore accuse des écritures synchrones sans garantir le stockage stable.

Décision : Si cela héberge des VM, des bases de données ou tout ce qui attend que fsync ait un sens, revenez à sync=standard.
Si vous conservez disabled, documentez la fenêtre de perte de données et obtenez un accord explicite.

Task 4: Check if you actually have a SLOG and what it is

cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
config:

        NAME           STATE     READ WRITE CKSUM
        tank           ONLINE       0     0     0
          raidz2-0     ONLINE       0     0     0
            sda        ONLINE       0     0     0
            sdb        ONLINE       0     0     0
            sdc        ONLINE       0     0     0
            sdd        ONLINE       0     0     0
        logs
          nvme0n1p2    ONLINE       0     0     0

Ce que cela signifie : Un périphérique de log séparé existe. Il peut accélérer les écritures synchrones si il dispose d’une protection contre la perte d’alimentation et d’une faible latence.

Décision : Validez que le NVMe est de type entreprise avec PLP. Sinon, vous risquez d’échanger latence contre intégrité en cas de coupure.

Task 5: Confirm ashift (misalignment can masquerade as “ZFS parity is slow”)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
35:        vdev_tree:
52:                ashift: 12
89:                ashift: 12
126:               ashift: 12

Ce que cela signifie : ashift=12 = secteurs 4K. Bon choix par défaut pour disques/SSDs modernes.

Décision : Si vous voyez ashift=9 sur des disques 4K, attendez-vous à des problèmes. La correction nécessite de recréer le vdev (pas de changement in-place).

Task 6: Check recordsize/volblocksize against workload (small writes + RAIDZ can bite)

cr0x@server:~$ zfs get -o name,property,value recordsize,volblocksize tank/db tank/vmstore tank/vmstore/vm-102
NAME                  PROPERTY      VALUE
tank/db               recordsize    128K
tank/vmstore          recordsize    128K
tank/vmstore/vm-102   volblocksize  8K

Ce que cela signifie : Les datasets utilisent recordsize. Les ZVOLs utilisent volblocksize. Un décalage peut provoquer une amplification d’écriture.

Décision : Pour les zvols VM, choisissez un volblocksize adapté à l’invité et à la charge (souvent 8K ou 16K). Pour les bases de données sur datasets,
envisagez un recordsize plus petit si l’IO est aléatoire et petit, mais mesurez avant.

Task 7: Observe real-time IO and spot whether you’re sync-bound

cr0x@server:~$ zpool iostat -v tank 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        3.21T  11.3T    112    980  22.4M  18.1M
  raidz2-0                  3.21T  11.3T    112    980  22.4M  18.1M
    sda                         -      -     18    160  3.7M   4.2M
    sdb                         -      -     19    165  3.8M   4.3M
    sdc                         -      -     18    162  3.7M   4.2M
    sdd                         -      -     18    158  3.6M   4.1M
  nvme0n1p2                      -      -      0    510  0     6.1M
--------------------------  -----  -----  -----  -----  -----  -----

Ce que cela signifie : Le périphérique de log reçoit beaucoup d’écritures. C’est cohérent avec une charge heavy sync absorbée par le SLOG.

Décision : Si la latence reste mauvaise, le SLOG peut être lent ou saturé. Envisagez un NVMe PLP plus rapide, ou réduisez la fréquence de sync côté application (batching).

Task 8: Check TXG sync behavior and whether you’re thrashing on syncs

cr0x@server:~$ grep -E "txg_sync|spa_sync" /proc/spl/kstat/zfs/* 2>/dev/null | head
/proc/spl/kstat/zfs/dbgmsg:txg_sync_start txg 89213
/proc/spl/kstat/zfs/dbgmsg:spa_sync: syncing tank txg 89213
/proc/spl/kstat/zfs/dbgmsg:txg_sync_done txg 89213

Ce que cela signifie : Vous voyez les cycles de sync. Des syncs fréquents sous charge peuvent indiquer de petites écritures forçant des commits.

Décision : Corrélez avec les patterns fsync de l’application. Si c’est une DB, ajustez son checkpointing/batching ; si c’est NFS, vérifiez les options de montage client et la sémantique sync.

Task 9: Validate drive write cache settings (and decide whether to disable volatile caches)

cr0x@server:~$ sudo hdparm -W /dev/sda
/dev/sda:
 write-caching =  1 (on)

Ce que cela signifie : Le cache d’écriture du disque est activé. Ce n’est pas automatiquement mauvais si les flushes sont honorés et si vous avez une protection contre la perte d’alimentation.

Décision : Si vous ne faites pas confiance à la plateforme (pas de PLP, expanders SATA douteux, antécédents de bugs de flush), envisagez de désactiver le cache d’écriture :
hdparm -W0 (après tests). Attendez-vous à un impact sur les performances.

Task 10: Confirm TRIM/autotrim for SSD pools (not write hole, but prevents performance decay)

cr0x@server:~$ zpool get autotrim tank
NAME  PROPERTY  VALUE     SOURCE
tank  autotrim  on        local

Ce que cela signifie : ZFS émettra TRIM automatiquement. Cela aide la performance soutenue des SSD et l’usure.

Décision : Laissez-le activé pour les SSD sauf si vous avez un périphérique qui se comporte mal avec TRIM (rare aujourd’hui, fréquent à l’époque).

Task 11: Run a scrub and understand what “repaired” vs “errors” means

cr0x@server:~$ sudo zpool scrub tank
cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
  scan: scrub in progress since Thu Dec 26 01:12:40 2025
        512G scanned at 1.21G/s, 96G issued at 233M/s, 3.21T total
        0B repaired, 2.99% done, 0:21:34 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0

errors: No known data errors

Ce que cela signifie : Le scrub lit et vérifie les checksums. « 0B repaired » est bon. « No known data errors » est ce que vous souhaitez voir.

Décision : Si les scrubs réparent continuellement des données, vous avez un problème de fiabilité (disques, câblage, backplane, HBA, mémoire).
Si les scrubs trouvent des erreurs permanentes, lancez la restauration/récupération des datasets affectés et enquêtez sur le matériel.

Task 12: Verify compression and its effect on IO patterns

cr0x@server:~$ zfs get -o name,property,value compression,compressratio tank/db
NAME     PROPERTY       VALUE
tank/db  compression    lz4
tank/db  compressratio  1.78x

Ce que cela signifie : La compression est activée et efficace. Cela réduit les écritures physiques pour le même volume logique.

Décision : Gardez généralement lz4 activé. Si la latence est dominée par le CPU (rare sur CPU modernes), mesurez avant de changer quoi que ce soit.

Task 13: Check ARC behavior (because people blame “ZFS write hole” when it’s actually memory pressure)

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
01:22:10   845    47      5    12   26    35   74     0    0   58G   64G
01:22:11   812    55      6    14   25    41   75     0    0   58G   64G
01:22:12   901    50      5    13   26    37   74     0    0   58G   64G

Ce que cela signifie : Faible taux de miss, ARC proche de sa cible. Les lectures sont principalement servies depuis le cache.

Décision : Si miss% est élevé et que les disques sont occupés, envisagez plus de RAM ou L2ARC (avec prudence). Mais n’utilisez pas L2ARC à la place de corriger un goulot d’écriture sync.

Task 14: Spot a lying device: confirm write cache, flush support, and kernel messages

cr0x@server:~$ dmesg | grep -iE "flush|fua|barrier|cache" | tail -n 8
[    2.913244] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    2.914881] sd 0:0:0:1: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    2.916402] sd 0:0:0:2: [sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    2.918103] sd 0:0:0:3: [sdd] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA

Ce que cela signifie : « doesn’t support DPO or FUA » n’est pas automatiquement fatal (les flushes peuvent encore fonctionner), mais c’est un indice :
la pile peut s’appuyer sur des flushes plutôt que sur les sémantiques force-unit-access.

Décision : Si c’est un pool critique et orienté intégrité sync, préférez des périphériques/HBA qui se comportent de façon prévisible avec les flushes et qui ont du PLP si nécessaire.
Envisagez des mirrors pour les bases de données.

Playbook de diagnostic rapide

Quand quelqu’un dit « ZFS est corrompu » ou « nous avons rencontré le write hole », votre travail est de séparer trois choses :
(1) défaillances d’intégrité des données, (2) sémantique de durabilité, et (3) goulots de performance. Rapidement.

Première étape : déterminer si vous avez des erreurs de données maintenant

  1. Vérifier la santé du pool : zpool status -v. Si vous voyez des erreurs de checksum ou des erreurs permanentes, traitez comme incident actif.
  2. Vérifier les scrubs récents : dans la ligne scan de zpool status. Si des scrubs ont réparé des données récemment, trouvez la source de la corruption.
  3. Vérifier les logs système : dmesg pour resets de lien, erreurs d’I/O, timeouts HBA. Si le transport est instable, tout ce qui est au-dessus est victime.

Deuxième étape : confirmer si la plainte est en fait des écritures accusées perdues

  1. Vérifier les settings sync des datasets : zfs get sync. Si vous voyez disabled, vous avez probablement une violation de contrat de durabilité, pas une corruption.
  2. Vérifier la présence/santé du SLOG : section logs de zpool status. S’il manque et que la charge est sync-heavy, la latence peut être attendue.
  3. Demandez ce que « perdu » signifie : l’app a-t-elle ack après fsync ? Si oui, enquêtez sur sync, PLP et honnêteté des flushes.

Troisième étape : trouver le goulot (CPU, disque, sync, fragmentation ou capacité)

  1. Vue I/O : zpool iostat -v 1 pour voir si les disques ou le SLOG sont saturés.
  2. Pression de capacité : zfs list et pourcentage d’allocation du pool. Les pools quasi pleins fragmentent et ralentissent les écritures.
  3. Inadéquation charge : petites écritures aléatoires sur RAIDZ sans tuning peuvent être lentes ; envisagez des mirrors pour les datasets sensibles à la latence.

Erreurs courantes : symptômes → cause racine → fix

1) « On a rebooté et maintenant la base est incohérente »

  • Symptômes : La DB signale des transactions récentes manquantes ; l’application rejoue des messages ; pas d’erreurs checksum ZFS.
  • Cause racine : sync=disabled (ou l’application comptait sur fsync mais la pile ne le fournissait pas), ou un cache volatil a menti.
  • Fix : Mettre sync=standard ; ajouter un SLOG PLP si besoin ; valider l’UPS et tester le comportement en cas de perte d’alimentation ; s’assurer que le hardware honore les flushes.

2) « Le scrub répare sans cesse des données chaque mois »

  • Symptômes : zpool status montre des octets réparés ou des compteurs de checksum en hausse ; les charges semblent correctes.
  • Cause racine : Corruption silencieuse due aux disques, câblage, backplane, HBA, firmware, ou mémoire (oui, la RAM compte).
  • Fix : Remplacer les disques suspects ; reseater/remplacer les câbles ; mettre à jour le firmware HBA ; lancer memtest ; conserver les scrubs ; ne pas ignorer les erreurs corrigées.

3) « RAIDZ est lent pour le stockage VM »

  • Symptômes : Latence élevée, surtout sur les écritures sync ; zpool iostat montre beaucoup de petites écritures ; CPU souvent inactif.
  • Cause racine : Charge de petites écritures aléatoires ; RAID parité a amplification d’écriture ; mismatch volblocksize/recordsize ; pas de SLOG pour les patterns sync-heavy.
  • Fix : Utiliser des mirrors pour VM/bases de données ; tuner volblocksize ; ajouter un SLOG PLP pour sync ; garder RAIDZ pour workloads throughput.

4) « Nous voyons des erreurs permanentes sur quelques fichiers après un crash »

  • Symptômes : zpool status -v liste des fichiers spécifiques avec erreurs permanentes ; erreurs de checksum sur un disque.
  • Cause racine : Corruption réelle sur disque que la redondance n’a pas pu guérir (ex. parité dépassée, mauvais secteurs sur plusieurs disques, ou réplicas obsolètes).
  • Fix : Restaurer les fichiers/volumes affectés depuis backup/snapshot replication ; remplacer le hardware défaillant ; lancer un scrub ; valider les backups par des tests de restauration.

5) « Après avoir ajouté un cache SSD, c’était pire »

  • Symptômes : Pics de latence ; SSD à 100 % d’utilisation ; peu d’amélioration du hit rate.
  • Cause racine : Mauvais usage de L2ARC ou SSD faible ; mise en cache d’une mauvaise charge ; pression mémoire due au overhead des métadonnées L2ARC.
  • Fix : Supprimer ou redimensionner L2ARC ; ajouter de la RAM en priorité ; se concentrer sur le SLOG si le problème vient des écritures sync, pas des lectures.

Checklists / plan étape par étape

Checklist : concevoir ZFS sans réintroduire la douleur du write hole

  1. Choisir les vdevs selon la latence : mirrors pour bases de données/VM ; RAIDZ2/3 pour stockage en masse et backups.
  2. Supposer que des événements d’alimentation arrivent : UPS aide, mais testez le comportement. Les brownouts et double-transfers existent.
  3. Ne pas désactiver sync à la légère : si nécessaire, isolez-le sur un dataset et documentez le risque.
  4. Utiliser des périphériques PLP là où la durabilité compte : surtout pour SLOG et workloads métadonnées-intensifs.
  5. Programmer des scrubs : mensuellement est courant ; plus fréquent pour les pools critiques avec beaucoup de churn.
  6. Alerter sur les erreurs de checksum : les erreurs corrigées sont un signal d’alerte, pas un motif de célébration.
  7. Valider les backups par des restores : « backup succeed » n’est pas « restore fonctionne ».
  8. Garder de la marge : éviter des pools proches de la saturation ; la fragmentation et la pression d’allocation vous puniront.

Étape par étape : enquêter sur des allégations de « possible write hole » dans une pile parité

  1. Collecter la timeline : ce qui a planté, quand, et ce qui écrivait à ce moment.
  2. Rechercher des symptômes de mises à jour partielles : incohérences au niveau app sans erreurs de lecture disque associées.
  3. Vérifier les caches volatils : cache d’écriture disque, mode cache contrôleur RAID, absence de BBWC/FBWC.
  4. Valider la sémantique des flushes : logs kernel ; settings contrôleur ; couches de virtualisation.
  5. Lancer des checks de cohérence : pour ZFS lancez scrub ; pour mdadm envisager des opérations check/repair (avec prudence) ; pour les bases vérifiez les checksums internes.
  6. Décider la remédiation : restaurer des copies connues bonnes, reconstruire depuis des replicas, ou migrer vers un design offrant intégrité bout en bout.

FAQ

1) ZFS élimine-t-il complètement le write hole ?

ZFS évite le write hole classique des RAID parité en n’effectuant pas de mises à jour en place et en commitant les changements de manière transactionnelle.
Mais ZFS dépend toujours du hardware pour honorer les sémantiques de flush. Si les périphériques mentent, vous pouvez perdre les écritures récentes.

2) RAIDZ n’est-il que RAID5 sous un autre nom ?

Non. RAIDZ est intégré à l’allocation ZFS et au comportement copy-on-write, supporte des largeurs de stripe variables, et est protégé par des checksums bout en bout.
Il se comporte différemment sous conditions de mises à jour partielles que le RMW classique de RAID5.

3) Si j’utilise RAIDZ2/3, puis-je ignorer les sauvegardes ?

Non. La parité protège contre la défaillance de périphérique, pas contre la suppression, le ransomware, les bugs applicatifs, ou « j’ai écrit le mauvais dataset ».
Les snapshots ZFS aident, mais ils ne sont pas des backups hors-système à moins d’être répliqués ailleurs.

4) Pourquoi recommande-t-on des mirrors pour les bases de données ?

Pour la latence. Les mirrors ont des chemins d’écriture plus simples et souvent de meilleurs IOPS pour les workloads aléatoires. RAIDZ est excellent pour l’efficacité capacité et le débit,
mais la parité plus l’amplification d’écriture peuvent pénaliser les petites écritures synchrones.

5) Ai-je besoin d’un périphérique SLOG ?

Seulement si vous avez des écritures synchrones significatives et que les dispositifs du pool sont trop lents pour atteindre les objectifs de latence.
Un SLOG n’est pas un cache d’écriture général ; il accélère le ZIL pour les écritures sync. N’ajoutez pas un SSD bon marché sans PLP et appelez-le « plus sûr ».

6) Que signifie « checksum error » dans zpool status ?

ZFS a lu des données qui ne correspondaient pas à la checksum stockée dans les métadonnées. C’est la preuve d’une corruption dans le chemin de lecture ou sur le média.
Avec la redondance, ZFS peut souvent s’auto-réparer en lisant une copie correcte.

7) Le journaling ext4 peut-il empêcher la corruption par write hole sur RAID5 ?

Le journaling ext4 peut garder la cohérence des métadonnées du système de fichiers après un crash, mais il ne peut pas corriger une mismatch de stripe de parité en dessous.
Il suppose que le périphérique de blocs fournit des blocs cohérents quand demandé.

8) Le write hole n’est-il pas résolu par l’utilisation d’un UPS ?

Un UPS réduit le risque mais ne l’élimine pas. Les crashs, kernel panics, resets de contrôleur et bugs de firmware interrompent toujours des séquences d’écriture.
De plus, beaucoup d’« événements d’alimentation » sont des brownouts et des reboots, pas des pertes d’alimentation propres.

9) Si ZFS est sûr, pourquoi des gens perdent encore des données ?

Parce que la sécurité est une propriété du système. Coupables courants : perte de redondance, hardware défaillant, caches qui mentent, désactivation de sync,
ignorer les erreurs de checksum, et ne pas tester les restaurations.

10) Quelle est la pratique opérationnelle la plus simple qui améliore l’intégrité ZFS ?

Des scrubs réguliers avec alertes sur toute erreur de checksum. Le scrub transforme la corruption silencieuse en ticket que vous pouvez gérer en semaine.

Étapes pratiques suivantes

Si vous exécutez un RAID parité en dehors de ZFS, supposez que le write hole existe jusqu’à preuve du contraire. Si vous exécutez ZFS, supposez que votre hardware
peut encore mentir et que vos opérateurs peuvent encore « optimiser » la durabilité hors service. Ces deux hypothèses sont saines.

  1. Inventoriez votre risque : identifiez les ensembles parité, les modes cache et si le cache d’écriture est persistant.
  2. Auditez les propriétés ZFS : trouvez les datasets avec sync=disabled et décidez si c’était vraiment voulu.
  3. Plan scrubs + alerting : assurez-vous que les scrubs tournent et que quelqu’un est alerté sur les erreurs de checksum.
  4. Alignez le design sur la charge : mirrors pour latence, RAIDZ pour capacité/débit, et ne prétendez pas qu’ils sont interchangeables.
  5. Testez un scénario de perte d’alimentation : pas en débranchant une prod, mais en validant que votre plateforme honore les flushes et que vos applis supportent la reprise après crash.
← Précédent
MySQL vs PostgreSQL : exercices PITR — testez la restauration avant d’en avoir besoin
Suivant →
Le pare-feu Proxmox vous a verrouillé : restaurer SSH/UI Web depuis la console sans panique

Laisser un commentaire