ZFS: La única configuración ‘segura’ que destruye el rendimiento con el tiempo

¿Te fue útil?

Todo parece sano. El pool está ONLINE. Sin errores. El ARC está caliente. La latencia es… rara. Lecturas que antes eran aburridas ahora son espasmódicas, y tus gráficas de almacenamiento muestran esa curva familiar de “la rana hirviendo lentamente”. La gente culpa a la red. O al hipervisor. O a “ZFS siendo ZFS”.

A veces no es nada de eso. A veces es una casilla, un ajuste por defecto “seguro” a nivel de checkbox que convierte lecturas rutinarias en un flujo constante de escrituras—y luego deja que esas escrituras erosionen tu rendimiento durante meses.

La configuración: atime=on (y por qué es una bomba de tiempo para el rendimiento)

Si administras ZFS el tiempo suficiente, verás este patrón: un pool que arranca rápido, se mantiene aceptable un tiempo y luego desarrolla gradualmente picos de latencia durante cargas intensas de lectura. El almacenamiento no “falla”. Simplemente se vuelve molesto.

El culpable suele ser el inocente predeterminado: atime=on. Actualizaciones de tiempo de acceso. Cada vez que se lee un archivo, se actualiza su marca de tiempo de acceso. En muchos sistemas eso está bien. En un dataset ZFS muy ocupado—especialmente con muchos archivos pequeños, cargas intensivas en metadata o VMs—es un generador de escrituras disfrazado de tráfico de lectura.

Aquí está la parte sucia: el coste en rendimiento no siempre es inmediato. ZFS puede absorber mucho mediante caché y transaction groups. Tu pool puede parecer bien durante semanas. Pero la amplificación de escrituras y la agitación de metadata se acumulan. La fragmentación se infiltra. La metadata pierde eficiencia en caché. La latencia sube, no porque ZFS sea “lento”, sino porque le pediste que haga trabajo extra para siempre.

Consejo con criterio: Para casi cualquier dataset en producción, configura atime=off. Actívalo solo cuando puedas nombrar el comportamiento de la aplicación que lo requiere y hayas medido el impacto.

Qué hace realmente atime en ZFS

En teoría, atime es simple: cuando se accede (se lee) un archivo, el sistema de ficheros actualiza la metadata: “este archivo fue accedido en la marca X”. Suena como un cambio minúsculo. Pero en sistemas copy-on-write como ZFS, “pequeñas actualizaciones de metadata” no siempre son pequeñas.

Por qué una actualización de metadata puede convertirse en una escritura real

ZFS es copy-on-write. Actualizar metadata normalmente implica escribir nuevos bloques de metadata, actualizar punteros de bloque y eventualmente confirmar esos cambios en el pool. Eso no es malo; es cómo ZFS mantiene la consistencia. Pero significa que una carga de trabajo que sería de solo lectura se convierte en un flujo constante de escrituras—a menudo pequeñas, dispersas, algo síncronas en las capas equivocadas y frecuentemente ubicadas en lugares que no se compactan bien en I/O secuencial.

Por qué esto importa más en hosts de VM y contenedores

Los hosts de virtualización leen mucho: librerías, binarios, caches de paquetes, capas de contenedor e imágenes de VM. También escanean directorios, hacen many stat de archivos y realizan accesos de corta duración. Si atime=on, esas “lecturas” también mutan metadata. Ahora el host realiza escrituras en segundo plano mientras jurabas que era “mayormente de solo lectura”. Tus gráficas de latencia discrepan.

“Pero es solo una marca de tiempo”

Sí. Y sin embargo: una actualización de marca de tiempo por lectura multiplicada por millones de lecturas por día deja de ser una marca de tiempo. Es una carga de trabajo.

Broma breve #1: Activar atime en producción es como poner un flag de “por favor registra todo” en tu camino crítico. Funcionará genial hasta que deje de hacerlo.

Por qué empeora con el tiempo (en silencio)

