Le préfetch (prélecture) est une de ces fonctionnalités que vous ne remarquez que lorsqu’elle vous trahit. La plupart du temps, elle transforme discrètement les lectures séquentielles en E/S fluides et efficaces. Puis, un jour, votre ARC devient une porte tournante, votre taux de hit s’effondre, la latence explose, et quelqu’un prononce la phrase la plus dangereuse du stockage : « Mais rien n’a changé. »
Ce guide de terrain explique cette trahison : ce que fait réellement le préfetch ZFS, comment il interagit avec l’ARC et le L2ARC, comment il peut provoquer le thrash du cache, et comment le corriger sans transformer votre stockage en foire scientifique. Je resterai ancré sur ce que vous pouvez mesurer en production et ce que vous pouvez modifier en toute sécurité quand des gens attendent.
Prefetch en termes simples (et pourquoi ça peut nuire)
Le préfetch ZFS est un mécanisme adaptatif de « lecture anticipée ». Quand ZFS détecte que vous lisez des données de fichier de façon séquentielle, il commence à récupérer les chunks suivants avant que vous ne les demandiez. C’est l’idée centrale : masquer la latence disque en gardant les blocs suivants prêts en mémoire.
Quand ça marche, c’est magique : les lectures en flux sont servies depuis l’ARC à la vitesse de la mémoire. Quand ça échoue, c’est une forme d’optimisme particulièrement coûteuse : il attire beaucoup de données dans l’ARC que vous n’utiliserez jamais, évincant les données que vous utiliserez réellement. Voilà le thrash du cache.
Le thrash du cache dû au préfetch apparaît généralement dans trois grandes situations :
- Grandes analyses « principalement séquentielles » qui ne se répètent pas (requêtes analytiques, sauvegardes, analyses antivirus, indexation média, envoi de logs). Le préfetch remplit l’ARC avec des données utilisées une seule fois.
- Lectures séquentielles sur de nombreux fichiers en parallèle (plusieurs VM, plusieurs workers, consommateurs parallèles). Chaque flux paraît séquentiel pour lui-même, mais collectivement ils forment un tuyau d’incendie.
- Charges semblant séquentielles mais inutiles pour le cache (par exemple lire d’énormes fichiers une seule fois, ou lire des blocs compressés/chiffrés où le CPU devient goulot et la résidence en ARC brûle juste de la RAM).
Deux blagues, comme promis, puis on devient sérieux :
Blague n°1 : Le préfetch, c’est comme un stagiaire qui commence à imprimer les e-mails de demain « pour gagner du temps ». Belle initiative, jugement catastrophe.
Comment le préfetch ZFS fonctionne sous le capot
Cartographions le territoire. Dans OpenZFS, une lecture normale passe par la DMU (Data Management Unit), qui localise les blocs (pointeurs de blocs, blocs indirects, dnodes), puis lance des E/S pour les blocs de données. L’ARC met en cache à la fois les métadonnées et les données. Le préfetch ZFS ajoute un comportement : quand ZFS détecte un accès séquentiel, il émet des lectures supplémentaires pour des blocs futurs.
ARC, MRU/MFU, et pourquoi le préfetch modifie l’éviction
ARC n’est pas un simple LRU. Il est adaptatif : il équilibre entre « récemment utilisé » (MRU) et « fréquemment utilisé » (MFU). Dans un modèle mental simplifié :
- MRU : « les trucs récents » que vous avez touchés récemment (souvent des lectures ponctuelles).
- MFU : « les choses chaudes » auxquelles vous revenez sans cesse.
Le préfetch a tendance à injecter des blocs dans l’ARC avant même qu’ils ne soient demandés par l’application. Selon les détails d’implémentation et la charge, ces blocs préfetchés peuvent gonfler la partie « récente », poussant hors de la cache des métadonnées et des données du working set. Le résultat est un taux de hit plus mauvais, plus de lectures disque réelles, et parfois une boucle de rétroaction désagréable : plus de misses causent plus de lectures, plus de lectures créent plus de préfetch, le préfetch évince plus de cache, etc.
Le préfetch n’est pas la même chose que le cache de pages OS
Sur Linux, ZFS se situe en dehors du cache de pages traditionnel. C’est une fonctionnalité, pas un bug : ZFS gère sa propre logique de cache. Mais cela signifie aussi que vous ne pouvez pas supposer que les réglages classiques de readahead Linux disent toute l’histoire. ZFS a son propre comportement de préfetch et ses paramètres de réglage, et les conséquences pèsent directement sur la pression mémoire de l’ARC, pas « seulement » sur le cache VFS.
Les métadonnées comptent : le préfetch peut évincer le contenu ennuyeux
Les pires incidents de performance que j’ai vus n’étaient pas causés par « les données sont lentes », mais par « les métadonnées manquent ». Quand l’ARC perd des métadonnées (dnodes, blocs indirects), chaque opération se transforme en plusieurs E/S : trouver le dnode, lire les blocs indirects, enfin lire les données. Un préfetch qui évince des métadonnées peut transformer « une lecture » en « une petite parade de lectures », et la parade est très polie mais très lente.
L2ARC : pas une carte de sortie gratuite
L2ARC (cache SSD) est souvent mal compris comme « ARC mais plus gros ». Ce n’est pas le cas. L2ARC est alimenté par les évictions de l’ARC ; il a son propre overhead de métadonnées en RAM ; et historiquement il ne persistait pas après un redémarrage (les versions récentes d’OpenZFS supportent le L2ARC persistant, mais ce n’est toujours pas magique). Si le préfetch remplit l’ARC de déchets, L2ARC peut devenir un musée d’objets lus une fois et jamais relus.
Les réglages dont vous entendrez parler
Différentes plateformes exposent des paramètres légèrement différents (FreeBSD vs Linux / versions d’OpenZFS), mais deux noms reviennent fréquemment :
zfs_prefetch_disable: instrument brutal ; désactive le préfetch.zfetch_max_distance/zfetch_max_streams: limites de « combien » et « combien de flux » pour le préfetch (noms variables selon la version).
Ne mémorisez pas les noms. Mémorisez la stratégie : mesurer, confirmer que le préfetch est la source de pression, puis le restreindre de la manière la plus fine qui corrige le problème.
Pourquoi le thrash du cache arrive : modes de défaillance
Mode de défaillance 1 : lectures séquentielles ponctuelles qui semblent « utiles »
Les jobs de sauvegarde sont l’exemple classique. Une sauvegarde lit des datasets entiers séquentiellement, généralement une fois par jour. Le préfetch se charge d’avancer, l’ARC se remplit, et votre working set de production est évincé pour faire de la place aux octets les moins réutilisables du système.
Pattern de symptôme : les sauvegardes démarrent, le taux de hit ARC chute, la latence augmente, et les requêtes orientées clients ralentissent même si les disques ne sont pas saturés en bande passante — ils sont saturés en IOPS aléatoires car les misses de cache forcent des seeks supplémentaires.
Mode de défaillance 2 : flux séquentiels parallèles
Un flux séquentiel est simple : c’est grosso modo un tapis roulant. Vingt flux séquentiels sur vingt VM, c’est un rond-point pendant une tempête. Chaque flux déclenche le préfetch. Le préfetch combiné ramène bien plus de données que l’ARC ne peut contenir, et l’éviction devient constante.
Mode de défaillance 3 : le préfetch concurrence les métadonnées et les petits blocs chauds
L’ARC doit garder les « fiches index » de votre système de fichiers : dnodes, blocs indirects, structures de répertoires. Le préfetch injecte souvent de grandes séries de données de fichiers. Si l’ARC est petit par rapport au working set, le préfetch peut faire pencher la balance : vous commencez à manquer des métadonnées qui étaient résidentes, et soudain tout devient lent, y compris des opérations sans rapport avec le scan séquentiel.
Mode de défaillance 4 : pollution du L2ARC et overhead RAM
L2ARC ressemble à un coussin, mais il coûte de la RAM pour son index et sa population n’est pas gratuite. Si le préfetch injecte des données jetables dans l’ARC, vous les évincez vers le L2ARC, dépensant I/O et RAM pour préserver quelque chose que vous ne relirez jamais. Ce n’est pas du caching ; c’est de l’accaparement.
Mode de défaillance 5 : compression, chiffrement et goulot CPU
Si les lectures sont liées au CPU (décompression, déchiffrement, checksum), le préfetch peut encore remplir l’ARC plus vite que le CPU ne peut consommer, ou ramener des données qui seront invalidées par d’autres besoins du working set. Vous verrez le CPU saturé, l’ARC qui churne, et des disques pas clairement saturés — une triade particulièrement déroutante lors des appels d’incident.
Faits et contexte historique (6–10 points rapides)
- ZFS a été conçu pour l’intégrité de bout en bout : checksums, copy-on-write, self-healing — les fonctions de performance comme le préfetch ont toujours été superposées à une conception où la correction prime.
- ARC a été créé pour dépasser la pensée LRU simple : il s’adapte entre récence et fréquence, ce qui lui permet de survivre à des charges mixtes — jusqu’à ce qu’on lui fournisse trop de « récence » via le préfetch.
- Les premières déployées ZFS avaient souvent beaucoup de RAM : un comportement de préfetch inoffensif sur des machines à 256 Go devient dramatique sur des nœuds serrés à 64 Go.
- L2ARC n’était historiquement pas persistant : les redémarrages froids signifiaient du cache froid, ce qui poussait certains à sur-tuner le préfetch pour « chauffer » le cache, créant parfois du churn auto-infligé.
- OpenZFS s’est scindé puis recombiné : lignée Solaris, illumos, FreeBSD, Linux — les knobs et valeurs par défaut du préfetch ont évolué différemment selon les écosystèmes.
- Le « recordsize » est devenu une religion de performance : le tuning du recordsize pour les charges dépasse souvent l’impact du préfetch, mais le préfetch interagit avec lui ; de gros enregistrements amplifient le coût des lectures spéculatives.
- Les SSD ont changé la forme de la douleur : le préfetch a été inventé dans un monde où la latence était chère ; avec NVMe, la pénalité d’un miss peut être plus petite, mais la pollution du cache peut toujours tuer la latence tail.
- La virtualisation a multiplié les flux séquentiels : un pool de stockage sert désormais des dizaines d’invités ; les algorithmes de préfetch qui supposent « quelques flux » peuvent mal se comporter en environnement multi-tenant.
- Les gens aiment les interrupteurs : l’existence de
zfs_prefetch_disableprouve que suffisamment d’opérateurs ont subi une vraie douleur pour justifier un gros interrupteur rouge.
Trois mini-histoires du monde de l’entreprise
Mini-histoire n°1 : Un incident causé par une mauvaise hypothèse
L’incident a commencé comme un ticket de « ralentissement mineur ». Une application interne basée sur une base de données — rien de glamour — commençait à subir des timeouts aux heures de pointe. Les graphes de stockage semblaient corrects au premier abord : la bande passante n’était pas saturée, les disques ne criaient pas, et le CPU n’était pas saturé. La conclusion préférée est apparue dans le chat : « Ça ne peut pas être le stockage. »
Mais la latence racontait une autre histoire. La latence au 99e percentile a bondi, pas la moyenne. C’est la marque des caches qui mentent : la plupart des requêtes vont bien, un sous-ensemble tombe de la falaise du cache et prend le chemin lent. La mauvaise hypothèse était subtile : l’équipe croyait que le job de sauvegarde nocturne était « séquentiel et donc amiable ».
En réalité, le job de sauvegarde tournait tard et chevauchait les heures d’activité. Il lisait d’énormes datasets en longues séries séquentielles. Le préfetch a vu un étudiant modèle — des lectures prévisibles — et a préchargé agressivement. L’ARC s’est rempli de données de sauvegarde. Les métadonnées et les petits blocs fréquemment accédés par l’application ont été évincés. L’application n’avait pas besoin d’un haut débit ; elle avait besoin de hits à faible latence sur un petit working set. La sauvegarde avait besoin de débit mais pas de cache car elle ne relirait pas bientôt les mêmes blocs.
La correction n’a pas été héroïque. L’équipe a d’abord confirmé que l’effondrement du taux de hit ARC corrélait avec la fenêtre de sauvegarde. Ensuite, ils ont limité la sauvegarde et ajusté la planification. Enfin, ils ont restreint le comportement du préfetch pour ce nœud (pas globalement sur la flotte) et redimensionné l’ARC pour protéger les métadonnées. La leçon n’était pas « le préfetch est mauvais ». La leçon était « séquentiel n’implique pas toujours ‘cachable’ ».
Mini-histoire n°2 : Une optimisation qui s’est retournée contre son auteur
Une équipe plateforme voulait accélérer les jobs analytiques sur un pool ZFS partagé. Ils ont observé de larges scans de tables et ont conclu que la meilleure action était d’« aider » ZFS en augmentant le parallélisme de lecture : plus de workers, plus gros chunks, scans plus agressifs. Sur le papier, ça semblait être un gain de bande passante.
Ça a marché — sur un cluster vide. En production, ça a percuté tout le reste. Chaque worker créait un flux séquentiel net, donc le préfetch se déclenchait pour chacun. Soudain le pool avait une douzaine de flux de préfetch en concurrence pour l’ARC. Le taux d’éviction ARC a monté jusqu’à ressembler à une machine à sous. Le trafic d’écriture vers le L2ARC a augmenté aussi, transformant les SSD de cache en périphériques de journalisation involontaires pour des données spéculatives.
Et puis la chute : les requêtes analytiques n’ont progressé que légèrement, mais le reste de l’entreprise a ralenti. Les jobs CI ont expiré. Les tempêtes de boot VM ont pris plus de temps. Même les listings de répertoire et les installations de paquets semblaient « collants » parce que les métadonnées manquaient du cache. L’optimisation était réelle mais elle externalisait le coût sur tous les voisins.
Le rollback n’a pas été « désactiver le préfetch partout ». L’équipe a réduit la concurrence, introduit une isolation des ressources (classe de pool séparée / niveau de stockage séparé), et ajouté des garde-fous : les nœuds analytiques avaient leur propre profil de réglage, incluant des limites de préfetch plus strictes. Le retour de bâton n’était pas dû à une idée stupide ; c’était parce que les systèmes partagés punissent l’égoïsme de débit.
Mini-histoire n°3 : Une pratique ennuyeuse mais correcte qui a sauvé la mise
Un incident de stockage n’est pas devenu un désastre uniquement parce que quelqu’un avait été ennuyeux. L’équipe avait une routine : avant tout tuning de performance, ils capturaient un petit ensemble de preuves — stats ARC, stats I/O ZFS, santé du pool, et un instantané de 60 secondes de la distribution de latence. Ça prenait cinq minutes et c’était considéré comme un « processus agaçant ».
Un après-midi, un ingénieur a proposé de mettre zfs_prefetch_disable=1 parce que « le préfetch nuit toujours aux bases de données ». Cette croyance est courante, parfois vraie, et dangereusement trop généralisée. La routine ennuyeuse a fourni le contre-exemple : l’ARC était majoritairement métadonnées, le taux de hit était élevé, et la douleur dominante venait en réalité d’écritures synchrones d’une application mal configurée. Les lectures n’étaient pas le goulot.
Au lieu de basculer le préfetch et d’introduire une nouvelle variable, ils ont corrigé le chemin d’écriture (comportement fsync de l’application, réglages sync du dataset alignés avec les exigences). L’incident s’est terminé rapidement et de façon prévisible. Une semaine plus tard, ils ont fait une expérience contrôlée sur le préfetch et ont constaté que ce n’était pas le coupable sur ce système.
Moral opérationnel : l’habitude « ennuyeuse » de collecter les mêmes métriques de base à chaque fois transforme le tuning de la tradition en ingénierie. Et ça vous évite de gagner l’argument et de perdre le système.
Mode d’emploi pour un diagnostic rapide
Voici l’ordre que j’utilise quand quelqu’un dit « ZFS est lent » et que les graphes crient en plusieurs couleurs.
Premier : confirmer la classe de symptôme (latence vs débit vs CPU)
- Est-ce la latence ? Regardez p95/p99 de l’application, puis mappez à la latence du stockage (périphérique et couche ZFS). Le thrash du cache apparaît généralement comme des pics de latence tail.
- Est-ce la saturation de débit ? Si vous maximisez la bande passante séquentielle et que tout est séquentiel, le préfetch peut aller bien.
- Est-ce le CPU ? Un CPU élevé dans des threads kernel effectuant checksums/compression peut faire paraître le « stockage » lent.
Deuxième : vérifier le comportement de l’ARC en 60 secondes
- Tendance du taux de hit ARC : a-t-il chuté quand la charge a démarré ?
- Taille ARC vs cible : l’ARC est-il bloqué au max avec beaucoup d’évictions ?
- Équilibre métadonnées vs données : les métadonnées se font-elles évincer ?
Troisième : corréler avec un scanner séquentiel
- Backups, scrubs, resilvers, lectures de réplication, scans analytiques, antivirus, indexation, transcodages média.
- Cherchez un timestamp « un job a démarré » qui correspond au churn ARC.
Quatrième : valider si le préfetch est réellement impliqué
- Cherchez des preuves de lecture anticipée agressive (bande passante élevée avec faible réutilisation du cache, forte éviction).
- Essayez un changement étroit : réduisez la distance/le nombre de streams de préfetch (ou désactivez temporairement le préfetch) sur un hôte, pendant une fenêtre contrôlée, et mesurez.
Cinquième : choisir l’atténuation la moins dangereuse
- Throttler ou replanifier le travail de scan.
- Réduire la concurrence (surtout les lecteurs séquentiels parallèles).
- Contraindre le préfetch plutôt que le tuer globalement.
- Ajuster la taille de l’ARC pour protéger les métadonnées si la RAM le permet.
Blague n°2 : Désactiver le préfetch en production parce que « ça pourrait aider » ressemble à réparer un cliquetis en enlevant la radio. Le bruit cesse, certes, mais vous n’avez rien appris.
Tâches pratiques : commandes, sens et actions suivantes
Ces tâches supposent Linux avec OpenZFS installé. Certaines commandes requièrent des paquets (par ex. zfsutils-linux) et les privilèges root. Le but n’est pas d’exécuter tout en permanence ; c’est d’avoir une boîte à outils et de savoir ce que chaque outil vous dit.
Task 1: Identify pool and dataset basics
cr0x@server:~$ zpool status
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:11:33 with 0 errors on Sun Dec 22 03:14:10 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
errors: No known data errors
Interprétation : Si le pool est dégradé/resilvering/scrubbing, les conclusions de performance sont suspectes. Le tuning du préfetch ne réparera pas un pool occupé à se guérir.
Task 2: See dataset properties that affect read patterns
cr0x@server:~$ zfs get -o name,property,value -s local,received recordsize,compression,primarycache,secondarycache,sync tank/data
NAME PROPERTY VALUE
tank/data recordsize 128K
tank/data compression lz4
tank/data primarycache all
tank/data secondarycache all
tank/data sync standard
Interprétation : recordsize et les politiques de cache comptent. Un recordsize large + préfetch agressif peut signifier des lectures spéculatives volumineuses. Si primarycache=metadata, les données ne resteront pas dans l’ARC quel que soit le préfetch.
Task 3: Check the prefetch knob state
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_prefetch_disable
0
Interprétation : 0 signifie que le préfetch est activé. Ne changez pas encore ; prouvez d’abord que c’est le coupable.
Task 4: Capture ARC summary quickly
cr0x@server:~$ arcstat 1 5
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:40:01 9234 1840 19 220 12 1620 88 95 5 48G 48G
12:40:02 10110 2912 28 240 8 2672 92 110 4 48G 48G
12:40:03 9988 3220 32 230 7 2990 93 105 3 48G 48G
12:40:04 9520 3011 31 210 7 2801 93 100 3 48G 48G
12:40:05 9655 3155 33 205 6 2950 94 99 3 48G 48G
Interprétation : Une augmentation du miss% pendant un scan est un signal classique de thrash. Si les misses sont majoritairement des pmis (prefetch misses), c’est suggestif, mais pas définitif. L’important est la tendance et la corrélation avec le démarrage de la charge.
Task 5: Check ARC size limits (Linux)
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_max
51539607552
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_min
4294967296
Interprétation : ARC max ~48 GiB ici. Si l’ARC est petit par rapport à la charge, le préfetch peut le faire churner rapidement. Si l’ARC est énorme et churn toujours, la charge est probablement « sans réutilisation » ou comporte trop de streams.
Task 6: Watch ZFS I/O at pool level
cr0x@server:~$ zpool iostat -v 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 3.2T 10.8T 2400 110 780M 6.2M
mirror-0 3.2T 10.8T 2400 110 780M 6.2M
nvme0n1 - - 1200 60 390M 3.1M
nvme1n1 - - 1200 60 390M 3.1M
-------------------------- ----- ----- ----- ----- ----- -----
Interprétation : Si la bande passante de lecture est élevée pendant le ralentissement mais que l’application n’en profite pas, vous lisez peut-être inutilement en avance. Croisez avec les misses ARC et la latence applicative.
Task 7: Confirm whether a scrub/resilver is competing
cr0x@server:~$ zpool status | sed -n '1,25p'
pool: tank
state: ONLINE
scan: scrub in progress since Thu Dec 25 12:10:01 2025
1.20T scanned at 6.9G/s, 210G issued at 1.2G/s, 3.20T total
0B repaired, 6.56% done, 00:38:21 to go
Interprétation : Un scrub est aussi un lecteur séquentiel, et il peut déclencher le préfetch et concurrencer pour l’ARC. Si votre thrash coïncide avec un scrub, vous avez d’abord un problème de planification.
Task 8: Identify top readers (process-level)
cr0x@server:~$ sudo iotop -oPa
Total DISK READ: 820.12 M/s | Total DISK WRITE: 6.41 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
18277 be/4 backup 612.55 M/s 0.00 B/s 0.00 % 98.00 % borg create ...
23110 be/4 postgres 84.12 M/s 2.40 M/s 0.00 % 12.00 % postgres: checkpointer
9442 be/4 root 32.90 M/s 0.00 B/s 0.00 % 4.00 % zfs send ...
Interprétation : Si un processus est un hog séquentiel, vous pouvez souvent corriger l’incident sans toucher au préfetch : le throttler, le déplacer, le replanifier.
Task 9: Verify whether prefetch is being disabled/enabled correctly (temporary)
cr0x@server:~$ echo 1 | sudo tee /sys/module/zfs/parameters/zfs_prefetch_disable
1
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_prefetch_disable
1
Interprétation : C’est un changement runtime pour des expériences rapides. Traitez-le comme un coupe-circuit : notez l’heure, mesurez pendant 5–15 minutes, et revenez en arrière si ça nuit au débit séquentiel. Rendez-le persistant seulement après avoir prouvé le gain et compris le compromis.
Task 10: Revert the change cleanly
cr0x@server:~$ echo 0 | sudo tee /sys/module/zfs/parameters/zfs_prefetch_disable
0
Interprétation : Restaurez toujours dans la même fenêtre d’incident si les données ne soutiennent pas le changement. Le tuning en production sans rollback est du jeu avec un meilleur vocabulaire.
Task 11: Check memory pressure that makes ARC eviction worse
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 128Gi 93Gi 2.1Gi 1.2Gi 33Gi 30Gi
Swap: 0B 0B 0B
Interprétation : Si « available » est faible, l’ARC peut se battre avec le reste du système. Le préfetch sous pression mémoire est plus susceptible d’évincer rapidement des pages utiles.
Task 12: Examine ARC memory breakdown (kstat)
cr0x@server:~$ sudo kstat -p zfs:0:arcstats:size zfs:0:arcstats:target_size zfs:0:arcstats:arc_meta_used zfs:0:arcstats:data_size
zfs:0:arcstats:size 51511234560
zfs:0:arcstats:target_size 51539607552
zfs:0:arcstats:arc_meta_used 8123456789
zfs:0:arcstats:data_size 43200000000
Interprétation : Si l’utilisation des métadonnées s’effondre pendant un scan, vous avez probablement trouvé le mécanisme pour « tout devient lent ». Protéger les métadonnées (via la taille ARC et le contrôle de la charge) restaure souvent la latence tail.
Task 13: Inspect L2ARC behavior (if present)
cr0x@server:~$ sudo kstat -p zfs:0:arcstats:l2_hits zfs:0:arcstats:l2_misses zfs:0:arcstats:l2_size
zfs:0:arcstats:l2_hits 182334
zfs:0:arcstats:l2_misses 992112
zfs:0:arcstats:l2_size 214748364800
Interprétation : Un L2ARC énorme avec un faible taux de hits pendant le thrash peut être pollué par des données préfetchées une seule fois. Si L2ARC manque aussi, il ne peut pas vous sauver ; il n’est qu’un autre niveau lent derrière l’ARC.
Task 14: Validate recordsize alignment for the workload (spot check)
cr0x@server:~$ zfs get recordsize tank/db tank/backup tank/vmstore
NAME PROPERTY VALUE SOURCE
tank/db recordsize 16K local
tank/backup recordsize 1M local
tank/vmstore recordsize 128K local
Interprétation : Si votre base de données est sur des records 1M, le préfetch lira de gros blocs qui peuvent contenir des pages inutilisées. Si vos sauvegardes sont sur de petits records, vous ferez plus d’I/O que nécessaire. Mettez les bases en ordre avant d’accuser le préfetch.
Task 15: Measure actual latency at the block device
cr0x@server:~$ iostat -x 1 5
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await aqu-sz %util
nvme0n1 1180 402000 0.0 0.00 4.10 340.7 60 3200 0.95 3.10 74.2
nvme1n1 1195 401000 0.0 0.00 4.05 335.6 62 3400 1.01 3.05 75.0
Interprétation : Si r_await du périphérique bondit quand le miss% ARC monte, vous allez sur disque. Si la latence du périphérique est stable mais que l’app est lente, vous pouvez être CPU-bound ou coincé sur des verrous/queues au-dessus du périphérique.
Task 16: Make prefetch setting persistent (only after proof)
cr0x@server:~$ echo "options zfs zfs_prefetch_disable=1" | sudo tee /etc/modprobe.d/zfs-prefetch.conf
options zfs zfs_prefetch_disable=1
cr0x@server:~$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.8.0-40-generic
Interprétation : Les changements persistants doivent être gérés via un change management. Si vous ne pouvez pas articuler l’inconvénient (lectures séquentielles plus lentes) et le chemin de rollback (supprimer le fichier, reconstruire initramfs, redémarrer), vous n’êtes pas prêt à le rendre persistant.
Erreurs courantes, symptômes et corrections
Mistake 1: “ARC hit ratio is low, so add L2ARC”
Symptôme : Vous ajoutez un gros SSD de cache, le taux de hit s’améliore à peine, et maintenant le système a une amplification d’écriture plus élevée et moins de RAM disponible.
Pourquoi ça arrive : La charge a une faible réutilisation ; le préfetch pollue l’ARC ; L2ARC ne fait que stocker la pollution avec un overhead supplémentaire.
Correction : Réduisez d’abord les lectures spéculatives (limites de préfetch ou throttling de la charge). Validez la réutilisation. Ensuite seulement considérez L2ARC, dimensionné et surveillé pour une vraie amélioration de hits.
Mistake 2: Disabling prefetch globally because “databases hate it”
Symptôme : Rapports et jobs batch deviennent plus lents ; les fenêtres de sauvegarde s’allongent ; les resilvers prennent plus de temps ; les utilisateurs se plaignent de « exports lents ».
Pourquoi ça arrive : Certaines charges bénéficient énormément du préfetch. Une désactivation globale pénalise tous les lecteurs séquentiels, y compris les légitimes.
Correction : Préférez contraindre la charge fautive (planification / throttling) ou utiliser un tuning ciblé par rôle hôte/pool. Si vous devez désactiver, documentez et mesurez l’impact sur les tâches séquentielles.
Mistake 3: Confusing “high bandwidth” with “good performance”
Symptôme : Le stockage lit 800 MB/s, pourtant l’application est lente.
Pourquoi ça arrive : Vous streamez des données dans l’ARC et les évincez immédiatement (ou lisez au-delà de ce qui est utilisé). C’est de l’activité, pas du progrès.
Correction : Vérifiez miss%, éviction, et temps de complétion applicatif. Si le débit applicatif ne suit pas la bande passante du pool, vous effectuez probablement des lectures spéculatives ou subissez des misses de métadonnées.
Mistake 4: Ignoring metadata residency
Symptôme : « Tout » ralentit pendant un scan : listings, petites lectures, réactivité des VM.
Pourquoi ça arrive : Les métadonnées ont été évincées ; les opérations ont maintenant besoin d’E/S supplémentaires pour localiser les données.
Correction : Assurez-vous que l’ARC est dimensionné correctement ; évitez les scans polluants en heures de pointe ; envisagez des changements de politique de cache (primarycache) seulement si vous en comprenez les conséquences.
Mistake 5: Tuning recordsize blindly
Symptôme : Les charges de lectures aléatoires montrent une latence élevée ; les scans séquentiels montrent d’énormes I/O ; le préfetch semble « pire que d’habitude ».
Pourquoi ça arrive : Des records surdimensionnés augmentent l’amplification de lecture ; le préfetch ramène de gros blocs qui contiennent peu de données utiles pour des lectures petites et aléatoires.
Correction : Alignez recordsize sur la charge (par ex. plus petit pour les bases de données, plus grand pour les sauvegardes/médias). Retestez le comportement du préfetch après un recordsize raisonnable.
Mistake 6: Making multiple changes at once
Symptôme : Après le « tuning », la performance change mais personne ne peut expliquer pourquoi ; des régressions ultérieures sont impossibles à déboguer.
Correction : Changez une variable à la fois, mesurez, gardez un rollback. Le tuning du préfetch est sensible au mix de charge ; vous avez besoin d’attribution.
Listes de contrôle / plan étape par étape
Plan étape par étape : prouver (ou infirmer) le thrash du préfetch
- Marquer la fenêtre d’incident. Notez quand le ralentissement a commencé et quels jobs tournaient.
- Vérifier la santé du pool. Si scrub/resilver est actif, notez-le.
- Collecter les stats ARC pendant 2–5 minutes. Utilisez
arcstat 1et capturez la tendance du miss%. - Collecter les stats I/O du pool. Utilisez
zpool iostat -v 1pour voir la charge de lecture. - Trouver les principaux lecteurs. Utilisez
iotop(ou l’équivalent de votre plateforme). - Corréler. Les misses ARC ont-ils monté quand le lecteur séquentiel a démarré ?
- Choisir l’atténuation la moins invasive. Throttle / replanifier le lecteur en premier lieu.
- Si vous devez tuner : désactivez temporairement le préfetch (ou réduisez son périmètre) et mesurez sur une courte fenêtre.
- Décider. Si la latence s’améliore nettement et que les tâches séquentielles restent acceptables, envisagez un changement persistant documenté.
- Après action : Relancez la charge dans une fenêtre contrôlée et validez la correction sous concurrence réaliste.
Checklist : hygiène sûre pour le tuning en production
- Ayez une commande de rollback prête avant d’appliquer le changement.
- Changez une chose à la fois.
- Mesurez à la fois les métriques système (ARC/pool) et les métriques de charge (durée des jobs, latence des requêtes).
- Privilégiez un tuning par rôle (nœuds de sauvegarde vs nœuds sensibles à la latence) plutôt que des bascules sur toute la flotte.
- Notez le « pourquoi », pas seulement le « quoi ». Votre futur vous ne se souviendra pas du contexte de panique.
FAQ
1) Qu’est-ce que le préfetch ZFS exactement ?
C’est la lecture anticipée adaptative de ZFS. Quand ZFS détecte un accès séquentiel, il émet des lectures supplémentaires pour des blocs futurs afin qu’ils soient dans l’ARC lorsque l’application les demandera.
2) Le préfetch est-il la même chose que le readahead Linux ?
Non. Le readahead Linux est lié au page cache et à la couche bloc. ZFS utilise l’ARC et sa propre pipeline I/O. Vous pouvez avoir un readahead Linux raisonnable et voir quand même le préfetch ZFS provoquer du churn dans l’ARC.
3) Quand dois-je désactiver le préfetch ?
Quand vous avez des preuves solides qu’il pollue l’ARC et nuit aux charges sensibles à la latence — typiquement lors de gros scans séquentiels ponctuels sur un système partagé avec peu de marge ARC. Désactivez-le d’abord en tant qu’expérience contrôlée.
4) Quand désactiver le préfetch est-ce une mauvaise idée ?
Si votre environnement dépend de lectures séquentielles à haut débit (sauvegardes, streaming média, lectures de gros fichiers, flux de réplication send) et que vous n’avez pas d’autres moyens pour planifier/throttler ces charges. La désactivation peut transformer des lectures en flux fluides en I/O disque soutenu.
5) Comment savoir s’il s’agit d’un thrash de préfetch et pas juste de « pas assez de RAM » ?
Le manque de RAM est souvent la condition sous-jacente, mais le thrash de préfetch en est le déclencheur. Vous verrez l’ARC bloqué près du max, le miss% monter pendant un job séquentiel, des lectures disque accrues, et une dégradation de la latence tail pour des charges non liées. Si le même footprint mémoire se comporte bien sans le scan, les lectures spéculatives font probablement partie du mécanisme.
6) L2ARC corrige-t-il le thrash du préfetch ?
Généralement non. L2ARC est alimenté par les évictions de l’ARC ; si l’ARC est plein de blocs préfetchés une seule fois, L2ARC stockera aussi cette pollution, avec un overhead RAM additionnel. L2ARC aide quand il y a une vraie réutilisation qui ne tient pas dans l’ARC.
7) Puis-je tuner le préfetch sans le désactiver ?
Souvent oui, selon la version d’OpenZFS et la plateforme. Vous pouvez limiter jusqu’où ZFS lit en avance ou combien de flux il suit. Les paramètres exacts varient, d’où l’importance d’inspecter ce que votre système expose sous /sys/module/zfs/parameters (Linux) ou via les interfaces loader/sysctl (FreeBSD).
8) Pourquoi le préfetch réduit-il parfois les performances même sur du NVMe rapide ?
Parce que le coût n’est pas seulement la latence du périphérique. La pollution du cache évince les métadonnées et les blocs chauds, créant plus de misses et plus d’I/O. Même si le NVMe est rapide, l’augmentation de la latence tail et le travail I/O supplémentaire peuvent toujours nuire à la réactivité applicative.
9) Quelle est la relation entre recordsize et préfetch ?
Le préfetch lit en unités qui se mappent finalement aux blocs sur disque. Des records plus grands peuvent augmenter la quantité de données lues de manière spéculative. Si votre application lit 8–16K de façon aléatoire et que votre recordsize est 1M, le préfetch et l’amplification de lecture deviennent très coûteux.
10) Si je désactive le préfetch, l’ARC arrêtera-t-il de mettre en cache les lectures ?
Non. L’ARC continuera de mettre en cache les lectures à la demande (ce que l’application a réellement demandé). Désactiver le préfetch réduit principalement les lectures spéculatives anticipées à la demande.
Conclusion
Le préfetch ZFS n’est ni héros ni vilain. C’est un assistant agressif qui suppose que demain ressemblera à aujourd’hui : les lectures séquentielles continueront et précharger sera payant. Dans des charges de streaming stables, cette hypothèse est correcte et la performance est excellente. Dans la réalité d’entreprise mixte, multi-tenant et riche en scans, cette hypothèse se casse — et la facture arrive sous forme de thrash ARC et de latence tail.
Le bon mouvement n’est pas « basculer le bit magique ». Le bon mouvement est un diagnostic discipliné : prouver la corrélation, identifier le scanner, protéger les métadonnées et les données chaudes, et appliquer la correction la plus étroite qui restaure la prévisibilité. Quand vous traitez le préfetch comme une hypothèse plutôt que comme une superstition, ZFS redevient ennuyeux. Et le stockage ennuyeux est le meilleur stockage.