ZFS secondarycache : quand L2ARC ne doit rien mettre en cache

Cet article vous a aidé ?

L2ARC a une réputation : « Ajoutez un SSD et votre pool ZFS devient rapide. » Parfois c’est vrai. Parfois c’est l’équivalent stockage de mettre un aileron sur un fourgon de livraison.
Le secret désagréable est que la meilleure configuration L2ARC pour de nombreux systèmes réels est… de ne rien mettre en cache du tout.

Cet article porte sur secondarycache — la propriété de dataset qui décide si des blocks sont éligibles pour L2ARC — et sur la réalité opérationnelle qui en découle.
Nous verrons pourquoi L2ARC peut être neutre, pourquoi il peut être activement nuisible, et comment prouver (pas deviner) ce qui se passe sur vos machines.

Ce que fait réellement secondarycache

ZFS utilise deux concepts de cache principaux que les gens confondent :

  • ARC (Adaptive Replacement Cache) : vit en RAM, met en cache automatiquement les données et métadonnées fréquemment et récemment utilisées.
  • L2ARC : une extension de l’ARC qui stocke sur un dispositif rapide (généralement SSD/NVMe) les blocks admissibles mis en cache.

La propriété qui détermine si les blocks d’un dataset peuvent aller dans L2ARC est secondarycache. C’est un réglage par dataset, héritable, et il contrôle ce que ZFS est autorisé à écrire dans L2ARC.

Valeurs que vous utiliserez réellement

  • secondarycache=all : les données et les métadonnées peuvent entrer dans L2ARC.
  • secondarycache=metadata : seules les métadonnées sont éligibles (souvent surprenamment efficace pour certains workloads).
  • secondarycache=none : rien de ce dataset n’est éligible pour L2ARC.

La conclusion : définir secondarycache=none n’est pas « désactiver le cache ». L’ARC continue de mettre en cache.
Vous dites seulement à ZFS : « N’utilise pas la bande passante et la gestion L2ARC pour ce dataset. »

Si vous avez déjà vu un système se dégrader parce que « nous avons ajouté du cache », vous savez pourquoi cela compte.
L2ARC n’est pas gratuit ; il consomme CPU, mémoire pour les métadonnées, et bande passante d’écriture pour se remplir.

Blague #1 : L2ARC, c’est comme un abonnement à la salle de sport — l’avoir ne vous rend pas plus rapide, bien l’utiliser peut.

Faits intéressants & un peu d’historique

Quelques éléments concrets qui expliquent pourquoi L2ARC se comporte comme il le fait aujourd’hui :

  1. L2ARC est arrivé tôt et a évolué lentement. Le design vient de l’époque Solaris, quand les SSD étaient plus petits, plus lents et beaucoup plus chers qu’aujourd’hui.
  2. L2ARC est un cache de lecture rempli par des écritures. Il est alimenté en copiant les buffers évincés de l’ARC vers le périphérique L2ARC — ce qui signifie qu’il consomme des I/O d’écriture même si votre workload est orienté lecture.
  3. Historiquement, le contenu L2ARC disparaissait au redémarrage. Longtemps, L2ARC démarrait « froid » après un redémarrage ; le réchauffement pouvait prendre des heures. Des L2ARC persistants sont apparus plus tard dans certaines implémentations, mais opérationnellement il faut encore prévoir un comportement de cache froid.
  4. L2ARC a besoin de RAM pour indexer ce qui est sur le disque. Le périphérique de cache est inutile sans en-têtes en mémoire qui indiquent ce qui est stocké où ; un gros L2ARC peut discrètement consommer une mémoire significative.
  5. Le prefetch de ZFS peut saboter un caching naïf. Les lectures séquentielles peuvent ramener des données qui semblent devoir être mises en cache mais qui ne seront jamais réutilisées ; L2ARC peut devenir un musée de blocks que vous n’avez regardés qu’une fois.
  6. L’ARC est généralement le meilleur cache que vous puissiez acheter. La latence et la bande passante de la RAM dominent encore, et l’ARC est plus intelligent que ce que l’on pense — surtout pour les workloads riches en métadonnées.
  7. L2ARC n’est pas un cache d’écriture. Les gens essaient encore de l’utiliser comme tel. C’est le rôle du SLOG (separate log) et même lui n’aide que les écritures synchrones.
  8. La compression change la donne. Mettre en cache des blocks compressés signifie plus de données logiques par octet, mais aussi des compromis CPU différents ; parfois la compression rend l’ARC tellement efficace que L2ARC devient marginal.
  9. Le caching des métadonnées peut être la vraie victoire. Les serveurs de fichiers et les magasins de VM bénéficient souvent plus du caching des blocks indirects, dnodes et répertoires que du caching du contenu des fichiers.