La parte de “con el tiempo” es lo que hace que esta configuración sea tan eficaz desperdiciando tu semana. Cuando atime=on, añades un flujo continuo de pequeñas actualizaciones de metadata. ZFS las agrupará en transaction groups, pero los patrones siguen importando. Con el paso de meses puedes acabar con:

  • Más escrituras pequeñas de las que tu modelo de carga había previsto.
  • Más bloques de metadata actualizados y reescritos de lo esperado.
  • Más fragmentación, especialmente si el espacio libre disminuye o las clases de asignación se estresan.
  • Más contención en la canalización de escritura: sincronización de TXG, mapas de espacio SPA, colas de vdev.
  • Menos caché efectiva, porque la agitación de metadata desplaza datos “útiles” de la caché.

No es solo IOPS; es la latencia de cola

La mayoría nota la degradación del rendimiento cuando las medias derivan. Lo que duele primero es la latencia en la cola: el percentil 99. Ahí es donde aparecen las escrituras aleatorias pesadas en metadata. Tu aplicación deja de ser fluida. Aparecen timeouts. Los ingenieros reciben alertas por “almacenamiento intermitente” y pasan dos días demostrando que la red es inocente.

Por qué se esconde a plena vista

La monitorización suele categorizar I/O como lectura vs escritura a nivel del dispositivo de bloque. Pero atime convierte lecturas en escrituras de metadata que quizá no parezcan “escrituras de la aplicación”. Parece actividad de sistema de ficheros en segundo plano. Y como es “normal”, rara vez se cuestiona.

Hechos & contexto histórico (lo breve y útil)

  1. El tiempo de acceso es anterior a gran parte de tu infraestructura. UNIX lleva trazando atime/mtime/ctime desde hace décadas, mucho antes de que los SSD, hipervisores y microservicios hicieran costosas las “pequeñas escrituras de metadata” a escala.
  2. Linux introdujo relatime como compromiso. La industria notó la sobrecarga de atime años atrás; relatime actualiza atime menos agresivamente (a menudo una vez al día o cuando cambian mtime/ctime).
  3. Los valores por defecto de ZFS históricamente favorecieron la corrección y las expectativas POSIX. Defaultear a atime=on se alinea con la semántica tradicional, no con las expectativas modernas de rendimiento.
  4. Los sistemas CoW pagan por la agitación de metadata de forma diferente. Ext* puede actualizar in-place; ZFS escribe nuevos bloques de metadata. Eso es una fortaleza para la integridad—y un coste para la agitación innecesaria.
  5. Atime interactúa con las snapshots. Las snapshots preservan metadata antigua; las reescrituras frecuentes de metadata pueden aumentar la rotación de bloques referenciados y complicar el comportamiento de contabilidad de espacio.
  6. Las cargas NFS y SMB pueden amplificar las actualizaciones de atime. Las operaciones de metadata sobre sistemas de archivos en red pueden desencadenar comprobaciones de acceso adicionales y toques de archivos, incrementando la frecuencia de actualizaciones.
  7. Los formatos de imagen de VM no te salvan. Incluso si el invitado realiza “solo lecturas”, el sistema de ficheros del host puede seguir actualizando atime en el archivo de imagen y en caches del host.
  8. Muchos appliances y productos NAS desactivan atime en silencio. No porque odien POSIX, sino porque odian tickets de soporte sobre “el NAS se volvió más lento”.
  9. Los special vdev cambiaron el juego para la metadata. Funciones modernas de OpenZFS como clases de asignación especiales pueden aislar metadata en dispositivos más rápidos—útil, pero también una forma de enmascarar el problema real (escrituras innecesarias).

Tres mini-historias corporativas desde el terreno

Mini-historia #1: El incidente causado por una suposición equivocada

La Compañía A operaba una flota grande de CI. Todo era “inmutable” por política: artefactos descargados, tests ejecutados, resultados subidos. El almacenamiento era un pool ZFS en SSDs con margen confortable. La suposición era simple y razonable: “Los runners de CI leen mayormente; no desgastarán los discos ni golpearán el pool.”

Semanas después, las builds empezaron a fallar por timeout. No de forma consistente—suficiente como para romper la confianza de los desarrolladores. El equipo persiguió a los sospechosos habituales: rendimiento del registry, resolución DNS, CPU steal en nodos Kubernetes, caídas de red. Los dashboards de almacenamiento mostraban principalmente lecturas. El pool no tenía errores de checksum. SMART estaba limpio. Todos estaban molestos por distintas razones.

