ZFS primarycache : règles de cache qui empêchent l’ARC de gaspiller la RAM

Cet article vous a aidé ?

Si vous avez déjà vu un hôte ZFS avec 256 Go de RAM paraître étrangement « plein » alors que les performances ne bougeaient pas d’un iota, vous avez rencontré la mauvaise habitude de l’ARC : mettre en cache des données qui ne méritent pas une seconde visite. L’ARC est brillant pour apprendre ce dont vous aurez à nouveau besoin. Il est aussi obéissant. Si vous continuez à lui fournir une charge qui est « lue une fois, jamais plus », il engloutira volontiers de la mémoire et expulsera les choses qui comptent vraiment.

C’est là que primarycache cesse d’être une note et devient une surface de contrôle opérationnel. Bien utilisé, c’est un scalpel : des règles de cache par dataset qui maintiennent l’ARC concentré sur les métadonnées et les blocs vraiment réutilisables, tout en laissant passer les lectures en streaming et les datasets jetables sans transformer la RAM en musée des sauvegardes d’hier.

Ce que primarycache contrôle réellement (et ce qu’il ne contrôle pas)

primarycache est une propriété de dataset ZFS qui contrôle ce que ZFS est autorisé à placer dans l’ARC (le cache en mémoire). Elle a trois valeurs :

  • all — mettre en cache à la fois les données des fichiers et les métadonnées dans l’ARC (par défaut)
  • metadata — mettre en cache uniquement les métadonnées (dnode, blocs indirects, etc.)
  • none — ne rien mettre de ce dataset dans l’ARC

Cela paraît simple, mais il y a deux vérités opérationnelles subtiles :

  1. Il s’agit d’admission, pas de politique d’éviction. Il décide quels types de blocs provenant de ce dataset sont éligibles pour entrer dans l’ARC. Il ne « fixe » ni ne « priorise » rien directement.
  2. C’est par dataset, hérité et facile à mal utiliser. C’est puissant précisément parce que vous pouvez l’appliquer à un dataset de sauvegarde sans pénaliser votre dataset de VM. C’est aussi facile de le définir sur un parent et de l’oublier, sabotant silencieusement les performances pour tout ce qui se trouve en dessous.

Autre point : primarycache n’est pas un interrupteur magique pour « utiliser L2ARC à la place ». C’est secondarycache. Vous pouvez définir les deux, et ils interagissent, mais ils résolvent des problèmes différents : admission dans l’ARC vs admission dans le L2ARC.

Encore un piège : définir primarycache=none ne veut pas dire « ZFS n’utilisera jamais la RAM pour ce dataset ». ZFS utilise toujours de la mémoire pour la tenue de l’état, et votre application utilise toujours le cache de pages (sur Linux) à moins d’utiliser I/O direct. L’ARC est seulement une des bouches qui mangent de la RAM.

Blague #1 (courte et pertinente) : L’ARC, c’est comme un stagiaire avec une excellente mémoire qui ne demande jamais pourquoi il mémorise tout l’annuaire téléphonique.

Comment l’ARC « gaspille » la RAM en pratique

Que l’ARC gaspille la RAM n’est généralement pas un bug. C’est un décalage entre votre charge et les hypothèses du cache.

L’ARC fonctionne mieux lorsque :

  • il y a une locality temporelle significative (vous relisez les mêmes blocs bientôt), et/ou
  • la réutilisation des métadonnées est élevée (parcours d’annuaire, petites IO aléatoires, churn des métadonnées VM), et/ou
  • votre jeu de travail tient en RAM ou y tient en grande partie.

L’ARC fonctionne mal (ou du moins « coûteux ») lorsque :

  • vous parcourez de gros fichiers une seule fois (sauvegardes, scrub, grandes lectures ETL),
  • le jeu de travail éclipse la mémoire, provoquant un renouvellement continu,
  • l’hôte exécute aussi des services gourmands en mémoire (bases de données, JVM) qui ont réellement besoin de cette RAM.

À quoi ressemble le « gaspillage » opérationnellement ?

  • La latence ne s’améliore pas après le premier passage, parce qu’il n’y a pas de second passage.
  • L’ARC grossit parce que ZFS fait son travail : il voit de la mémoire libre et l’utilise.
  • Les autres processus sont comprimés, entraînant swap, OOM kills ou une pression de reclaim agressive.
  • Le taux de hits ARC semble correct mais les misses restent coûteuses parce qu’elles se produisent dans le chemin critique (souvent métadonnées ou petites lectures aléatoires), et vos « hits » sont une montagne de données séquentielles sans valeur.