L2ARC en termes simples (et pourquoi c’est délicat)

L’ARC réside en RAM et garde ce qu’il pense que vous aurez besoin ensuite. Quand l’ARC manque d’espace, il évince des buffers.
Certains de ces buffers évincés peuvent être écrits dans L2ARC. Plus tard, si le même block est demandé à nouveau et qu’il n’est plus dans l’ARC,
ZFS peut le récupérer depuis L2ARC au lieu d’aller vers les disques du pool.

Ça a l’air simple jusqu’à ce que vous exécutiez des charges de production, parce que le mot « plus tard » cache beaucoup de choses dans cette phrase.
L2ARC n’aide que lorsque :

  • la charge implique des relectures des mêmes blocks,
  • l’ARC est trop petit pour les maintenir chauds,
  • le périphérique L2ARC est assez rapide pour être plus rapide que le pool,
  • et remplir le L2ARC ne vole pas des ressources nécessaires ailleurs.

Les deux coûts invisibles : trafic de remplissage et métadonnées

Le premier coût est le trafic de remplissage. L2ARC est peuplé par des écritures. Ces écritures entrent en compétition avec le travail normal du pool :
cycles CPU, bande passante mémoire, et parfois le même ordonnanceur I/O et budget d’interruptions.

Le deuxième coût est le surcharge de métadonnées. ZFS doit se souvenir de ce qui est sur L2ARC. Cet index vit en RAM. Si vous manquez déjà de mémoire
(la raison pour laquelle vous vouliez L2ARC initialement), ajouter un grand L2ARC peut être comme acheter un frigo plus grand et ensuite se plaindre que la cuisine est plus petite.

Pourquoi « tout mettre en cache » est rarement la meilleure politique

La plupart des datasets de production contiennent un mélange de patterns d’accès :

  • Lectures en flux/séquentielles (sauvegardes, médias, ETL) : catastrophique pour L2ARC.
  • Relire aléatoire (bases de données, images de VM, workloads de petits fichiers) : potentiellement excellent pour L2ARC.
  • Churn de métadonnées (serveurs de build, espaces CI) : parfois mieux servi par le caching metadata-only ou simplement plus de RAM.

La propriété secondarycache vous permet d’être sélectif.
Les meilleurs opérateurs que je connais n’« activent pas L2ARC » ; ils décident quels datasets sont autorisés à l’utiliser, et lesquels se voient poliment refuser l’accès.

Quand ne rien mettre en cache est le bon choix

« L2ARC ne devrait rien mettre en cache » ne signifie pas « L2ARC est inutile ». Cela signifie : pour un dataset particulier (ou un pool entier), le coût du caching dépasse le bénéfice.
Voici les scénarios où secondarycache=none n’est pas seulement raisonnable, c’est souvent la solution la plus propre.

1) Le workload est principalement séquentiel ou « one-and-done »

Pensez : cibles de sauvegarde, archives de logs, fichiers vidéo, ingestion de data lake, exports nocturnes. Le prefetch de ZFS lit en avance, l’ARC voit un flot de données, et L2ARC
commence à écrire les buffers évincés. Mais rien n’est relu. Vous avez construit un cache pour un workload qui ne se répète pas.

Symptômes :

  • L2ARC a beaucoup d’I/O d’écriture ; le taux de hits L2ARC reste faible.
  • Les disques du pool montrent une activité supplémentaire même si le « SSD de cache » est occupé.
  • La latence est pire pendant les scans lourds parce que le système effectue du travail supplémentaire.

2) Vous êtes contraint en mémoire et L2ARC vole la dernière bonne RAM

L’ARC a besoin de RAM. L’OS a besoin de RAM. Les applications ont besoin de RAM. L2ARC a aussi besoin de RAM (pour les en-têtes et la gestion).
Si vous êtes déjà proche du swapping ou si vous voyez l’ARC rétrécir constamment sous pression, ajouter L2ARC peut rendre les performances plus erratiques.

C’est là que secondarycache=none agit comme un scalpel : vous pouvez garder L2ARC pour les datasets qui en bénéficient vraiment, tout en empêchant le « thrash » de cache
des datasets en flux/streaming.

3) Le périphérique de cache est rapide, mais pas assez (ou pas assez constant)

Tous les SSD ne se valent pas. Certains semblent excellents dans une fiche technique puis s’effondrent sous écritures soutenues, workloads mixtes ou forte profondeur de file d’attente.
L2ARC génère des écritures soutenues. Si le comportement d’écriture du périphérique est mauvais, le remplissage L2ARC peut provoquer des pics de latence.

Si votre pool est déjà sur NVMe, un « NVMe de cache » peut ne pas être significativement plus rapide que le pool.
Dans ce cas, vous payez le coût L2ARC pour remplacer une lecture rapide par une autre lecture rapide. Ce n’est pas un crime, mais c’est du gaspillage.