La pista estuvo en las IOPS de escritura que no coincidían con ninguna ruta de escritura conocida. En el host ZFS, el dataset que guardaba caches de runners tenía atime=on. Cada dependencia leída durante las builds actualizaba tiempos de acceso en cientos de miles de archivos. La carga no era “solo lectura”. Era “lectura más escritura de metadata”. Bajo carga, las ráfagas de sincronización de TXG se alineaban con fases de pruebas, haciendo que la latencia de cola pareciera una lentitud aleatoria de compute.

Desactivaron atime en el dataset de cache y no reiniciaron nada. Los timeouts desaparecieron. La postmortem fue corta y algo embarazosa, que es la mejor: un cambio pequeño que te enseña a desconfiar de los “por defecto”.

Mini-historia #2: La optimización que salió mal

La Compañía B alojaba apps multi-tenant en un clúster de VM respaldado por ZFS. Se pusieron ingeniosos: pusieron datasets “hot” en mirrors SSD rápidos y datasets “cold” en almacenamiento masivo. Luego intentaron optimizar la recolección de estadísticas habilitando un conteo de accesos más detallado. Cierto middleware quería atime para heurísticas de expiración de cache, así que activaron atime=on a gran escala.

Funcionó—por un tiempo. Entonces el pool “hot” en SSD empezó a mostrar picos periódicos de latencia. No saturación, no profundidad de cola consistente, solo ráfagas feas. El equipo respondió como adultos: añadieron más SSDs. Los picos fueron menos frecuentes pero no desaparecieron. Actualizaron firmware. Seguían ahí.

Lo ocurrido fue clásico: la optimización asumió que atime era “solo metadata” y por tanto barato en SSD. Pero las actualizaciones de atime crearon un flujo constante de escrituras pequeñas y dispersas que interfirieron con la coalescencia normal de escrituras del pool. Peor aún, esas escrituras aumentaron la fragmentación de metadata. Tras meses, incluso las lecturas requirieron más búsquedas de metadata que fallaban en ARC y llegaban al disco.

Finalmente movieron los pocos datasets que realmente necesitaban atime a pools aislados con ajustes afinados y dejaron el resto en atime=off. Añadir hardware ayudó, pero desactivar la carga autoinfligida ayudó más. La lección: si no puedes explicar por qué necesitas una característica, no estás optimizando—estás añadiendo variables.

Mini-historia #3: La práctica aburrida pero correcta que salvó el día

La Compañía C tenía un entorno mixto: shares de archivos, almacenamiento de VM y algunas bases de datos. Tenían una regla: cada dataset debía declarar su intención. Los datasets de VM reciben un conjunto conocido de propiedades. Los shares reciben otro. Cualquier cosa “especial” necesita un ticket describiendo por qué. Suena burocrático. En realidad es una forma de evitar complejidad accidental.

Cuando un equipo nuevo incorporó un servicio de analítica de logs, pidieron un dataset con semántica POSIX estricta “por si acaso”. El ingeniero de almacenamiento presionó: “Define tus requisitos reales”. Probaron y descubrieron que el servicio no usaba atime en absoluto; usaba mtime y su propio indexado. El dataset se entregó con atime=off, compression=on y un recordsize alineado con la carga.

Seis meses después, otro entorno ejecutando el mismo servicio en otro lugar tenía un problema lento: latencia en ascenso, fragmentación creciente y tormentas de sync periódicas. La Compañía C no. Sus sistemas no eran mágicos; eran aburridos. Configuraciones aburridas, baselines aburridos, auditorías aburridas.

El momento de “salvar el día” no fue una depuración heroica. Fue la ausencia del problema—porque tenían un perfil por defecto que lo evitaba.

Guion de diagnóstico rápido

Esta es la secuencia de “tengo 20 minutos antes de la llamada por el incidente”. No filosofes. No toques diez cosas. Identifica el cuello de botella dominante y confirma si la agitación por atime está en la foto.