Dans de nombreux incidents en production, la première mauvaise hypothèse est que « le taux de hit du cache » est un nombre unique qui peut valider tout un système. Ce n’est pas le cas. Vous pouvez avoir un taux de hits convenable et perdre quand même parce que le cache est plein de blocs à faible valeur.

Faits & historique : pourquoi ce réglage existe

Un peu de contexte facilite le respect de cette propriété. Voici quelques faits concrets et notes historiques qui influencent le comportement de primarycache :

  1. ZFS a été conçu avec la RAM comme niveau de performance premier. Quand ZFS est né chez Sun, les serveurs à grande mémoire faisaient partie de la cible. L’ARC n’est pas un détail ; il est au cœur de la conception.
  2. L’ARC n’est pas « juste un cache » ; c’est un moteur de politique. Il divise le cache en segments MRU/MFU (récent vs fréquent) pour éviter d’être trompé par des scans ponctuels.
  3. Même avec une résistance aux scans, les charges séquentielles remplissent l’ARC. L’ARC peut essayer de ne pas détruire votre ensemble chaud pendant un scan, mais il doit quand même décider quoi admettre. primarycache est le levier « ne pas admettre ces trucs ».
  4. L’idée du « metadata-only » est plus vieille que ZFS. Les systèmes de fichiers reconnaissent depuis longtemps la locality des métadonnées comme un grand gain. ZFS l’a formalisé au niveau du dataset.
  5. Les datasets ZFS sont faits pour être des frontières de politique. Des propriétés comme recordsize, compression, sync et primarycache existent pour que vous puissiez exécuter des charges mixtes sur un même pool sans qu’une charge n’écrase les autres.
  6. L2ARC avait historiquement une « taxe RAM ». Stocker les en-têtes L2ARC en mémoire était coûteux. Les implémentations plus récentes ont réduit l’overhead, mais la leçon reste : la mise en cache n’est jamais gratuite.
  7. Sur Linux, le page cache et l’ARC peuvent entrer en concurrence. OpenZFS sur Linux s’intègre soigneusement, mais du point de vue de l’opérateur, cela signifie toujours « deux couches de cache existent sauf si vous utilisez l’I/O direct ».
  8. Le recordsize interagit avec la valeur du cache. De gros records rendent le débit séquentiel heureux, mais ils signifient aussi que chaque admission dans le cache peut être volumineuse. Vous pouvez polluer l’ARC plus vite avec des charges à grands records.
  9. La dédup consomme du cache même si vous n’« utilisez » pas le cache. Si vous activez la dédup, le DDT demande de la mémoire. C’est un axe de douleur séparé que primarycache ne résoudra pas.

Patrons de charge : quand utiliser all, metadata ou none

primarycache=all (par défaut) : pour les charges qui réutilisent réellement les données

Conservez all lorsque les données du dataset sont réutilisées. Cas courants :

  • Images de VM où les invités touchent régulièrement les mêmes blocs
  • Bases de données avec tables et index « chauds » qui tiennent partiellement en mémoire
  • Répertoires personnels et arbres de code source où les métadonnées et petits fichiers se répètent
  • Services sensibles à la latence qui lisent les mêmes configs/templates à répétition

Signal opérationnel : le taux de hits ARC devrait se corréler aux améliorations de latence perçues par les utilisateurs. Si vous redémarrez et que le système « chauffe » sur des minutes à des heures, l’ARC paye son loyer.

primarycache=metadata : le point d’équilibre sous-estimé

metadata est ma réponse par défaut pour « ce dataset est grand, majoritairement séquentiel, mais a besoin tout de même de lister des répertoires et de traverser des snapshots sans souffrir ».

Bonnes correspondances :

  • Dépôts de sauvegarde où les restaurations sont occasionnelles mais les listings et opérations sur snapshots fréquents
  • Archives média volumineuses : beaucoup de gros fichiers, peu de relectures, mais vous parcourez quand même
  • Data lakes où des nœuds de calcul parcourent les données en streaming une fois par job

Ce que vous obtenez : l’ARC stocke le cerveau du système de fichiers, pas le corps en masse. Les parcours d’annuaire, les diffs de snapshots et les opérations lourdes en métadonnées restent rapides, tandis que les lectures en streaming n’évinceront pas vos vraies données chaudes d’autres datasets.