4) Votre véritable goulot d’étranglement n’est pas la lecture

J’ai vu des équipes courir après les hits L2ARC alors que le vrai problème était :

  • la latence d’écriture synchrone (nécessite SLOG, alignement recordsize, ou changements applicatifs),
  • saturation CPU due à la compression/checksumming,
  • latence réseau,
  • fragmentation et petits record sizes,
  • ou simplement « trop de clients ».

Dans ces cas, L2ARC est une quête annexe. Fixer secondarycache=none sur des datasets non pertinents empêche L2ARC de consommer des ressources
pendant que vous corrigez le vrai problème.

5) Vous mettez en cache des données qui devraient être « spéciales » à la place

L2ARC est un cache de lecture. Un vdev spécial est différent : il peut stocker en permanence des métadonnées (et éventuellement des petits blocks) sur du média rapide.
Si votre douleur est la latence des métadonnées (parcours de répertoires, petites lectures aléatoires, métadonnées VM), l’accélération « correcte » peut être un vdev spécial — pas L2ARC.

Cela ne signifie pas « toujours déployer un vdev spécial ». Cela signifie : si vous utilisez L2ARC pour masquer un problème de métadonnées, vous payez peut-être un coût continu
pour un bénéfice qui pourrait être rendu structurel.

6) Vous n’avez pas de fenêtre de réutilisation stable

L2ARC brille lorsque votre jeu de travail est plus grand que la RAM mais possède encore de la localité — c’est‑à‑dire que les blocks sont relus dans les minutes/heures.
Certains workloads ont un working set plus grand que la RAM et aucune réutilisation utile. Vous ne pouvez pas cacher votre chemin hors de l’entropie.

Blague #2 : si votre workload est du streaming pur, L2ARC est essentiellement une boutique de souvenirs très chère — beaucoup d’inventaire, personne ne revient.

Mode d’intervention rapide (premier, deuxième, troisième)

Quand les performances sont mauvaises et que L2ARC est en jeu, vous avez besoin d’une routine de triage rapide et reproductible. Voici ce que je vérifie, dans l’ordre, quand quelqu’un signale « le stockage est lent »
et qu’un périphérique L2ARC est fièrement installé.

Premier : est-ce un problème de lecture, d’écriture, ou de latence ?

  • Regardez la latence du pool et les opérations : les lectures sont-elles lentes, les écritures lentes, ou les deux ?
  • Vérifiez si l’application est bloquée sur l’I/O ou sur le CPU.
  • Confirmez si la dégradation coïncide avec des scans séquentiels, sauvegardes, réplications ou scrubs.

Deuxième : l’ARC fait-il déjà le travail ?

  • Si le taux de hit de l’ARC est élevé et stable, L2ARC n’aidera souvent pas.
  • Si l’ARC est sous pression mémoire, corrigez cela avant « d’ajouter du cache ».

Troisième : est-ce que L2ARC sert réellement des lectures (hits), ou est‑il juste écrit ?

  • Vérifiez le taux de hits L2ARC, le rythme d’alimentation et le ratio lectures/écritures L2ARC.
  • Si L2ARC est occupé à écrire mais ne produit pas de hits, bridez son admission avec secondarycache.

Quatrième : le périphérique de cache se comporte-t-il mal ?

  • Surveillez la latence du périphérique pendant le remplissage.
  • Cherchez une utilisation soutenue élevée, de la mise en file d’attente, ou des blocages périodiques.
  • Vérifiez qu’il ne partage pas des lanes/contrôleurs avec d’autres périphériques chauds.

Cinquième : restreindre le périmètre

  • Définissez secondarycache=none sur les datasets évidemment en streaming en premier.
  • Conservez le caching des métadonnées pour les datasets où cela aide.
  • Mesurez à nouveau. Ne vous fiez pas à votre ressenti.

Tâches pratiques : commandes + interprétation

L’objectif ici n’est pas « exécuter ces commandes une fois ». C’est d’acquérir une habitude : observer, changer une chose, observer à nouveau.
Ci‑dessous des tâches pratiques à réaliser sur un hôte ZFS réel. Les sorties varient selon la plateforme, mais l’intention et l’interprétation restent valides.

Task 1: Confirm which datasets are eligible for L2ARC

cr0x@server:~$ zfs get -r -o name,property,value,source secondarycache tank
NAME                 PROPERTY        VALUE     SOURCE
tank                 secondarycache  all       default
tank/backups         secondarycache  all       inherited from tank
tank/vmstore         secondarycache  all       inherited from tank
tank/logarchive      secondarycache  all       inherited from tank