Primero: demuestra que tienes un problema de latencia de almacenamiento (no CPU/red)

  1. Comprueba la latencia a nivel de aplicación frente a la latencia del disco en el host. Si los picos de latencia de la app se correlacionan con la latencia de vdev de ZFS, es real.
  2. Busca encolamiento: alto await y aumento de aqu-sz indican que el dispositivo no puede seguir el ritmo.

Segundo: identifica si “las lecturas causan escrituras”

  1. Comprueba atime en los datasets del camino caliente.
  2. Correlaciona la carga de lectura con IOPS de escritura inesperadas y actividad de sincronización de TXG.

Tercero: decide si el problema está ligado a metadata

  1. Operaciones altas de metadata, escrituras pequeñas frecuentes, ARC presionado por agitación de metadata: estás ligado a metadata.
  2. Si tienes un special vdev, verifica si está sobrecargado. Si no lo tienes, valora si merece la pena añadir uno—pero solo después de desactivar las actualizaciones de atime innecesarias.

Cuarto: arregla lo menos riesgoso primero

  1. Desactiva atime en los dataset(s) donde no sea requerido.
  2. Vuelve a comprobar latencia e IOPS de escritura en horas, no días.

Broma breve #2: Si tu servicio “solo lectura” está haciendo 5.000 IOPS de escritura, el servicio o está mintiendo o tu sistema de ficheros está muy entusiasta.

Tareas prácticas: comandos, salida y qué significa la salida

Abajo hay tareas reales que puedes ejecutar en un host ZFS. Cada una incluye: un comando, salida de ejemplo, qué significa y la decisión operativa a tomar. El objetivo es pasar de “se siente lento” a “este dataset genera escrituras de metadata porque atime está on”.

Tarea 1: Encontrar datasets con atime activado

cr0x@server:~$ zfs get -r -o name,property,value,source atime tank
NAME                 PROPERTY  VALUE  SOURCE
tank                 atime     on     default
tank/vm              atime     off    local
tank/home            atime     on     inherited from tank
tank/ci-cache        atime     on     local

Qué significa: tank/home heredó on. tank/ci-cache está explícitamente puesto en on.

Decisión: Identifica cuáles de esos datasets están en caminos críticos de rendimiento. Planea poner atime=off en los que no lo requieran de verdad.

Tarea 2: Confirma dónde está el I/O caliente (I/O por dataset)

cr0x@server:~$ zfs iostat -v tank 2 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        3.12T  1.88T    850   2100   110M   95.0M
  mirror                    3.12T  1.88T    850   2100   110M   95.0M
    nvme0n1                     -      -    430   1050  55.2M  48.0M
    nvme1n1                     -      -    420   1050  54.8M  47.0M
--------------------------  -----  -----  -----  -----  -----  -----

Qué significa: Las escrituras son altas en relación con lo esperado. Esta salida por sí sola no prueba atime, pero indica presión real de escritura.

Decisión: Si la carga debería ser de solo lectura, investiga por qué ocurren escrituras (atime, sync, logs de la aplicación, archivos temporales).

Tarea 3: Comprueba si el dataset está montado con el comportamiento esperado

cr0x@server:~$ zfs get -o name,property,value,source mountpoint,canmount,atime tank/ci-cache
NAME          PROPERTY    VALUE             SOURCE
tank/ci-cache mountpoint  /tank/ci-cache    local
tank/ci-cache canmount    on                default
tank/ci-cache atime       on                local

Qué significa: Está montado y actualizando atime activamente.

Decisión: Si la aplicación no consume atime, desactívalo.

Tarea 4: Desactivar atime de forma segura (nivel dataset)

cr0x@server:~$ sudo zfs set atime=off tank/ci-cache

Qué significa: Nuevos accesos no actualizarán la metadata de tiempo de acceso en ese dataset.

Decisión: Aplica primero a los datasets más problemáticos. Evita cambiar datasets raíz/sistema hasta que estés seguro de que nada depende de la semántica de atime.

Tarea 5: Verificar que el cambio se aplicó

cr0x@server:~$ zfs get -o name,property,value,source atime tank/ci-cache
NAME          PROPERTY  VALUE  SOURCE
tank/ci-cache atime     off    local

