ZFS est un système qui vous ment poliment—brièvement, et pour votre bien. La plupart des écritures atterrissent d’abord en RAM, puis ZFS les transforme en réalité sur disque par de grandes opérations en lot efficaces. Ce regroupement est une fonctionnalité : c’est ainsi que ZFS assure une forte cohérence et un bon débit sans transformer chaque petite écriture en catastrophe.
Le problème est que ces « grosses opérations en lot efficaces » peuvent ressembler à des rafales : vos disques se taisent, puis s’illuminent soudainement ; les graphiques de latence ressemblent à des battements de cœur ; les bases de données se plaignent toutes les quelques secondes ; et quelqu’un demande pourquoi le stockage « saccade ». Un suspect fréquent est txg_timeout : le minuteur qui pousse ZFS à fermer et synchroniser un groupe de transaction (TXG). Cet article explique ce qui se passe réellement, comment le prouver en production et comment lisser la latence sans sacrifier la sécurité.
Le modèle mental : des TXG, pas des « écritures »
Si vous ne retenez qu’une chose, retenez ceci : ZFS ne fait pas « une écriture » quand une application appelle write(2). ZFS construit une nouvelle version du système de fichiers en mémoire puis engage cette version sur disque. Ces engagements se font par groupes de transaction (TXG).
Un TXG est une fenêtre de lot. Pendant cette fenêtre, ZFS collecte les métadonnées modifiées et les buffers de données (données sales). Quand le TXG se ferme, ZFS commence à le synchroniser : allocation de blocs, écriture des données et métadonnées, mise à jour du MOS (Meta Object Set), et finalement écriture des uberblocks qui rendent le nouvel état durable. L’essentiel : fermer un TXG est peu coûteux ; le vrai travail est de le synchroniser.
Trois TXG en pipeline
ZFS a typiquement trois TXG en cours :
- TXG ouvert : accepte de nouveaux changements (accumulation de données sales).
- TXG en quarantaine : en train de se fermer, gelant l’ensemble des changements.
- TXG en synchronisation : écriture de cet ensemble gelé sur disque.
Cette file d’exécution explique pourquoi ZFS peut continuer à accepter des écritures pendant qu’il synchronise le lot précédent. C’est aussi la raison pour laquelle vous pouvez obtenir des rafales périodiques : la phase de synchronisation est quand le stockage voit le vrai travail, et elle arrive souvent concentrée dans un intervalle.
Blague n°1 : ZFS ne perd pas vos données, il les « éternue temporairement » en RAM—comme vous avec vos clés, mais avec des sommes de contrôle.
Ce que txg_timeout fait réellement (et ce qu’il ne fait pas)
txg_timeout est souvent décrit comme « à quelle fréquence ZFS vide ». C’est suffisamment proche pour être utile, et suffisamment faux pour gâcher un week-end.
Dans la plupart des implémentations OpenZFS, txg_timeout est le temps maximal (en secondes) pendant lequel un TXG peut rester ouvert avant que ZFS le force à se mettre en quarantaine et à commencer la synchronisation. La valeur par défaut est souvent 5 secondes. Ce défaut n’est pas arbitraire : c’est un compromis entre amortir les coûts de métadonnées (regroupement) et limiter la quantité de travail par engagement (latence).
Ce que changer txg_timeout modifie réellement
- Le réduire tend à créer des TXG plus fréquents et plus petits. Cela peut réduire la taille maximale des « rafales de commit » mais augmente les frais généraux (plus de commits, plus d’agitation des métadonnées, débit potentiellement plus faible).
- L’augmenter tend à créer moins de TXG, mais plus grands. Cela peut améliorer le débit pour les écritures en flux continu mais amplifier les pics périodiques de latence (et peut augmenter la quantité de données sales en mémoire).
Ce que txg_timeout ne corrige PAS
Il ne transforme pas magiquement des écritures de synchronisation aléatoires en E/S lisses et à faible latence si votre périphérique sous-jacent ne suit pas, si votre SLOG est mal dimensionné, si votre pool est fragmenté, ou si votre charge impose des sémantiques synchrones. Le timing TXG est le batteur ; vos disques restent le groupe.
Pourquoi le motif « tous les 5 secondes » apparaît
Si vous voyez des pics de latence à cadence régulière—souvent près de 5 secondes—votre première hypothèse devrait être : « commits TXG ». txg_timeout est une horloge qui peut rendre cette cadence visible. Mais les rafales peuvent aussi être entraînées par les limites des données sales (throttling), la pression des écritures synchrones et le comportement du cache des périphériques.
Pourquoi les écritures arrivent par rafales
En production, les « écritures en rafale » ont rarement une cause unique. Ce sont généralement plusieurs mécanismes rationnels qui alignent leurs pics. Voici les causes courantes.
1) Le regroupement est voulu
ZFS est copy-on-write. Pour de nombreuses charges, le chemin efficace est : écrire de nouveaux blocs ailleurs, puis basculer atomiquement les pointeurs. Si ZFS devait faire cette chorégraphie de pointeurs pour chaque écriture de 4 KB, vous auriez la correction sans le bonheur. Les TXG permettent à ZFS de faire la chorégraphie une fois par lot.
2) Les limites de données sales et le throttling créent des « falaises »
ZFS autorise une certaine quantité de données sales en RAM. Quand vous atteignez le seuil de données sales, ZFS commence à freiner les écrivains pour éviter une croissance mémoire incontrôlée et forcer la synchronisation à rattraper le retard. Cet « événement de throttling » ressemble souvent à une falaise : la latence va bien jusqu’au moment où elle ne va plus.
3) Les écritures synchrones peuvent forcer l’activité immédiate du journal d’intent
Les écritures synchrones (O_SYNC, fsync(), réglages WAL des bases de données, sémantiques NFS sync) n’attendent pas que le TXG se synchronise sur le pool principal. Elles attendent que le ZIL (ZFS Intent Log) enregistre l’intention. Sur un pool sans SLOG dédié, ce journal est sur les mêmes disques que le reste, et les écritures de journal peuvent se sérialiser ou entrer en contention de façon désagréable.
4) Petits blocs + amplification des métadonnées
Une charge écrivant beaucoup de petits blocs aléatoires n’écrit pas que des données. Elle génère des mises à jour de métadonnées : pointeurs de blocs, blocs indirects, mises à jour de spacemap, sommes de contrôle, etc. ZFS gère cela bien, mais il doit quand même le faire. Lors d’un commit TXG, les E/S de métadonnées peuvent devenir une tempête courte et intense.
5) Le comportement des périphériques sous-jacents est aussi en rafales
Les SSD ont un GC interne ; les ensembles HDD ont des vidanges de cache d’écriture ; les contrôleurs RAID réordonnent les E/S ; le firmware NVMe a ses propres files et tâches de maintenance. Les rafales ZFS peuvent se synchroniser avec les rafales des appareils et donner l’impression que le graphique respire lourdement.
6) ZFS est franc sur la pression inverse
Certaines piles de stockage cachent la contention en tamponnant sans fin jusqu’à la panne. ZFS a tendance à repousser quand il doit le faire. C’est une meilleure ingénierie—et une pire optique sur un tableau de bord.
Faits et histoire à connaître
- ZFS a été conçu chez Sun avec le stockage d’entreprise en tête, où le regroupement et les commits atomiques priment sur le « tout écrire immédiatement ».
- Les TXG sont antérieurs au terme « DevOps » ; ce style de regroupement transactionnel vient des idées classiques de systèmes de fichiers et de bases de données, pas du marketing cloud moderne.
- Le défaut « 5 secondes » pour le timing TXG apparaît dans plusieurs lignées ZFS car il équilibre souvent latence et débit sur des systèmes polyvalents.
- Le ZIL n’est pas un cache d’écriture. C’est un journal d’intention pour les sémantiques synchrones ; la plupart de son contenu est jeté après le commit du TXG.
- Les dispositifs SLOG ne servent que pour les écritures synchrones. Si votre charge est asynchrone, un SLOG n’aidera pas et peut même devenir un domaine de défaillance supplémentaire à gérer.
- Le copy-on-write rend les écritures partielles plus sûres, mais il déplace la complexité dans l’allocation et les mises à jour de métadonnées, qui sont payées pendant la synchronisation du TXG.
- ZFS calcule des sommes de contrôle sur tout (métadonnées et données), ce qui est excellent pour l’intégrité et non nul en termes de CPU et de trafic mémoire lors de commits lourds.
- OpenZFS a divergé selon les plateformes (Illumos, FreeBSD, Linux) et les réglages diffèrent légèrement ; les concepts restent, mais les noms et défauts peuvent varier.
- Les pics de latence corrèlent souvent avec les écritures d’uberblock parce que c’est l’étape finale de « publication » d’un TXG ; on peut parfois voir la cadence dans des traces bas-niveau.
Reconnaître la latence en forme de TXG
La douleur liée aux TXG a une odeur particulière :
- Cadence régulière : pics toutes les ~5 secondes (ou selon votre
txg_timeout). - Principalement côté écriture : la latence de lecture peut rester correcte tant que le système n’est pas saturé.
- Moments « tout se met en pause » : pas un arrêt complet, mais les applications rapportent du jitter—surtout les bases de données et les hôtes VM.
- CPU non saturé : vous pouvez être limité par l’I/O avec du CPU inactif, ou limité CPU par checksum/compression d’une manière qui ne semble pas être 100 % user CPU.
- Oscillation des données sales : l’usage mémoire et les compteurs de données sales augmentent, puis chutent net lors du commit.
Blague n°2 : si vos graphiques montent en flèche toutes les cinq secondes, félicitations—vous avez découvert que le temps est un cercle plat, et que votre stockage est le cercle.
Playbook de diagnostic rapide
Ceci est la séquence « entrer sur le pont d’incident ». Elle est ordonnée pour répondre rapidement à une question : voyons-nous une pression de commit TXG, une pression de journal d’écritures synchrones, ou une saturation brute des dispositifs ?
Premier : confirmer que le symptôme est périodique et lié aux écritures
- Vérifiez la cadence de latence : s’aligne-t-elle sur ~5 secondes ?
- Séparez lectures et écritures : est-ce que la latence d’écriture pique tandis que les lectures semblent normales ?
- Vérifiez si la charge est heavy en sync (bases de données, NFS, stockage VM avec barrières).
Second : déterminer si ce sont les écritures synchrones qui déclenchent
- Inspectez l’activité ZIL/SLOG : les écritures de journal dominent-elles ?
- Vérifiez les propriétés dataset
syncetlogbias. - Validez la santé et la latence du SLOG (si présent).
Troisième : déterminer si les données sales + throttling TXG déclenchent
- Consultez les compteurs de données sales et l’état TXG (spécifique à la plateforme, mais observable).
- Vérifiez si les écrivains sont throttlés (temps d’attente élevé, bloqués dans les chemins I/O du noyau).
- Corrélez avec la bande passante écrite du pool : atteignez-vous les limites des dispositifs pendant les fenêtres de sync ?
Quatrième : confirmer le comportement des périphériques sous-jacents
- Vérifiez la latence par vdev : un seul dispositif lent peut dicter le temps de commit du pool.
- Vérifiez la profondeur de file et la saturation.
- Vérifiez les compteurs d’erreurs et les retransmissions ; « lent » est parfois « en train de réessayer ».
Tâches pratiques (commandes + interprétation)
Les commandes ci-dessous supposent un environnement Linux + OpenZFS. Si vous êtes sur FreeBSD/Illumos, traduisez l’intention ; le flux de travail s’applique toujours.
Tâche 1 : Identifier les datasets et propriétés clés (sync, logbias, recordsize)
cr0x@server:~$ sudo zfs list -o name,used,avail,recordsize,compression,sync,logbias -r tank
NAME USED AVAIL RECORDSIZE COMPRESS SYNC LOGBIAS
tank 8.21T 5.44T 128K lz4 standard latency
tank/vm 4.92T 5.44T 16K lz4 standard latency
tank/db 1.10T 5.44T 16K lz4 standard latency
tank/backups 2.04T 5.44T 1M lz4 disabled throughput
Interprétation : Si vos plaintes de latence viennent de tank/db ou tank/vm, le petit recordsize et logbias=latency suggèrent que vous tenez à la sémantique sync. Si le dataset est en sync=disabled, vous avez peut‑être « corrigé » la latence en supprimant la durabilité—utile pour des benchmarks, dangereux pour des jobs réels.
Tâche 2 : Surveiller l’I/O au niveau du pool et repérer les rafales périodiques
cr0x@server:~$ sudo zpool iostat -v tank 1
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 8.21T 5.44T 210 980 28.1M 210M
mirror 2.73T 1.81T 105 490 14.0M 105M
nvme0n1 - - 52 245 7.0M 52.5M
nvme1n1 - - 53 245 7.0M 52.4M
mirror 2.73T 1.81T 105 490 14.1M 105M
nvme2n1 - - 52 245 7.0M 52.6M
nvme3n1 - - 53 245 7.1M 52.4M
Interprétation : Cherchez une bande passante d’écriture qui passe de « modérée » à « saturée » selon une cadence, avec les opérations qui montent brusquement. Les rafales alignées sur la minuterie TXG sont un indice, pas une condamnation.
Tâche 3 : Ajouter des colonnes de latence pour exposer le vdev lent
cr0x@server:~$ sudo zpool iostat -v tank 1 -l
operations bandwidth total_wait disk_wait
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
mirror 105 490 14.1M 105M 1ms 18ms 0ms 15ms
nvme2n1 52 245 7.0M 52.6M 1ms 18ms 0ms 15ms
nvme3n1 53 245 7.1M 52.4M 1ms 40ms 0ms 38ms
Interprétation : Un seul périphérique (nvme3n1) montre une attente d’écriture beaucoup plus élevée. Un membre retardataire peut allonger le temps de sync du TXG et créer des rafales visibles. C’est le moment d’arrêter de jouer avec les réglages et de se demander « un périphérique est-il en train de mourir ou mal configuré ? »
Tâche 4 : Vérifier la santé du pool et les compteurs d’erreurs
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Replace the device or clear the errors.
scan: scrub repaired 0B in 03:12:55 with 0 errors on Sun Dec 22 02:12:19 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
mirror-1 ONLINE 0 5 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 5 0
errors: No known data errors
Interprétation : Des erreurs d’écriture sur un membre vdev unique peuvent se traduire par des réessais et des timeouts, qui ressemblent à des rafales de latence. Si vous voyez des erreurs, traitez-les comme des problèmes de performance et de fiabilité.
Tâche 5 : Confirmer txg_timeout et les réglages associés
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
5
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max
17179869184
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max_percent
10
Interprétation : Le timeout par défaut de 5 secondes plus un dirty data max généreux peuvent créer le comportement « calme puis tempête ». Mais ne touchez pas encore à ces paramètres—mesurez d’abord, puis changez une chose à la fois.
Tâche 6 : Observer la pression ARC (parce que la mémoire façonne les données sales)
cr0x@server:~$ grep -E 'c_max|c_min|size|memory_throttle_count' /proc/spl/kstat/zfs/arcstats
c_max 4 137438953472
c_min 4 68719476736
size 4 92341784576
memory_throttle_count 4 0
Interprétation : Si l’ARC se bat pour la mémoire, ZFS peut throttler ou se comporter plus agressivement. Si memory_throttle_count augmente, vos « rafales » peuvent être le système qui halète pour la RAM plutôt qu’un simple problème de cadence TXG.
Tâche 7 : Identifier si la charge est heavy en sync
cr0x@server:~$ sudo zfs get -H -o name,property,value sync tank/db
tank/db sync standard
cr0x@server:~$ sudo iostat -x 1
avg-cpu: %user %nice %system %iowait %steal %idle
12.00 0.00 8.00 18.00 0.00 62.00
Device r/s w/s r_await w_await aqu-sz %util
nvme3n1 55.0 280.0 0.9 35.0 9.8 98.0
Interprétation : Un w_await élevé et une util proche de 100 % sur un dispositif s’alignent avec la pression de commit. Pour décider si c’est lié au ZIL, il faut regarder la présence et le comportement d’un SLOG.
Tâche 8 : Vérifier s’il existe un dispositif de journal dédié (SLOG)
cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
pool: tank
state: ONLINE
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
mirror-1 ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 0 0
logs
mirror-2 ONLINE 0 0 0
nvme4n1 ONLINE 0 0 0
nvme5n1 ONLINE 0 0 0
Interprétation : Un SLOG miroir existe. Bien. Vous devez maintenant vérifier qu’il a une faible latence et qu’il est sûr en cas de perte d’alimentation, sinon il transformera les écritures synchrones en otage de performance.
Tâche 9 : Mesurer la latence SLOG indirectement via les temps d’attente par vdev
cr0x@server:~$ sudo zpool iostat -v tank 1 -l | sed -n '1,40p'
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror-0 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
logs
mirror-2 0 320 0.0K 19.2M 0ms 2ms 0ms 1ms
nvme4n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
nvme5n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
Interprétation : Un temps d’attente d’écriture du vdev de journal autour de 1–2 ms est correct pour beaucoup de charges sync. Si vous voyez 10–50 ms ici, votre SLOG n’est pas un SLOG ; c’est un journal lent.
Tâche 10 : Vérifier la propriété dataset logbias pour correspondre à l’intention
cr0x@server:~$ sudo zfs get -H -o name,property,value logbias tank/db tank/vm
tank/db logbias latency
tank/vm logbias latency
Interprétation : logbias=latency favorise l’utilisation du journal pour les écritures synchrones (bon pour les bases de données). throughput peut réduire le trafic de journal dans certains cas mais augmenter les écritures sur le pool principal pour les opérations sync. Le changer sans comprendre la charge est un classique piège.
Tâche 11 : Observer les kstats liés aux TXG (données sales et temps de sync)
cr0x@server:~$ ls /proc/spl/kstat/zfs/ | head
arcstats
dbufstats
dmu_tx
vdev_queue
vdev_mirror
zfetchstats
cr0x@server:~$ cat /proc/spl/kstat/zfs/dmu_tx
13 1 0x01
name type data
dmu_tx_assigned 4 12800451
dmu_tx_delay 4 21438
dmu_tx_error 4 0
dmu_tx_memory_reserve 4 0
dmu_tx_memory_reclaim 4 0
Interprétation : Sur Linux, une visibilité TXG est indirecte. Une hausse de dmu_tx_delay indique que les écrivains sont retardés—souvent à cause des limites de données sales et de la pression de sync. Vous cherchez une corrélation : quand la latence pique, ces compteurs bondissent-ils ?
Tâche 12 : Utiliser perf pour confirmer que vous êtes bloqué dans des chemins I/O ZFS (et non CPU)
cr0x@server:~$ sudo perf top -g
12.4% [kernel] [k] __schedule
8.9% [kernel] [k] io_schedule
6.1% [kernel] [k] blk_mq_get_tag
5.7% zfs [k] zio_wait
4.8% zfs [k] zio_execute
4.1% zfs [k] vdev_queue_io
Interprétation : Voir du temps dans io_schedule et les fonctions ZFS zio_* suggère que le système attend le stockage, pas qu’il consomme du CPU pour la compression ou les checksums. Cela vous oriente loin du réglage de compression ou de l’ordonnancement CPU et vers la latence vdev, le SLOG et le comportement TXG.
Tâche 13 : Vérification rapide de la saturation au niveau du dispositif et de l’ordonnancement
cr0x@server:~$ sudo nvme smart-log /dev/nvme3n1 | sed -n '1,25p'
Smart Log for NVME device:nvme3n1 namespace-id:ffffffff
critical_warning : 0
temperature : 47 C
available_spare : 100%
percentage_used : 3%
media_errors : 0
num_err_log_entries : 0
warning_temp_time : 0
critical_comp_time : 0
Interprétation : Ce n’est pas un benchmark de performance ; c’est une vérification de sanity. Si les erreurs média ou les entrées du journal d’erreurs augmentent, votre « problème TXG » peut être un problème matériel déguisé en ZFS.
Tâche 14 : Changer txg_timeout en sécurité (temporaire) et observer
cr0x@server:~$ sudo sh -c 'echo 3 > /sys/module/zfs/parameters/zfs_txg_timeout'
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
3
Interprétation : C’est un test, pas un mode de vie. Si vos pics de latence changent de cadence et voient leur amplitude réduite, vous avez confirmé que le timing TXG fait partie du tableau. Si rien ne change, arrêtez de bidouiller et regardez les écritures synchrones ou la latence vdev.
Trois mini-histoires du monde de l’entreprise
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
L’équipe plateforme a reçu un ticket : « La base de données se fige toutes les cinq secondes. » C’était le genre de plainte qui vous fait douter du système de monitoring en premier lieu. Mais les graphiques étaient honnêtes : la latence d’écriture piquait avec une précision métronomique.
Quelqu’un a émis une hypothèse propre : « ZFS vide toutes les cinq secondes, donc la base doit forcer des flushs. » Cette hypothèse a pris de l’ampleur parce qu’elle sonnait comme de la physique. Ils se sont concentrés sur la base de données, ont ajusté les checkpoints, débattu des réglages WAL et modifié le batching applicatif. Les pics de latence n’en avaient rien à faire.
Le vrai problème était plus simple et plus agaçant : un membre miroir du pool avait commencé à mettre plus de temps à compléter les écritures—pas de défaillances nettes, juste des stalls occasionnels. ZFS, en bon architecte, attendait la composante la plus lente pour accomplir les obligations du TXG. Le temps de commit TXG s’est étiré, les données sales se sont accumulées et le système a atteint le throttling. Le motif de cinq secondes n’était que le minuteur exposant un goulot d’étranglement qui aurait fait mal de toute façon.
Ils l’ont prouvé en ajoutant des colonnes de temps d’attente à zpool iostat. Un périphérique montrait systématiquement un disk_wait élevé pendant les pics. SMART avait l’air « correct », parce que SMART est souvent « correct » jusqu’à ce qu’il ne le soit plus. Remplacer le périphérique a éliminé les stalls périodiques sans toucher à un seul réglage ZFS.
La leçon retenue : un comportement périodique ne signifie pas toujours « problème de minuteur ». Parfois c’est « une composante lente cause une douleur périodique quand le système est forcé de se synchroniser ». Les TXG ne font que rendre la synchronisation visible.
Mini-histoire 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation gérait une ferme VM sur des zvols ZFS. Ils voulaient une latence plus lisse pour les écritures invitées. Quelqu’un a lu que réduire txg_timeout peut réduire la taille des rafales, alors ils l’ont baissé agressivement sur toute la flotte.
Les graphiques se sont améliorés—pendant environ un jour. Puis le débit a chuté pendant les jobs nocturnes. Les fenêtres de sauvegarde ont débordé. Les nœuds de stockage n’étaient pas « lents » au sens classique ; ils faisaient plus de travail par unité de donnée. Plus de TXG signifiait des commits de métadonnées plus fréquents, plus d’agitation et moins de temps en état efficace stable.
Pire, les TXG plus petits ont accru la part relative des frais pour des charges avec beaucoup de petites écritures synchrones. Le SLOG a commencé à être martelé par des patterns fsync plus fréquents, et un journal auparavant « correct » est devenu l’élément limitant. Leur latence est devenue plus piquante sous charge, pas plus lisse.
Ils ont annulé le changement et adopté une approche plus chirurgicale : séparer les datasets, corriger le volblocksize pour les zvols, ajouter un SLOG miroir bas-latence et PLP pour les locataires sync-heavy, et plafonner les données sales en accord avec la mémoire disponible. Le tuning final était ennuyeux et incrémental—le seul type de tuning que vous voulez près de l’argent.
La leçon : txg_timeout est un levier, pas une cure. Le tirer trop fort et vous échangez la rafale pour des frais généraux, ce qui se manifeste souvent par une pire latence en queue quand le système est réellement occupé.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une société SaaS gérait un stockage multi‑tenant avec des SLO stricts. Ils avaient appris, à la dure, que tuner ZFS sans observabilité est de l’astrologie. Ils ont donc fait la chose ennuyeuse : suivi de latence par vdev, dashboards de cadence TXG, et scrubs planifiés. Personne n’adorait ça. Tout le monde en a bénéficié.
Un après-midi, la latence d’écriture a commencé à développer un rythme faint mais croissant de 5 secondes. Pas encore un incident complet—juste le genre de « hmm » que de bons ingénieurs en on-call remarquent en faisant semblant de ne pas. Les dashboards montraient que les pics liés aux commits s’aggravaient lentement, mais seulement sur un pool.
Parce qu’ils avaient déjà des histogrammes d’attente par vdev, ils ont repéré un seul périphérique avec une longue traîne de latence en hausse. Il ne tombait pas en panne complètement. Il mettait juste parfois assez de temps pour étirer le temps de sync du TXG. Ils ont drainé les locataires de ce pool et remplacé le périphérique pendant une fenêtre de maintenance normale. Pas de changement d’urgence, pas de héros de minuit, pas de « on l’a redémarré et ça allait mieux ».
Après le remplacement, le rythme TXG a disparu. L’équipe a eu l’air de génies, ce qui était injuste—ils étaient simplement préparés. La pratique ennuyeuse n’était pas du tuning ; c’était remarquer assez tôt pour que le tuning ne soit pas nécessaire.
La leçon : la meilleure façon de « lisser la latence » est de prévenir les conditions qui créent de grandes fenêtres de sync—surtout la dégradation silencieuse des périphériques et la saturation non observée.
Stratégie de réglage : quoi changer, dans quel ordre
Quand on dit « tuner ZFS », on entend souvent « changer un param jusqu’à ce que le graphique soit joli ». Ce n’est pas du tuning ; c’est de la réalisation de souhaits graphiques. Voici un ordre d’opérations plus sûr.
Étape 1 : Décidez si votre problème est la latence sync ou la latence de commit TXG
Si votre charge est dominée par des opérations synchrones, le moyen le plus rapide de réduire la latence tail est généralement un SLOG approprié (miroir, protégé contre la perte de puissance, faible latence) et des propriétés dataset qui correspondent à la charge (sync, logbias). Si c’est surtout du débit asynchrone, le comportement TXG et les données sales sont plus importants.
Étape 2 : Corrigez d’abord le goulot évident (matériel et topologie)
- Remplacez ou retirez les périphériques lents.
- Assurez-vous que les vdev sont équilibrés ; un vdev sous‑performant fixe le rythme.
- Confirmez les réglages du contrôleur, le firmware, les profondeurs de file, et que vous ne combattez pas un HBA mal configuré.
Étape 3 : Utilisez les réglages au niveau dataset avant les réglages globaux
Les propriétés dataset sont réversibles et ciblées. Les paramètres globaux du module sont partagés entre pools et peuvent créer du couplage inter‑tenant.
recordsizepour les filesystems : adaptez à la taille d’I/O (les bases aiment souvent 16K ; les backups 1M).volblocksizepour les zvols : défini à la création ; adaptez au pattern bloc invité/fs.logbias: choisirlatencypour les charges sync faibles latences ; envisagerthroughputpour les patterns en flux.sync: garderstandardsauf si vous acceptez explicitement un compromis de durabilité.
Étape 4 : Ensuite seulement, envisager le timing TXG et les limites de données sales
txg_timeout ajuste la cadence. Les réglages de dirty-data ajustent combien de travail peut s’accumuler avant que ZFS ne force la pression inverse. Ensemble ils façonnent « l’amplitude des rafales » et la « fréquence des rafales ».
Heuristiques pratiques valables en environnement réel :
- Baissez
txg_timeoutmodestement (par ex. 5 → 3) si vous avez des pics périodiques et que votre système n’est pas déjà dominé par les frais de métadonnées. - Faites preuve de prudence pour l’augmenter ; des TXG plus grands peuvent produire une pire latence tail et un temps de récupération plus long après un ralentissement transitoire.
- Ajustez le dirty data max seulement quand vous comprenez la mémoire disponible et le comportement ARC. Trop haut crée des rafales plus grandes ; trop bas throttle constamment les écrivains.
Étape 5 : Validez avec une charge réelle, pas un tour de piste synthétique
Exécutez la charge qui fait mal : votre base de données, votre ferme VM, votre trafic NFS. Un benchmark d’écriture séquentielle peut « prouver » qu’une mauvaise idée est bonne, si vous choisissez la bonne taille de bloc et ignorez la latence tail.
Erreurs courantes, symptômes et correctifs
Erreur 1 : Traiter txg_timeout comme un « intervalle de vidage » de type checkpoint base de données
Symptôme : Vous réduisez txg_timeout, et les pics changent de fréquence mais la latence tail reste mauvaise sous charge.
Ce qui se passe : Le système est limité par le dispositif ou par le sync ; vous avez changé le rythme, pas la capacité.
Correctif : Mesurez le temps d’attente par vdev ; vérifiez le SLOG ; regardez le taux d’écritures sync ; remplacez les périphériques lents ou ajoutez de la capacité avant d’aller plus loin.
Erreur 2 : Désactiver sync pour « corriger » la latence
Symptôme : La latence semble excellente, puis après un événement d’alimentation vous restaurez depuis des backups en expliquant « c’était juste un test ».
Ce qui se passe : sync=disabled transforme les requêtes synchrones en comportement asynchrone. Cela améliore la perf en changeant le contrat.
Correctif : Utilisez un SLOG approprié ; ajustez logbias ; corrigez la latence matérielle ; laissez sync à standard sauf si vous pouvez accepter la perte de données.
Erreur 3 : Ajouter un SSD grand public bon marché comme SLOG
Symptôme : La latence sync se dégrade ; des stalls occasionnels empirent ; parfois vous voyez des timeouts du device de journal.
Ce qui se passe : Le SLOG nécessite une latence faible et cohérente et une protection contre la perte de puissance. Un disque avec cache d’écriture volatile et un GC imprévisible transforme le fsync en roulette.
Correctif : Utilisez un périphérique de classe entreprise, PLP-capable ; mettez‑le en miroir ; confirmez la latence via zpool iostat -l.
Erreur 4 : Surdimensionner les données sales « parce que la RAM est gratuite »
Symptôme : Pics plus longs et plus vilains ; stalls de plusieurs secondes quand le système est sous pression.
Ce qui se passe : Des buffers sales plus gros signifient des commits plus gros. Vous avez augmenté l’amplitude des rafales. Lors de ralentissements transitoires, vous avez aussi augmenté la quantité de travail à vider avant de libérer les écrivains.
Correctif : Dimensionnez correctement les limites de données sales en fonction du débit des dispositifs et de la latence tail acceptable ; gardez l’ARC sain ; évitez la contention mémoire avec d’autres services.
Erreur 5 : Ignorer le déséquilibre par vdev
Symptôme : « Le pool est rapide » dans les métriques agrégées, mais les pics de latence persistent.
Ce qui se passe : Un vdev ou un dispositif est lent ; la synchronisation TXG attend la plus lente des E/S requises. La bande passante moyenne masque le comportement tail.
Correctif : Utilisez zpool iostat -v -l, remplacez l’outlier, et vérifiez la cohérence des contrôleurs/firmwares.
Erreur 6 : Changer cinq réglages à la fois
Symptôme : Le système semble « différent », mais vous ne savez pas pourquoi, et les rollbacks font peur.
Ce qui se passe : Vous avez détruit votre capacité à raisonner sur cause et effet.
Correctif : Un changement, une validation, un plan de rollback. Notez ce que vous avez changé et pourquoi.
Listes de contrôle / plan étape par étape
Checklist A : Confirmer que vous voyez des rafales de commit TXG
- Graphez la latence d’écriture et voyez si les pics s’alignent sur ~5 secondes.
- Exécutez
zpool iostat -v 1 -lpendant les pics ; identifiez si les temps d’attente montent à la même cadence. - Vérifiez
/sys/module/zfs/parameters/zfs_txg_timeoutet comparez à la cadence observée. - Confirmez si le vdev de journal (si présent) montre une augmentation des temps d’attente pendant les pics.
- Vérifiez
dmu_tx_delayou compteurs équivalents ; cherchez une croissance lors des événements.
Checklist B : Lisser la latence sans casser la durabilité
- Validez d’abord le matériel : remplacez les périphériques lents/erronés ; confirmez l’uniformité des firmwares.
- Confirmez le comportement sync : identifiez quels datasets/apps sont sync-heavy ; gardez
sync=standard. - Corrigez le SLOG si nécessaire : miroir, PLP-capable, faible latence ; vérifiez les temps d’attente via
zpool iostat -l. - Réglage dataset : définissez
recordsizecorrectement ; vérifiezlogbias. - Expérience txg_timeout petite : ajustez 5→3 temporairement ; observez latence tail et débit.
- Sanity dirty data : assurez-vous que les limites de données sales ne créent pas de commits géants ou de throttling constant.
- Rollback si les compromis nuisent : préférez un débit stable et une latence tail prévisible à une moyenne plus jolie.
Checklist C : Gestion du changement qui ne vous hantera pas
- Capturez les métriques « avant » : attente par vdev, latence applicative, taux d’écritures sync.
- Changez un réglage à la fois avec une fenêtre d’observation minutée.
- Documentez l’hypothèse (« baisser txg_timeout réduit l’amplitude des commits »).
- Définissez les critères de succès (p99 latence écriture, pas seulement la moyenne).
- Gardez une commande de rollback prête et testée.
FAQ
1) Est-ce que txg_timeout est la raison pour laquelle mes écritures piquent toutes les cinq secondes ?
Il peut être le métronome visible, mais ce n’est pas toujours la cause. La cause est généralement que le travail de sync TXG se concentre dans le temps, souvent parce que des périphériques ou le journal sync ne peuvent pas absorber la charge de façon fluide. Vérifiez en corrélant les pics avec les temps d’attente par vdev et les compteurs de pression inverse liés au TXG.
2) Dois‑je baisser txg_timeout pour lisser la latence ?
Parfois, modestement. Passer de 5 à 3 secondes peut réduire la taille des rafales, mais cela peut aussi réduire le débit et augmenter les frais généraux. Si votre goulot est un vdev lent ou un SLOG lent, baisser txg_timeout ne change que le tempo de la souffrance.
3) Un SLOG résoudra‑t‑il les rafales TXG ?
Un SLOG aide pour la latence d’écritures synchrones (charges fsync-heavy). Il n’élimine pas directement le travail de commit TXG vers le pool principal. Beaucoup d’environnements ont les deux problèmes : le SLOG règle la douleur fsync, et le tuning TXG/matériel règle la douleur des commits.
4) Pourquoi cela s’aggrave‑t‑il quand le pool est presque plein ?
À mesure que les pools se remplissent, l’allocation devient plus difficile, la fragmentation augmente, et les mises à jour de métadonnées peuvent coûter plus cher. La synchronisation TXG peut prendre plus de temps, ce qui augmente la durée pendant laquelle les données sales attendent d’être durables, ce qui accroît le throttling et la perception de rafales.
5) Est‑ce que recordsize est lié à txg_timeout ?
Indirectement. recordsize change la forme des I/O et les frais de métadonnées. Des records plus petits peuvent augmenter le travail de métadonnées et la demande IOPS, rendant le sync TXG plus intense. Le minuteur ne change pas, mais le travail par TXG change.
6) Quelle est la façon la plus sûre de « lisser la latence » pour des bases de données sur ZFS ?
Gardez sync=standard, utilisez un SLOG miroir PLP approprié pour WAL/fsync, réglez recordsize à une valeur adaptée (souvent 16K pour beaucoup de bases), puis corrigez les outliers de latence par vdev. Envisagez de petites modifications de txg_timeout seulement après avoir posé ces bases.
7) Si je me fiche de la durabilité, puis‑je simplement mettre sync=disabled ?
Vous le pouvez, mais traitez‑le comme enlever les ceintures de sécurité parce qu’elles froissent votre chemise. Cela peut être acceptable pour des données temporaires, caches éphémères ou environnements de test avec tolérance explicite à la perte. Ne le faites pas pour quelque chose dont vous seriez gêné après un crash.
8) Comment savoir si je suis throttlé par les limites de données sales ?
Cherchez des retards d’écrivains en hausse (par ex. dmu_tx_delay croissant) et une latence applicative qui se corrèle avec l’oscillation des données sales et les fenêtres de sync TXG. Vous verrez souvent des rafales d’I/O suivies de périodes où les écrivains sont bloqués plus que d’habitude.
9) La compression peut‑elle aider contre les rafales TXG ?
Parfois. La compression peut réduire les octets écrits, ce qui réduit le temps des périphériques pendant le sync TXG, mais elle augmente le travail CPU et peut changer les patterns I/O. Si vous êtes limité par le dispositif et avez du CPU disponible, la compression peut réduire l’amplitude des rafales. Si vous êtes déjà CPU-bound ou proche de la saturation, elle peut empirer la latence tail.
10) Ce problème est‑il spécifique à Linux ?
Non. Les TXG sont fondamentaux pour ZFS sur toutes les plateformes. Ce qui diffère, c’est la visibilité (quels compteurs sont exposés) et les noms/défauts exacts des réglages.
Conclusion
txg_timeout ne rend pas ZFS rafaleux ; il révèle le regroupement que ZFS utilise pour être rapide et correct. Ces rafales d’écriture sont souvent normales—jusqu’à ce qu’elles ne le soient plus. Quand elles deviennent un problème de latence, c’est généralement parce que la phase de sync TXG est étirée par un périphérique lent, la pression des écritures synchrones, ou l’accumulation de données sales qui déclenche le throttling.
Lisser la latence consiste principalement à respecter la pipeline : assurez‑vous que le vdev le plus lent n’est pas secrètement lent, donnez aux charges synchrones un chemin de journal bas‑latence approprié, adaptez les datasets à la forme des I/O, et seulement ensuite ajustez le timing TXG et les limites des données sales—prudemment, mesurablement et avec un rollback prêt. L’objectif n’est pas d’éliminer totalement les rafales ; c’est de les garder suffisamment petites pour que vos applications ne les remarquent plus—et pour que votre on-call ne développe pas une réponse pavlovienne aux intervalles de cinq secondes.