Interprétation : si vous voyez des datasets en streaming comme tank/logarchive héritant de all, c’est un signal d’alerte.
Vous autorisez L2ARC à ingérer les données les moins cacheables du système.

Task 2: Set streaming datasets to cache nothing

cr0x@server:~$ sudo zfs set secondarycache=none tank/logarchive
cr0x@server:~$ sudo zfs set secondarycache=none tank/backups

Interprétation : cela ne purge pas l’ARC ; cela empêche simplement les nouveaux blocks de ces datasets d’être admis dans L2ARC à l’avenir.
Vous réduisez le churn futur sur L2ARC.

Task 3: Try metadata-only caching for “lots of filenames, not much reread data”

cr0x@server:~$ sudo zfs set secondarycache=metadata tank/home
cr0x@server:~$ zfs get secondarycache tank/home
NAME       PROPERTY        VALUE     SOURCE
tank/home  secondarycache  metadata  local

Interprétation : le mode metadata-only est un compromis pragmatique. Pour les serveurs de fichiers, il peut améliorer les parcours de répertoires et les workloads riches en stat
sans remplir L2ARC avec des contenus de fichiers qui ne seront pas relus.

Task 4: Verify you actually have an L2ARC device attached

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
config:

        NAME                         STATE     READ WRITE CKSUM
        tank                         ONLINE       0     0     0
          raidz2-0                   ONLINE       0     0     0
            sda                      ONLINE       0     0     0
            sdb                      ONLINE       0     0     0
            sdc                      ONLINE       0     0     0
            sdd                      ONLINE       0     0     0
        cache
          nvme0n1p1                  ONLINE       0     0     0

errors: No known data errors

Interprétation : les périphériques L2ARC apparaissent sous cache. S’il n’y a pas de vdev cache, secondarycache ne compte qu’en vue d’une politique future.

Task 5: Check whether the pool is IOPS-bound or bandwidth-bound

cr0x@server:~$ zpool iostat -v tank 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        6.12T  5.88T    410    120  52.1M  14.2M
  raidz2-0                  6.12T  5.88T    410    120  52.1M  14.2M
    sda                         -      -     80     25  10.6M  3.1M
    sdb                         -      -     85     24  10.5M  3.0M
    sdc                         -      -     78     22  10.2M  2.8M
    sdd                         -      -     82     25  10.8M  3.2M
--------------------------  -----  -----  -----  -----  -----  -----

Interprétation : si vous voyez une grande bande passante avec peu d’IOPS, ça sent la lecture séquentielle. C’est là où L2ARC est souvent inutile.
Si les IOPS sont élevés et que les lectures sont aléatoires, L2ARC peut aider — si la réutilisation existe.

Task 6: Watch system-level I/O latency and identify the actual pain

cr0x@server:~$ iostat -x 1 5
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.30    0.00    4.10    8.90    0.00   74.70

Device            r/s   w/s   rkB/s   wkB/s  await  r_await  w_await  %util
sda              95.0  28.0  11520.0  3200.0  18.2    16.9     22.1   96.0
nvme0n1           5.0  900.0    80.0 51200.0   2.1     1.2      2.2   82.0

Interprétation : regardez await et %util. Ici le NVMe effectue beaucoup d’écritures (probablement alimentation L2ARC),
tandis que les HDD sont proches de la saturation. Si L2ARC ne produit pas de hits, ces écritures ne sont que du travail supplémentaire.

Task 7: Check ARC and L2ARC counters (common on Linux with OpenZFS)

cr0x@server:~$ kstat -p zfs:0:arcstats:hits zfs:0:arcstats:misses zfs:0:arcstats:l2_hits zfs:0:arcstats:l2_misses
zfs:0:arcstats:hits                             389002134
zfs:0:arcstats:misses                            54200121
zfs:0:arcstats:l2_hits                             120934
zfs:0:arcstats:l2_misses                          3812490

Interprétation : les hits ARC éclatent les misses (bon), et les hits L2ARC sont minuscules comparés aux l2 misses (mauvais). Dans cette configuration, L2ARC n’apporte pas de valeur.
Vous devriez envisager de restreindre secondarycache et/ou retirer complètement L2ARC s’il génère une surcharge.

Task 8: Calculate quick-and-dirty hit ratios

cr0x@server:~$ python3 - <<'PY'
hits=389002134
misses=54200121
l2hits=120934
l2misses=3812490
print("ARC hit ratio:", hits/(hits+misses))
print("L2ARC hit ratio:", l2hits/(l2hits+l2misses))
PY
ARC hit ratio: 0.8777331151094286
L2ARC hit ratio: 0.03076857030623538

Interprétation : un taux d’environ 3% pour L2ARC est un fort indice que le périphérique agit surtout comme un puits d’écriture pour les buffers évincés.
Il existe des cas particuliers, mais en pratique cela signifie généralement « arrêtez de le nourrir avec des déchets ».