Qué significa: La propiedad está establecida localmente y persistirá.

Decisión: Controla las diferencias de rendimiento en las siguientes horas. Si no ves mejora, sigue investigando—no asumas que atime era el único factor.

Tarea 6: Ver si el “tráfico de lectura” sigue provocando escrituras tras el cambio

cr0x@server:~$ zpool iostat -v tank 1 5
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        3.12T  1.88T    900    420   120M   18.0M
  mirror    3.12T  1.88T    900    420   120M   18.0M
    nvme0n1     -      -    450    210  60.0M  9.1M
    nvme1n1     -      -    450    210  60.0M  8.9M

Qué significa: Si las escrituras cayeron bruscamente mientras las lecturas se mantenían similares, acabas de eliminar una fuente importante de escrituras.

Decisión: Si la latencia mejoró, sigue desplegando el cambio a datasets similares. Si no, el pool probablemente esté limitado por otro lado (writes sync, fragmentación, saturación de special vdev o límites del dispositivo).

Tarea 7: Comprobar la salud del pool y errores (no lo omitas)

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 03:12:44 with 0 errors on Sun Feb  2 03:20:11 2026
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          mirror    ONLINE       0     0     0
            nvme0n1 ONLINE       0     0     0
            nvme1n1 ONLINE       0     0     0

errors: No known data errors

Qué significa: Sin problemas de integridad. Bien—el trabajo de rendimiento tiene sentido ahora.

Decisión: Si tienes errores, deja de afinar y empieza a arreglar hardware/cableado/firmware. Afinar el rendimiento de un pool enfermo es cosplay.

Tarea 8: Inspeccionar comportamiento de ARC y presión de memoria (Linux OpenZFS)

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
12:44:01   850    90     10    40   44    50   56     0    0   48G   64G
12:44:02   900    85      9    38   45    47   55     0    0   48G   64G
12:44:03   920    82      9    36   44    46   56     0    0   48G   64G

Qué significa: Una tasa de miss ~9–10% podría estar bien, pero si los misses suben con operaciones pesadas en metadata, verás lecturas a disco y latencia.

Decisión: Si ARC está constantemente presionado y los misses de metadata son altos, considera si la agitación de metadata (como atime) está expulsando caché útil. Desactivar atime es la ganancia barata.

Tarea 9: Medir el comportamiento de TXG sync (proxy de “estrés en la canalización de escritura”)

cr0x@server:~$ grep -i txg /proc/spl/kstat/zfs/txg | head
12 1 0x01 87 4224 1122334455 987654321

Qué significa: En Linux, las estadísticas de txg pueden ser opacas; normalmente usas herramientas de más alto nivel y las correlacionas con zpool iostat y latencia. La idea es buscar ráfagas periódicas de sync.

Decisión: Si ves escrituras en ráfagas que coinciden con picos de latencia de la app, reduce primero la agitación en segundo plano (atime), luego examina ajustes de sync y dispositivos log.

Tarea 10: Comprobar recordsize del dataset y la alineación con la carga

cr0x@server:~$ zfs get -o name,property,value,source recordsize tank/vm tank/home
NAME      PROPERTY    VALUE   SOURCE
tank/vm   recordsize  16K     local
tank/home recordsize  128K    default

Qué significa: Los datasets de VM suelen usar bloques más pequeños. Los directorios home con muchos archivos pequeños podrían estar bien en 128K, pero la agitación de metadata domina en cualquier caso si atime está on.

Decisión: No persigas cambios de recordsize antes de arreglar las fuentes obvias de agitación. Afinar recordsize no te salvará de escrituras autoinfligidas por atime.

Tarea 11: Confirmar si un dataset se usa para bases de datos o cargas tipo log

cr0x@server:~$ zfs get -o name,property,value,source logbias,sync tank/db
NAME     PROPERTY  VALUE    SOURCE
tank/db  logbias   latency  default
tank/db  sync      standard default

Qué significa: Los valores por defecto son conservadores. Para bases de datos puedes tener comportamiento de sync intencional. Eso es independiente de atime, pero no debes diagnosticar mal la latencia por sync como si fuera atime.