primarycache=none : le réglage « quarantaine »

none n’est pas maléfique. C’est une zone de quarantaine pour les datasets que vous savez toxiques pour le cache.

Bonne utilisation :

  • Espaces scratch pour des exports batch une fois
  • Zones de staging pour des réceptions de réplication (selon le workflow)
  • Pipelines d’ingestion en masse où les données sont écrites une fois puis expédiées ailleurs

Quand ça se retourne contre vous : petites lectures aléatoires, charges lourdes en métadonnées, ou tout ce qui fait des relectures. Si vous définissez none sur un dataset de VM parce que « l’ARC est gros », attendez-vous à des misères d’IOPS et à des collègues déconcertés.

Blague #2 (courte et pertinente) : Mettre primarycache=none sur votre dataset de VM, c’est comme enlever les chaises pour empêcher les gens de s’asseoir — techniquement efficace, socialement catastrophique.

Trois mini-récits du monde corporate (succès, échecs et un héros ennuyeux)

Mini-récit #1 : L’incident causé par une mauvaise hypothèse (ARC ≠ « RAM gratuite »)

Dans une entreprise de taille moyenne exploitant un cluster de virtualisation mixte, un hôte a commencé à swapper pendant les heures ouvrables. Les graphes semblaient étranges : beaucoup de mémoire « disponible », mais le noyau récupérait agressivement, et la latence sur une API client a grimpé.

La personne d’astreinte a fait ce que beaucoup font sous pression : a regardé la taille de l’ARC, a vu qu’elle était grande, et en a conclu « ZFS vole la RAM ». Ils ont abaissé le max de l’ARC sur l’hôte et sont passés à autre chose. Le lendemain fut plus calme — jusqu’à la sauvegarde hebdomadaire.

Pendant la fenêtre de sauvegarde, l’hôte devait servir des lectures de VM et écrire un flux de sauvegarde séquentiel massif à partir d’un dataset qui stockait aussi des disques VM (parce que « stockage c’est stockage »). L’ARC, maintenant contraint, avait moins de place pour l’ensemble chaud des VM. Le job de sauvegarde a quand même streamé, mais les VM ont ralenti, et les plaintes sur la latence de l’API sont revenues.

La mauvaise hypothèse n’était pas « l’ARC utilise de la RAM ». La mauvaise hypothèse était « la taille de l’ARC est le problème ». Le vrai problème était qu’un workload de sauvegarde polluait l’ARC et évincait l’ensemble chaud des VM. La solution n’a pas été de priver l’ARC ; c’était d’isoler la politique : déplacer les sauvegardes vers leur propre dataset et définir primarycache=metadata là-bas. L’ARC est resté grand, mais désormais il stockait des choses utiles.

Après ce changement, la latence API est redevenue ennuyeuse. Le job de sauvegarde hebdomadaire a continué. Pas d’héroïsme. Juste une propriété de dataset qui a transformé un cache chaotique en cache discipliné.

Mini-récit #2 : L’optimisation qui a mal tourné (trop zélé « metadata-only »)

Une autre organisation avait un ingénieur stockage amateur de règles propres. Il a décidé : « Les données de la base ne doivent jamais être mises en cache parce que la base a son propre cache. » Ils ont mis primarycache=metadata sur le dataset hébergeant un cluster PostgreSQL, en s’attendant à libérer l’ARC pour des « choses plus importantes ».

Sur le papier, cela semblait défendable. En réalité, cela ignorait deux détails : (1) toutes les lectures de la base ne sont pas des hits dans le buffer-cache, surtout lors de déploiements et migrations, et (2) le cache de la base est contraint par la mémoire configurée et la concurrence de charge, pas par des souhaits.

Ils ne l’ont pas remarqué immédiatement. La latence a augmenté progressivement pendant les heures de reporting, puis a grimpé pendant une migration de schéma qui a forcé de grosses lectures d’index. La demande d’IOPS du système a fortement augmenté, mais le pool était à base de disques durs avec des performances aléatoires limitées. L’ARC aurait pu absorber une partie de cela. À la place, chaque miss de cache est devenu un événement disque.

Le retour en arrière fut simple : remettre le dataset en primarycache=all (et ajuster d’autres réglages plus judicieusement). La leçon : « les applis cachent aussi » ne signifie pas que le cache du système de fichiers est redondant. Cela signifie qu’il faut comprendre la hiérarchie des caches et les modes de défaillance, surtout lors d’opérations atypiques comme migrations, VACUUM ou redémarrages à froid.