Task 9: Watch L2ARC feed rate and size (platform-dependent, still useful)

cr0x@server:~$ kstat -p zfs:0:arcstats:l2_size zfs:0:arcstats:l2_asize zfs:0:arcstats:l2_write_bytes zfs:0:arcstats:l2_read_bytes
zfs:0:arcstats:l2_size                           81234165760
zfs:0:arcstats:l2_asize                          79877812224
zfs:0:arcstats:l2_write_bytes                   918230012928
zfs:0:arcstats:l2_read_bytes                      9112346624

Interprétation : près d’un téraoctet écrit, et seulement ~9 Go relus. Ce n’est pas un « cache », c’est un tapis roulant.
Cela implique aussi une usure du périphérique de cache pour très peu de retour.

Task 10: Identify datasets causing heavy reads (to decide who gets L2ARC)

cr0x@server:~$ sudo zpool iostat -r -v tank 1 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        6.12T  5.88T    900    140  95.0M  16.0M
  raidz2-0                  6.12T  5.88T    900    140  95.0M  16.0M
--------------------------  -----  -----  -----  -----  -----  -----

Interprétation : selon votre build OpenZFS, vous n’aurez peut‑être pas d’iostat par dataset ici.
Si vous disposez d’outils par dataset dans votre environnement, utilisez‑les ; sinon corrélez avec les logs applicatifs et les patterns d’accès.
Le point opérationnel : choisissez des gagnants et des perdants pour secondarycache plutôt que d’activer tout.

Task 11: Inspect recordsize and see whether you’re caching giant blocks you never reread

cr0x@server:~$ zfs get -o name,property,value recordsize tank/backups tank/vmstore
NAME         PROPERTY    VALUE   SOURCE
tank/backups recordsize  1M      local
tank/vmstore recordsize  128K    local

Interprétation : un dataset de sauvegarde avec recordsize=1M est souvent large, séquentiel, et non relu.
Mettre en cache des blocks de 1 Mo dans L2ARC est rarement un gain. C’est un candidat parfait pour secondarycache=none.

Task 12: Make the change safely, then validate with before/after snapshots of counters

cr0x@server:~$ kstat -p zfs:0:arcstats:l2_hits zfs:0:arcstats:l2_misses zfs:0:arcstats:l2_write_bytes > /tmp/l2arc.before
cr0x@server:~$ sudo zfs set secondarycache=none tank/backups tank/logarchive
cr0x@server:~$ sleep 300
cr0x@server:~$ kstat -p zfs:0:arcstats:l2_hits zfs:0:arcstats:l2_misses zfs:0:arcstats:l2_write_bytes > /tmp/l2arc.after
cr0x@server:~$ diff -u /tmp/l2arc.before /tmp/l2arc.after
--- /tmp/l2arc.before
+++ /tmp/l2arc.after
@@
-zfs:0:arcstats:l2_write_bytes                   918230012928
+zfs:0:arcstats:l2_write_bytes                   918380112896

Interprétation : après cinq minutes, les octets écrits sur L2ARC ont à peine bougé. C’est ce qu’on veut si ces datasets étaient la plus grande partie de votre alimentation.
Confirmez ensuite que la latence applicative s’est améliorée et que les I/O du pool ont diminué. Les métriques gagnent les débats.

Task 13: Check for memory pressure that could make L2ARC counterproductive

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           128Gi        92Gi       4.1Gi       1.2Gi        32Gi        28Gi
Swap:           16Gi       3.4Gi        13Gi

Interprétation : l’usage de swap sur un serveur ZFS n’est pas toujours mauvais, mais des swap-in soutenus pendant les pics d’I/O sont une taxe sur les performances.
Si vous voyez du swapping plus la surcharge L2ARC, réduire l’admission L2ARC (via secondarycache) est souvent une victoire rapide.

Task 14: If you suspect the cache device is misbehaving, look at its error counters

cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | sed -n '1,120p'
smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.8.0] (local build)
=== START OF INFORMATION SECTION ===
Model Number:                       ExampleNVMe 1TB
Serial Number:                      XYZ123
Firmware Version:                   1.02
...
Percentage Used:                    23%
Data Units Written:                 912,345,678
Media and Data Integrity Errors:    0
Error Information Log Entries:      0

Interprétation : si « Data Units Written » explose sur un système où L2ARC n’apporte pas de hits, vous brûlez l’endurance pour le plaisir.
C’est une autre raison de définir secondarycache=none pour les mauvais datasets.

Trois mini-histoires du monde de l’entreprise

Mini-histoire #1 : L’incident causé par une mauvaise hypothèse