Decisión: Si el dataset es una base de datos, valida los requisitos de durabilidad de la app antes de tocar sync. Aún así puedes desactivar atime de forma segura en la mayoría de los casos DB.

Tarea 12: Identificar si un dataset está pesado en snapshots (visibilidad de espacio y agitación)

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,mountpoint -s used | tail -n 5
tank/home@daily-2026-01-30     12.4G   220G  -
tank/home@daily-2026-01-31     13.1G   220G  -
tank/home@daily-2026-02-01     13.8G   220G  -
tank/home@daily-2026-02-02     14.2G   220G  -
tank/home@daily-2026-02-03     14.9G   220G  -

Qué significa: El crecimiento en “used” de snapshots puede reflejar agitación en el dataset. Las actualizaciones de atime pueden contribuir a la agitación, especialmente en patrones de metadata, incluso cuando el contenido de archivos no cambia.

Decisión: Si el crecimiento de snapshots es sorprendente para un dataset “mayormente de solo lectura”, audita atime y otros comportamientos que cambian metadata.

Tarea 13: Comprobar el espacio libre del pool (acelerador de fragmentación)

cr0x@server:~$ zpool list tank
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  5.00T  3.12T  1.88T        -         -    38%    62%  1.00x  ONLINE  -

Qué significa: Fragmentación al 38% y CAP al 62% no es catastrófico, pero si CAP sube hacia 80–90%, la asignación se vuelve más dispersa. La agitación por atime añade más pequeñas asignaciones a ese desorden.

Decisión: Mantén los pools cómodamente por debajo de marcas críticas, especialmente para cargas con escrituras aleatorias. Desactiva atime para reducir la agitación y frenar el crecimiento de fragmentación.

Tarea 14: Mirar latencia por vdev (dónde duele)

cr0x@server:~$ zpool iostat -v tank -l 1 3
                              capacity     operations     bandwidth    total_wait     disk_wait
pool                        alloc   free   read  write   read  write   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
tank                        3.12T  1.88T    920    480   125M   22.0M    2ms   18ms    1ms   16ms
  mirror                    3.12T  1.88T    920    480   125M   22.0M    2ms   18ms    1ms   16ms
    nvme0n1                     -      -    460    240  62.5M  11.1M    2ms   17ms    1ms   15ms
    nvme1n1                     -      -    460    240  62.5M  10.9M    2ms   19ms    1ms   17ms
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----

Qué significa: La espera en escritura es mucho mayor que la de lectura, aunque la carga parezca “de lectura”. Eso es coherente con ráfagas de escrituras de metadata.

Decisión: Si al desactivar atime reduces la espera en escritura, lo has confirmado como contribuyente. Si no, investiga writes sync, comportamiento del SLOG y saturación de vdev.

Tarea 15: Validar herencia de propiedades de dataset (capturar defaults accidentales)

cr0x@server:~$ zfs get -r -o name,property,value,source atime tank/home
NAME                 PROPERTY  VALUE  SOURCE
tank/home            atime     on     inherited from tank
tank/home/users      atime     on     inherited from tank
tank/home/projects   atime     on     inherited from tank

Qué significa: Un “harmless” default en la raíz del pool puede envenenar todos los datasets hijos.

Decisión: Establece defaults sensatos en los datasets de nivel superior y sobreescribe solo cuando sea necesario. Si no puedes estandarizar, al menos documenta las excepciones.

Errores comunes: síntomas → causa raíz → solución

1) “Nuestra carga mayormente de lectura genera montones de escrituras”

Síntomas: Altas IOPS de escritura durante picos de lectura; ráfagas periódicas de escritura; picos en la latencia de cola.

Causa raíz: atime=on en datasets calientes; la actividad de lectura desencadena escrituras de metadata.

Solución: Desactiva atime en esos datasets: zfs set atime=off pool/dataset. Verifica la caída de IOPS de escritura.

2) “El rendimiento empeoró gradualmente durante meses”

Síntomas: Mismo hardware, misma carga nominal, latencia en aumento; más variabilidad; peor 99p.

Causa raíz: Agitación acumulada de metadata + fragmentación; atime es una fuente constante de agitación.