Mini-récit #3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (frontières de politique + tests)

Une équipe d’entreprise exploitait un objet-store soutenu par ZFS et un ensemble d’exports NFS sur le même pool. La charge de l’objet-store était majoritairement de grosses lectures/écritures séquentielles ; les exports NFS généraient beaucoup de churn de petites métadonnées dû au CI.

Ils ont fait quelque chose de profondément non sexy : ils ont créé des datasets séparés pour chaque classe de charge, documenté les propriétés prévues et les ont appliquées via un simple contrôle de configuration dans leur pipeline de provisioning. Pour les datasets de l’objet-store : primarycache=metadata. Pour les datasets NFS CI : primarycache=all. Pour le staging éphémère : primarycache=none.

Des mois plus tard, une équipe interne a lancé un nouveau job d’analytics qui lisait des dizaines de téraoctets en séquentiel pendant la journée. Dans beaucoup d’entreprises, c’est le début d’une guerre. Ici, ce fut un non-événement : le job tournait sur un dataset déjà mis en quarantaine pour le caching des données. La ferme CI est restée rapide. Les exports NFS n’ont pas ralenti. Le seul « incident » fut quelqu’un demandant pourquoi rien n’a pris feu.

Voilà le vrai gain : la correction ennuyeuse. Pas une manœuvre héroïque à minuit sur des sysctl, mais un ensemble de frontières qui rendent les charges imprévisibles moins dangereuses.

Tâches pratiques : commandes, interprétation et quoi changer

Ci-dessous des tâches pratiques que vous pouvez exécuter sur un système OpenZFS typique (exemples Linux). Les commandes sont réalistes ; adaptez les noms de dataset/pool.

Task 1: List datasets and find primarycache settings

cr0x@server:~$ zfs get -r -o name,property,value,source primarycache tank
NAME                 PROPERTY      VALUE     SOURCE
tank                 primarycache  all       default
tank/vm              primarycache  all       local
tank/backup          primarycache  metadata  local
tank/backup/staging  primarycache  none      local

Interprétation : Cherchez une héritage inattendu. Si un parent a none, tout ce qui est en dessous peut souffrir silencieusement. La colonne SOURCE vous indique si c’est local, hérité ou par défaut.

Task 2: Check secondarycache too (L2ARC admission)

cr0x@server:~$ zfs get -r -o name,property,value,source secondarycache tank
NAME                 PROPERTY        VALUE     SOURCE
tank                 secondarycache  all       default
tank/backup          secondarycache  metadata  local

Interprétation : Une combinaison sensée courante pour les sauvegardes est primarycache=metadata et secondarycache=metadata, gardant ARC et L2ARC concentrés sur les métadonnées.

Task 3: Change primarycache safely on one dataset

cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup

Interprétation : Cela change les admissions futures dans l’ARC. Cela ne purge pas instantanément les données déjà mises en cache de ce dataset. Attendez-vous à des changements de comportement au fil du temps à mesure que l’ARC se renouvelle.

Task 4: Verify your change and see inheritance

cr0x@server:~$ zfs get -o name,value,source primarycache tank/backup tank/backup/staging
NAME                VALUE     SOURCE
tank/backup         metadata  local
tank/backup/staging none      local

Interprétation : Confirmez que le dataset qui vous intéresse n’hérite pas de quelque chose que vous auriez oublié.

Task 5: Identify “cache-toxic” datasets by workload (top talkers)

cr0x@server:~$ zfs iostat -v tank 5 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        48.2T  21.7T      -      -      -      -
  raidz2-0                  48.2T  21.7T    980    430  1.20G   210M
    sda                         -      -    120     55   150M    28M
    sdb                         -      -    115     54   148M    27M
    ...
--------------------------  -----  -----  -----  -----  -----  -----

cr0x@server:~$ zfs iostat -v tank/backup 5 3
                              capacity     operations     bandwidth
dataset                     alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank/backup                 22.1T      -    820     40  1.05G    18M
--------------------------  -----  -----  -----  -----  -----  -----

Interprétation : Si un dataset effectue de larges lectures/écritures séquentielles, il est un candidat idéal pour primarycache=metadata ou none selon la réutilisation.

Task 6: Observe ARC size and basic behavior

cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep "^(size|c |c_min|c_max|hits|misses|mru_hits|mfu_hits|demand_data_hits|demand_data_misses|demand_metadata_hits|demand_metadata_misses) " | head -n 20
size                            4    68719476736
c                               4    82463372032
c_min                           4    17179869184
c_max                           4    103079215104
hits                            4    9812334401
misses                          4    554332110
mru_hits                        4    431228001
mfu_hits                        4    9272116400
demand_data_hits                4    7122331100
demand_data_misses              4    431002200
demand_metadata_hits            4    2690000000
demand_metadata_misses          4    12329910

Interprétation : Ne vénérez pas un taux de hit unique. Comparez demand data vs demand metadata. Si les misses de métadonnées sont élevés et que la latence souffre, vous avez probablement besoin de plus de cache métadonnées, pas moins.

Task 7: Watch ARC behavior over time during a streaming job

cr0x@server:~$ for i in {1..5}; do
> awk '
> /^(size|hits|misses|demand_data_hits|demand_data_misses|demand_metadata_hits|demand_metadata_misses)/ {print}
> ' /proc/spl/kstat/zfs/arcstats | paste - - - - - - -;
> echo "----";
> sleep 5;
> done
size	4	69123481600	hits	4	9813334401	misses	4	554532110	demand_data_hits	4	7123331100	demand_data_misses	4	431202200	demand_metadata_hits	4	2690100000	demand_metadata_misses	4	12339910
----
size	4	69811097600	hits	4	9815334401	misses	4	555032110	demand_data_hits	4	7127331100	demand_data_misses	4	431802200	demand_metadata_hits	4	2690200000	demand_metadata_misses	4	12349910
----

Interprétation : Pendant une lecture en streaming, vous pouvez voir les misses de data augmenter tandis que les hits n’aident pas significativement par la suite. C’est la signature pour « arrêter de mettre en cache les données de ce dataset ».

Task 8: Check memory pressure and swapping (is ARC competing with your apps?)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           251Gi       182Gi       4.1Gi       2.0Gi        65Gi        18Gi
Swap:           16Gi       1.2Gi        14Gi

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 4  0 123456 4200000  90000 61200000   0   0   120   900 3200 8800 28 10 59  3  0
 5  0 123456 3800000  90000 60800000   0   0   110   950 3300 9100 27 11 58  4  0

Interprétation : L’utilisation du swap plus une faible mémoire « available » est un signe que vous devez revoir la politique de cache. L’ARC peut être gros et toujours acceptable, mais si vos applis paginent, vous payez le loyer deux fois.

Task 9: Determine whether a dataset is primarily sequential (quick sampling)

cr0x@server:~$ iostat -x 1 3
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   w_await wareq-sz  aqu-sz  %util
sda             120.0  155000.0     0.2    0.2    7.10   1291.7    55.0   28500.0    9.80   518.2     0.92   88.0

Interprétation : Un grand rareq-sz et un haut débit suggèrent du streaming. C’est là que primarycache=metadata gagne souvent, surtout si les relectures sont rares.

Task 10: Measure a real before/after with a cold-ish cache (careful)

Ceci est dangereux en production si vous êtes imprudent. Mais vous pouvez tester avec un fichier spécifique et des lectures contrôlées. Utilisez un dataset non critique ou un hôte de test.

cr0x@server:~$ sudo zfs set primarycache=all tank/testseq
cr0x@server:~$ dd if=/tank/testseq/bigfile.bin of=/dev/null bs=16M status=progress
21474836480 bytes (21 GB, 20 GiB) copied, 22 s, 976 MB/s

cr0x@server:~$ sudo zfs set primarycache=metadata tank/testseq
cr0x@server:~$ dd if=/tank/testseq/bigfile.bin of=/dev/null bs=16M status=progress
21474836480 bytes (21 GB, 20 GiB) copied, 22 s, 974 MB/s

Interprétation : Si les performances ne changent pas mais que la pollution de l’ARC diminue, vous avez trouvé de l’argent gratuit. Les lectures en streaming bénéficient souvent peu du cache, mais elles peuvent évincer des données précieuses.

Task 11: Inspect dataset properties that amplify cache pollution risk

cr0x@server:~$ zfs get -o name,property,value recordsize,compression,atime,logbias,primarycache tank/backup
NAME        PROPERTY      VALUE     SOURCE
tank/backup recordsize    1M        local
tank/backup compression   zstd      local
tank/backup atime         off       local
tank/backup logbias       throughput local
tank/backup primarycache  metadata  local