Une entreprise de taille moyenne exploitait un cluster NFS basé sur ZFS pour des répertoires home développeurs et des artefacts de build.
Ils ont eu une saison chargée — nouveau produit, beaucoup d’intégrations continues, beaucoup de « relance simple » — et l’équipe stockage a reçu la plainte classique :
les builds se bloquaient sur l’I/O, surtout l’après-midi.

Quelqu’un a proposé la solution évidente : « Ajoutez du L2ARC. » Ils avaient des SSD d’entreprise en spare, une fenêtre de maintenance, et le changement était simple.
Après coup, les tableaux de bord semblaient « améliorés » dans le sens où les tableaux de bord le font quand tout le monde veut voir une amélioration : le périphérique de cache montrait de l’activité, ce qui donnait l’impression de succès.

Deux jours plus tard, l’incident réel : les clients ont commencé à signaler des gels intermittents. Pas une lenteur constante — des gels.
Les nœuds de stockage ne plantaient pas, mais les graphes de latence ressemblaient à un moniteur cardiaque.
Les tickets helpdesk étaient presque poétiques : « Parfois tout va bien, parfois ça gèle pendant 20 secondes. »

La cause racine était une mauvaise hypothèse : ils ont supposé que le workload était « relectures aléatoires ». Il ne l’était pas. Le trafic dominant était des uploads d’artefacts CI et de grandes lectures séquentielles
des outputs de build rarement relus sur le même nœud. Le prefetch de ZFS a fait son œuvre, l’ARC s’est rempli puis évincé, et l’alimentation L2ARC a commencé à marteler les SSD.
Le périphérique de cache ne servait pas de lectures ; il était occupé à ingérer des évictions. Pendant ce temps, le système payait le coût de gestion et d’ordonnancement I/O.

La correction fut embarrassante de simplicité : définir secondarycache=none sur les datasets d’artefacts de build, et secondarycache=metadata sur les répertoires home.
L2ARC a cessé d’agir comme un amplificateur de churn, la latence s’est stabilisée, et les tickets de « gel aléatoire » ont disparu.
La leçon est restée : si vous ne pouvez pas décrire la fenêtre de réutilisation, vous ne peaufinez pas un cache — vous jouez.

Mini-histoire #2 : L’optimisation qui a échoué

Un autre atelier exploitait un environnement virtualisé où un pool ZFS servait des disques VM via iSCSI.
Ils avaient des NVMe convenables pour L2ARC, une RAM modeste, et beaucoup de VM. Quelqu’un avait lu que « L2ARC est excellent pour les workloads VM », ce qui est souvent vrai.
Ils ont mis secondarycache=all à la racine du pool, donc tous les datasets l’ont hérité.

Les performances se sont initialement améliorées lors d’un test limité : les boot storms étaient plus rapides. C’était le piège.
En production, l’environnement incluait des sauvegardes complètes de VM nocturnes qui lisaient chaque block de chaque VM. Ces sauvegardes étaient séquentielles et énormes.
Une fois la sauvegarde lancée, le taux d’alimentation L2ARC a explosé, le périphérique de cache a écrit en continu, et la latence pour les I/O normales des VM a augmenté.

Le retour de bâton avait un parfum particulier : les graphes de stockage montraient le périphérique de cache « aidant » (occupé !), alors que les VM étaient plus lentes.
Les ingénieurs ont essayé de « réparer » en ajoutant un second périphérique de cache et en augmentant la taille du cache, ce qui a empiré le trafic de remplissage et consommé plus de RAM pour l’indexation.
À ce stade le cache n’était plus un cache ; c’était un job en arrière‑plan avec un SSD très coûteux.

Le tournant est venu d’une mesure ennuyeuse : ils ont comparé les octets lus L2ARC aux octets écrits pendant la fenêtre de sauvegarde.
Les écritures dominaient largement les lectures. Le périphérique de cache absorbait des données qui ne seraient jamais relues avant éviction.
Ils ont changé la politique : dataset de sauvegarde en secondarycache=none, disques VM en all.

Après cela, les boot storms sont restés améliorés, les sauvegardes ont cessé de pénaliser tout le monde, et l’endurance du périphérique L2ARC a cessé de fondre comme un compte à rebours.
La morale : « L2ARC pour les VM » n’est pas faux ; « L2ARC pour tout dans le même pool » l’est souvent.

Mini-histoire #3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe de services financiers (du genre qui craint la latence imprévisible plus que la latence lente) exploitait un appliance ZFS pour de l’analyse.
Leur workload principal était une base de données avec un working set connu, plus des exports volumineux occasionnels et des opérations de réindexation.
Ils avaient une règle : aucun changement de cache sans capture de compteurs avant/après et sans plan de rollback.

