Si alguna vez has visto un host ZFS con 256 GB de RAM sentirse extrañamente «lleno» mientras el rendimiento no mejoraba, te has encontrado con el mal hábito del ARC: cachear datos que no merecen una segunda oportunidad. El ARC es brillante al aprender qué vas a necesitar de nuevo. También es obediente. Si sigues alimentándolo con una carga que es «leer una vez, nunca más», devorará memoria con gusto y expulsará las cosas que realmente importan.
Aquí es donde primarycache deja de ser una nota al pie y se convierte en una superficie de control operativo. Bien aplicado, es un bisturí: reglas de caché por dataset que mantienen el ARC caliente para metadatos y bloques realmente reutilizables, mientras permiten que lecturas en streaming y datasets desechables pasen sin convertir la RAM en un museo de copias de seguridad de ayer.
Qué controla realmente primarycache (y qué no)
primarycache es una propiedad de dataset de ZFS que controla qué está permitido que ZFS coloque en el ARC (la caché en memoria). Tiene tres valores:
all— cachear tanto datos de archivo como metadatos en ARC (por defecto)metadata— cachear solo metadatos (dnode, bloques indirectos, etc.)none— no cachear nada de ese dataset en ARC
Parece simple, pero hay dos verdades operativas sutiles:
- Esto trata sobre admisión, no sobre la política de expulsión. Decide qué tipos de bloques de ese dataset son elegibles para entrar al ARC. No «fija» ni «prioriza» nada directamente.
- Es por dataset, heredable y fácil de usar mal. Es poderoso precisamente porque puedes aplicarlo a un dataset de backups sin castigar tu dataset de VMs. También es fácil establecerlo en un padre y olvidarlo, lesionando silenciosamente el rendimiento de todo lo que esté debajo.
Además: primarycache no es un interruptor mágico para «usar L2ARC en su lugar». Eso es secondarycache. Puedes configurar ambos y se interrelacionan, pero resuelven problemas diferentes: admisión al ARC frente a admisión al L2ARC.
Una trampa más: poner primarycache=none no significa «ZFS nunca usará RAM para este dataset». ZFS sigue usando memoria para bookkeeping, y tu aplicación sigue usando page cache (en Linux) salvo que uses I/O directo. El ARC es solo una de varias bocas que consumen RAM.
Chiste #1 (corto y relevante): ARC es como un becario con gran memoria que nunca pregunta por qué está memorizando toda la guía telefónica.
Cómo ARC «desperdicia» RAM en la práctica
Que el ARC desperdicie RAM normalmente no es un bug. Es un desajuste entre tu patrón de carga y las suposiciones de la caché.
ARC funciona mejor cuando:
- hay localidad temporal significativa (lees los mismos bloques de nuevo pronto), y/o
- la reutilización de metadatos es alta (recorridos de directorios, I/O aleatorio pequeño, churn de metadatos de VM), y/o
- tu working set cabe o casi cabe en RAM.
ARC funciona mal (o al menos «con coste») cuando:
- transmites archivos enormes una vez (backups, scrubs, grandes lecturas ETL),
- el working set es mucho mayor que la memoria, provocando churn continuo,
- el host corre además servicios hambrientos de memoria (bases de datos, JVM) que realmente necesitan esa RAM.
¿Cómo se ve el «desperdicio» operacionalmente?
- La latencia no mejora tras la primera pasada, porque no hay una segunda pasada.
- El ARC crece porque ZFS hace su trabajo: ve memoria libre y la usa.
- Otros procesos se estrechan, llevando a swapping, OOM kills o presión agresiva de reclaim.
- La ratio de aciertos del ARC parece aceptable pero las faltas siguen siendo caras porque las misses están en el camino crítico (a menudo metadatos o lecturas aleatorias pequeñas), y tus «hits» son una montaña de datos secuenciales irrelevantes.
En muchos incidentes de producción, la primera suposición errónea es que «la tasa de aciertos de la caché» es un único número que puede validar todo el sistema. No puede. Puedes tener una buena tasa de aciertos y aun así perder porque la caché está llena de bloques de bajo valor.
Hechos e historia: por qué existe este control
Un poco de contexto hace que esta propiedad sea más fácil de respetar. Aquí hay algunos datos concretos y notas históricas que influyen en cómo primarycache se comporta en el mundo real:
- ZFS fue diseñado con la RAM como una capa de rendimiento de primera clase. Cuando ZFS nació en Sun, los servidores con mucha memoria eran parte del objetivo. El ARC no es un añadido; es central en el diseño.
- ARC no es «solo una caché»; es un motor de políticas. Divide la caché en segmentos MRU/MFU (reciente vs frecuente) para evitar dejarse engañar por escaneos de una sola pasada.
- Incluso con resistencia a escaneos, las cargas secuenciales llenan el ARC. ARC puede intentar no destruir tu conjunto caliente durante un escaneo, pero aún tiene que decidir qué admitir.
primarycachees la palanca de «no admitir esto». - La idea de «solo metadatos» es anterior a ZFS. Los sistemas de archivos hace tiempo que reconocen la localidad de metadatos como una gran ventaja. ZFS lo formalizó a nivel de dataset.
- Los datasets ZFS están pensados como límites de política. Propiedades como
recordsize,compression,syncyprimarycacheexisten para que puedas correr cargas mixtas en un pool sin que una carga intimide a las demás. - L2ARC históricamente tenía un «impuesto de RAM». Almacenar encabezados de L2ARC en memoria solía ser caro. Las implementaciones más nuevas redujeron la sobrecarga, pero la lección permanece: cachear nunca es gratis.
- En Linux, la page cache y ARC pueden competir. OpenZFS en Linux se integra con cuidado, pero desde el punto de vista del operador sigue significando «existen dos capas de caché a menos que uses I/O directo».
- Recordsize interactúa con el valor de la caché. Registros grandes hacen feliz al rendimiento secuencial, pero también significan que cada admisión a la caché puede ser gruesa. Puedes contaminar el ARC más rápido con cargas de registros grandes.
- La deduplicación consume caché incluso cuando «no usas cache». Si activas dedup, el DDT quiere memoria. Esa es un eje de dolor separado que
primarycacheno resolverá.
Patrones de carga: cuándo usar all, metadata o none
primarycache=all (por defecto): para cargas que realmente reutilizan datos
Mantén all cuando los datos del dataset se reutilicen. Casos comunes:
- Imágenes de VM donde los invitados tocan repetidamente los mismos bloques
- Bases de datos con tablas e índices calientes que caben parcialmente en memoria
- Directorios personales y árboles de código donde metadatos y archivos pequeños se repiten
- Servicios sensibles a latencia que leen la misma configuración/plantillas repetidamente
Señal operativa: la ratio de aciertos del ARC debería correlacionar con mejoras de latencia perceptibles por el usuario. Si reinicias y el sistema «se calienta» en minutos u horas, el ARC está pagando su alquiler.
primarycache=metadata: el punto dulce infravalorado
metadata es mi respuesta por defecto para «este dataset es grande, mayormente secuencial, pero aún necesita listar directorios y recorrer snapshots sin sufrir».
Buen encaje:
- Repositorios de backups donde las restauraciones son ocasionales pero los listados y operaciones de snapshot son frecuentes
- Archivos multimedia grandes: muchos archivos grandes, pocas relecturas, pero aún navegas por ellos
- Data lakes donde los nodos de cómputo transmiten datos una vez por trabajo
Lo que obtienes: ARC almacena el cerebro del sistema de archivos, no el cuerpo en masa. Los recorridos de directorio, las diferencias de snapshot y las operaciones centradas en metadatos se mantienen rápidas, mientras que las lecturas en streaming no expulsan tus datos realmente calientes de otros datasets.
primarycache=none: el ajuste de «cuarentena»
none no es malvado. Es una zona de cuarentena para datasets que sabes que son tóxicos para la caché.
Buen encaje:
- Espacios scratch para exportaciones batch de una sola pasada
- Áreas de staging para recepciones de replicación (dependiendo del flujo)
- Canales de ingest masiva donde los datos se escriben una vez y se envían inmediatamente a otro sitio
Cuando revienta: lecturas aleatorias pequeñas, cargas centradas en metadatos o cualquier cosa con relecturas. Si pones none en un dataset de VM porque «ARC es grande», espera miseria en IOPS y compañeros de equipo confundidos.
Chiste #2 (corto y relevante): Poner primarycache=none en tu dataset de VM es como quitar las sillas para evitar que la gente se siente — técnicamente efectivo, socialmente catastrófico.
Tres micro-historias del mundo corporativo (victorias, errores y un héroe aburrido)
Micro-historia #1: El incidente causado por una suposición errónea (ARC ≠ «RAM gratis»)
En una empresa mediana con un clúster de virtualización mixto, un host empezó a hacer swap durante horas de trabajo. Los gráficos se veían raros: mucha memoria «disponible», pero el kernel reclamaba agresivamente y la latencia en una API de cara al cliente se disparó.
El on-call hizo lo que muchos hacemos bajo presión: miró el tamaño del ARC, vio que era grande y concluyó «ZFS está robando RAM». Redujeron el máximo del ARC en el host y se fueron a dormir. Al día siguiente estuvo más tranquilo — hasta que corrió el job semanal de backup.
Durante la ventana de backup, el host tuvo que servir lecturas de VM y escribir un enorme stream secuencial de backup desde un dataset que también almacenaba algunos discos de VM (porque «almacenamiento es almacenamiento»). El ARC, ahora constreñido, tenía menos espacio para el conjunto caliente de las VMs. El backup siguió transmitiendo felizmente, pero las VMs se volvieron más lentas y volvieron las quejas por latencia de la API.
La suposición errónea no fue «ARC usa RAM». La suposición errónea fue «el tamaño del ARC es el problema». El problema real era que una carga de backup estaba contaminando el ARC y expulsando el working set de VM. La solución no fue asfixiar el ARC; fue aislar la política: mover los backups a su propio dataset y poner primarycache=metadata allí. El ARC siguió siendo grande, pero ahora almacenaba cosas útiles.
Tras ese cambio, la latencia de la API volvió a ser aburrida. El job semanal de backup siguió corriendo. Sin heroicidades. Solo una propiedad de dataset que convirtió una caché caótica en una disciplinada.
Micro-historia #2: La optimización que salió mal (exceso de «solo metadatos»)
Otra organización tenía un ingeniero de almacenamiento al que le encantaban las reglas limpias. Decidió: «Los datos de la base de datos nunca deberían cachearse porque la base de datos tiene su propia caché». Puso primarycache=metadata en el dataset que contenía un clúster PostgreSQL, esperando liberar ARC para «cosas más importantes».
En papel sonaba defendible. En la práctica, ignoraba dos detalles: (1) no todas las lecturas de la base de datos son un hit en el buffer cache, especialmente durante despliegues y migraciones, y (2) la caché de la base de datos está limitada por memoria configurada y concurrencia de carga, no por deseos.
No lo notaron de inmediato. La latencia subió durante las horas de reporting y luego se disparó durante una migración de esquema que forzó grandes lecturas de índices. La demanda de IOPS del sistema subió bruscamente, pero el pool estaba dominado por HDD con rendimiento aleatorio limitado. El ARC podría haber absorbido una parte de eso. En cambio, cada miss de caché se volvió un evento de disco.
El rollback fue simple: devolver el dataset a primarycache=all (y ajustar otros parámetros con más cuidado). La lección: «las apps tienen caché» no significa que la caché del sistema de archivos sea redundante. Significa que necesitas entender la jerarquía de caché y los modos de fallo, especialmente durante operaciones atípicas como migraciones, VACUUM o reinicios en frío.
Micro-historia #3: La práctica aburrida pero correcta que salvó el día (límites de política + pruebas)
Un equipo empresarial ejecutaba un object store respaldado por ZFS y un conjunto de exportaciones NFS en el mismo pool. La carga del object store era mayormente lecturas/escrituras secuenciales grandes; las exportaciones NFS eran mucho churn de archivos pequeños desde jobs de CI.
Hicieron algo profundamente poco sexy: crearon datasets separados por clase de carga, documentaron las propiedades previstas y las hicieron cumplir con una simple comprobación de configuración en su pipeline de provisioning. Para los datasets del object store: primarycache=metadata. Para los datasets NFS de CI: primarycache=all. Para staging efímero: primarycache=none.
Meses después, un equipo interno lanzó un nuevo job de analítica que leía decenas de terabytes secuencialmente durante el día. En muchas empresas, eso es el inicio de una guerra. Aquí, fue un no-evento: el job corrió sobre un dataset ya en cuarentena para caching de datos. La granja de CI se mantuvo rápida. Las exportaciones NFS no se pusieron lentas. El único «incidente» fue que alguien preguntó por qué no ardió nada.
Esa es la ganancia real: corrección aburrida. No una heroicidad a medianoche con sysctl, sino un conjunto de límites que hacen que cargas impredecibles sean menos peligrosas.
Tareas prácticas: comandos, interpretación y qué cambiar
Abajo hay tareas prácticas que puedes ejecutar en un sistema OpenZFS típico (ejemplos en Linux). Los comandos son realistas; adapta nombres de dataset/pool.
Tarea 1: Listar datasets y encontrar ajustes de primarycache
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
Interpretación: Busca herencias inesperadas. Si un padre tiene none, todo lo que esté debajo puede estar sufriendo silenciosamente. La columna SOURCE te dice si es local, inherited o default.
Tarea 2: Revisar también secondarycache (admisión a L2ARC)
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
Interpretación: Una combinación sensata y común para backups es primarycache=metadata y secondarycache=metadata, manteniendo ARC y L2ARC enfocados en metadatos.
Tarea 3: Cambiar primarycache de forma segura en un dataset
cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup
Interpretación: Esto cambia las admisiones futuras al ARC. No purgará instantáneamente los datos ya cacheados de ese dataset. Espera cambios de comportamiento con el tiempo a medida que el ARC va rotando.
Tarea 4: Verificar tu cambio y ver la herencia
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
Interpretación: Confirma que el dataset que te importa no esté heredando algo que olvidaste.
Tarea 5: Identificar datasets «tóxicos para la caché» por carga (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
-------------------------- ----- ----- ----- ----- ----- -----
Interpretación: Si un dataset está generando gran ancho de banda secuencial de lectura/escritura, es candidato principal para primarycache=metadata o none dependiendo de la reutilización.
Tarea 6: Observar tamaño y comportamiento básico del ARC
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
Interpretación: No adores una única ratio de aciertos. Compara demand data vs demand metadata. Si las misses de metadatos son altas y la latencia duele, probablemente necesites más caché de metadatos, no menos.
Tarea 7: Vigilar el comportamiento del ARC en el tiempo durante un job de streaming
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
----
Interpretación: Durante una lectura en streaming, puedes ver que las demand data misses suben mientras los hits no ayudan de forma significativa después. Esa es la señal de «deja de cachear los datos de este dataset».
Tarea 8: Comprobar presión de memoria y swapping (¿compite ARC con tus 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
Interpretación: Uso de swap más baja memoria «available» es una señal de que necesitas revisar la política de caché. El ARC puede ser grande y estar bien, pero si tus apps están intercambiando, estás pagando doble alquiler.
Tarea 9: Determinar si un dataset es principalmente secuencial (muestreo rápido)
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
Interpretación: Un gran rareq-sz y alto throughput sugiere streaming. Ahí es donde primarycache=metadata suele ganar, especialmente si las relecturas son raras.
Tarea 10: Medir un antes/después real con caché algo fría (con cuidado)
Esto es peligroso en producción si eres descuidado. Pero puedes probar con un archivo específico y lecturas controladas. Usa un dataset no crítico o un host de pruebas.
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
Interpretación: Si el rendimiento no cambia pero la contaminación del ARC sí, has encontrado dinero gratis. Las lecturas en streaming a menudo no se benefician de cachear, pero pueden expulsar datos valiosos.
Tarea 11: Inspeccionar propiedades del dataset que amplifican el riesgo de contaminación
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
Interpretación: Un recordsize grande es perfecto para backups, pero también significa que cada registro cacheado es enorme. Emparejar registros grandes con primarycache=metadata suele ser la forma más sensata de evitar que la RAM se convierta en un museo de backups.
Tarea 12: Encontrar datasets con «none» heredado (el asesino silencioso de rendimiento)
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
Interpretación: Si algo importante está heredando none, esa es una política de rendimiento accidental. Arréglalo al nivel correcto (sobreescribir el hijo o corregir el padre).
Tarea 13: Confirmar límites del ARC (no luches ciegamente contra el OS)
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_max
109951162777
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_arc_min
17179869184
Interpretación: Si has constreñido demasiado el ARC, podrías estar forzando lecturas de disco para metadatos calientes. Usa primarycache para mejorar la calidad del ARC antes de reducir su tamaño.
Tarea 14: Validar una política prevista en un árbol (detección de deriva)
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
Interpretación: Este es el hábito SRE: comprobar que la realidad coincide con tu modelo mental. La mayoría de desastres de caché vienen de «pensábamos que estaba configurado» en lugar de teoría mala.
Guía rápida de diagnóstico
Este es el camino de «está lento y la gente te mira». Está ordenado para encontrar el cuello de botella probable rápido, no para ser académicamente completo.
Paso 1: ¿Está el sistema falto de memoria o haciendo swap?
cr0x@server:~$ free -h
cr0x@server:~$ vmstat 1 5
cr0x@server:~$ swapon --show
Qué buscas: baja memoria available, actividad de swap-in/swap-out y síntomas como pausas aleatorias. Si hay swapping, decide si la admisión al ARC (primarycache) está contaminando la memoria o si realmente necesitas más RAM.
Paso 2: ¿Está el pool limitado por I/O o por CPU?
cr0x@server:~$ iostat -x 1 5
cr0x@server:~$ zpool iostat -v 1 5
Qué buscas: alto %util de dispositivo, alto await y colas. Si los discos están saturados, el tuning del ARC solo puede ayudar si aumentas la tasa de aciertos para los datos/metadatos relevantes. Si la CPU está saturada (compresión, checksums), cambiar la caché no solucionará la saturación de cómputo.
Paso 3: ¿Está ARC ayudando la carga que te importa?
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 )"
Qué buscas: ¿Las misses de metadatos son altas? ¿Suben las demand data misses durante un job de streaming? Si sí, considera poner primarycache=metadata en el dataset que está haciendo I/O en streaming.
Paso 4: Identificar el dataset que causa churn
cr0x@server:~$ zfs iostat -v tank 2 10
Qué buscas: Qué dataset/pool está empujando el ancho de banda/IOPS. Asócielo con contexto de negocio (backups, analítica, almacenamiento de VM) y aplica la política de caché en consecuencia.
Paso 5: Aplicar un cambio dirigido (pequeño radio de impacto)
cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup
Qué buscas: Reducción del churn del ARC y mejora de la estabilidad para otras cargas. No cambies el máximo del ARC primero a menos que estés muriendo; deja de admitir basura antes de reducir la caché.
Errores comunes (síntomas y soluciones)
Error 1: Poner primarycache=none en «almacenamiento lento» (y empeorarlo)
Síntomas: Tormentas de arranque de VM empeoran, aumentan las latencias de lecturas aleatorias pequeñas, las operaciones centradas en metadatos se ralentizan, la ratio de aciertos del ARC cae para lecturas significativas.
Solución: Restaura primarycache=all para datasets con reutilización (VMs, bases de datos) y pon en cuarentena solo los datasets de streaming. Si necesitas proteger ARC de escaneos, pon primarycache=metadata en backups/archivos en lugar de eliminar la caché por completo.
Error 2: Aplicar la política de caché en el nivel equivocado (herencia peligrosa)
Síntomas: «Lo cambiamos pero nada mejoró» o «¿por qué cambió el comportamiento del datastore de VM?»
Solución: Usa zfs get -r ... source. Corrige el dataset padre si la política debe ser amplia; sobreescribe en el hijo si es una excepción. Documenta el árbol de herencia previsto.
Error 3: Confundir primarycache con secondarycache
Síntomas: Añades un dispositivo L2ARC y ves poco beneficio; el ARC se llena de basura de todos modos.
Solución: Recuerda: primarycache controla la admisión al ARC, secondarycache controla la admisión al L2ARC. Para datasets de streaming, considera primarycache=metadata y secondarycache=metadata, no confiar en «L2ARC me salvará».
Error 4: Sobreoptimizar por ratio de aciertos en lugar de latencia
Síntomas: El tablero de métricas «mejora» después de un cambio, pero los usuarios se quejan más.
Solución: Trata la ratio de aciertos como una pista, no como un KPI. Enfócate en demand metadata misses, latencia de cola alta y profundidad de cola de disco. Cachea datos irrelevantes y puedes inflar los hits mientras deprimas la carga real.
Error 5: Ignorar recordsize y alineación con la carga
Síntomas: El ARC crece rápido durante operaciones masivas; la presión de memoria aumenta; poco beneficio de rendimiento.
Solución: Registro grande (recordsize) más lecturas en streaming suele significar «contaminación de caché a escala». Usa primarycache=metadata en esos datasets y mantén registros grandes donde pertenecen (backup/archivo), no en cargas aleatorias.
Error 6: Luchar con ARC max/min antes de arreglar la calidad de admisión
Síntomas: Reduces el máximo del ARC para «liberar memoria», pero el rendimiento se vuelve inestable, suben las misses de metadatos y aumenta la I/O de disco.
Solución: Primero, deja de admitir basura con primarycache. Después, si aún necesitas limitar ARC para cargas co-localizadas, hazlo con cuidado y valida las misses de metadatos.
Listas de verificación / plan paso a paso
Checklist A: Decidir el valor correcto de primarycache para un dataset
- Clasifica el dataset: VM/bases de datos, compartidos generales, backups, archivo, scratch.
- Haz una pregunta brutal: «¿Volvemos a leer los mismos bloques de datos dentro de horas?»
- Si la respuesta es sí: empieza con
primarycache=all. - Si mayormente no, pero los metadatos importan: elige
primarycache=metadata. - Si es desechable/de una sola pasada: considera
primarycache=none. - Valida con
zfs iostaty estadísticas de ARC durante la ventana de trabajo real.
Checklist B: Implementar de forma segura (mínimo radio de impacto)
- Confirma la ruta del dataset y el árbol de herencia:
cr0x@server:~$ zfs list -r tank cr0x@server:~$ zfs get -r -o name,value,source primarycache tank - Cambia solo un dataset a la vez:
cr0x@server:~$ sudo zfs set primarycache=metadata tank/backup - Observa durante 15–60 minutos (o un ciclo 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)" - Registra el resultado: latencia, swap, await de disco, comportamiento del ARC.
- Si ayudó, codifícalo (infra-as-code, plantillas de provisioning o un runbook).
Checklist C: Separar cargas para que la política de caché funcione
- Crea datasets por clase de carga, no por organigrama.
- Mantén backups/archivos fuera del árbol de datasets de VM.
- Aplica
primarycacheen el límite de dataset donde la carga sea homogénea. - Audita trimestralmente para «alguien volcó una nueva carga en el dataset equivocado».
Preguntas frecuentes
1) ¿El cambio de primarycache afecta los datos cacheados existentes inmediatamente?
No. Afecta lo que se admite en adelante. Los contenidos existentes del ARC caducarán a medida que la caché rote. Si necesitas impacto inmediato, el enfoque operativo suele ser «esperar el churn» o programar cambios antes del job pesado, no purgar cachés forzadamente en producción.
2) ¿Debería poner primarycache=metadata para todos los backups?
A menudo sí, porque los backups son clásicos streams «leer una vez» que pueden contaminar el ARC, mientras que los metadatos siguen importando para navegar, gestionar snapshots y seleccionar restauraciones. Pero si restauras rutinariamente el mismo subconjunto (restauraciones de prueba, restauraciones parciales frecuentes), podrías beneficiarte de cachear algunos datos—mídelo.
3) ¿primarycache=none es alguna vez correcto?
Sí, para datasets verdaderamente desechables (scratch, staging) o para flujos donde cachear daña activamente cargas más importantes. El error es usar none como respuesta única a «ZFS usa demasiada RAM».
4) ¿Cómo difieren primarycache y secondarycache en términos sencillos?
primarycache decide qué puede ir a RAM (ARC). secondarycache decide qué puede ir a L2ARC (normalmente SSD). Puedes decirle a ZFS «solo metadatos» para ambos para mantener las dos capas de caché centradas en la estructura del sistema de archivos en lugar de datos en bloque.
5) ¿Poner primarycache=metadata acelerará las escrituras?
No directamente. Se trata de cacheo de lectura. Sin embargo, indirectamente, puede mejorar la estabilidad general del sistema y reducir la presión de memoria, lo que ayuda a sistemas con muchas escrituras que sufrían por reclaim o swapping. Las escrituras están más influenciadas por ZIL/SLOG, tuning de transaction group, latencia de dispositivos y ajustes de sync.
6) Si mi aplicación tiene su propia caché (Redis, PostgreSQL shared_buffers), ¿debo desactivar el cacheo de datos del ARC?
No automáticamente. Muchas aplicaciones cachéan algunas cosas bien y otras mal, y no cubren el arranque en frío o operaciones atípicas. La caché del sistema de archivos suele ayudar con índices, binarios, bibliotecas compartidas y actividad del OS. Trata primarycache como una herramienta de carga: prueba los cambios durante operaciones realistas (incluyendo reinicios, migraciones y picos).
7) ¿Por qué ARC sigue creciendo si pongo primarycache=metadata en un dataset grande?
Porque otros datasets pueden seguir cacheando datos, los metadatos pueden ser sustanciales y ARC también contiene estructuras internas. Además, el crecimiento del ARC no es inherentemente malo; es malo cuando ahoga a consumidores de memoria más valiosos o cuando está lleno de bloques de bajo valor.
8) ¿Cuál es la política «buena por defecto» más simple para un pool mixto?
Mantén primarycache=all para VM/bases de datos y compartidos interactivos. Usa primarycache=metadata para backups/archivos. Usa primarycache=none para scratch/staging que sea realmente de una sola pasada. El truco real no son los valores: es crear datasets que separen claramente las cargas.
9) ¿Puedo arreglar la contaminación del ARC sin cambiar primarycache?
A veces. ARC está diseñado para resistir la contaminación por escaneos, y también puedes gestionar límites de tamaño del ARC. Pero primarycache es la política de admisión por dataset más limpia. Si el problema es «este dataset no debe consumir RAM para datos», no confíes en heurísticas globales para adivinarlo.
10) ¿Cómo sé si la caché de metadatos es el cuello de botella?
Mira las demand metadata misses en arcstats y corrélalas con latencia durante recorridos de directorio, operaciones de snapshot y cargas de archivos pequeños. Si las misses de metadatos suben y los discos están ocupados, tu «problema de caché» probablemente sea metadatos, no datos en bloque.
Conclusión
primarycache es una de esas propiedades de ZFS que parece una preferencia menor hasta que operas un pool de cargas mixtas el tiempo suficiente. Entonces se convierte en un límite: la diferencia entre ARC siendo una capa de rendimiento con alta IQ y ARC siendo un acaparador con espacio de armario ilimitado.
La jugada práctica es simple: separa las cargas en datasets que tengan sentido, mantiene primarycache=all donde los datos se reutilizan realmente, y usa primarycache=metadata (o ocasionalmente none) para evitar que cargas en streaming y desechables expulsen tu conjunto caliente real. Hazlo con medición, no con sensaciones. En producción, lo «aburrido y correcto» vence a lo «listo y global» cada vez.