Interprétation : Un grand recordsize est parfait pour les sauvegardes, mais cela signifie aussi que chaque record mis en cache est énorme. Associer de gros records avec primarycache=metadata est souvent le moyen le plus sensé d’empêcher la RAM de se transformer en musée de sauvegardes.

Task 12: Find datasets with inherited “none” (the silent performance killer)

cr0x@server:~$ zfs get -r -H -o name,value,source primarycache tank | awk '$2=="none" || $3=="inherited" {print}'
tank/staging none local
tank/staging/tmp none inherited

Interprétation : Si quelque chose d’important hérite de none, c’est une politique de performance accidentelle. Corrigez au bon niveau (override sur l’enfant ou correction du parent).

Task 13: Confirm ARC limits (don’t fight the OS blindly)

cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_max
109951162777

cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_min
17179869184

Interprétation : Si vous avez contraint l’ARC trop sévèrement, vous pouvez forcer des lectures disque pour des métadonnées chaudes. Utilisez primarycache pour améliorer la qualité de l’ARC avant de réduire sa taille.

Task 14: Validate an intended policy on a tree (drift detection)

cr0x@server:~$ zfs get -r -o name,value,source primarycache tank | egrep "tank/(vm|backup|staging)"
tank/vm all local
tank/backup metadata local
tank/staging none local

Interprétation : C’est l’habitude SRE : vérifier que la réalité correspond à votre modèle mental. La plupart des désastres de cache viennent du « on croyait que c’était configuré » plutôt que d’une mauvaise théorie.

Guide de diagnostic rapide

C’est le chemin « c’est lent et les gens vous regardent ». Il est ordonné pour trouver rapidement le goulot probable, pas pour être académiquement complet.

Step 1: Is the system memory-starved or swapping?

cr0x@server:~$ free -h
cr0x@server:~$ vmstat 1 5
cr0x@server:~$ swapon --show

Ce que vous cherchez : faible mémoire available, activité swap-in/swap-out et symptômes comme des pauses aléatoires. Si du swapping se produit, décidez si la pollution de l’ARC (primarycache) remplit la mémoire ou si vous avez réellement besoin de plus de RAM.

Step 2: Is the pool I/O-bound or CPU-bound?

cr0x@server:~$ iostat -x 1 5
cr0x@server:~$ zpool iostat -v 1 5

Ce que vous cherchez : haut %util du device, forte await et files d’attente. Si les disques sont saturés, le tuning de l’ARC n’aidera que si vous pouvez augmenter le hit rate pour les données/métadonnées pertinentes. Si le CPU est saturé (compression, checksums), changer le cache ne résoudra pas la saturation processeur.

Step 3: Is ARC helping the workload you care about?

cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep "^(hits|misses|demand_data_hits|demand_data_misses|demand_metadata_hits|demand_metadata_misses|size|c )"

Ce que vous cherchez : Les misses de métadonnées sont-elles élevées ? Les misses de data augmentent-ils pendant un job de streaming ? Si oui, envisagez de définir primarycache=metadata sur le dataset qui réalise l’I/O en streaming.

Step 4: Identify the dataset causing churn

cr0x@server:~$ zfs iostat -v tank 2 10

Ce que vous cherchez : Quel dataset/pool pousse la bande passante/IOPS. Cartographiez-le au contexte métier (sauvegardes, analytics, stockage VM) et appliquez la politique de cache en conséquence.

Step 5: Apply a targeted change (small blast radius)

cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup

Ce que vous cherchez : Réduction du churn ARC et meilleure stabilité pour les autres workloads. Ne réduisez pas d’abord le max de l’ARC à moins d’être en train de mourir ; arrêtez d’abord ce qui est admis avant de réduire la taille du cache.

Erreurs courantes (symptômes et correctifs)

Mistake 1: Setting primarycache=none on “storage that is slow” (and making it slower)

Symptômes : Les tempêtes de boot de VM empirent, les petites lectures aléatoires voient leur latence augmenter, les opérations lourdes en métadonnées ramollissent, le taux de hits ARC chute pour les lectures significatives.

Correction : Restaurez primarycache=all pour les datasets avec réutilisation (VM, bases), et mettez en quarantaine seulement les datasets de streaming. Si vous devez protéger l’ARC des scans, choisissez primarycache=metadata sur les datasets de sauvegarde/archive au lieu de supprimer totalement le cache.

Mistake 2: Applying cache policy at the wrong level (inheritance foot-gun)

