Le premier signe n’est jamais « le pool est en train de mourir ». C’est un message Slack : « Pourquoi la base de données se fige au hasard ? »
Puis un tableau de bord : la latence p99 devient de l’art moderne. Le CPU va bien. Le réseau va bien. ZFS indique « ONLINE ».
Et pourtant, toutes les quelques minutes votre pool SSD-only se heurte à un mur comme s’il avait oublié comment écrire.
Ce mur est généralement la garbage collection du SSD qui rencontre les schémas d’écriture de ZFS, puis qui multiplie la douleur via des écritures sync,
des petits blocs ou une fragmentation accidentelle. Ce n’est pas théorique. Cela arrive en production, à 2h du matin, juste après que quelqu’un
eut « optimisé » quelque chose.
À quoi ressemble un « effondrement par garbage collection » sur ZFS
Les SSD n’écrivent pas en place. Ils écrivent sur des pages fraîches puis nettoient plus tard les anciennes. Ce nettoyage est la garbage collection (GC),
et il est normalement masqué par le firmware du contrôleur et l’espace réservé. ZFS n’écrit pas non plus en place. C’est un système copy-on-write.
Vous avez donc deux couches de « écrire ailleurs, nettoyer plus tard » empilées. Quand les conditions sont réunies (ou mauvaises),
ces couches se synchronisent en misère périodique.
En production vous verrez :
- Pics de latence sans goulot d’étranglement CPU évident. Le système est « inactif » tandis que le stockage fond.
- Effondrement des IOPS d’écriture après des écritures aléatoires soutenues, surtout quand le pool est fortement utilisé.
- Les charges riches en écritures sync (bases de données, NFS, hyperviseurs VM) se bloquent soudainement même si « c’est du NVMe ».
- zpool iostat affiche d’énormes temps d’attente alors que la bande passante semble modeste.
- Les pics coïncident avec les commits de transaction group (TXG) ou des rafales périodiques de petites écritures.
L’essentiel : l’effondrement GC n’est pas « ZFS est lent ». C’est une boucle de rétroaction au niveau système : ZFS produit un motif d’écriture ; le firmware SSD réagit ;
la latence augmente ; ZFS met plus en file d’attente ; le firmware dispose de moins de temps libre ; la latence augmente encore. C’est comme un embouteillage où tout le monde
décide de changer de voie en même temps.
Blague n°1 : la garbage collection SSD, c’est comme ranger votre appartement en fourrant tout dans un placard — jusqu’à ce que le placard commence à demander le loyer.
Faits intéressants et contexte historique
- TRIM n’a pas toujours été acquis. Les premières déploiements SSD tournaient souvent sans TRIM/discard efficace, si bien que les disques « oubliaient » quelles pages étaient libres et les performances se dégradaient avec le temps.
- Le copy-on-write de ZFS précède l’omniprésence des SSD. ZFS a été conçu pour l’intégrité des données et les grands stockages ; le comportement des flashs est devenu une préoccupation de réglage plus tard, quand le flash est devenu la norme.
- La write amplification est une double taxe. Les SSD réécrivent déjà des structures internes ; les systèmes de fichiers CoW peuvent augmenter les mouvements internes si la charge fragmente l’espace libre.
- Les SSD entreprise ne sont pas juste « plus rapides ». Ils sont généralement livrés avec plus d’overprovisioning, un comportement steady-state meilleur et un firmware optimisé pour les charges mixtes.
- La protection contre la perte d’alimentation (PLP) a changé l’histoire du SLOG. Dès que vous dépendez des sémantiques sync, des SSD avec condensateur (capacité PLP) deviennent une exigence de conception, pas un luxe.
- Les erreurs d’alignement 4K sont anciennes et persistent. L’industrie est passée de secteurs 512 octets à des 4K physiques, et le mauvais alignement détruit silencieusement les performances.
- NVMe a résolu un problème de file d’attente, pas la physique. De profondes files et une faible surcharge aident, mais les blocs d’effacement flash et la GC existent toujours ; les falaises de latence sont réelles.
- La compression ZFS est devenue un outil de performance sur SSD. Avec LZ4, moins de NAND écrit peut signifier plus de débit et moins de stress GC, pas seulement de l’espace économisé.
- Les special vdev sont une arme moderne avec des bords tranchants. Ils peuvent être incroyables pour les métadonnées/petits blocs, mais le domaine de défaillance et les erreurs de dimensionnement ont fait tomber plus d’un pool « rapide ».
Un modèle mental qui prédit réellement les pannes
1) ZFS écrit par groupes de transactions, pas selon le rythme de votre application
Les applications émettent des écritures. ZFS collecte les données sales en mémoire (ARC) et les vide par groupes de transactions (TXG).
Ces commits sont en rafales. Les écritures en rafales passent pour les SSD jusqu’à ce que le SSD manque de pages propres et doive faire de la GC
exactement quand vous le martellez. Alors la rafale se transforme en arrêt.
2) Les SSD ont un « état stable » que le marketing n’imprime pas
Les performances à la sortie de la boîte ne sont pas les performances steady-state. Après que le disque se remplit et se réécrit, la write amplification augmente et
les performances d’écriture aléatoires soutenues peuvent chuter drastiquement. Un pool à 80–90 % d’utilisation est la configuration classique : moins d’espace libre signifie
moins d’options faciles pour l’allocateur ZFS et pour l’FTL (flash translation layer) du SSD.
3) Les écritures sync sont les meilleures amies de la latence (malheureusement)
Si votre charge nécessite des écritures sync, ZFS doit confirmer la durabilité sur le stockage stable avant d’accuser réception.
Sans périphérique de journal séparé (SLOG) capable d’accepter des écritures à faible latence en toute sécurité, vos périphériques de pool prennent le coup.
Si ces périphériques de pool sont simultanément occupés par la GC, votre p99 devient votre personnalité.
4) La fragmentation n’est pas seulement « des fichiers éparpillés »
Sur ZFS, la fragmentation concerne la segmentation de l’espace libre et les schémas d’allocation de blocs. Sur les SSD, elle se couple avec le mapping propre à l’FTL.
Une forte fragmentation rend plus difficile l’écriture de segments contigus importants ; le SSD finit par faire plus de copies internes pendant la GC.
Le résultat classique : « mais c’est un SSD, pourquoi l’écriture séquentielle n’est que 150 MB/s ? »
5) La compression peut être un réglage pour SSD
La compression n’est pas seulement pour économiser de l’espace. Si vous compressez, vous écrivez moins d’octets, ce qui peut réduire la write amplification.
Sur les CPU modernes, LZ4 est souvent un gain net en débit et latence. L’exception : les données déjà compressées ou si le CPU est le goulot.
Une idée paraphrasée de Werner Vogels (CTO d’Amazon) : « Tout tombe en panne, tout le temps — concevez de façon à pouvoir y survivre. »
ZFS sur SSD, c’est exactement ça : supposez que des falaises de latence existent et concevez autour.
Méthode de diagnostic rapide (premier/deuxième/troisième)
Premier : confirmer que le symptôme est la latence du stockage, pas le CPU ni le réseau
- Vérifier la répartition de la latence applicative (temps de commit DB, temps fsync, latence disque invité VM).
- Vérifier la charge système vs iowait : une charge élevée avec faible usage CPU signifie souvent blocage sur IO.
- Confirmer que ZFS est le périphérique attendu et non un mount réseau aval ou une seule file NIC saturée.
Second : déterminer si la douleur vient du sync, de la fragmentation ou de l’état steady-state du périphérique
- Si les blocages s’alignent sur fsync/commit : suspecter le chemin d’écriture sync (SLOG, réglages sync, PLP).
- Si les performances se dégradent à mesure que le pool se remplit : suspecter la pression d’espace libre + fragmentation et la pression GC du SSD.
- Si « les écritures aléatoires s’effondrent après quelques minutes » : suspecter le steady-state du SSD et un manque d’overprovisioning/TRIM.
Troisième : isoler quelle couche s’effondre
- Couche ZFS : timing TXG, limites de données sales, mismatch recordsize/volblocksize, saturation de special vdev.
- Couche périphérique : firmware GC, throttling thermique, erreurs média, saturation des files internes.
- Couche topologie : RAIDZ trop large sur SSD pour votre profil IOPS, ou goulot sur un seul vdev.
Si vous ne retenez qu’une habitude opérationnelle : pendant un incident, collectez d’abord des preuves.
Tuner à l’aveugle, c’est la façon dont on finit avec un « correctif de performance » qui devient un postmortem de perte de données.
Tâches pratiques : commandes, sorties, décisions (12+)
Task 1: Check pool health and topology (you’re not tuning a broken system)
cr0x@server:~$ zpool status -v ssdpool
pool: ssdpool
state: ONLINE
scan: scrub repaired 0B in 00:12:19 with 0 errors on Sun Dec 22 03:10:11 2025
config:
NAME STATE READ WRITE CKSUM
ssdpool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR-00000 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR-00001 ONLINE 0 0 0
errors: No known data errors
Ce que cela signifie : Si vous êtes dégradé ou en train de resilver, les données de performance sont contaminées. Les mirrors se comportent différemment que RAIDZ.
Décision : Ne touchez pas au tuning tant que le pool n’est pas stable et que les scrubs sont propres. Si la topologie est RAIDZ et que la charge est sync-random write intensive, reconsidérez le design.
Task 2: Watch real-time latency and queueing per vdev
cr0x@server:~$ zpool iostat -v ssdpool 1
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
ssdpool 620G 280G 120 1800 12.3M 210M
mirror-0 620G 280G 120 1800 12.3M 210M
nvme0n1 - - 60 900 6.1M 105M
nvme1n1 - - 60 900 6.2M 105M
-------------------------- ----- ----- ----- ----- ----- -----
Ce que cela signifie : Ceci montre le débit, pas la latence directement. Si les ops chutent tandis que la latence applicative augmente, le périphérique est en train de se bloquer.
Décision : Si un vdev sous-performe ou est inégal, suspectez un firmware périphérique/un souci thermique ou un chemin défaillant.
Task 3: Check detailed per-device latency (OpenZFS on Linux)
cr0x@server:~$ cat /proc/spl/kstat/zfs/vdev_queue | head -n 25
nthreads 4
vdev_queue_max_active 1000
vdev_queue_min_active 1
vdev_queue_max_pending 1000
vdev_queue_min_pending 1
vdev_queue_max_threads 8
vdev_queue_min_threads 1
vdev_queue_agg_lim 131072
vdev_queue_read_gap_limit 32768
vdev_queue_write_gap_limit 65536
...
Ce que cela signifie : Le tuning des queues existe, mais ce n’est rarement le premier levier. Les valeurs par défaut sont généralement correctes sauf si vous avez un pattern de contention très spécifique.
Décision : Ne commencez pas par bidouiller les internals de la queue. Confirmez d’abord que vous ne causez pas un effondrement GC via l’espace, le sync et le dimensionnement des blocs.
Task 4: Confirm autotrim state and enable it if appropriate
cr0x@server:~$ zpool get autotrim ssdpool
NAME PROPERTY VALUE SOURCE
ssdpool autotrim off default
Ce que cela signifie : Avec autotrim off, les blocs libérés peuvent ne pas être communiqués rapidement aux SSD. Certains disques s’en accommodent ; d’autres se dégradent lentement vers un triste steady-state.
Décision : Pour les pools SSD-only, activez autotrim sauf si vous avez un problème connu avec le firmware/les drives et les TRIM mis en file.
cr0x@server:~$ sudo zpool set autotrim=on ssdpool
Task 5: Verify discard/TRIM is actually reaching the device (Linux)
cr0x@server:~$ lsblk -D
NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
nvme0n1 0 4K 2G 0
nvme1n1 0 4K 2G 0
Ce que cela signifie : DISC-GRAN montre la granularité du discard. Si c’est 0B, le périphérique/la pile peut ne pas supporter le discard.
Décision : Si le discard n’est pas supporté, autotrim ne servira à rien. Prévoyez plus d’overprovisioning et maintenez une utilisation de pool plus basse.
Task 6: Check pool utilization and fragmentation
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag,ashift,health ssdpool
NAME SIZE ALLOC FREE CAP FRAG ASHIFT HEALTH
ssdpool 932G 620G 312G 66% 18% 12 ONLINE
Ce que cela signifie : CAP et FRAG corrèlent avec la douleur de l’allocateur et la pression GC. Sur les pools SSD-only, les problèmes commencent souvent à apparaître à mesure que vous approchez d’une CAP élevée.
Décision : Considérez 80%+ comme « zone de danger » pour les écritures aléatoires mixtes. Si vous devez fonctionner à chaud, utilisez des drives avec sérieux OP et concevez pour cela.
Task 7: Identify datasets with bad recordsize for the workload
cr0x@server:~$ zfs get -r recordsize,compression,atime ssdpool/app
NAME PROPERTY VALUE SOURCE
ssdpool/app recordsize 128K default
ssdpool/app compression lz4 local
ssdpool/app atime off local
Ce que cela signifie : 128K recordsize convient aux IO de streaming et à beaucoup de charges générales. Il peut être terrible pour les petites mises à jour aléatoires (bases de données) en augmentant la write amplification.
Décision : Pour les fichiers de données DB ou les images VM, considérez 16K ou 8K recordsize (datasets) ou volblocksize approprié (zvols), puis mesurez et observez.
Task 8: For zvols, check volblocksize and alignment expectations
cr0x@server:~$ zfs get volblocksize,compression,logbias,sync ssdpool/vmstore
NAME PROPERTY VALUE SOURCE
ssdpool/vmstore volblocksize 8K local
ssdpool/vmstore compression lz4 inherited from ssdpool
ssdpool/vmstore logbias latency default
ssdpool/vmstore sync standard default
Ce que cela signifie : volblocksize est fixé à la création. Un zvol 8K est souvent judicieux pour les IO aléatoires VM, mais il peut augmenter les métadonnées et la surcharge IO selon la charge.
Décision : Faites correspondre volblocksize au filesystem invité/taille de page DB et à la charge. Si vous vous êtes trompé, recréez le zvol. Il n’y a pas de bascule magique ensuite.
Task 9: Check whether sync writes are dominating
cr0x@server:~$ sudo zpool iostat -v ssdpool 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
ssdpool 620G 280G 100 2400 10.1M 140M
mirror-0 620G 280G 100 2400 10.1M 140M
nvme0n1 - - 50 1200 5.0M 70M
nvme1n1 - - 50 1200 5.1M 70M
-------------------------- ----- ----- ----- ----- ----- -----
cr0x@server:~$ sudo cat /proc/spl/kstat/zfs/zil | head
zil_commit_count 98122
zil_commit_writer_count 98050
zil_commit_waiter_count 72
zil_commit_time 19123456789
zil_commit_lwb_count 220001
Ce que cela signifie : Un ZIL occupé indique beaucoup de sémantiques synchrones. C’est normal pour certaines charges.
Décision : Si vous avez besoin de durabilité sync, pensez à un SLOG approprié avec PLP. Si vous n’en avez pas besoin, corrigez les options de montage/export de l’application plutôt que de tromper ZFS.
Task 10: Verify SLOG presence and whether it’s actually helping
cr0x@server:~$ zpool status ssdpool
pool: ssdpool
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
ssdpool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
logs
nvme2n1 ONLINE 0 0 0
Ce que cela signifie : Un périphérique de log existe. Cela ne veut pas dire qu’il est sûr ou rapide. S’il manque de PLP, une coupure de courant peut transformer « sync rapide » en « corruption créative ».
Décision : N’utilisez que des périphériques SLOG conçus pour la protection contre la perte d’alimentation. Si vous ne pouvez pas le garantir, acceptez une latence sync plus élevée ou ré-architecturez.
Task 11: Inspect dirty data behavior (TXG pressure)
cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep 'dirty|txg|memory_throttle' | head -n 12
dirty_data 1879048192
dirty_over_target 0
memory_throttle_count 12
txg_syncing 5321
txg_synced 5320
Ce que cela signifie : Si memory_throttle_count augmente, ZFS force les écrivains à attendre parce que les données sales sont trop élevées.
Cela corrèle souvent avec un stockage qui ne suit pas (ou des arrêts périodiques).
Décision : Si le sync TXG est lent parce que les périphériques se bloquent, corrigez d’abord les causes device/GC. Tuner les limites de dirty peut masquer les symptômes mais ne guérira pas la maladie.
Task 12: Check for thermal throttling on NVMe (the silent performance killer)
cr0x@server:~$ sudo nvme smart-log /dev/nvme0n1 | egrep 'temperature|warning|critical'
temperature : 77 C
warning_temp_time : 143
critical_comp_time : 0
Ce que cela signifie : warning_temp_time indique que le contrôleur a passé du temps au-dessus du seuil d’alerte. Le throttling ressemble souvent à un « effondrement GC aléatoire ».
Décision : Corrigez le flux d’air, les dissipateurs, la disposition du châssis. Si vous réglez ZFS pour compenser la chaleur, vous écrivez une politique de ventilation avec étapes supplémentaires.
Task 13: Check NAND wear and spare blocks (a proxy for steady-state behavior)
cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | egrep 'Percentage Used|Data Units Written|Available Spare'
Available Spare: 100%
Available Spare Threshold: 10%
Percentage Used: 3%
Data Units Written: 18,442,118
Ce que cela signifie : Un « Percentage Used » élevé ne cause pas directement l’effondrement GC, mais des disques usés peuvent se comporter différemment, et une réserve faible peut réduire la marge de manœuvre en performance.
Décision : Suivez ces indicateurs dans le temps. Si vous constatez une instabilité de performance et que l’usure est élevée, remplacez avant de déboguer le firmware un vendredi soir.
Task 14: Measure sync latency directly (quick and dirty)
cr0x@server:~$ sudo zfs create -o sync=standard -o compression=off ssdpool/testsync
cr0x@server:~$ cd /ssdpool/testsync
cr0x@server:~$ /usr/bin/time -f "%e seconds" bash -c 'for i in {1..5000}; do dd if=/dev/zero of=f bs=4k count=1 conv=fsync oflag=dsync status=none; done'
12.84 seconds
Ce que cela signifie : Ce n’est pas une suite de benchmark ; c’est un canari. Si cela devient soudainement 60+ secondes sous charge, votre chemin sync est malsain.
Décision : Si la latence sync est inacceptable, soit ajoutez un SLOG correct, réduisez les exigences sync côté application (seulement si sûr), soit déplacez la charge.
Les réglages qui comptent (et ceux qui n’en valent généralement pas la peine)
Gardez de l’espace libre comme si c’était sérieux
Les pools SSD-only sont plus heureux avec de l’espace tampon. L’allocateur ZFS a besoin de marge, et le SSD a besoin de marge.
Si votre pool dépasse régulièrement 80% et que vous effectuez des écritures aléatoires, vous demandez un effondrement GC.
Les mirrors tolèrent mieux cela que RAIDZ, mais ne soyez pas arrogant.
Que faire :
- Visez 60–75% d’utilisation pour les charges mixtes d’écritures aléatoires qui vous importent.
- Utilisez des disques plus grands que ce dont vous « avez besoin » et traitez l’excédent comme réserve de performance.
- Envisagez de l’overprovisioning explicite (laisser de l’espace non partitionné) si le firmware SSD en profite.
Autotrim : activez-le, puis vérifiez qu’il ne vous nuit pas
Sur les stacks modernes, autotrim vaut généralement le coup pour les pools SSD-only. Il aide à maintenir des performances steady-state en informant le périphérique de ce qui est libre.
Mais TRIM n’est pas gratuit : il ajoute des IO et peut interagir mal avec certains disques grand public ou firmware bogué.
Position opérationnelle :
- Activez autotrim et surveillez la latence pendant des périodes de suppression intense.
- Si vous voyez des pics induits par TRIM, envisagez des fenêtres de trim planifiées (lorsque supportées) ou des SSD différents.
Recordsize/volblocksize : cessez de prétendre qu’une seule taille convient à tout
Le recordsize par défaut (128K) convient à beaucoup de charges fichier. Pour des bases de données qui font des pages 8K avec des mises à jour fréquentes,
128K peut gonfler la write amplification : ZFS réécrit des enregistrements logiques plus gros et touche plus de métadonnées.
Conseils opinionnés :
- Bases de données sur datasets : essayez recordsize 16K (parfois 8K), compression lz4, atime off.
- Images VM sur datasets : souvent recordsize 64K ou 128K est acceptable si l’IO est plutôt séquentiel ; si c’est aléatoire, des tailles plus petites peuvent aider.
- Images VM sur zvols : définissez volblocksize à 8K ou 16K selon l’IO invité et acceptez qu’il faille recréer pour changer.
Compression : LZ4 est la valeur par défaut pour une raison
La compression réduit les octets écrits. Moins d’octets écrits signifie moins d’usure NAND, moins de pression GC, et parfois un meilleur débit.
Avec les pools SSD-only, la compression est souvent une fonctionnalité de performance déguisée en fonctionnalité de capacité.
Utilisez compression=lz4 de manière générale sauf raison mesurée contraire. Si le CPU est le goulot, vous le remarquerez vite.
Comportement sync : le réglage rapide et sûr est « standard »
Il y a trois choix courants dont on débat :
- sync=standard : ZFS honore les demandes applicatives. C’est le défaut et généralement correct.
- sync=disabled : ZFS ment sur le sync. Les performances s’améliorent. Votre profil de risque aussi.
- sync=always : force le sync pour tout, souvent utilisé pour des tests ou des exigences de conformité spécifiques, et cela exposera vite des SLOG/pool faibles.
Si vous exécutez des bases de données, hyperviseurs ou NFS pour des applications sérieuses : n’utilisez pas sync=disabled comme « étape de réglage ».
Ce n’est pas du tuning. C’est changer le contrat de durabilité. Parfois acceptable dans des environnements éphémères ; en production générale, c’est expliquer au financement ce que « accusé mais pas durable » signifie.
SLOG : uniquement quand vous en avez besoin, et uniquement si c’est le bon périphérique
Un SLOG aide la latence des écritures sync quand votre charge émet beaucoup d’écritures sync et que la latence du pool principal est le goulot.
Il n’accélère pas les écritures async normales. Il ne corrige pas un pool plein et fragmenté. Et il doit absolument être sûr contre la perte d’alimentation.
Règles de conception SLOG qui tiennent dans la vraie vie :
- PLP est obligatoire pour un SLOG dans tout environnement où une coupure est possible (donc : tous).
- Miroirez-le si le perdre serait opérationnellement pénible ; un SLOG perdu ne perdra pas les données principales, mais peut coûter du temps d’arrêt et du stress.
- Ne le sur-dimensionnez pas : vous avez besoin d’assez pour des rafales ; des SLOG énormes n’achètent pas grand-chose.
Ashift : faites-le bien à la création ou payez pour toujours
ashift définit l’exposant de taille de secteur du pool. Sur des SSD avec secteurs physiques 4K, ashift=12 est typique.
Un mauvais ashift provoque des cycles read-modify-write et une write amplification inutile. C’est une de ces erreurs qui paraît petite jusqu’à ce que vous graphez la latence.
Special vdevs : outil fantastique, bords tranchants
Mettre métadonnées et petits blocs sur un special vdev peut réduire l’amplification IO et améliorer la latence. Cela peut aussi devenir un point unique de défaillance du pool si non redondant,
et il peut chauffer s’il est trop petit. Les gens aiment les special vdev parce que le graphe de performance monte immédiatement. Ils les détestent au rebuild.
Si vous déployez des special vdev :
- Miroirez-les. Traitez-les comme une infrastructure critique, parce que c’est le cas.
- Dimensionnez-les pour le long terme, surtout si vous y stockez de petits blocs.
- Surveillez leur usure séparément. Ils peuvent s’user plus vite que les périphériques du pool principal.
Sur quoi ne pas s’obséder en premier
Les gens adorent bidouiller ce qu’ils peuvent changer rapidement : tailles d’arc, paramètres obscurs de module, ordonnanceurs IO.
Parfois cela compte. La plupart du temps, l’effondrement GC est causé par pression d’espace, sémantiques sync, mismatch de taille de bloc,
mélange de classes de disques, ou throttling thermique. Réglez les gros leviers avant de danser avec des sysctls noyau.
Blague n°2 : Si vous réglez un effondrement GC SSD en changeant douze sysctls, félicitations — vous avez inventé un nouveau mode d’incident avec un nom plus joli.
Trois mini-récits d’entreprise depuis le terrain
Incident causé par une mauvaise hypothèse : « NVMe signifie que le sync est gratuit »
Une entreprise SaaS de taille moyenne a migré d’un mix HDD/SSD vers un pool ZFS tout NVMe. Le plan était simple :
« Disques plus rapides, même architecture, tout le monde gagne. » Leur base principale tournait sur des ZVOL présentés aux VM.
La migration était un projet de week-end. Ça a plutôt bien fonctionné. Puis lundi est arrivé.
Des pics de latence ont frappé toutes les quelques minutes. Les nœuds applicatifs ont expiré sur des transactions. Les ingénieurs voyaient des NVMe avec des IOPS annoncés énormes et ont supposé
que la base ne pouvait pas attendre le stockage. Ils ont chassé des problèmes réseau. Ils ont chassé des contentions de verrou. Ils ont scaled les pods applicatifs.
Ça a empiré, parce que la base a fait plus de retries, ce qui a généré plus d’écritures sync.
La mauvaise hypothèse était que la latence NVMe est toujours basse, et que ZFS sur NVMe se comporte comme « NVMe brut mais avec checksums ».
En réalité la charge était sync-heavy, le pool se remplissait vite, et les NVMe grand public avaient des performances random write steady-state médiocres.
La GC faisait de longues copies internes pendant les flush TXG.
La correction n’a pas été exotique : déplacer le journal de la base vers un SLOG PLP correct, garder l’utilisation du pool sous contrôle, et aligner volblocksize avec la taille de page de la base.
Ils ont aussi amélioré le refroidissement ; les périphériques flirtait avec le throttling lors des pics.
L’incident s’est terminé sans patch kernel héroïque, mais par une demande d’achat et un plan de capacité.
Optimisation qui a mal tourné : « Désactivons le sync pour la vitesse »
Une autre société avait une ferme de build sur des exports NFS d’un pool ZFS SSD-only. Les temps de build comptaient, et les développeurs se plaignaient fort.
Quelqu’un a remarqué que les clients NFS faisaient beaucoup d’écritures synchrones. Quelqu’un d’autre a vu un article suggérant sync=disabled.
Vous devinez la suite.
Les temps de build se sont améliorés immédiatement. Les graphiques étaient beaux. L’équipe a déclaré la victoire et a passé à autre chose.
Quelques semaines plus tard, un événement électrique a touché un rack. L’UPS couvrait partiellement et le nœud de stockage a redémarré. Le pool s’est importé proprement.
Le système de fichiers a été monté. Tout le monde s’est relaxé. Puis le cache de build a commencé à retourner des artefacts corrompus de façon subtile : mauvais objets, checksums incompatibles,
échecs de tests intermittents qui semblaient être un code flaky.
Le postmortem a été gênant parce que rien n’avait « crashé » de façon évidente. Le système a fait exactement ce qu’on lui avait demandé :
accuser réception des écritures sans attendre le stockage stable. C’est ce que signifie sync=disabled. Ils avaient échangé la durabilité contre la vitesse sans documenter,
ni circonscrire l’impact.
La correction a été ennuyeuse et légèrement humiliante : revenir à sync=standard, ajouter un SLOG correct dimensionné pour la charge sync,
et tuner les options d’export NFS/montage client pour réduire les sémantiques sync inutiles sur les chemins de cache qui pouvaient les tolérer.
Ils ont aussi instauré une politique : les changements de performance qui modifient la durabilité exigent approbation et plan de rollback.
Pratique ennuyeuse mais correcte qui a sauvé la mise : « On a gardé 30% de libre et suivi l’usure »
Une équipe de services financiers exploitait une plateforme analytique sur ZFS avec mirrors SSD. La charge était moche : rafales d’ingest, fortes compactions,
beaucoup de suppressions et des reconstructions périodiques de datasets dérivés. C’était le genre de profil IO qui transforme les designs optimistes en générateurs d’incidents.
Leur arme secrète n’était pas une astuce intelligente. C’était de la discipline.
Ils avaient une règle stricte : ne pas dépasser un seuil d’utilisation sur le pool. Quand le business demandait « juste un peu plus », ils achetaient de la capacité.
Ils suivaient aussi la température des disques, les indicateurs d’usure et le comportement hebdomadaire du trim. Pas parce qu’ils adoraient les dashboards, mais parce que cela leur permettait de repérer
que « ça s’aggrave » avant que cela ne devienne « c’est tombé ».
Un trimestre, le volume d’ingest a bondi. La latence a commencé à augmenter pendant les jobs nocturnes. L’on-call l’a vu tôt : la fragmentation a augmenté, les trims ont pris plus de temps,
et l’espace libre du pool est descendu sous leur ligne de confort. Ils ont agrandi le pool et rééquilibré les charges avant la vague d’ingest suivante.
Pas d’incident. Pas de drame. Juste un ticket clos avec une note : « Capacité tampon rétablie. »
La leçon est ennuyeusement peu sexy : la meilleure mitigation d’effondrement GC est de ne pas être à l’étroit en espace, plus une surveillance qui vous dit quand vous dérivez vers la falaise.
La plupart des gains de fiabilité ressemblent à « rien ne s’est passé », ce qui explique pourquoi des gens essaient sans cesse de les remplacer par quelque chose d’excitant.
Erreurs communes : symptômes → cause racine → correctif
1) Symptom: periodic multi-second stalls, especially on database commits
- Root cause: sync-heavy workload hitting pool devices (no SLOG), compounded by SSD GC during TXG flushes.
- Fix: add PLP SLOG (and mirror if needed), keep utilization lower, confirm cooling, tune recordsize/volblocksize for smaller updates.
2) Symptom: performance great after deployment, then slowly degrades over weeks
- Root cause: no TRIM/autotrim, high delete churn, SSDs losing free-page knowledge and hitting steady-state write amplification.
- Fix: enable autotrim, validate discard support, consider scheduled trims, leave more free space / OP, consider different drive class.
3) Symptom: random write IOPS collapse when pool hits ~85–90% usage
- Root cause: allocator and FTL both starved for clean space; fragmentation increases; GC has fewer options.
- Fix: expand capacity, migrate cold data out, enforce quotas/reservations, set a policy threshold and alert before it’s critical.
4) Symptom: one NVMe device shows worse performance, pool looks “lumpy”
- Root cause: thermal throttling, firmware differences, PCIe link issues, or a drive entering a different GC regime.
- Fix: check NVMe SMART temperatures, verify PCIe link speed/width, update firmware in a controlled window, improve airflow.
5) Symptom: tuning “worked,” then a reboot made it worse
- Root cause: changes were applied ad hoc (module params, sysctls) without config management; rollback wasn’t real.
- Fix: codify settings, use change control, record baseline metrics, and test under load with the same kernel/module versions.
6) Symptom: special vdev wears rapidly and becomes the bottleneck
- Root cause: special_small_blocks set too high, special vdev too small, metadata/small-block hot set concentrated there.
- Fix: mirror and size special vdev appropriately, reconsider special_small_blocks threshold, monitor wear and bandwidth separately.
7) Symptom: “We disabled sync and it’s fast,” followed by mysterious corruption after power loss
- Root cause: durability contract changed; acknowledged writes weren’t actually durable.
- Fix: revert to sync=standard, use SLOG for legitimate sync acceleration, and document where async durability is acceptable (if anywhere).
Listes de contrôle / plan étape par étape
Step-by-step: stabilize an SSD-only ZFS pool showing GC-collapse symptoms
- Capture evidence during the spike. Run
zpool iostat -v 1, check NVMe temps, and note pool utilization and frag. - Confirm sync pressure. Check ZIL activity and whether the workload is truly sync-heavy (db commits, NFS semantics, hypervisor settings).
- Check TRIM/autotrim. Enable autotrim if it’s off, verify discard support, and watch for TRIM-induced latency in delete-heavy periods.
- Get utilization under control. Free space, expand, or move data. If you’re above ~80% with random writes, treat that as the primary bug.
- Fix thermal and firmware basics. NVMe throttling looks like storage “mood swings.” Don’t ignore it.
- Right-size blocks. Adjust dataset recordsize (or recreate zvol with correct volblocksize). Do not cargo-cult 128K everywhere.
- Use compression. Prefer lz4 unless you measured a reason not to.
- If you need sync performance, add a correct SLOG. PLP, ideally mirrored. Then measure again.
- Re-test under realistic load. A synthetic sequential benchmark is not evidence for a database workload.
- Set alerts. CAP %, FRAG %, NVMe temps, write latency, and ZFS throttle counters. Problems are easier to prevent than to heroically solve.
Build checklist: designing an SSD-only ZFS pool that won’t embarrass you
- Topology: Mirrors for latency and IOPS-heavy workloads; be cautious with wide RAIDZ for random writes.
- Drives: Prefer enterprise SSDs with predictable steady-state and PLP where needed.
- Space headroom: Plan capacity so you can keep healthy free space without begging for budget monthly.
- ashift: Confirm 4K alignment and correct ashift at pool creation.
- autotrim: Enable and validate with your drive model/firmware.
- Workload mapping: Set recordsize/volblocksize per dataset/zvol, not per pool “vibe.”
- Monitoring: Latency percentiles, temperatures, wear indicators, ZFS throttle counters, and utilization trends.
- Change control: Any change that alters durability (sync, write cache settings) needs review and rollback.
FAQ
1) Is garbage collection collapse a “bad SSD” problem or a “bad ZFS” problem?
Généralement ni l’un ni l’autre. C’est un problème d’interaction : un système de fichiers CoW plus le firmware SSD sous pression d’espace et des écritures en rafales.
De meilleurs SSD (plus d’OP, meilleur firmware) élargissent la zone d’opération sûre. Une meilleure conception ZFS (marge d’espace, bonnes tailles de blocs, chemin sync sain) prévient la falaise.
2) Should I always enable autotrim on SSD-only pools?
Dans la plupart des environnements modernes, oui. Puis vérifiez le support du discard et surveillez toute régression pendant des périodes de suppression intense.
Si votre modèle de disque spécifique se comporte mal avec TRIM continu, vous devrez peut-être programmer des trims ou changer de matériel.
3) Does adding more RAM (ARC) fix SSD stalls?
Cela peut les masquer en absorbant les rafales, mais cela ne change pas le comportement steady-state du SSD.
Si le pool ne peut pas flush assez vite parce que le périphérique se bloque, vous atteindrez toujours la throttling éventuellement.
4) When is a SLOG worth it?
Quand vous avez un goulot mesurable d’écritures sync : bases de données qui commit souvent, NFS avec sémantiques sync, workloads VM faisant beaucoup de fsync.
Un SLOG n’est pas un cache d’écriture général. Et sans PLP ce n’est pas une optimisation ; c’est un bug de fiabilité.
5) Can I fix volblocksize after creating a zvol?
Non. Vous recréez le zvol et migrez les données. Planifiez volblocksize en amont et notez-le pour que le futur vous ne « optimise » pas cela jusqu’au chaos.
6) Does compression really help on fast NVMe?
Souvent oui. Moins de données écrites signifie moins de travail NAND et moins de pression GC. LZ4 est typiquement assez peu coûteux pour améliorer le débit.
Mesurez pour votre charge, surtout si vous êtes CPU-bound ou que vous stockez des données incompressibles.
7) Why do things get worse as the pool fills even if the SSD has spare capacity?
Parce que « capacité de réserve » sur les SSD n’est pas la même chose que « espace libre » dans ZFS. ZFS a besoin de metaslabs libres ; l’FTL du SSD a besoin de blocs libres.
Une forte utilisation du pool et la fragmentation réduisent la flexibilité pour les deux couches.
8) Is RAIDZ bad for SSD pools?
Pas universellement, mais c’est moins tolérant pour les petites écritures aléatoires et les charges sync-heavy. Les mirrors tendent à offrir une meilleure latence et une constance IOPS.
Si votre charge est surtout séquentielle en lecture/écriture et que la capacité est prioritaire, RAIDZ peut convenir — si vous maintenez une utilisation raisonnable.
9) Is sync=disabled ever acceptable?
Parfois, pour des données vraiment éphémères où vous acceptez explicitement de perdre les écritures récentes en cas de crash ou de coupure de courant.
Cela doit être une décision de politique consciente, documentée et circonscrite, pas un « tweak » de performance.
10) How do I know if my problem is GC or just thermal throttling?
Vérifiez les logs SMART NVMe pour la température et le temps d’avertissement, corrélez les pics avec la chaleur, et vérifiez le comportement du lien PCIe.
Le throttling thermique produit souvent une dégradation reproductible « après X minutes de charge » qui revient quand on refroidit.
Conclusion : prochaines étapes pratiques
L’effondrement GC sur pools ZFS uniquement SSD est évitable. Pas avec de la magie. Avec les fondamentaux :
gardez de l’espace libre, adaptez les tailles de blocs aux charges, respectez les sémantiques sync, et ne radinez pas sur les périphériques qui définissent la durabilité.
Prochaines étapes qui rapportent cette semaine :
- Activez et vérifiez autotrim (et confirmez le support du discard).
- Définissez une politique d’utilisation et alertez avant de la dépasser.
- Auditez recordsize/volblocksize par dataset/zvol important.
- Si vous êtes sync-heavy, implémentez un SLOG PLP correct et mesurez à nouveau.
- Vérifiez les températures NVMe en charge maximale et corrigez l’aération avant de « tuner ».
- Consignez votre contrat de durabilité (réglages sync, attentes applicatives) pour que le travail de performance ne devienne pas un pari sur la perte de données.
Le stockage est la partie du système qui se souvient de vos erreurs. Tondez comme s’il devait témoigner plus tard.