Solución: Detén la agitación (desactivar atime), mantén el espacio del pool saludable y considera reequilibrar/migrar si la fragmentación es severa.

3) “Añadimos discos más rápidos y apenas ayudó”

Síntomas: La mejora de hardware es mínima; los picos persisten.

Causa raíz: La carga está dominada por escrituras random pequeñas de metadata; escalaste el eje equivocado.

Solución: Elimina escrituras de metadata innecesarias (atime), luego perfila el cuello de botella restante (sync, special vdev, ARC).

4) “Las snapshots consumen espacio en un dataset mayormente de lectura”

Síntomas: used de snapshots crece más rápido de lo esperado; los usuarios insisten “no cambiamos nada”.

Causa raíz: Los cambios de metadata cuentan como cambios. Las actualizaciones de atime son cambios.

Solución: Desactiva atime; reevalúa la frecuencia/retención de snapshots. No culpes a los usuarios por la física.

5) “Los shares NFS/SMB se sienten lentos, pero los discos no están saturados”

Síntomas: Lentitud interactiva; listados de directorio que se cuelgan; operaciones pequeñas con lag.

Causa raíz: Las operaciones de metadata son sensibles a la latencia; las actualizaciones de atime añaden presión de escritura que aparece como jitter.

Solución: Desactiva atime en los datasets de shares salvo que sea necesario. Si la metadata sigue caliente, evalúa un special vdev para metadata.

6) “Cambiamos atime y no pasó nada”

Síntomas: Sin mejora visible tras desactivar atime.

Causa raíz: La carga no estaba impulsada por atime, o otra configuración domina (writes sync, recordsize pequeño con sync, SLOG problemático, discos SMR, firmware malo).

Solución: Sigue el guion de diagnóstico rápido: valida latencia de vdev, comportamiento de sync, misses de ARC y capacidad/fragmentación. No sigas cambiando opciones a ciegas.

Listas de verificación / plan paso a paso

Checklist A: Decidir dónde debe estar atime (por lo general en ningún sitio)

  1. Lista los datasets y el estado actual de atime (zfs get -r atime).
  2. Clasifica datasets por carga: VM, DB, CI cache, home dirs, backups, object store, shares.
  3. Para cada dataset, responde: “¿Qué se rompe si atime está off?” Si la respuesta es “no estoy seguro”, por defecto ponlo off y prueba.
  4. Identifica los pocos consumidores reales de atime (algunos sistemas de mail, lógica de expiración de cache nicho, flujos de cumplimiento).
  5. Documenta las excepciones como parte de la creación de datasets.

Checklist B: Plan de despliegue seguro para desactivar atime

  1. Elige un dataset de alto tráfico (no la raíz). Desactiva atime.
  2. Mide: IOPS de escritura, espera en vdev de escritura, latencia de app y crecimiento de snapshots durante 24 horas.
  3. Despliega a datasets similares por lotes.
  4. Si tienes preocupaciones de cumplimiento, valida que las señales de auditoría requeridas no usen atime (normalmente no lo hacen).
  5. Establece un default sensato en el padre para nuevos datasets (típicamente atime=off).

Checklist C: Si el pool ya está degradado con el tiempo

  1. Detén la agitación primero: atime off en los datasets calientes.
  2. Confirma que el espacio libre del pool es saludable; planifica expansión de capacidad si CAP es alto.
  3. Comprueba si la metadata es el cuello de botella (misses en ARC, I/O pequeño, alta espera en escritura).
  4. Si la fragmentación es severa y el rendimiento sigue mal, considera una migración controlada vía send/receive a un pool o layout de dataset nuevo.
  5. Solo entonces evalúa añadidos como special vdevs para metadata. Son potentes, pero no son excusa para mantener atime activado por todas partes.

Una cita de operaciones (porque sigue siendo cierta)

Idea parafraseada, atribuida a Donald Knuth: La optimización prematura puede ser la raíz de muchos problemas.

En este contexto, “optimización” incluye “activar semánticas que no necesitas”. atime es teatro de corrección a menos que algo lo consuma.

Preguntas frecuentes

1) ¿Es atime=on realmente “inseguro”?