Symptômes : « On a changé mais rien n’a amélioré » ou « pourquoi le datastore VM a changé de comportement ? »

Correction : Utilisez zfs get -r ... source. Corrigez le dataset parent si la politique doit être large ; override sur l’enfant si c’est une exception. Documentez l’arbre d’héritage prévu.

Mistake 3: Confusing primarycache with secondarycache

Symptômes : Vous ajoutez un device L2ARC et voyez peu de bénéfice ; l’ARC se remplit quand même de déchets.

Correction : Rappelez-vous : primarycache contrôle l’admission dans l’ARC, secondarycache contrôle l’admission dans le L2ARC. Pour les datasets de streaming, pensez primarycache=metadata et secondarycache=metadata, pas « L2ARC va me sauver ».

Mistake 4: Over-optimizing for hit ratio instead of latency

Symptômes : Le tableau de bord des métriques semble « mieux » après un changement, mais les utilisateurs se plaignent davantage.

Correction : Traitez le taux de hits comme un indice, pas comme un KPI. Concentrez-vous sur les misses de metadata à la demande, la latence de queue (tail latency) et la profondeur de file disque. Cachez des données non pertinentes et vous pouvez gonfler les hits tout en affamant le vrai workload.

Mistake 5: Ignoring recordsize and workload alignment

Symptômes : L’ARC grossit rapidement lors d’opérations en masse ; la pression mémoire monte ; peu de gain en performance.

Correction : Gros recordsize plus lectures en streaming signifie souvent « pollution du cache à grande échelle ». Utilisez primarycache=metadata sur ces datasets, et gardez de grands records là où ils appartiennent (backup/archive), pas sur des workloads I/O aléatoires.

Mistake 6: Fighting ARC max/min before fixing admission quality

Symptômes : Vous réduisez le max de l’ARC pour « libérer de la mémoire », mais la performance devient instable, les misses de métadonnées augmentent et l’I/O disque augmente.

Correction : D’abord, arrêtez d’admettre des déchets avec primarycache. Ensuite, si vous devez encore limiter l’ARC pour des workloads co-localisés, faites-le prudemment et validez le comportement des misses de métadonnées.

Checklists / plan étape par étape

Checklist A: Decide the right primarycache value for a dataset

  1. Classifiez le dataset : VM/bases, partages de fichiers généraux, sauvegardes, archive, scratch.
  2. Posez une question brutale : « Relisons-nous les mêmes blocs de données dans les heures qui suivent ? »
  3. Si oui : commencez par primarycache=all.
  4. Si majoritairement non, mais que les opérations sur métadonnées comptent : choisissez primarycache=metadata.
  5. Si c’est jetable / un seul passage : considérez primarycache=none.
  6. Validez avec zfs iostat et les stats ARC pendant la fenêtre réelle du job.

Checklist B: Implement safely (minimal blast radius)

  1. Confirmez le chemin du dataset et l’arbre d’héritage :
    cr0x@server:~$ zfs list -r tank
    cr0x@server:~$ zfs get -r -o name,value,source primarycache tank
  2. Changez un seul dataset à la fois :
    cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup
  3. Surveillez 15–60 minutes (ou un run de job) :
    cr0x@server:~$ zfs iostat -v tank 5
    cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep "^(size|hits|misses|demand_metadata_misses|demand_data_misses)"
  4. Consignez le résultat : latence, swap, await disque, comportement ARC.
  5. Si ça a aidé, codifiez-le (infra-as-code, templates de provisioning ou runbook).

Checklist C: Separate workloads so caching policy can work

  1. Créez des datasets par classe de workload, pas par organigramme.
  2. Gardez les sauvegardes/archives en dehors de l’arbre des VM.
  3. Appliquez primarycache à la limite du dataset où le workload est homogène.
  4. Auditez trimestriellement pour « quelqu’un a déposé une nouvelle charge dans le mauvais dataset ».

FAQ

1) Does primarycache change affect existing cached data immediately?

Non. Cela affecte ce qui est admis à partir de maintenant. Le contenu existant de l’ARC vieillira naturellement au fil du renouvellement du cache. Si vous avez besoin d’un impact immédiat, l’approche opérationnelle est généralement « attendre le churn » ou planifier les changements avant le job intensif, plutôt que de purger de force les caches en production.

2) Should I set primarycache=metadata for all backups?

