Tout semble sain. Le pool est ONLINE. Pas d’erreurs. L’ARC est chaud. La latence est… étrange. Des lectures qui étaient autrefois routinières deviennent ponctuellement lentes, et vos graphiques de stockage affichent cette courbe familière de « grenouille qui bout doucement ». On blâme le réseau. Ou l’hyperviseur. Ou « ZFS qui est ZFS ».
Parfois ce n’est rien de tout ça. Parfois c’est un seul réglage au niveau d’une case à cocher, « sûr par défaut », qui transforme des lectures ordinaires en un flux continu d’écritures — puis laisse ces écritures sabler vos performances pendant des mois.
Le réglage : atime=on (et pourquoi c’est une bombe à retardement pour les performances)
Si vous administrez ZFS assez longtemps, vous rencontrerez ce schéma : un pool qui démarre rapide, reste correct pendant un certain temps, puis développe progressivement des accrocs de latence lors de charges de lecture intensives. Le stockage ne « tombe pas en panne ». Il devient simplement irritant.
Le coupable est souvent le défaut apparemment inoffensif : atime=on. Mise à jour du temps d’accès. À chaque lecture d’un fichier, son horodatage d’accès est mis à jour. Sur de nombreux systèmes, cela passe inaperçu. Sur un dataset ZFS chargé — surtout avec beaucoup de petits fichiers, des charges lourdes en métadonnées, ou des VM — c’est un générateur d’écritures déguisé en trafic de lecture.
Voici la partie sale : le coût en performance n’est pas toujours immédiat. ZFS peut absorber beaucoup via le cache et les groupes de transactions. Votre pool peut sembler correct pendant des semaines. Mais l’amplification des écritures et le churn des métadonnées s’accumulent. La fragmentation s’installe. Les métadonnées deviennent moins adaptées au cache. La latence augmente, pas parce que ZFS est « lent », mais parce que vous lui avez demandé de faire un travail supplémentaire pour toujours.
Conseil tranché : Pour presque tous les datasets en production, définissez atime=off. Activez-le uniquement si vous pouvez nommer précisément le comportement applicatif qui nécessite les temps d’accès, et si vous avez mesuré le coût.
Ce que atime fait réellement sur ZFS
Sur le papier, atime est simple : quand un fichier est accédé (lu), le système de fichiers met à jour des métadonnées : « ce fichier a été accédé au timestamp X ». Ça paraît mineur. Mais sur des fichiers systèmes copy-on-write comme ZFS, des « petites mises à jour de métadonnées » ne sont pas toujours petites.
Pourquoi une mise à jour de métadonnée peut devenir une vraie écriture
ZFS est copy-on-write. Mettre à jour des métadonnées signifie typiquement écrire de nouveaux blocs de métadonnées, mettre à jour des pointeurs de blocs, et finir par engager ces changements dans le pool. Ce n’est pas mal en soi ; c’est ainsi que ZFS conserve la cohérence. Mais cela signifie qu’une charge autrement en lecture seule devient un flux constant d’écritures — souvent petites, souvent dispersées, souvent un peu synchrones aux mauvais niveaux, et souvent écrites dans des zones qui ne se compressent pas bien en I/O séquentiel.
Pourquoi cela compte plus sur des hôtes VM et conteneurs
Les hôtes de virtualisation lisent beaucoup : bibliothèques, binaires, caches de packages, couches de conteneurs, et images de VM. Ils aiment aussi scanner des répertoires, exécuter des stat sur des fichiers, et faire beaucoup d’accès courts et éphémères. Si atime=on, ces « lectures » modifient aussi les métadonnées. L’hôte effectue alors des écritures en arrière-plan alors que vous jurez qu’il est « majoritairement en lecture ». Vos graphiques de latence diront le contraire.
« Mais ce n’est qu’un seul timestamp »
Oui. Et pourtant : une mise à jour de timestamp par lecture multipliée par des millions de lectures par jour n’est plus un timestamp. C’est une charge de travail.
Petite blague #1 : Activer atime en production, c’est comme ajouter un drapeau « veuillez tout journaliser » dans votre chemin chaud. Ça marche très bien… jusqu’à ce que ça ne marche plus.
Pourquoi ça empire avec le temps (silencieusement)
La partie « avec le temps » est ce qui rend ce réglage si efficace pour ruiner vos semaines. Quand atime=on, vous ajoutez un flux continu de petites mises à jour de métadonnées. ZFS les regroupe en groupes de transactions, mais les schémas importent toujours. Au bout de quelques mois, vous pouvez vous retrouver avec :
- Plus d’écritures petites que votre modèle de charge n’avait prévu.
- Plus de blocs de métadonnées mis à jour et réécrits que prévu.
- Plus de fragmentation, surtout si l’espace libre diminue ou si les classes d’allocation sont sollicitées.
- Plus de contention dans la pipeline d’écriture : synchronisation TXG, cartes d’espace SPA, files d’attente vdev.
- Moins d’efficacité du cache, parce que le churn des métadonnées évince des données « utiles » du cache.
Ce n’est pas seulement les IOPS ; c’est la latence tail
La plupart des gens remarquent la « dégradation » des performances quand les moyennes dérivent. Ce qui souffre en premier, c’est la latence tail : le 99ème percentile. C’est là que les écritures aléatoires lourdes en métadonnées apparaissent. Votre application cesse d’être fluide. Des timeouts apparaissent. Les ingénieurs sont alertés pour un « stockage intermittent » et passent deux jours à prouver que le réseau est innocent.
Pourquoi ça se cache à la vue de tous
La surveillance a tendance à catégoriser les E/S comme lecture vs écriture au niveau du périphérique bloc. Mais atime transforme des lectures en écritures de métadonnées qui ne ressemblent pas forcément à des « écritures de votre appli ». Ça ressemble à une activité de fond du système de fichiers. Et comme c’est « normal », on le remet rarement en question.
Faits & contexte historique (la version courte et utile)
- Le temps d’accès est plus ancien que la plupart de vos infrastructures. UNIX suit atime/mtime/ctime depuis des décennies, bien avant que les SSD, hyperviseurs et microservices rendent les « petites écritures de métadonnées » coûteuses à grande échelle.
- Linux a introduit relatime comme compromis. L’industrie a remarqué le coût d’atime il y a des années ; relatime met à jour atime moins agressivement (souvent une fois par jour ou quand mtime/ctime changent).
- Les défauts de ZFS privilégiaient historiquement la correction et les attentes POSIX. Par défaut
atime=onrespecte la sémantique traditionnelle, pas les attentes modernes de performance. - Les systèmes CoW paient différemment pour le churn des métadonnées. Ext* peut mettre à jour en place ; ZFS écrit de nouveaux blocs de métadonnées. C’est une force pour l’intégrité — et un coût pour le churn inutile.
- Atime interagit avec les snapshots. Les snapshots préservent les anciennes métadonnées ; des réécritures fréquentes de métadonnées peuvent augmenter le churn des blocs référencés et compliquer le comportement du comptage d’espace.
- Les charges NFS et SMB peuvent amplifier les mises à jour d’atime. Les opérations de métadonnées sur des systèmes de fichiers réseau peuvent déclencher des contrôles d’accès supplémentaires et des touches de fichiers, augmentant la fréquence des mises à jour.
- Les formats d’images VM ne vous protègent pas. Même si la machine virtuelle est « en lecture seule », le système de fichiers hôte peut toujours mettre à jour atime sur le fichier image VM et sur les caches côté hôte.
- Beaucoup d’appliances et de produits NAS désactivent discrètement atime. Non pas parce qu’ils détestent POSIX, mais parce qu’ils détestent les tickets de support « le NAS est devenu lent ».
- Les vdevs spéciaux ont changé la donne pour les métadonnées. Les fonctionnalités modernes d’OpenZFS comme les classes d’allocation spéciales peuvent isoler les métadonnées sur des périphériques plus rapides — utile, mais aussi un moyen de masquer le vrai problème (des écritures inutiles).
Trois mini-récits d’entreprise depuis le terrain
Mini-récit #1 : L’incident causé par une mauvaise hypothèse
La société A exploitait une grande flotte CI. Tout était « immuable » par politique : artefacts récupérés, tests lancés, résultats envoyés. Le stockage était un pool ZFS sur SSD avec une marge confortable. L’hypothèse était simple et raisonnable : « Les runners CI lisent surtout ; ils n’useront pas les disques ni ne satureront le pool. »
Quelques semaines plus tard, les builds commençaient à expirer. Pas systématiquement — juste assez pour miner la confiance des développeurs. L’équipe a chassé les suspects habituels : performance du registre, résolution DNS, steal CPU sur les nœuds Kubernetes, pertes réseau. Les tableaux de bord de stockage montraient surtout des lectures. Le pool n’avait pas d’erreurs de checksum. SMART était propre. Tout le monde était exaspéré pour des raisons différentes.
L’indice venait des IOPS d’écriture qui ne correspondaient à aucun chemin d’écriture connu. Sur l’hôte ZFS, le dataset contenant les caches des runners avait atime=on. Chaque dépendance lue pendant les builds mettait à jour les temps d’accès sur des centaines de milliers de fichiers. La charge n’était pas « en lecture seule ». Elle était « lecture + écriture de métadonnées ». Sous charge, les rafales de TXG sync s’alignaient sur des phases de tests, rendant la latence tail semblable à une lenteur de calcul aléatoire.
Ils ont désactivé atime sur le dataset de cache et n’ont rien redémarré. Les timeouts ont disparu. Le postmortem fut court et un peu embarrassant, ce qui est le meilleur type : un petit changement qui vous apprend à vous méfier des « défauts ».
Mini-récit #2 : L’optimisation qui s’est retournée contre eux
La société B hébergeait des applications multi-tenant sur un cluster VM soutenu par ZFS. Ils ont eu une idée : mettre les datasets « chauds » sur des miroirs SSD rapides et les datasets « froids » sur du stockage économique. Puis ils ont tenté d’optimiser la collecte de statistiques en activant un comptage d’accès aux fichiers plus détaillé. Certains middlewares voulaient atime pour des heuristiques d’éviction de cache, alors ils ont activé atime=on largement.
Cela a fonctionné — pendant un temps. Puis le pool SSD « chaud » a commencé à afficher des pics de latence périodiques. Ni saturation, ni profondeur de file constante, juste des rafales désagréables. L’équipe a réagi comme des professionnels : ils ont ajouté des SSD. Les pics se sont raréfiés mais n’ont pas disparu. Ils ont mis à jour le firmware. Toujours là.
Ce qui s’était passé était classique : l’optimisation supposait qu’atime était « métadonnée seulement » et donc peu coûteux sur SSD. Mais les mises à jour d’atime ont créé un flux constant de petites écritures dispersées qui ont perturbé la coalescence d’écriture normale du pool. Pire, ces écritures ont augmenté la fragmentation des métadonnées. Après des mois, même les lectures nécessitaient plus de recherches de métadonnées qui manquaient l’ARC et touchaient le disque.
Ils ont finalement déplacé les quelques datasets qui nécessitaient vraiment atime vers des pools isolés avec des paramètres ajustés et laissé le reste en atime=off. Ajouter du matériel a aidé, mais désactiver la charge auto-infligée a aidé davantage. La leçon : si vous ne pouvez pas expliquer pourquoi une fonctionnalité est nécessaire, vous n’optimisez pas — vous ajoutez des variables.
Mini-récit #3 : La pratique ennuyeuse mais correcte qui a sauvé la situation
La société C exploitait un environnement mixte : partages de fichiers, stockage VM et quelques bases de données. Ils avaient une règle : chaque dataset doit déclarer son intention. Les datasets VM reçoivent un ensemble connu de propriétés. Les partages de fichiers en reçoivent un autre. Tout ce qui est « spécial » nécessite un ticket expliquant pourquoi. Ça semble bureaucratique. C’est en réalité une façon d’éviter la complexité accidentelle.
Quand une nouvelle équipe a mis en place un service d’analyse de logs, elle a demandé un dataset avec une stricte sémantique POSIX « au cas où ». L’ingénieur stockage a résisté : « Définissez vos exigences réelles. » Ils ont testé et constaté que le service n’utilisait pas atime ; il utilisait mtime et son propre indexage. Le dataset a été livré avec atime=off, compression=on et un recordsize aligné sur la charge.
Six mois plus tard, un autre environnement exécutant le même service ailleurs avait un problème de lenteur progressive : latence en hausse, fragmentation rampante et tempêtes de sync périodiques. La société C n’en avait pas. Leurs systèmes n’étaient pas magiques ; ils étaient ennuyeux. Paramètres ennuyeux, baselines ennuyeuses, audits ennuyeux.
Le moment où ils ont « sauvé la situation » n’était pas un dépannage héroïque. C’était l’absence du problème — parce qu’ils avaient un profil par défaut qui l’évitait.
Playbook de diagnostic rapide
Ceci est la séquence « j’ai 20 minutes avant l’appel d’incident ». Ne philosophez pas. Ne touchez pas dix choses. Identifiez le goulot dominant et confirmez si le churn d’atime est en cause.
Premier : prouvez que vous avez un problème de latence stockage (pas CPU/réseau)
- Comparez la latence côté application et la latence disque côté hôte. Si les pics de latence applicative se corrèlent avec la latence vdev ZFS, c’est réel.
- Cherchez de l’ordonnancement : un
awaitélevé et uneaqu-szcroissante indiquent que le périphérique n’arrive pas à suivre.
Deuxième : identifiez si « les lectures causent des écritures »
- Vérifiez
atimesur les datasets du chemin chaud. - Corrélez la charge de lecture avec des IOPS d’écriture inattendues et l’activité de sync TXG.
Troisième : décidez si le problème est lié aux métadonnées
- Opérations élevées sur les métadonnées, écritures fréquentes et petites, ARC sous pression : vous êtes lié aux métadonnées.
- Si vous avez un special vdev, vérifiez s’il est surchargé. Si vous n’en avez pas, vérifiez si en ajouter un est justifié — mais seulement après avoir désactivé les mises à jour atime inutiles.
Quatrième : corrigez la chose la moins risquée d’abord
- Désactivez atime sur les dataset où ce n’est pas requis.
- Revérifiez la latence et les IOPS d’écriture en quelques heures, pas en plusieurs jours.
Petite blague #2 : Si votre service « en lecture seule » effectue 5 000 IOPS d’écriture, soit le service ment, soit votre système de fichiers est très enthousiaste.
Tâches pratiques : commandes, sorties et ce que la sortie signifie
Ci-dessous se trouvent des tâches réelles que vous pouvez exécuter sur un hôte ZFS. Chaque tâche inclut : une commande, une sortie d’exemple, ce que cela signifie et la décision opérationnelle à prendre. L’objectif est de passer de « ça semble lent » à « ce dataset génère des écritures de métadonnées parce que atime est activé ».
Task 1: Find datasets with atime enabled
cr0x@server:~$ zfs get -r -o name,property,value,source atime tank
NAME PROPERTY VALUE SOURCE
tank atime on default
tank/vm atime off local
tank/home atime on inherited from tank
tank/ci-cache atime on local
Ce que ça signifie : tank/home a hérité on. tank/ci-cache est explicitement défini sur on.
Décision : Identifiez lesquels de ces datasets se trouvent sur des chemins critiques pour la performance. Prévoyez de définir atime=off pour ceux qui n’en ont pas réellement besoin.
Task 2: Confirm where the hot I/O is (dataset I/O)
cr0x@server:~$ zfs iostat -v tank 2 3
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 3.12T 1.88T 850 2100 110M 95.0M
mirror 3.12T 1.88T 850 2100 110M 95.0M
nvme0n1 - - 430 1050 55.2M 48.0M
nvme1n1 - - 420 1050 54.8M 47.0M
-------------------------- ----- ----- ----- ----- ----- -----
Ce que ça signifie : Les écritures sont élevées par rapport à ce que vous attendiez. Cette sortie ne prouve pas à elle seule atime, mais elle indique qu’il y a une vraie pression d’écriture.
Décision : Si la charge est censée être axée sur la lecture, enquêtez pourquoi des écritures ont lieu (atime, sync, logs applicatifs, fichiers temporaires).
Task 3: Check whether the dataset is mounted with the expected behavior
cr0x@server:~$ zfs get -o name,property,value,source mountpoint,canmount,atime tank/ci-cache
NAME PROPERTY VALUE SOURCE
tank/ci-cache mountpoint /tank/ci-cache local
tank/ci-cache canmount on default
tank/ci-cache atime on local
Ce que ça signifie : Il est monté et met activement à jour atime.
Décision : Si l’application ne consomme pas atime, désactivez-le.
Task 4: Disable atime safely (dataset-level)
cr0x@server:~$ sudo zfs set atime=off tank/ci-cache
Ce que ça signifie : Les accès futurs ne mettront pas à jour les métadonnées de temps d’accès sur ce dataset.
Décision : Appliquez d’abord aux datasets les plus problématiques. Évitez de changer les datasets racine/système tant que vous n’êtes pas sûr que rien ne dépend de la sémantique atime.
Task 5: Verify the change actually applied
cr0x@server:~$ zfs get -o name,property,value,source atime tank/ci-cache
NAME PROPERTY VALUE SOURCE
tank/ci-cache atime off local
Ce que ça signifie : La propriété est définie localement et persistera.
Décision : Suivez les deltas de performance sur les prochaines heures. Si vous ne voyez pas d’amélioration, continuez à creuser : ne supposez pas qu’atime était le seul facteur.
Task 6: See if “read traffic” still triggers writes after the change
cr0x@server:~$ zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.12T 1.88T 900 420 120M 18.0M
mirror 3.12T 1.88T 900 420 120M 18.0M
nvme0n1 - - 450 210 60.0M 9.1M
nvme1n1 - - 450 210 60.0M 8.9M
Ce que ça signifie : Si les écritures ont chuté fortement alors que les lectures sont restées similaires, vous avez supprimé une source d’écriture majeure.
Décision : Si la latence s’est améliorée, poursuivez le déploiement sur des datasets similaires. Si ce n’est pas le cas, le pool est probablement lié ailleurs (écritures sync, fragmentation, saturation du special vdev, ou limites des périphériques).
Task 7: Check pool health and errors (don’t skip this)
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 03:12:44 with 0 errors on Sun Feb 2 03:20:11 2026
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Ce que ça signifie : Pas de problèmes d’intégrité. Bien — le travail sur la performance est pertinent maintenant.
Décision : Si vous avez des erreurs, arrêtez d’optimiser et commencez à réparer le matériel/câblage/firmware. Optimiser un pool malade, c’est du cosplay.
Task 8: Inspect ARC behavior and memory pressure (Linux OpenZFS)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:44:01 850 90 10 40 44 50 56 0 0 48G 64G
12:44:02 900 85 9 38 45 47 55 0 0 48G 64G
12:44:03 920 82 9 36 44 46 56 0 0 48G 64G
Ce que ça signifie : Un taux de miss ~9–10% peut être acceptable, mais si les misses augmentent avec des opérations lourdes en métadonnées, vous verrez des lectures disque et de la latence.
Décision : Si l’ARC est constamment sous pression et que les misses de métadonnées sont élevés, considérez si le churn des métadonnées (comme atime) évince le cache utile. Désactiver atime est la victoire à moindre coût.
Task 9: Measure TXG sync behavior (a proxy for “write pipeline stress”)
cr0x@server:~$ grep -i txg /proc/spl/kstat/zfs/txg | head
12 1 0x01 87 4224 1122334455 987654321
Ce que ça signifie : Sur Linux, les stats txg peuvent être opaques ; on utilise typiquement des outils de plus haut niveau et on les corrèle avec zpool iostat et la latence. Le but est de repérer des rafales de sync périodiques.
Décision : Si vous voyez des écritures par rafales alignées avec des pics de latence applicative, réduisez d’abord le churn de fond (atime), puis examinez les réglages de sync et les dispositifs de log.
Task 10: Check dataset recordsize and workload alignment
cr0x@server:~$ zfs get -o name,property,value,source recordsize tank/vm tank/home
NAME PROPERTY VALUE SOURCE
tank/vm recordsize 16K local
tank/home recordsize 128K default
Ce que ça signifie : Les datasets VM utilisent souvent des blocs plus petits. Les répertoires home avec beaucoup de petits fichiers peuvent néanmoins tenir à 128K, mais le churn des métadonnées domine de toute façon si atime est activé.
Décision : Ne vous lancez pas dans l’ajustement du recordsize avant d’avoir corrigé les sources évidentes de churn. Le tuning du recordsize ne vous sauvera pas des écritures auto-infligées par atime.
Task 11: Confirm whether a dataset is used for databases or log-like workloads
cr0x@server:~$ zfs get -o name,property,value,source logbias,sync tank/db
NAME PROPERTY VALUE SOURCE
tank/db logbias latency default
tank/db sync standard default
Ce que ça signifie : Les valeurs par défaut sont conservatrices. Pour les bases de données vous pouvez avoir un comportement sync intentionnel. C’est distinct d’atime, mais ne confondez pas la latence de sync avec atime.
Décision : Si le dataset contient une base de données, validez les exigences de durabilité de l’appli avant de toucher sync. Vous pouvez généralement désactiver atime en toute sécurité dans la plupart des cas DB.
Task 12: Identify whether the dataset is snapshot-heavy (space and churn visibility)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,mountpoint -s used | tail -n 5
tank/home@daily-2026-01-30 12.4G 220G -
tank/home@daily-2026-01-31 13.1G 220G -
tank/home@daily-2026-02-01 13.8G 220G -
tank/home@daily-2026-02-02 14.2G 220G -
tank/home@daily-2026-02-03 14.9G 220G -
Ce que ça signifie : L’augmentation du « used » des snapshots peut refléter le churn dans le dataset. Les mises à jour atime peuvent contribuer au churn, surtout dans les schémas métadonnées, même quand le contenu des fichiers ne change pas.
Décision : Si la croissance des snapshots surprend pour un dataset « principalement en lecture », auditez atime et d’autres comportements qui changent les métadonnées.
Task 13: Check pool free space (fragmentation accelerant)
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 5.00T 3.12T 1.88T - - 38% 62% 1.00x ONLINE -
Ce que ça signifie : Une fragmentation à 38% et une capacité à 62% n’est pas catastrophique, mais si CAP monte vers 80–90%, l’allocation devient plus dispersée. Le churn d’atime ajoute encore plus de petites allocations dans ce désordre.
Décision : Gardez les pools confortablement en dessous des seuils critiques, surtout pour des charges d’écriture aléatoires. Désactivez atime pour réduire le churn et ralentir la croissance de la fragmentation.
Task 14: Look at per-vdev latency (where the pain is)
cr0x@server:~$ zpool iostat -v tank -l 1 3
capacity operations bandwidth total_wait disk_wait
pool alloc free read write read write read write read write
-------------------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
tank 3.12T 1.88T 920 480 125M 22.0M 2ms 18ms 1ms 16ms
mirror 3.12T 1.88T 920 480 125M 22.0M 2ms 18ms 1ms 16ms
nvme0n1 - - 460 240 62.5M 11.1M 2ms 17ms 1ms 15ms
nvme1n1 - - 460 240 62.5M 10.9M 2ms 19ms 1ms 17ms
-------------------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
Ce que ça signifie : L’attente d’écriture est bien plus élevée que l’attente de lecture, même si la charge semble « orientée lecture ». C’est cohérent avec des rafales d’écritures de métadonnées.
Décision : Si la désactivation d’atime réduit l’attente d’écriture, vous l’avez confirmé comme contributeur. Sinon, investiguez les écritures sync, le comportement du SLOG et la saturation des vdevs.
Task 15: Validate dataset property inheritance (catch accidental defaults)
cr0x@server:~$ zfs get -r -o name,property,value,source atime tank/home
NAME PROPERTY VALUE SOURCE
tank/home atime on inherited from tank
tank/home/users atime on inherited from tank
tank/home/projects atime on inherited from tank
Ce que ça signifie : Un « innocent » défaut au niveau du pool racine peut empoisonner chaque dataset enfant.
Décision : Définissez des valeurs par défaut sensées au niveau des dataset parent et ne surchargez qu’en cas de besoin. Si vous ne pouvez pas standardiser, documentez au moins les exceptions.
Erreurs courantes : symptômes → cause racine → fix
1) « Notre charge en lecture génère des tonnes d’écritures »
Symptômes : IOPS d’écriture élevés pendant les pics de lecture ; rafales d’écriture périodiques ; pics de latence tail.
Cause racine : atime=on sur des datasets chauds ; l’activité de lecture déclenche des écritures de métadonnées.
Correctif : Désactivez atime sur ces datasets : zfs set atime=off pool/dataset. Vérifiez la baisse des IOPS d’écriture.
2) « Les performances ont empiré progressivement sur des mois »
Symptômes : Même matériel, même charge nominale, latence en hausse ; plus de variance ; pire 99p.
Cause racine : Accumulation de churn des métadonnées + fragmentation ; atime est une source de churn continue.
Correctif : Arrêtez le churn (atime off), gardez une capacité de pool saine, et envisagez une rééquilibrage/migration si la fragmentation est sévère.
3) « Nous avons ajouté des disques plus rapides et ça n’a guère aidé »
Symptômes : Upgrade matériel apporte une amélioration mineure ; les pics persistent.
Cause racine : La charge est dominée par de petites écritures aléatoires de métadonnées ; vous avez monté le mauvais axe.
Correctif : Supprimez les écritures de métadonnées inutiles (atime), puis profilez le goulot restant (sync, special vdev, ARC).
4) « Les snapshots mangent de l’espace sur un dataset principalement en lecture »
Symptômes : Le champ used des snapshots augmente plus vite que prévu ; les utilisateurs insistent « nous n’avons rien changé ».
Cause racine : Les changements de métadonnées comptent comme des modifications. Les mises à jour atime sont des changements.
Correctif : Désactivez atime ; réévaluez la fréquence/ rétention des snapshots. Ne blâmez pas les utilisateurs pour la physique du stockage.
5) « Les partages NFS/SMB semblent lents, mais les disques ne sont pas saturés »
Symptômes : Lenteur interactive ; listes de répertoires qui traînent ; petites opérations qui piquent.
Cause racine : Les opérations sur métadonnées sont sensibles à la latence ; les mises à jour d’atime ajoutent une pression d’écriture qui se traduit par de la gigue.
Correctif : Désactivez atime sur les datasets de partage sauf si nécessaire. Si les métadonnées restent chaudes, étudiez un special vdev pour les métadonnées.
6) « Nous avons changé atime et rien n’a changé »
Symptômes : Aucune amélioration visible après la désactivation d’atime.
Cause racine : La charge n’était pas entraînée par atime, ou un autre réglage domine (écritures sync, petit recordsize avec sync, problèmes SLOG, disques SMR, firmware défectueux).
Correctif : Suivez le playbook de diagnostic rapide : validez la latence vdev, le comportement sync, les misses ARC et la capacité/fragmentation. Ne continuez pas à basculer des réglages au hasard.
Listes de vérification / plan pas-à-pas
Checklist A: Decide where atime belongs (usually nowhere)
- Listez les datasets et l’état actuel d’atime (
zfs get -r atime). - Classifiez les datasets par charge : VM, DB, cache CI, répertoires home, sauvegardes, objet store, partages.
- Pour chaque dataset, répondez : « Qu’est-ce qui casse si atime est off ? » Si la réponse est « pas sûr », par défaut off et testez.
- Identifiez les rares consommateurs d’atime réels (certains systèmes de mail, logiciles d’éviction de cache spécialisés, workflows de conformité).
- Documentez les exceptions lors de la création des datasets.
Checklist B: Safe rollout plan for disabling atime
- Choisissez un dataset à fort trafic (pas racine). Désactivez atime.
- Mesurez : IOPS d’écriture, attente vdev d’écriture, latence applicative et croissance des snapshots pendant 24 heures.
- Déployez par lots sur des datasets similaires.
- Si vous avez des contraintes de conformité, validez que les signaux d’audit requis n’utilisent pas atime (généralement non).
- Définissez un défaut parent sensé pour les nouveaux datasets (habituellement
atime=off).
Checklist C: If the pool is already degraded over time
- Arrêtez d’abord le churn : atime off sur les datasets chauds.
- Confirmez que l’espace libre du pool est sain ; planifiez une augmentation de capacité si CAP est élevé.
- Vérifiez si les métadonnées sont le goulot (miss ARC, petites IO, forte attente d’écriture).
- Si la fragmentation est sévère et que les performances restent mauvaises, envisagez une migration contrôlée par send/receive vers un pool ou un layout de dataset neuf.
- Ce n’est qu’ensuite que vous évaluez des ajouts comme les special vdevs pour les métadonnées. Ils sont puissants, mais ce n’est pas une excuse pour laisser atime activé partout.
One operations quote (because it’s still true)
Idée paraphrasée, attribuée à Donald Knuth : L’optimisation prématurée peut être la racine de nombreux problèmes.
Dans ce contexte, « optimiser » inclut « activer des sémantiques dont vous n’avez pas besoin ». atime, c’est du théâtre de correction sauf si quelque chose l’utilise réellement.
FAQ
1) Est-ce que atime=on est réellement « dangereux » ?
Non. Ce n’est pas dangereux pour l’intégrité des données. C’est risqué pour la prévisibilité des performances à grande échelle parce que ça convertit silencieusement des lectures en écritures et ajoute du churn.
2) Si Linux a relatime, ZFS a-t-il quelque chose de similaire ?
ZFS expose atime comme propriété de dataset (on/off). Certaines plateformes ont des comportements additionnels, mais opérationnellement vous devriez le traiter comme un choix binaire et le mettre par défaut sur off sauf nécessité.
3) Quelles applications ont réellement besoin d’atime ?
Quelques-unes : certains workflows de distribution mail et maildir, certains scripts de sauvegarde/audit écrits il y a des décennies, et des logiques d’éviction de cache de niche. La plupart des systèmes modernes utilisent mtime, ctime, des mécanismes type inotify, ou des métadonnées au niveau applicatif.
4) Désactiver atime casse-t-il la conformité POSIX ?
Ça assouplit une attente comportementale spécifique (mise à jour du temps d’accès). Beaucoup de systèmes en production acceptent ce compromis. Si vous avez une exigence stricte, activez atime seulement sur les datasets qui en ont besoin.
5) Désactiver atime réduit-il l’usure des SSD ?
Souvent oui, car cela élimine une classe d’écritures. Si cela importe dépend de l’intensité de la charge et de l’endurance des SSD, mais réduire des écritures inutiles est rarement une mauvaise idée.
6) Pourquoi la dégradation des performances apparaît-elle « avec le temps » plutôt qu’immédiatement ?
ZFS peut bufferiser et regrouper les écritures, l’ARC peut masquer les lectures de métadonnées, et les premiers agencements d’espace libre sont plus favorables. Avec le temps, le churn augmente la fragmentation et l’inefficacité du cache, et le pool passe plus de temps à trouver de l’espace et à lire des métadonnées dispersées.
7) Dois-je mettre atime=off au niveau racine du pool ?
Généralement, oui — sur les datasets de haut niveau que vous utilisez comme parents pour des charges réelles. Ensuite activez explicitement atime sur les rares datasets qui en ont besoin. Cela évite l’héritage accidentel d’un défaut coûteux.
8) Est-ce qu’atime est la seule raison pour laquelle ZFS ralentit ?
Non. La pression de capacité, la fragmentation, les schémas d’écriture sync, un recordsize mal dimensionné, l’absence d’un special vdev pour les métadonnées, et un mauvais design de vdev peuvent tous nuire. atime est juste le plus sournois parce qu’il se cache derrière des « lectures ».
9) Si j’ai déjà un special vdev pour les métadonnées, puis-je garder atime activé ?
Vous le pouvez, mais ce n’est pas recommandé par défaut. Les special vdevs peuvent absorber les I/O de métadonnées, mais vous générez toujours du travail inutile et augmentez le churn. Corrigez la cause d’abord, puis utilisez les special vdevs pour une intensité de métadonnées légitime.
10) À quelle vitesse dois-je attendre une amélioration après la désactivation d’atime ?
Souvent en quelques minutes à quelques heures sous forme de réduction des IOPS d’écriture et d’une baisse de l’attente d’écriture. Les améliorations à long terme (moins de croissance de fragmentation, moins de pics) apparaissent sur plusieurs jours à semaines.
Conclusion : quoi changer dès lundi matin
Si vous utilisez ZFS en production et que vous n’avez pas audité atime, vous payez probablement une taxe non budgétée. Ce n’est pas spectaculaire. C’est le problème. Cela transforme silencieusement vos chemins de lecture en pression d’écriture, puis laisse les conséquences s’accumuler jusqu’à ce que votre équipe commence à blâmer des fantômes.
Étapes pratiques :
- Inventoriez les datasets et trouvez où
atime=onest hérité ou défini localement. - Désactivez atime sur les datasets critiques pour la performance sauf si vous pouvez prouver qu’il est nécessaire.
- Mesurez de nouveau les IOPS d’écriture, la latence vdev et la croissance de l’espace des snapshots après le changement.
- Standardisez des profils de propriétés de dataset pour ne pas réintroduire le problème dans six mois lors d’une mise en service « rapide ».
ZFS est une machine de fiabilité. Mais il exécutera fidèlement du travail inutile si vous lui demandez. Ne le lui demandez pas.