Quand l’équipe DB a demandé « plus de cache », l’ingénierie stockage n’a pas argumenté. Ils ont demandé : « Quel dataset, quel pattern d’accès, et quelle est la fenêtre de réutilisation ? »
Le dataset DB a reçu secondarycache=all. Le dataset d’exports a reçu secondarycache=none. La zone de staging partagée a eu metadata.
Ce n’était pas de l’héroïsme ; c’était simplement refuser de traiter tous les I/O comme moralement équivalents.

Des mois plus tard, un patch de plateforme a exigé un reboot. Après le reboot, des plaintes de performance sont arrivées — prévisibles — parce que les caches étaient froids.
Mais l’incident ne s’est pas transformé en incendie. Ils avaient des compteurs de référence et une attente pour le temps de réchauffement.
Plus important, parce que le dataset d’exports n’avait jamais alimenté le L2ARC, le cache s’est réchauffé sur le vrai working set de la DB au lieu d’être noyé par le trafic d’exports.

Ils ont fini par avoir l’air chanceux. Ils ne l’étaient pas. Ils étaient ennuyeux volontairement.
La pratique qui a sauvé la mise était la politique : décisions explicites de secondarycache par dataset, validées par des compteurs, réexaminées trimestriellement.
« Pas de surprises » est une fonctionnalité que l’on peut concevoir.

Erreurs courantes : symptômes et corrections

Mistake 1: Enabling L2ARC globally and forgetting that backups exist

Symptômes : le périphérique L2ARC montre des écritures constantes pendant les fenêtres de sauvegarde ; la latence du pool augmente ; le taux de hits L2ARC reste faible.

Correction : Définir secondarycache=none sur les datasets de sauvegarde/réplication/export. Envisager metadata pour les répertoires et catalogues.

Mistake 2: Interpreting “L2ARC is busy” as “L2ARC is working”

Symptômes : forte bande passante d’écriture vers le périphérique de cache ; peu de bande passante de lecture ; aucune amélioration significative de la latence applicative.

Correction : Comparez l2_write_bytes à l2_read_bytes. Si les lectures ne suivent pas les écritures, restreignez l’admission avec secondarycache.

Mistake 3: Using L2ARC to compensate for insufficient RAM without measuring memory pressure

Symptômes : swapping, activité kswapd, latence instable, ARC qui rétrécit constamment ; L2ARC ne produit toujours pas de hits.

Correction : Réduisez l’empreinte L2ARC (ou retirez‑le) et priorisez la RAM. Si vous devez garder L2ARC, utilisez secondarycache=metadata pour de nombreux datasets.

Mistake 4: Expecting L2ARC to fix synchronous write latency

Symptômes : latence de commit élevée, fsync de base de données lent, écritures NFS sync lentes, mais les métriques de lecture sont correctes.

Correction : Regardez le SLOG, le comportement sync, et le tuning du chemin d’écriture. L2ARC n’est pas un cache d’écriture ; les changements secondarycache n’aideront pas ici.

Mistake 5: Caching “too large” blocks and destroying cache efficiency

Symptômes : L2ARC se remplit rapidement ; le taux de hit reste faible ; le workload est mixte mais dominé par de grandes lectures de records.

Correction : Définir secondarycache=none pour les datasets avec de gros records en streaming (souvent les sauvegardes), ou réduire ce qui est éligible via séparation des datasets.

Mistake 6: Treating L2ARC as mandatory once installed

Symptômes : régressions de performance persistantes ; les équipes résistent à le désactiver parce que « nous avons payé le SSD ».

Correction : Opérez pour les résultats, pas pour les coûts irrécupérables. Désactivez l’admission pour les mauvais datasets ; si nécessaire, retirez le vdev de cache et redéployez le SSD ailleurs.

Checklists / step-by-step plan

Step-by-step: deciding whether a dataset should cache nothing

  1. Identifiez le pattern d’accès du dataset. S’il s’agit de scans séquentiels, supposez secondarycache=none jusqu’à preuve du contraire.
  2. Capturez des compteurs de référence. Enregistrez iostat du pool, hits/misses ARC, octets lus/écrits L2ARC.
  3. Changez un seul dataset en premier. Commencez par le dataset le plus manifestement en streaming (sauvegardes/log archives).
  4. Appliquez secondarycache=none. Ne touchez pas à cinq réglages en même temps.
  5. Attendez une fenêtre représentative. Au moins un cycle de workload (par ex. une sauvegarde complète ou l’heure de pointe).
  6. Comparez les métriques après. Les octets écrits L2ARC doivent baisser ; la latence du pool doit s’améliorer ou rester stable.
  7. Itérez. Déplacez la frontière de la politique. Gardez all pour les datasets à bénéfice prouvé, utilisez metadata pour les fichiers mixtes, et none pour le streaming.