No. Es seguro para la integridad de los datos. Es inseguro para la previsibilidad del rendimiento a escala porque silenciosamente convierte lecturas en escrituras y añade agitación.

2) Si Linux tiene relatime, ¿ZFS tiene algo similar?

ZFS expone atime como propiedad de dataset (on/off). Algunas plataformas tienen comportamientos adicionales, pero operativamente debes tratarlo como una elección binaria y por defecto ponerlo off salvo que se requiera.

3) ¿Qué aplicaciones realmente necesitan atime?

Algunas lo hacen: ciertos flujos de entrega de mail y maildir, scripts de backup/auditoría escritos hace décadas y lógica de expiración de cache muy específica. La mayoría de sistemas modernos usan mtime, ctime, mecanismos tipo inotify o metadata a nivel de aplicación.

4) ¿Desactivar atime rompe la compatibilidad POSIX?

Relaja una expectativa concreta de comportamiento (actualizar el tiempo de acceso). Muchos sistemas de producción aceptan este compromiso. Si tienes un requisito estricto, activa atime solo en los datasets que lo necesiten.

5) ¿Desactivar atime reduce el desgaste de los SSD?

A menudo sí, porque elimina una clase de escrituras. Si importa depende de la intensidad de la carga y la resistencia del SSD, pero reducir escrituras innecesarias rara vez es mala idea.

6) ¿Por qué la degradación aparece “con el tiempo” en lugar de inmediatamente?

ZFS puede agrupar y batchar escrituras, ARC puede enmascarar lecturas de metadata y las primeras disposiciones de espacio libre son más favorables. Con el tiempo la agitación aumenta la fragmentación y la ineficiencia de la caché, y el pool gasta más esfuerzo en encontrar espacio y leer metadata dispersa.

7) ¿Debo poner atime=off en la raíz del pool?

Normalmente sí—en los datasets de nivel superior que uses como padres para cargas reales. Luego habilita atime explícitamente en los pocos datasets que lo necesiten. Así evitas herencias accidentales del costoso valor por defecto.

8) ¿Es atime la única razón por la que ZFS se vuelve más lento?

No. La presión de capacidad, fragmentación, patrones de writes sync, recordsize mal dimensionado, falta de special vdev para metadata y un diseño de vdev pobre pueden afectar. atime es simplemente el sigiloso porque se esconde tras las “lecturas”.

9) Si ya tengo un special vdev para metadata, ¿puedo mantener atime activado?

Puedes, pero no deberías por defecto. Los special vdev pueden absorber I/O de metadata, pero aún así estás generando trabajo innecesario y aumentando la agitación. Arregla la causa primero y luego usa special vdevs para intensidad legítima de metadata.

10) ¿Qué tan rápido debería esperar mejoras tras desactivar atime?

Con frecuencia en minutos u horas en forma de reducción de IOPS de escritura y menor espera en escritura. Las mejoras a largo plazo (menos crecimiento de fragmentación, menos picos) aparecen en días o semanas.

Conclusión: qué cambiar el lunes por la mañana

Si ejecutas ZFS en producción y no has auditado atime, probablemente estás pagando un impuesto que no presupuestaste. No es dramático. Ese es el problema. Convierte silenciosamente tus caminos de lectura en presión de escritura y deja que las consecuencias se acumulen hasta que tu equipo empiece a culpar fantasmas.

Pasos prácticos siguientes:

  1. Inventaria los datasets y localiza dónde atime=on está heredado o configurado localmente.
  2. Desactiva atime en datasets críticos de rendimiento a menos que puedas demostrar que se necesita.
  3. Vuelve a medir IOPS de escritura, latencia de vdev y crecimiento de snapshots tras el cambio.
  4. Estandariza perfiles de propiedades de dataset para no reintroducir el problema en seis meses durante una provisión “rápida”.

ZFS es una máquina de fiabilidad. Pero la hará trabajo inútil de forma fiel si se lo pides. No lo hagas.

← Anterior
TLS en correo: por qué un “certificado válido” sigue fallando (y la solución real)
Siguiente →
Visor de eventos para humanos: encuentra errores reales en 5 minutos

Deja un comentario