L2ARC tiene una reputación: “Añade un SSD y tu pool ZFS se vuelve rápido.” A veces eso es cierto. Otras veces es el equivalente en almacenamiento de poner un alerón en una furgoneta de reparto.
El secreto feo es que la mejor configuración de L2ARC para muchos sistemas reales es… no cachear nada en absoluto.
Este artículo trata sobre secondarycache, la propiedad del dataset que decide si los bloques son elegibles para L2ARC, y la realidad operativa que hay detrás.
Hablaremos de por qué L2ARC puede ser neutro, por qué puede ser activamente perjudicial, y cómo demostrar (no adivinar) qué está pasando en tus servidores.
Qué hace realmente secondarycache
ZFS tiene dos conceptos de caché principales que la gente confunde:
- ARC (Adaptive Replacement Cache): vive en RAM, cachea automáticamente datos y metadatos frecuentemente y recientemente usados.
- L2ARC: una extensión del ARC que almacena bloques elegibles en un dispositivo rápido (normalmente SSD/NVMe).
La propiedad que determina si los bloques de un dataset son elegibles para L2ARC es secondarycache. Es una configuración por dataset, heredable, y controla qué está permitido que ZFS escriba en L2ARC.
Valores que realmente usarás
secondarycache=all: tanto datos como metadatos pueden entrar en L2ARC.secondarycache=metadata: solo metadatos son elegibles (a menudo sorprendentemente efectivo para ciertas cargas).secondarycache=none: nada de este dataset es elegible para L2ARC.
La idea principal: establecer secondarycache=none no es “apagar la caché.” ARC sigue cacheando.
Solo le estás diciendo a ZFS: “No gastes ancho de banda ni gestión de L2ARC en este dataset.”
Si alguna vez has visto un sistema desmoronarse porque “añadimos caché”, ya sabes por qué esto importa.
L2ARC no es gratis; cuesta CPU, memoria para metadatos y ancho de banda de escritura para poblarse.
Broma #1: L2ARC es como una membresía al gimnasio—tenerla no te hace más rápido, usarla correctamente quizá sí.
Hechos interesantes y un poco de historia
Unos cuantos datos concretos que explican por qué L2ARC se comporta como lo hace hoy:
- L2ARC llegó temprano y evolucionó despacio. El diseño se originó en la era de Solaris, cuando los SSD eran más pequeños, lentos y mucho más caros que hoy.
- L2ARC es una caché de lectura, poblada mediante escrituras. Se llena copiando buffers expulsados del ARC al dispositivo L2ARC—lo que significa que consume I/O de escritura incluso si tu carga es de solo lectura.
- Históricamente, el contenido de L2ARC desaparecía al reiniciar. Durante mucho tiempo L2ARC siempre arrancaba “frío” tras un reinicio; el calentamiento podía tardar horas. La persistencia llegó más tarde en algunas implementaciones, pero operativamente aún debes planear un comportamiento de caché fría.
- L2ARC necesita RAM para indexar lo que hay en disco. El dispositivo de caché es inútil sin cabeceras en memoria que digan qué está almacenado y dónde; un L2ARC “grande” puede consumir memoria de forma silenciosa.
- El prefetch de ZFS puede sabotear un cacheo ingenuo. Las lecturas secuenciales pueden traer datos que parecen cacheables pero nunca se volverán a usar; L2ARC puede convertirse en un museo de bloques que solo viste una vez.
- ARC suele ser la mejor caché que puedes comprar. La latencia y el ancho de banda de la RAM siguen dominando, y ARC es más inteligente de lo que muchos suponen—especialmente en cargas intensivas en metadatos.
- L2ARC no es una caché de escritura. La gente sigue intentando usarlo como tal. Para eso existe SLOG (separate log), y aún así solo ayuda con escrituras síncronas.
- La compresión cambia las cuentas. Cachear bloques comprimidos significa más datos lógicos por byte, pero también otros costes de CPU; a veces la compresión hace que ARC sea tan eficiente que L2ARC quede en marginalidad.
- Cachear metadatos puede ser la verdadera ganancia. Servidores de archivos y almacenes de VM a menudo se benefician más de cachear bloques indirectos, dnodes y directorios que del contenido de archivos.
L2ARC en cristiano (y por qué es complicado)
ARC vive en RAM y mantiene lo que cree que necesitarás a continuación. Cuando ARC se queda sin espacio, expulsa buffers.
Algunos de esos buffers expulsados pueden escribirse en L2ARC. Más adelante, si se solicita el mismo bloque y ya no está en ARC,
ZFS puede recuperarlo de L2ARC en lugar de ir a los discos del pool.
Eso suena sencillo hasta que ejecutas cargas de producción, porque “más adelante” implica muchas condiciones.
L2ARC solo ayuda cuando:
- la carga vuelve a leer los mismos bloques,
- ARC es demasiado pequeño para mantenerlos calientes,
- el dispositivo L2ARC es lo bastante rápido para superar al pool,
- y poblar L2ARC no roba recursos necesarios en otros lugares.
Los dos costes invisibles: tráfico de llenado y metadatos
El primer coste es el tráfico de llenado. L2ARC se puebla escribiendo en él. Esas escrituras compiten con el trabajo normal del pool:
ciclos de CPU, ancho de banda de memoria y a veces el mismo planificador de I/O y presupuesto de interrupciones.
El segundo coste es la sobrecarga de metadatos. ZFS debe recordar qué hay en L2ARC. Ese índice vive en RAM. Si ya tienes poca memoria
(la razón por la que querías L2ARC en primer lugar), añadir un L2ARC grande puede ser como comprar una nevera más grande y luego quejarte de que la cocina es más pequeña.
Por qué “cachear todo” rara vez es la mejor política
La mayoría de los datasets de producción contienen una mezcla de patrones de acceso:
- Lecturas en streaming/secuenciales (backups, medios, ETL): terrible para L2ARC.
- Relecturas aleatorias (bases de datos, imágenes de VM, cargas de ficheros pequeños): potencialmente excelentes para L2ARC.
- Agitación de metadatos (servidores de compilación, workspaces de CI): a veces mejor servido por solo caché de metadatos o simplemente más RAM.
La propiedad secondarycache te permite ser selectivo.
Los mejores operadores que conozco no “habilitan L2ARC”; deciden qué datasets pueden usarlo y a cuáles se les dice amablemente que se mantengan fuera.
Cuándo es correcto no cachear nada
“L2ARC debería cachear nada” no significa “L2ARC es inútil.” Significa: para un dataset particular (o para todo un pool), el coste de cachear supera al beneficio.
Aquí están los escenarios donde secondarycache=none no solo es razonable, sino que a menudo es la solución más clara.
1) La carga es mayoritariamente secuencial o de una sola pasada
Piensa en: destinos de backups, archivos de logs, archivos de vídeo, ingestión en lagos de datos, exportaciones nocturnas. ZFS prefetch lee por adelantado, ARC ve una avalancha de datos y L2ARC
empieza a escribir buffers expulsados. Pero nada se vuelve a leer. Has construido una caché para una carga que no se repite.
Síntomas:
- La I/O de escritura a L2ARC es alta; la tasa de aciertos de L2ARC se mantiene baja.
- Los discos del pool muestran actividad extra aunque el “SSD de caché” esté ocupado.
- La latencia empeora durante escaneos intensos porque el sistema está haciendo trabajo extra.
2) Estás limitado de memoria y L2ARC se lleva la última RAM buena
ARC necesita RAM. El sistema operativo necesita RAM. Las aplicaciones necesitan RAM. L2ARC también necesita RAM (para cabeceras y gestión).
Si ya estás cerca de hacer swap o ves ARC encogiéndose constantemente bajo presión, añadir L2ARC puede hacer que el rendimiento sea más errático.
Aquí secondarycache=none es un bisturí: puedes mantener L2ARC para los datasets que realmente se benefician, mientras evitas la “sacudida de caché”
por datasets de volumen/streaming.
3) El dispositivo de caché es rápido, pero no lo bastante (o no es consistente)
No todos los SSD son iguales. Algunos lucen bien en la hoja de especificaciones y luego se vienen abajo con escrituras sostenidas, cargas mixtas o alta profundidad de cola.
L2ARC crea escrituras sostenidas. Si el comportamiento de escritura del dispositivo es malo, el llenado de L2ARC puede crear picos de latencia.
Si tu pool ya está en NVMe, un “NVMe para caché” podría no ser significativamente más rápido que el pool.
En ese caso, pagas la sobrecarga de L2ARC para reemplazar una lectura rápida por otra lectura rápida. No es un crimen, pero sí un despilfarro.
4) Tu verdadero cuello de botella no son las lecturas
He visto equipos perseguir aciertos de L2ARC mientras el problema real era:
- latencia de escrituras síncronas (requiere ajuste de SLOG, alineación de recordsize, o cambios en la aplicación),
- saturación de CPU por compresión/checksum,
- latencia de red,
- fragmentación y pequeños tamaños de registro,
- o simplemente “demasiados clientes.”
En esos casos, L2ARC es una misión secundaria. Fijar secondarycache=none en datasets irrelevantes evita que L2ARC consuma recursos
mientras arreglas el problema real.
5) Estás cacheando datos que deberían ser “especiales” en su lugar
L2ARC es una caché de lectura. Un vdev especial es diferente: puede almacenar permanentemente metadatos (y opcionalmente bloques pequeños) en medios rápidos.
Si tu dolor es la latencia de metadatos (recorridos de directorios, lecturas aleatorias pequeñas, metadatos de VM), la aceleración “correcta” podría ser un vdev especial—no L2ARC.
Eso no significa “siempre desplegar vdev especial.” Significa: si usas L2ARC para parchear un problema de metadatos, podrías estar pagando una sobrecarga continua
por un beneficio que podría hacerse estructural.
6) No tienes una ventana de reutilización estable
L2ARC brilla cuando tu conjunto de trabajo es mayor que la RAM pero aún tiene localidad—es decir, los bloques se vuelven a leer en minutos/horas.
Algunas cargas tienen un conjunto de trabajo mayor que la RAM y ninguna reutilización útil. No puedes cachear tu salida frente a la entropía.
Broma #2: Si tu carga es puro streaming, L2ARC es básicamente una tienda de souvenirs muy cara—mucho inventario, nadie vuelve.
Guía rápida de diagnóstico (primero, segundo, tercero)
Cuando el rendimiento es malo y L2ARC está en la ecuación, necesitas una rutina de triaje rápida y repetible. Esto es lo que reviso, en orden, cuando alguien dice “el almacenamiento va lento”
y hay un dispositivo L2ARC instalado con orgullo.
Primero: ¿es un problema de lectura, de escritura o de latencia?
- Mira la latencia y las operaciones del pool: ¿las lecturas son lentas, las escrituras son lentas, o ambas?
- Comprueba si la app está bloqueada por I/O o por CPU.
- Confirma si la degradación se correlaciona con escaneos secuenciales, backups, replicación o scrubs.
Segundo: ¿está ARC ya haciendo el trabajo?
- Si la tasa de aciertos de ARC es alta y estable, L2ARC a menudo no ayudará.
- Si ARC está bajo presión de memoria, arregla eso antes de “añadir caché.”
Tercero: ¿L2ARC realmente está sirviendo lecturas (aciertos), o solo se está escribiendo?
- Revisa la tasa de aciertos de L2ARC, la tasa de alimentación, y la relación lecturas/escrituras de L2ARC.
- Si L2ARC está ocupada escribiendo pero no produce aciertos, limita su uso con
secondarycache.
Cuarto: ¿muestra el dispositivo de caché un mal comportamiento?
- Observa la latencia del dispositivo durante el llenado.
- Busca utilización sostenida alta, colas o paradas periódicas.
- Verifica que no esté compartiendo lanes/controladores con otros dispositivos calientes.
Quinto: reduce el radio de impacto
- Configura
secondarycache=noneen datasets de streaming obvios primero. - Mantén la caché de metadatos para datasets donde ayuda.
- Mide de nuevo. No te guíes por sensaciones.
Tareas prácticas: comandos + interpretación
El objetivo aquí no es “ejecutar estos comandos una vez.” Es crear el hábito: observar, cambiar una cosa, observar otra vez.
Abajo hay tareas prácticas que puedes hacer en un host ZFS real. La salida varía según la plataforma, pero la intención e interpretación se mantienen.
Task 1: Confirmar qué datasets son elegibles para 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
Interpretación: si ves datasets de streaming como tank/logarchive heredando all, eso es una señal de alarma.
Estás permitiendo que L2ARC ingiera los datos menos cacheables del sistema.
Task 2: Poner datasets de streaming para que no cacheen nada
cr0x@server:~$ sudo zfs set secondarycache=none tank/logarchive
cr0x@server:~$ sudo zfs set secondarycache=none tank/backups
Interpretación: esto no purga ARC; simplemente evita que nuevos bloques de esos datasets sean admitidos en L2ARC a partir de ahora.
Has reducido la agitación futura de L2ARC.
Task 3: Probar caché solo de metadatos para “muchos nombres de archivo, pocos datos reread”
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
Interpretación: solo metadatos es un punto medio pragmático. Para servidores de archivos, puede mejorar recorridos de directorios y cargas intensivas en stat
sin llenar L2ARC con contenidos de archivos que no se volverán a leer.
Task 4: Verificar que realmente tienes un dispositivo L2ARC adjunto
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
Interpretación: los dispositivos L2ARC aparecen bajo cache. Si no hay vdev de caché, secondarycache importa solo en sentido de política futura.
Task 5: Comprobar si el pool está limitado por IOPS o por ancho de banda
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
-------------------------- ----- ----- ----- ----- ----- -----
Interpretación: si ves gran ancho de banda con pocas IOPS, huele a streaming secuencial. Ahí L2ARC a menudo no hace nada útil.
Si las IOPS son altas y las lecturas son aleatorias, L2ARC podría ayudar—si existe la reutilización.
Task 6: Vigilar la latencia de I/O a nivel sistema e identificar el dolor real
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
Interpretación: observa await y %util. Aquí el NVMe está haciendo muchas escrituras (probablemente alimentación de L2ARC),
mientras los HDDs están cerca de saturación. Si L2ARC no está produciendo aciertos, esas escrituras son solo trabajo extra.
Task 7: Comprobar contadores ARC y L2ARC (común en Linux con 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
Interpretación: los aciertos de ARC eclipsan a los fallos (bien), y los aciertos de L2ARC son diminutos comparados con las faltas de L2 (mal). En esta forma, L2ARC no aporta valor.
Debes considerar restringir secondarycache y/o eliminar L2ARC por completo si causa sobrecarga.
Task 8: Calcular ratios de acierto rápidos y burdos
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
Interpretación: un ~3% de aciertos en L2ARC es una señal fuerte de que el dispositivo actúa mayormente como un sumidero de escrituras para buffers expulsados.
Hay casos límite, pero en la práctica esto suele significar “deja de alimentarlo con basura.”
Task 9: Vigilar la tasa de alimentación y el tamaño de L2ARC (depende de la plataforma, sigue siendo útil)
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
Interpretación: casi un terabyte escrito y solo ~9 GB leídos. Eso no es “caché”, es una caminadora.
También implica desgaste en el dispositivo de caché por poco retorno.
Task 10: Identificar datasets que causan lecturas intensas (para decidir quién obtiene 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
-------------------------- ----- ----- ----- ----- ----- -----
Interpretación: dependiendo de tu build de OpenZFS, puede que no obtengas per-dataset iostat aquí.
Si tienes herramientas por dataset en tu entorno, úsalas; de lo contrario correlaciona con logs de aplicaciones y patrones de acceso.
El punto operativo: elige ganadores y perdedores para secondarycache en lugar de habilitar todo.
Task 11: Inspeccionar recordsize y ver si estás cacheando bloques gigantes que nunca vuelves a leer
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
Interpretación: un dataset de backups con recordsize=1M suele ser grande, secuencial y no reread.
Cachear bloques de 1 MB en L2ARC rara vez es una ganancia. Es un candidato principal para secondarycache=none.
Task 12: Hacer el cambio de forma segura, luego validar con capturas antes/después de contadores
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
Interpretación: después de cinco minutos, los bytes escritos en L2ARC apenas se movieron. Eso es lo que quieres si esos datasets eran la mayor parte de tu alimentación.
Ahora confirma que la latencia de la aplicación mejoró y que el I/O del pool disminuyó. Las métricas ganan discusiones.
Task 13: Comprobar la presión de memoria que podría hacer contraproducente a L2ARC
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
Interpretación: el uso de swap en un servidor ZFS no siempre es malo, pero el swap sostenido durante picos de I/O es un impuesto al rendimiento.
Si ves swapping más la sobrecarga de L2ARC, reducir la admisión a L2ARC (vía secondarycache) suele ser una victoria rápida.
Task 14: Si sospechas que el dispositivo de caché se comporta mal, mira sus contadores de errores
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
Interpretación: si “Data Units Written” se dispara en un sistema donde L2ARC no está rindiendo aciertos, estás consumiendo resistencia por deporte.
Esa es otra razón para poner secondarycache=none en los datasets equivocados.
Tres mini-historias del mundo corporativo
Mini-historia #1: El incidente causado por una suposición errónea
Una compañía mediana operaba un clúster NFS respaldado por ZFS para directorios home de desarrolladores y artefactos de compilación.
Tuvieron una temporada ocupada—nuevo producto, mucho CI, mucho “vuelve a ejecutarlo”—y el equipo de almacenamiento recibió la queja clásica:
las builds se quedaban atascadas en I/O, especialmente por la tarde.
Alguien propuso la solución obvia: “Añadir L2ARC.” Tenían SSDs empresariales de sobra, una ventana de mantenimiento y el cambio era sencillo.
Después, los paneles parecían “mejorados” en el sentido en que los paneles mejoran cuando todos quieren que mejoren: el dispositivo de caché mostraba actividad, lo que se sentía como éxito.
Dos días después, el incidente real: los clientes empezaron a reportar bloqueos intermitentes. No una degradación continua—bloqueos.
Los nodos de almacenamiento no estaban fallando, pero las gráficas de latencia parecían un monitor cardíaco.
Los tickets del helpdesk eran casi poéticos: “A veces está bien, a veces se congela 20 segundos.”
La causa raíz fue una suposición errónea: asumieron que la carga era “relecturas aleatorias.” No lo era. El tráfico dominante eran subidas de artefactos CI y grandes lecturas secuenciales
de salidas de compilación que rara vez se volvían a leer en el mismo nodo. ZFS prefetch hizo lo suyo, ARC se llenó y expulsó, y la alimentación de L2ARC empezó a golpear los SSDs.
El dispositivo de caché no estaba sirviendo lecturas; estaba ocupado ingiriendo expulsiones. Mientras tanto, el sistema pagó el coste de gestión y el coste de planificación de I/O.
La solución fue embarazósamente simple: poner secondarycache=none en los datasets de artefactos de compilación, y secondarycache=metadata en los directorios home.
L2ARC dejó de actuar como amplificador de agitación, la latencia se estabilizó y desaparecieron los tickets de “bloqueo aleatorio”.
La lección quedó: si no puedes describir la ventana de reutilización, no estás afinando una caché—estás apostando.
Mini-historia #2: La optimización que salió mal
Otro equipo operaba un entorno virtualizado donde un pool ZFS servía discos de VM vía iSCSI.
Tenían buenos NVMe para L2ARC, RAM modesta y muchas VMs. Alguien leyó que “L2ARC es genial para cargas de VM”, lo cual suele ser cierto.
Pusieron secondarycache=all en la raíz del pool, así que todos los datasets lo heredaron.
El rendimiento mejoró inicialmente en una prueba limitada: los boot storms fueron más rápidos. Esa fue la trampa.
En producción, el entorno incluía backups completos nocturnos de VM que leían cada bloque de cada VM. Esos backups eran secuenciales y enormes.
Cuando el backup arrancó, la tasa de alimentación de L2ARC se disparó, el dispositivo de caché escribió sin cesar y la latencia para el I/O normal de VM subió.
El fracaso tuvo un sabor específico: las gráficas de almacenamiento mostraban el dispositivo de caché “ayudando” (ocupado!), pero las VMs estaban más lentas.
Los ingenieros intentaron “arreglarlo” añadiendo un segundo dispositivo de caché y aumentando el tamaño de la caché, lo que empeoró la tensión y consumió más RAM para indexado.
En ese punto la caché ya no era una caché; era un trabajo en segundo plano con un SSD muy caro.
El punto de inflexión vino de una medición aburrida: compararon los bytes leídos de L2ARC con los bytes escritos durante la ventana de backup.
Las escrituras eclipsaban las lecturas. El dispositivo de caché estaba absorbiendo datos que nunca volverían a leerse antes de ser expulsados.
Cambiaron la política: el dataset de backups se puso secondarycache=none, los discos de VM conservaron all.
Tras eso, los boot storms siguieron mejorando, los backups dejaron de penalizar a todos y la resistencia del dispositivo L2ARC dejó de caer como un contador regresivo.
La moraleja: “L2ARC para VMs” no es incorrecto; “L2ARC para todo en el mismo pool” a menudo sí lo es.
Mini-historia #3: La práctica aburrida pero correcta que salvó el día
Un equipo de servicios financieros (de esos que temen la latencia impredecible más que la latencia lenta) operaba un appliance ZFS para analítica.
Su carga principal era una base de datos con un conjunto de trabajo conocido, más exportaciones grandes ocasionales y reindexados.
Tenían una regla clara: ningún cambio de caché sin captura de contadores antes/después y un plan de rollback.
Cuando el equipo de BD pidió “más caché”, ingeniería de almacenamiento no discutió. Preguntaron: “¿Qué dataset, qué patrón de acceso y cuál es la ventana de reutilización?”
El dataset de BD obtuvo secondarycache=all. El dataset de exportaciones obtuvo secondarycache=none. El área compartida de staging quedó en metadata.
Esto no fue heroico; fue negarse a tratar todo I/O como moralmente equivalente.
Meses después, un parche de plataforma requirió un reinicio. Tras el reinicio, llegaron las quejas de rendimiento—predeciblemente—porque las cachés estaban frías.
Pero el incidente no se convirtió en un fuego. Tenían contadores base y una expectativa de tiempo de calentamiento.
Más importante: porque el dataset de exportaciones nunca alimentó L2ARC, la caché se calentó con el verdadero conjunto de trabajo de la BD en lugar de ahogarse en tráfico de exportación.
Acabaron pareciendo afortunados. No lo eran. Eran deliberadamente aburridos.
La práctica que salvó el día fue la política: decisiones explícitas de secondarycache por dataset, validadas con contadores y revisadas trimestralmente.
“Sin sorpresas” es una característica que puedes diseñar.
Errores comunes: síntomas y soluciones
Mistake 1: Habilitar L2ARC globalmente y olvidar que existen backups
Síntomas: el dispositivo L2ARC muestra escrituras constantes durante ventanas de backup; la latencia del pool aumenta; la tasa de aciertos de L2ARC permanece baja.
Solución: Poner secondarycache=none en datasets de backups/replicación/exportación. Considerar metadata para directorios y catálogos.
Mistake 2: Interpretar “L2ARC está ocupado” como “L2ARC está funcionando”
Síntomas: alto ancho de banda de escritura al dispositivo de caché; poco ancho de banda de lectura; ninguna mejora significativa en la latencia de la aplicación.
Solución: Compara l2_write_bytes con l2_read_bytes. Si las lecturas no siguen a las escrituras, restringe la admisión con secondarycache.
Mistake 3: Usar L2ARC para compensar RAM insuficiente sin medir presión de memoria
Síntomas: swapping, actividad de kswapd, latencia irregular, ARC encogiéndose constantemente; L2ARC aún sin producir aciertos.
Solución: Reduce la huella de L2ARC (o quítalo) y prioriza RAM. Si debes mantener L2ARC, usa secondarycache=metadata para muchos datasets.
Mistake 4: Esperar que L2ARC arregle la latencia de escrituras síncronas
Síntomas: latencia de commits alta, fsync de base de datos lento, escrituras NFS sync lentas, pero métricas de lectura bien.
Solución: Mira SLOG, comportamiento sync y ajustes de la ruta de escritura. L2ARC no es una caché de escritura; cambios en secondarycache no ayudarán aquí.
Mistake 5: Cachear bloques “demasiado grandes” y destruir la eficiencia de la caché
Síntomas: L2ARC se llena rápido; la tasa de aciertos sigue baja; la carga es mixta pero dominada por lecturas de grandes registros.
Solución: Poner secondarycache=none para datasets con grandes registros secuenciales (a menudo backups), o separar por datasets para reducir lo elegible.
Mistake 6: Tratar L2ARC como obligatorio una vez instalado
Síntomas: regresiones persistentes de rendimiento; equipos se resisten a desactivarlo porque “pagamos por el SSD.”
Solución: Opera por resultados, no por costes hundidos. Desactiva la admisión para los datasets equivocados; si es necesario, quita el vdev de caché y redepliega el SSD en otro sitio.
Listas de verificación / plan paso a paso
Paso a paso: decidir si un dataset debe no cachear nada
- Identifica el patrón de acceso del dataset. Si es escaneos secuenciales, asume
secondarycache=nonehasta que se demuestre lo contrario. - Captura contadores base. Registra iostat del pool, aciertos/fallos de ARC, bytes leídos/escritos de L2ARC.
- Cambia solo un dataset primero. Empieza por el dataset de streaming más obvio (backups/log archives).
- Aplica
secondarycache=none. No cambies cinco ajustes a la vez. - Espera una ventana representativa. Al menos un ciclo de la carga (por ejemplo, un backup completo o la hora pico).
- Compara métricas después. Los bytes escritos en L2ARC deberían caer; la latencia del pool debería mejorar o mantenerse estable.
- Itera. Mueve el límite de la política. Mantén
allpara datasets con ganancia demostrada, usametadatapara cargas mixtas ynonepara streaming.
Lista operativa: “Tenemos L2ARC, pero no ayuda”
- Confirma que no estás limitado por CPU o por red.
- Revisa la tasa de aciertos de ARC y la presión de memoria. Arregla la falta de RAM primero.
- Mide aciertos, fallos y bytes leídos/escritos de L2ARC.
- Si L2ARC escribe mucho y acierta poco, deja de alimentarlo con
secondarycache=noneen datasets de streaming. - Si L2ARC acierta mucho pero aún así es lento, inspecciona la latencia y salud del dispositivo de caché.
- Si las mejoras siguen siendo marginales, considera que tu pool ya es lo bastante rápido o que el conjunto de trabajo no tiene reutilización.
Preguntas frecuentes
1) ¿Desactiva secondarycache=none a ARC?
No. ARC continúa normalmente en RAM. secondarycache solo controla la admisión a L2ARC (el dispositivo de caché secundario).
2) Si L2ARC es una caché de lectura, ¿por qué escribe tanto?
Porque debe poblarse. ZFS llena L2ARC escribiendo buffers expulsados del ARC hacia el dispositivo de caché. Si tu carga atraviesa muchos datos únicos,
L2ARC escribirá constantemente produciendo pocos aciertos.
3) ¿Debo poner secondarycache=metadata en todas partes?
No en todas partes, pero es un buen valor por defecto para cargas de archivos mixtas donde la reutilización de metadatos es alta y la reutilización de datos es incierta.
También es una opción más segura cuando sospechas de “polución de caché” por lecturas secuenciales grandes.
4) ¿Cuál es la diferencia entre primarycache y secondarycache?
primarycache gobierna qué puede entrar en ARC (RAM). secondarycache gobierna qué puede entrar en L2ARC (dispositivo de caché).
La mayoría de los sistemas deben tener mucha cautela al cambiar primarycache; usa secondarycache para gestionar la política de L2ARC.
5) Si L2ARC no persiste tras un reinicio, ¿es inútil?
No es inútil, pero debes tener en cuenta el tiempo de calentamiento. Si tu sistema se reinicia con frecuencia o tus SLOs de rendimiento no toleran periodos de caché fría,
L2ARC puede ser la herramienta equivocada—o debes asegurarte de que solo datasets de alta reutilización lo alimenten para que se caliente rápido en lo que importa.
6) ¿Puede L2ARC empeorar el rendimiento incluso si obtiene algunos aciertos?
Sí. Si la sobrecarga de llenado y gestión causa contención de CPU, presión de memoria o picos de latencia en el dispositivo, puedes acabar más lento en general.
La cura es la admisión selectiva: pon secondarycache=none donde la tasa de aciertos y la reutilización no justifican el coste.
7) ¿Cómo sé si mi carga tiene suficiente reutilización para L2ARC?
Mide. Un proxy práctico es: los bytes leídos de L2ARC deberían ser una fracción significativa de los bytes escritos en L2ARC durante una ventana representativa,
y la latencia de la aplicación debería mejorar. Si mayormente escribes en L2ARC y rara vez lees de él, la ventana de reutilización no existe.
8) ¿Agregar más capacidad de L2ARC es una buena solución para una baja tasa de aciertos?
A veces, pero suele ser la primera decisión equivocada. Una baja tasa de aciertos suele ser más un problema de política (cachear los datasets equivocados) que de tamaño.
Aumenta la capacidad solo después de excluir datasets de streaming con secondarycache=none y confirmar reutilización real.
9) ¿Debo eliminar el dispositivo L2ARC por completo si pongo muchos datasets en none?
Si al final solo te quedan unos pocos datasets que se benefician, mantener L2ARC aún puede valer la pena.
Pero si tus mediciones muestran aciertos mínimos y sobrecarga significativa, eliminarlo es ingeniería válida. Los SSDs tienen mejores usos que ser una decepción.
Conclusión
L2ARC no es magia; es un intercambio. La propiedad de dataset secondarycache es cómo controlas ese intercambio con precisión.
Si recuerdas solo una cosa: una caché que sirve poco no es “inofensiva.” Consume recursos—CPU, RAM, ancho de banda de escritura y resistencia de SSD.
La configuración de L2ARC más robusta en producción que he visto también es la menos espectacular: cachea los datasets que demostrablemente vuelven a leer,
considera solo metadatos donde encaje, y pon secondarycache=none para cargas de streaming sin disculpas. No cachear nada puede ser la opción más rápida.