Souvent oui, car les sauvegardes sont des flux classiques « lus une fois » qui peuvent polluer l’ARC, tandis que les métadonnées sont utiles pour la navigation, la gestion des snapshots et la sélection des restaurations. Mais si vous restaurez régulièrement le même sous-ensemble (restores de test, restaurations partielles fréquentes), vous pourriez bénéficier de la mise en cache de certaines données — mesurez-le.

3) Is primarycache=none ever correct?

Oui, pour des datasets vraiment jetables (scratch, staging) ou pour des workflows où le caching nuit activement à des workloads plus importants. L’erreur est d’utiliser none comme réponse générique « ZFS utilise trop de RAM ».

4) How do primarycache and secondarycache differ in plain terms?

primarycache décide ce qui peut aller en RAM (ARC). secondarycache décide ce qui peut aller en L2ARC (généralement SSD). Vous pouvez dire à ZFS « metadata only » pour les deux afin de garder les couches de cache concentrées sur la structure du système de fichiers plutôt que sur les données en masse.

5) Will setting primarycache=metadata speed up writes?

Pas directement. Il s’agit du caching en lecture. Cependant, indirectement, cela peut améliorer la stabilité globale du système et réduire la pression mémoire, ce qui peut aider des systèmes à forte écriture qui souffraient de reclaim ou de swapping. Les écritures sont plus influencées par le comportement du ZIL/SLOG, le tuning des transaction groups, la latence des devices et les réglages sync.

6) If my application has its own cache (Redis, PostgreSQL shared_buffers), should I disable ARC data caching?

Pas automatiquement. Beaucoup d’applications cachent bien certaines choses et mal d’autres, et elles ne couvrent pas le cold-start ou les opérations atypiques. Le cache du système de fichiers aide souvent pour les index, binaires, bibliothèques partagées et activités OS. Traitez primarycache comme un outil de workload : testez les changements pendant des opérations réalistes (redémarrages, migrations et pics de lecture inclus).

7) Why does ARC still grow if I set primarycache=metadata on a big dataset?

Parce que d’autres datasets peuvent toujours mettre des données en cache, les métadonnées peuvent être substantielles, et l’ARC contient aussi différentes structures internes. De plus, la croissance de l’ARC n’est pas intrinsèquement mauvaise ; elle devient problématique quand elle évince des consommateurs de mémoire plus précieux ou quand elle est remplie de blocs à faible valeur.

8) What’s the simplest “good default” policy for a mixed pool?

Gardez primarycache=all pour VM/bases et partages interactifs. Utilisez primarycache=metadata pour sauvegardes/archives. Utilisez primarycache=none pour le scratch/staging vraiment en un seul passage. L’astuce réelle n’est pas tant dans les valeurs que dans la séparation des workloads en datasets clairs.

9) Can I fix ARC pollution without changing primarycache?

Parfois. L’ARC est conçu pour résister à la pollution par scan, et vous pouvez aussi gérer les caps de taille de l’ARC. Mais primarycache est la façon la plus propre d’appliquer une politique d’admission par dataset. Si le problème est « ce dataset ne doit jamais consommer de RAM pour les données », ne comptez pas sur des heuristiques globales pour deviner cela.

10) How do I know if metadata caching is the bottleneck?

Regardez les demand metadata misses dans arcstats et corrélez avec la latence pendant les parcours de répertoire, les opérations sur snapshots et les workloads de petits fichiers. Si les misses de métadonnées augmentent et que les disques sont occupés, votre « problème de cache » est probablement les métadonnées, pas les données en masse.

Conclusion

primarycache est l’une de ces propriétés ZFS qui ressemble à une préférence mineure jusqu’à ce que vous exploitiez un pool à charges mixtes assez longtemps. Ensuite, elle devient une frontière : la différence entre l’ARC en tant que niveau de performance à haute QI et l’ARC en tant que collectionneur avec un espace de placard illimité.

Le jeu pratique est simple : séparez les workloads en datasets sensés, gardez primarycache=all là où les données sont réellement réutilisées, et utilisez primarycache=metadata (ou parfois none) pour empêcher les workloads en streaming et jetables d’évincer votre vrai ensemble chaud. Faites-le avec des mesures, pas des impressions. En production, « ennuyeux et correct » bat « intelligent et global » à tous les coups.

← Précédent
Résoudre l’erreur « Authentication Failed » de Proxmox PBS : jetons, autorisations et empreintes
Suivant →
PostgreSQL vs SQLite — Concurrence d’écriture : qui gagne et pourquoi

Laisser un commentaire