Operational checklist: “We have L2ARC, but it’s not helping”

  1. Confirmez que vous n’êtes pas CPU-bound ou network-bound.
  2. Vérifiez le taux de hit ARC et la pression mémoire. Corrigez la famine RAM d’abord.
  3. Mesurez les hits, misses, et octets lus/écrits L2ARC.
  4. Si L2ARC est écrit majoritairement avec peu de hits, arrêtez de l’alimenter via secondarycache=none sur les datasets en streaming.
  5. Si L2ARC est fortement hit mais reste lent, inspectez la latence et la santé du périphérique de cache.
  6. Si les améliorations restent marginales, considérez que votre pool est déjà assez rapide ou que le working set manque de réutilisation.

FAQ

1) Does secondarycache=none disable ARC?

Non. L’ARC continue comme d’habitude en RAM. secondarycache contrôle uniquement l’admission vers L2ARC (le périphérique de cache secondaire).

2) If L2ARC is a read cache, why does it write so much?

Parce qu’il doit être peuplé. ZFS remplit L2ARC en écrivant des buffers évincés de l’ARC vers le périphérique de cache. Si votre workload traverse beaucoup de données uniques,
L2ARC écrira constamment tout en générant peu de hits.

3) Should I set secondarycache=metadata everywhere?

Pas partout, mais c’est un bon choix par défaut pour les workloads de fichiers mixtes où la réutilisation des métadonnées est élevée et la réutilisation des données incertaine.
C’est aussi une option plus sûre lorsque vous suspectez une « pollution » du cache par de grandes lectures en streaming.

4) What’s the difference between primarycache and secondarycache?

primarycache gouverne ce qui peut entrer dans l’ARC (RAM). secondarycache gouverne ce qui peut entrer dans le L2ARC (périphérique de cache).
La plupart des systèmes doivent être très prudents avant de changer primarycache ; utilisez secondarycache pour gérer la politique L2ARC.

5) If L2ARC doesn’t persist across reboot, is it useless?

Pas inutile, mais vous devez tenir compte du temps de réchauffement. Si votre système redémarre fréquemment ou si vos SLO de performance ne tolèrent pas des périodes de cache froid,
L2ARC peut être le mauvais outil — ou vous devez vous assurer que seuls les datasets à forte réutilisation l’alimentent afin qu’il se réchauffe rapidement sur l’essentiel.

6) Can L2ARC make performance worse even if it gets some hits?

Oui. Si le coût de remplissage et de gestion provoque une contention CPU, une pression mémoire, ou des pics de latence du périphérique, vous pouvez finir plus lent globalement.
La solution est une admission sélective : mettez secondarycache=none là où le taux de hit et la réutilisation ne justifient pas le coût.

7) How do I know whether my workload has enough reuse for L2ARC?

Mesurez. Un indicateur pratique : les octets lus L2ARC devraient représenter une fraction significative des octets écrits L2ARC sur une fenêtre représentative,
et la latence applicative devrait s’améliorer. Si vous écrivez surtout vers L2ARC et que vous le lisez rarement, la fenêtre de réutilisation n’existe pas.

8) Is adding more L2ARC capacity a good fix for low hit ratio?

Parfois, mais c’est généralement le mauvais premier réflexe. Un faible taux de hit est le plus souvent un problème de politique (mettre en cache les mauvais datasets) plutôt qu’un problème de taille.
Augmentez la capacité seulement après avoir exclu les datasets en streaming via secondarycache=none et confirmé une réelle réutilisation.

9) Should I remove the L2ARC device entirely if I set many datasets to none?

Si vous ne conservez qu’un petit nombre de datasets qui en bénéficient, garder L2ARC peut encore valoir le coup.
Mais si vos mesures montrent peu de hits et une surcharge significative, le retirer est une décision d’ingénierie valide. Les SSD ont de meilleures utilisations que d’être une déception.

Conclusion

L2ARC n’est pas magique ; c’est un compromis. La propriété par dataset secondarycache est la façon de contrôler ce compromis avec précision.
Si vous ne retenez qu’une seule chose : un cache qui sert peu n’est pas « sans conséquence ». Il brûle des ressources — CPU, RAM, bande passante d’écriture et endurance SSD.

La configuration L2ARC la plus robuste en production que j’ai vue est aussi la moins spectaculaire : mettre en cache les datasets qui relisent de manière démontrable,
considérer le metadata-only quand c’est adapté, et définir secondarycache=none pour les workloads en streaming sans honte. Ne rien mettre en cache peut être l’option la plus rapide.

← Précédent
Mars Climate Orbiter : le décalage d’unités qui a fait perdre un vaisseau spatial
Suivant →
Limites de taille des messages e-mail : augmentez-les en toute sécurité sans ouvrir la porte aux abus

Laisser un commentaire