ZFS es un sistema que te miente con educación—brevemente y para tu propio bien. La mayoría de las escrituras aterrizan primero en RAM y luego ZFS las convierte en realidad en disco en lotes grandes y eficientes. Ese agrupamiento es una característica: es la forma en que ZFS entrega fuerte consistencia y buen rendimiento sin convertir cada pequeña escritura en un pequeño desastre.
El problema es que «lotes grandes y eficientes» pueden parecer ráfagas: tus discos están tranquilos y de pronto se activan; las gráficas de latencia parecen latidos; las bases de datos se quejan cada pocos segundos; y alguien pregunta por qué el almacenamiento “se entrecorta”. Un sospechoso común es txg_timeout: el temporizador que empuja a ZFS a cerrar y sincronizar un grupo de transacciones (TXG). Este artículo explica qué ocurre realmente, cómo demostrarlo en producción y cómo suavizar la latencia sin sacrificar la seguridad.
El modelo mental: TXGs, no “escrituras”
Si recuerdas una cosa, recuerda esto: ZFS no está haciendo “una escritura” cuando una aplicación llama a write(2). ZFS está construyendo una nueva versión del sistema de archivos en memoria y luego comprometiendo esa versión en disco. Esos compromisos ocurren en grupos de transacciones (TXGs).
Un TXG es una ventana de lote. Durante esa ventana, ZFS recopila metadatos modificados y buffers de datos (datos sucios). Cuando el TXG se cierra, ZFS comienza a sincronizarlo: asigna bloques, escribe datos y metadatos, actualiza el MOS (Meta Object Set) y finalmente escribe los uberblocks que hacen que el nuevo estado sea duradero. La clave: cerrar un TXG es barato; sincronizarlo es el trabajo real.
Tres TXGs a la vez
ZFS típicamente tiene tres TXGs en vuelo:
- TXG abierto: aceptando nuevos cambios (datos sucios acumulándose).
- TXG en quiescencia: cerrándose, congelando el conjunto de cambios.
- TXG en sincronización: escribiendo ese conjunto congelado en disco.
Esta canalización es por qué ZFS puede seguir aceptando escrituras mientras sincroniza el lote anterior. También explica por qué puedes obtener ráfagas periódicas: la fase de sincronización es cuando el almacenamiento ve trabajo real y a menudo cae en un intervalo concentrado.
Broma #1: ZFS no pierde tus datos, solo los “extravía temporalmente” en RAM—como tú con tus llaves, pero con sumas de comprobación.
Qué hace realmente txg_timeout (y qué no hace)
txg_timeout suele describirse como “qué tan a menudo ZFS vacía.” Eso es bastante aproximado para ser útil, y lo bastante errado para arruinar un fin de semana.
En la mayoría de las implementaciones de OpenZFS, txg_timeout es el tiempo máximo (en segundos) que se permite que un TXG permanezca abierto antes de que ZFS lo fuerce a quiescer y comenzar a sincronizar. El valor por defecto suele ser 5 segundos. Ese valor no es aleatorio: es un compromiso entre amortizar los costos de metadatos (agrupar) y limitar la cantidad de trabajo por commit (latencia).
Qué cambia realmente al modificar txg_timeout
- Reducirlo tiende a crear TXGs más frecuentes y más pequeños. Esto puede reducir el tamaño máximo de las “ráfagas de commit” pero aumenta la sobrecarga (más commits, más churn de metadatos, potencialmente menor throughput).
- Aumentarlo tiende a crear TXGs menos frecuentes y más grandes. Esto puede mejorar el throughput en escrituras continuas pero puede amplificar picos periódicos de latencia (y aumentar la cantidad de datos sucios en memoria).
Qué NO arregla txg_timeout
No convierte mágicamente escrituras sincronizadas aleatorias en E/S de baja latencia uniforme si tu dispositivo subyacente no puede mantenerse al día, tu SLOG está mal dimensionado, tu pool está fragmentado o tu carga fuerza semánticas síncronas. El timing de TXG es el baterista; tus discos siguen siendo la banda.
Por qué aparece el patrón “cada 5 segundos”
Si ves picos de latencia con cadencia regular—a menudo cerca de 5 segundos—tu primera hipótesis debe ser: “commits de TXG.” txg_timeout es un reloj que puede hacer visible esa cadencia. Pero las ráfagas también pueden ser impulsadas por límites de datos sucios (throttling), presión de escrituras sync y comportamiento de caché de dispositivo.
Por qué las escrituras vienen en ráfagas
En producción, las “escrituras ráfaga” rara vez tienen una única causa raíz. Suelen ser varios mecanismos sensatos alineando sus picos. Aquí están los más comunes.
1) Agrupar es el objetivo
ZFS es copy-on-write. Para muchas cargas, la ruta eficiente es: escribir nuevos bloques en otro lugar y luego cambiar punteros de forma atómica. Si ZFS tuviera que hacer esa coreografía de punteros para cada escritura de 4 KB, tendrías corrección y miseria. Los TXGs permiten que ZFS haga la coreografía una vez por lote.
2) Límites de datos sucios y throttling crean “acantilados”
ZFS permite cierta cantidad de datos sucios en RAM. Cuando alcanzas el umbral de datos sucios, ZFS comienza a limitar a los escritores para evitar crecimiento de memoria sin límite y forzar que la sincronización se ponga al día. Ese “evento de throttle” a menudo parece un acantilado: la latencia está bien hasta que deja de estarlo.
3) Las escrituras sync pueden forzar actividad inmediata del intent-log
Las escrituras sincrónicas (O_SYNC, fsync(), ajustes WAL de bases de datos, semánticas NFS sync) no esperan a que el TXG se sincronice en la pool principal. En su lugar, esperan a que el ZIL (ZFS Intent Log) registre la intención. En una pool sin SLOG dedicado, ese log está en los mismos discos que todo lo demás y las escrituras de log pueden serializarse o competir de formas desagradables.
4) Bloques pequeños + amplificación de metadatos
Una carga que escribe muchos bloques pequeños no solo escribe datos. Genera actualizaciones de metadatos: punteros de bloque, bloques indirectos, actualizaciones de spacemap, checksums, etc. ZFS es bueno en esto, pero aún tiene que hacerlo. En un commit de TXG, la I/O de metadatos puede convertirse en una tormenta corta e intensa.
5) El comportamiento del dispositivo subyacente también es ráfaga
Los SSD tienen recolección de basura interna; los arrays HDD tienen flushes de caché de escritura; los controladores RAID reordenan E/S; el firmware NVMe tiene sus propias colas y tareas de mantenimiento. Las ráfagas de ZFS pueden sincronizarse con las ráfagas del dispositivo y hacer que la gráfica parezca que respira fuerte.
6) ZFS es honesto respecto al retroceso (backpressure)
Algunas pilas de almacenamiento ocultan la contención bufferizando sin fin hasta caerse. ZFS tiende a devolver presión cuando tiene que hacerlo. Eso es mejor ingeniería—y peor óptica en un tablero de control.
Hechos e historia que vale la pena conocer
- ZFS se construyó en Sun con almacenamiento empresarial en mente, donde agrupar y commits atómicos vencen a “escribir todo sin excepción”.
- Los TXGs son anteriores al término “DevOps”; este estilo de batching transaccional viene de ideas clásicas de sistemas de archivos y bases de datos, no del marketing cloud moderno.
- El valor por defecto “5 segundos” para el timing de TXG aparece en varias líneas de ZFS porque a menudo equilibra latencia y throughput en sistemas de propósito general.
- El ZIL no es una caché de escritura. Es un registro de intención para semánticas síncronas; la mayor parte se descarta después de que el TXG hace commit.
- Los dispositivos SLOG solo importan para escrituras síncronas. Si tu carga es asíncrona, un SLOG no ayudará e incluso puede convertirse en otro dominio de fallo que gestionar.
- Copy-on-write hace las escrituras parciales más seguras, pero desplaza la complejidad a la asignación y las actualizaciones de metadatos, que se pagan durante la sincronización del TXG.
- ZFS verifica todo con checksums (metadatos y datos), lo cual es fantástico para integridad y no gratuito para CPU y tráfico de memoria durante commits pesados.
- OpenZFS divergió entre plataformas (Illumos, FreeBSD, Linux) y las perillas de ajuste difieren ligeramente; los conceptos permanecen, pero los nombres y valores pueden variar.
- Los picos de latencia suelen correlacionar con escrituras de uberblock porque ese es el paso final de “publicación” de un TXG; a veces puedes ver la cadencia en trazas de bajo nivel.
Reconocer la latencia con forma de TXG
El dolor relacionado con TXG tiene un olor particular:
- Cadencia regular: picos cada ~5 segundos (o el valor de tu
txg_timeout). - Principalmente del lado de las escrituras: la latencia de lectura puede permanecer bien hasta que el sistema está saturado.
- Momentos de “todo se pausa”: no un bloqueo completo, pero las aplicaciones reportan jitter—especialmente bases de datos y hosts de VM.
- CPU no saturada: puedes estar limitado por I/O con CPU ociosa, o limitado por CPU en checksum/compresión de formas que no parecen 100% CPU de usuario.
- Oscilación de datos sucios: uso de memoria y contadores de datos sucios suben y luego caen bruscamente en el commit.
Broma #2: Si tus gráficas hacen picos cada cinco segundos, felicidades—has descubierto que el tiempo es un círculo plano, y tu almacenamiento es el círculo.
Guion rápido de diagnóstico
Esta es la secuencia “entrar al puente de incidentes”. Está ordenada para responder una pregunta rápidamente: ¿estamos viendo presión por commits de TXG, presión por logs de sync, o saturación cruda del dispositivo?
Primero: confirma que el síntoma es periódico y relacionado con escrituras
- Comprueba la cadencia de latencia: ¿se alinea con ~5 segundos?
- Separa lecturas de escrituras: ¿la latencia de escritura sube mientras las lecturas parecen normales?
- Verifica si la carga es muy síncrona (bases de datos, NFS, almacenamiento de VM con barreras).
Segundo: determina si las escrituras sync son el desencadenante
- Inspecciona la actividad de ZIL/SLOG: ¿están dominando las escrituras de log?
- Comprueba las propiedades del dataset
syncylogbias. - Valida la salud y latencia del SLOG (si existe).
Tercero: determina si los datos sucios de TXG + throttling son el desencadenante
- Mira los contadores de datos sucios y estado TXG (específicos de plataforma, pero observables).
- Comprueba si los escritores están siendo throttled (alto tiempo de espera, bloqueados en rutas de I/O del kernel).
- Correlaciona con el ancho de banda de escritura del pool: ¿estás alcanzando límites de dispositivo durante ventanas de sincronización?
Cuarto: confirma el comportamiento del dispositivo subyacente
- Comprueba latencia por vdev: un dispositivo lento puede dictar el tiempo de commit del pool.
- Revisa profundidad de cola y saturación.
- Verifica contadores de errores y retransmisiones; “lento” a veces es “reintentando”.
Tareas prácticas (comandos + interpretación)
Los comandos abajo asumen un entorno Linux + OpenZFS. Si estás en FreeBSD/Illumos, puedes traducir la intención; el flujo de trabajo sigue aplicando.
Tarea 1: Identificar datasets y propiedades clave (sync, logbias, recordsize)
cr0x@server:~$ sudo zfs list -o name,used,avail,recordsize,compression,sync,logbias -r tank
NAME USED AVAIL RECORDSIZE COMPRESS SYNC LOGBIAS
tank 8.21T 5.44T 128K lz4 standard latency
tank/vm 4.92T 5.44T 16K lz4 standard latency
tank/db 1.10T 5.44T 16K lz4 standard latency
tank/backups 2.04T 5.44T 1M lz4 disabled throughput
Interpretación: Si tus quejas de latencia vienen de tank/db o tank/vm, el pequeño recordsize y logbias=latency sugieren que te importa el comportamiento sync. Si el dataset está en sync=disabled, puede que hayas “arreglado” la latencia eliminando durabilidad—útil para benchmarks, peligroso para trabajos reales.
Tarea 2: Vigilar I/O a nivel de pool y detectar ráfagas periódicas
cr0x@server:~$ sudo zpool iostat -v tank 1
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 8.21T 5.44T 210 980 28.1M 210M
mirror 2.73T 1.81T 105 490 14.0M 105M
nvme0n1 - - 52 245 7.0M 52.5M
nvme1n1 - - 53 245 7.0M 52.4M
mirror 2.73T 1.81T 105 490 14.1M 105M
nvme2n1 - - 52 245 7.0M 52.6M
nvme3n1 - - 53 245 7.1M 52.4M
Interpretación: Busca ancho de banda de escritura que pase de “moderado” a “al máximo” con una cadencia, con operaciones subiendo bruscamente. Las ráfagas que se alinean con el temporizador TXG son una pista, no un veredicto.
Tarea 3: Añadir columnas de latencia para exponer el vdev lento
cr0x@server:~$ sudo zpool iostat -v tank 1 -l
operations bandwidth total_wait disk_wait
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
mirror 105 490 14.1M 105M 1ms 18ms 0ms 15ms
nvme2n1 52 245 7.0M 52.6M 1ms 18ms 0ms 15ms
nvme3n1 53 245 7.1M 52.4M 1ms 40ms 0ms 38ms
Interpretación: Un dispositivo (nvme3n1) muestra espera de escritura mucho mayor. Un solo rezagado puede alargar el tiempo de sincronización del TXG y crear ráfagas visibles. Aquí es donde dejas de ajustar y empiezas a preguntar “¿está fallando o mal configurado un dispositivo?”.
Tarea 4: Comprobar salud del pool y contadores de errores
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Replace the device or clear the errors.
scan: scrub repaired 0B in 03:12:55 with 0 errors on Sun Dec 22 02:12:19 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
mirror-1 ONLINE 0 5 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 5 0
errors: No known data errors
Interpretación: Errores de escritura en un miembro vdev pueden traducirse en reintentos y timeouts, que se ven como ráfagas de latencia. Si ves errores, trátalos como problemas de rendimiento y de fiabilidad.
Tarea 5: Confirmar txg_timeout y tunables relacionados
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
5
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max
17179869184
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max_percent
10
Interpretación: El timeout por defecto de 5 segundos más un dirty data max generoso pueden crear comportamiento de “silencio y luego tormenta”. Pero no toques esto todavía—mide primero y cambia una cosa a la vez.
Tarea 6: Observar presión del ARC (porque la memoria modela los datos sucios)
cr0x@server:~$ grep -E 'c_max|c_min|size|memory_throttle_count' /proc/spl/kstat/zfs/arcstats
c_max 4 137438953472
c_min 4 68719476736
size 4 92341784576
memory_throttle_count 4 0
Interpretación: Si ARC está compitiendo por memoria, ZFS puede throttlear o comportarse más agresivamente. Si memory_throttle_count sube, tus “ráfagas” pueden ser que el sistema se queda sin RAM en lugar de un simple problema de cadencia TXG.
Tarea 7: Identificar si la carga es sync-heavy
cr0x@server:~$ sudo zfs get -H -o name,property,value sync tank/db
tank/db sync standard
cr0x@server:~$ sudo iostat -x 1
avg-cpu: %user %nice %system %iowait %steal %idle
12.00 0.00 8.00 18.00 0.00 62.00
Device r/s w/s r_await w_await aqu-sz %util
nvme3n1 55.0 280.0 0.9 35.0 9.8 98.0
Interpretación: Alto w_await y utilidad cercana al 100% en un dispositivo se alinean con presión de commit. Para decidir si es relacionado con ZIL, necesitas mirar la presencia y comportamiento de un SLOG.
Tarea 8: Comprobar si hay dispositivo de log dedicado (SLOG)
cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
mirror-1 ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 0 0
logs
mirror-2 ONLINE 0 0 0
nvme4n1 ONLINE 0 0 0
nvme5n1 ONLINE 0 0 0
Interpretación: Existe un SLOG en espejo. Bien. Ahora debes verificar que tenga baja latencia y protección ante pérdida de energía, o convertirá las escrituras sync en rehenes del rendimiento.
Tarea 9: Medir la latencia del SLOG indirectamente vía esperas por vdev
cr0x@server:~$ sudo zpool iostat -v tank 1 -l | sed -n '1,40p'
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror-0 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
logs
mirror-2 0 320 0.0K 19.2M 0ms 2ms 0ms 1ms
nvme4n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
nvme5n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
Interpretación: Espera de escritura en el vdev de log alrededor de 1–2 ms es aceptable para muchas cargas sync. Si ves 10–50 ms aquí, tu SLOG no es un SLOG; es un log lento.
Tarea 10: Comprobar la propiedad logbias del dataset para coincidir con la intención
cr0x@server:~$ sudo zfs get -H -o name,property,value logbias tank/db tank/vm
tank/db logbias latency
tank/vm logbias latency
Interpretación: logbias=latency favorece usar el log para escrituras sync (bueno para bases de datos). throughput puede reducir tráfico de log en algunos casos pero puede aumentar escrituras a la pool principal para operaciones sync. Cambiarlo sin entender la carga es una clásica trampa.
Tarea 11: Observar kstats relacionados con TXG (datos sucios y tiempo de sync)
cr0x@server:~$ ls /proc/spl/kstat/zfs/ | head
arcstats
dbufstats
dmu_tx
vdev_queue
vdev_mirror
zfetchstats
cr0x@server:~$ cat /proc/spl/kstat/zfs/dmu_tx
13 1 0x01
name type data
dmu_tx_assigned 4 12800451
dmu_tx_delay 4 21438
dmu_tx_error 4 0
dmu_tx_memory_reserve 4 0
dmu_tx_memory_reclaim 4 0
Interpretación: En Linux, parte de la visibilidad de TXG es indirecta. Un aumento de dmu_tx_delay indica que los escritores están siendo retrasados—a menudo por límites de datos sucios y presión de sync. Buscas correlación: cuando la latencia hace picos, ¿estos contadores suben?
Tarea 12: Usar perf para confirmar que estás bloqueado en rutas de I/O de ZFS (no en CPU)
cr0x@server:~$ sudo perf top -g
12.4% [kernel] [k] __schedule
8.9% [kernel] [k] io_schedule
6.1% [kernel] [k] blk_mq_get_tag
5.7% zfs [k] zio_wait
4.8% zfs [k] zio_execute
4.1% zfs [k] vdev_queue_io
Interpretación: Ver tiempo en io_schedule y funciones ZFS zio_* sugiere que el sistema espera almacenamiento, no consume CPU en compresión o checksums. Eso te orienta lejos de afinar compression o escalado de CPU y hacia latencia de vdev, SLOG y comportamiento TXG.
Tarea 13: Chequeo rápido de saturación y colas a nivel de dispositivo
cr0x@server:~$ sudo nvme smart-log /dev/nvme3n1 | sed -n '1,25p'
Smart Log for NVME device:nvme3n1 namespace-id:ffffffff
critical_warning : 0
temperature : 47 C
available_spare : 100%
percentage_used : 3%
media_errors : 0
num_err_log_entries : 0
warning_temp_time : 0
critical_comp_time : 0
Interpretación: Esto no es una prueba de rendimiento; es una comprobación de cordura. Si los errores de medio o entradas en el log de errores suben, tu “problema TXG” puede ser un problema de hardware disfrazado de ZFS.
Tarea 14: Cambiar txg_timeout de forma segura (temporal) y observar
cr0x@server:~$ sudo sh -c 'echo 3 > /sys/module/zfs/parameters/zfs_txg_timeout'
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
3
Interpretación: Esto es una prueba, no un modo de vida. Si tus picos de latencia cambian de cadencia y reducen amplitud, has confirmado que el timing TXG es parte del problema. Si nada cambia, deja de fisgonear y mira las escrituras sync o la latencia de vdev.
Tres mini-historias del mundo corporativo
Mini-historia 1: El incidente causado por una suposición equivocada
El equipo de plataforma recibió un ticket: “La base de datos se queda atascada cada cinco segundos.” Era el tipo de queja que te hace sospechar del sistema de monitorización primero. Pero las gráficas eran honestas: la latencia de escritura hacía picos con precisión metronómica.
Alguien tuvo una suposición ordenada: “ZFS hace flush cada cinco segundos, así que la base de datos debe estar forzando flushes.” Esa suposición ganó tracción porque sonaba a física. Se centraron en la base de datos, ajustaron checkpoints, discutieron sobre WAL y cambiaron el batching de la aplicación. Los picos de latencia no se inmutaron.
El problema real era más simple y más molesto: un miembro del mirror en el pool había empezado a tardar más en completar escrituras—sin fallos duros, solo ocasionales stalls. ZFS, siendo correcto, esperó al componente más lento para completar las obligaciones del TXG. El tiempo de commit del TXG se alargó, los datos sucios se acumularon y el sistema activó throttling. El patrón de cinco segundos era solo el temporizador exponiendo un cuello de botella que de todas formas habría perjudicado.
Lo demostraron añadiendo columnas de tiempo de espera a zpool iostat. Un dispositivo mostraba consistentemente disk_wait elevado durante los picos. SMART parecía “bien”, porque SMART suele estar “bien” hasta que no lo está. Reemplazar el dispositivo eliminó los stalls periódicos sin tocar una sola perilla de ZFS.
La lección: el comportamiento periódico no siempre significa “problema de temporizador”. A veces es “un componente lento causa dolor periódico cuando el sistema se fuerza a sincronizar.” Los TXGs solo hacen visible la sincronización.
Mini-historia 2: La optimización que salió mal
Otra organización tenía una granja de VM sobre zvols en ZFS. Querían latencia más suave para escrituras de invitados. Alguien leyó que bajar txg_timeout puede reducir el tamaño de las ráfagas, así que lo redujeron agresivamente en toda la flota.
Las gráficas mejoraron—por aproximadamente un día. Luego el throughput colapsó durante trabajos nocturnos. Las ventanas de backup se extendieron. Los nodos de almacenamiento no estaban “lentos” en sentido clásico; estaban ocupados haciendo más trabajo por unidad de datos. Más TXGs significaron commits de metadatos más frecuentes, más churn y menos tiempo en estado estable eficiente.
Peor aún, los TXGs más pequeños aumentaron la proporción relativa de overhead para cargas con muchas escrituras síncronas pequeñas. El SLOG empezó a recibir más fsyncs frecuentes y un dispositivo de log que antes “estaba bien” se convirtió en el elemento que marcaba el ritmo. La latencia se volvió más errática bajo carga, no más suave.
Revirtieron el cambio y tomaron un enfoque más quirúrgico: separar datasets, configurar correctamente volblocksize para zvols, añadir un SLOG espejo de baja latencia para inquilinos sync-heavy y limitar datos sucios acorde a la memoria disponible. El ajuste final fue aburrido e incremental—que es el único tipo de ajuste que quieres cerca de dinero real.
La lección: txg_timeout es una palanca, no una cura. Tirarla demasiado fuerte cambia las ráfagas por overhead, que a menudo aparece como peor latencia cola cuando el sistema está realmente ocupado.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Una compañía SaaS gestionaba almacenamiento multi-tenant con SLOs estrictos. Aprendieron, a las malas, que afinar ZFS sin observabilidad es astrología. Así que hicieron lo aburrido: seguimiento de latencia por vdev, dashboards de cadencia TXG y scrubs rutinarios. Nadie lo amaba. Todos se beneficiaron.
Una tarde, la latencia de escritura empezó a desarrollar un ritmo faint pero creciente de 5 segundos. No era un incidente completo—solo el tipo de “hmm” que buenos ingenieros on-call notan mientras fingen no hacerlo. Los dashboards mostraron que los picos relacionados con commits empeoraban lentamente, pero solo en un pool.
Porque ya tenían histogramas de espera por vdev, detectaron un único dispositivo con una cola larga de latencia de escritura en crecimiento. No estaba fallando abiertamente. Simplemente, ocasionalmente tardaba lo suficiente para estirar el tiempo de sync del TXG. Dranearon tenants del pool y reemplazaron el dispositivo en una ventana de mantenimiento normal. Sin cambios de emergencia, sin heroísmos nocturnos, sin “lo reiniciamos y mejoró”.
Tras el reemplazo, el ritmo TXG desapareció. El equipo pareció genial, lo cual fue injusto—solo estaban preparados. La práctica aburrida no era ajustar; era notar lo suficientemente temprano como para que el ajuste no fuera necesario.
La lección: la mejor manera de “suavizar la latencia” es prevenir las condiciones que crean grandes ventanas de sync—especialmente degradación silenciosa de dispositivos y saturación no observada.
Estrategia de ajuste: qué cambiar y en qué orden
Cuando la gente dice “ajustar ZFS”, a menudo quiere decir “cambiar un parámetro hasta que la gráfica se vea mejor.” Eso no es ajuste; es deseo gráfico. Aquí hay un orden de operaciones más seguro.
Paso 1: Decide si tu problema es latencia de sync o latencia de commit TXG
Si tu carga está dominada por operaciones síncronas, la forma más rápida de reducir la latencia cola suele ser un SLOG apropiado (en espejo, protegido contra pérdida de energía, de baja latencia) y propiedades de dataset que coincidan con la carga (sync, logbias). Si es mayormente asincrónica, el comportamiento de TXG y datos sucios importa más.
Paso 2: Arregla el cuello de botella obvio primero (hardware y topología)
- Reemplaza o elimina dispositivos lentos/erróneos.
- Asegura que los vdevs estén balanceados; un vdev subrendidor marca el ritmo.
- Confirma ajustes del controlador, firmware, profundidades de cola y que no luchas contra un HBA mal configurado.
Paso 3: Usa perillas a nivel de dataset antes que perillas globales
Las propiedades de dataset son reversibles y con ámbito. Los parámetros globales del módulo se comparten entre pools y pueden crear acoplamiento entre tenants.
recordsizepara sistemas de archivos: ajusta al tamaño de I/O (las bases de datos suelen preferir 16K; backups 1M).volblocksizepara zvols: se define al crear; casarlo con el patrón de bloques del invitado/sistema de archivos.logbias: elegirlatencypara cargas sync de baja latencia; considerarthroughputpara patrones de streaming.sync: mantenerstandardsalvo que aceptes explícitamente sacrificar durabilidad.
Paso 4: Solo entonces, considera el timing TXG y los límites de datos sucios
txg_timeout ajusta la cadencia. Los tunables de dirty-data ajustan cuánto trabajo puede acumularse antes de que ZFS fuerce backpressure. Juntos moldean “amplitud de ráfaga” y “frecuencia de ráfaga”.
Heurísticas prácticas que se sostienen en entornos reales:
- Baja
txg_timeoutmodestamente (por ejemplo, 5 → 3) si tienes picos periódicos y el sistema no está ya dominado por overhead de metadatos. - Sé cauteloso al aumentarlo; TXGs más grandes pueden producir peores latencias cola y tiempos de recuperación más largos tras stalls transitorios.
- Ajusta dirty data max solo cuando entiendas la memoria disponible y el comportamiento del ARC. Demasiado alto puede crear ráfagas más grandes; demasiado bajo puede throttlear escritores constantemente.
Paso 5: Valida con la carga real, no con una victoria sintética
Ejecuta la carga que te duele: tu base de datos, tu granja de VM, tu tráfico NFS. Un benchmark de escritura secuencial puede “demostrar” que cualquier mala idea es buena si eliges el tamaño de bloque correcto e ignoras la latencia cola.
Errores comunes, síntomas y soluciones
Error 1: Tratar txg_timeout como un “intervalo de flush” como un checkpoint de base de datos
Síntoma: Reduces txg_timeout y la frecuencia de picos cambia pero la latencia cola sigue mala bajo carga.
Qué está pasando: El sistema está limitado por dispositivo o por sync; cambiaste el ritmo, no la capacidad.
Solución: Mide espera por vdev; verifica SLOG; revisa tasa de escrituras sync; reemplaza dispositivos lentos o añade capacidad antes de más ajustes.
Error 2: Desactivar sync para “arreglar” latencia
Síntoma: La latencia parece increíble, y luego tras un corte de energía restauras desde backups mientras explicas “era solo una prueba”.
Qué está pasando: sync=disabled convierte peticiones síncronas en asincrónicas. Mejora el rendimiento cambiando el contrato.
Solución: Usa un SLOG adecuado; ajusta logbias; arregla la latencia del dispositivo subyacente; deja sync en standard salvo que aceptes pérdida de datos.
Error 3: Añadir un SSD barato de consumo como SLOG
Síntoma: La latencia sync empeora; los stalls ocasionales se agravan; a veces ves timeouts en el dispositivo de log.
Qué está pasando: El SLOG necesita latencia baja y consistente y protección frente a pérdida de energía. Un drive con caché volátil de escritura e GC impredecible convierte fsync en ruleta.
Solución: Usa un dispositivo enterprise con PLP; ponlo en espejo; confirma latencias con zpool iostat -l.
Error 4: Sobredimensionar datos sucios “porque la RAM es gratis”
Síntoma: Picos más largos y feos; stalls de varios segundos ocasionales cuando el sistema está bajo presión.
Qué está pasando: Buffers sucios más grandes significan commits más grandes. Has aumentado la amplitud de la ráfaga. Bajo desaceleraciones transitorias, también aumentaste la cantidad de trabajo que debe vaciarse antes de liberar a los escritores.
Solución: Dimensiona límites sucios acorde al throughput de dispositivos y la latencia cola aceptable; mantiene el ARC saludable; evita contención de memoria con otros servicios.
Error 5: Ignorar desequilibrio por vdev
Síntoma: “El pool es rápido” en métricas agregadas, pero los picos de latencia persisten.
Qué está pasando: Un vdev o dispositivo es lento; el sync de TXG espera por la I/O más lenta requerida. El ancho de banda promedio oculta el comportamiento cola.
Solución: Usa zpool iostat -v -l, reemplaza el outlier y verifica consistencia de controladora/firmware.
Error 6: Cambiar cinco perillas a la vez
Síntoma: El sistema se siente “diferente”, pero no puedes saber por qué, y las reversions dan miedo.
Qué está pasando: Has destruido tu capacidad de razonar sobre causa y efecto.
Solución: Un cambio, una validación, un plan de rollback. Anota lo que cambiaste y por qué.
Listas de verificación / plan paso a paso
Checklist A: Confirma que estás viendo ráfagas de commit TXG
- Grafica la latencia de escritura y mira si los picos se alinean a ~5 segundos.
- Ejecuta
zpool iostat -v 1 -ldurante los picos; identifica si los tiempos de espera saltan con la misma cadencia. - Comprueba
/sys/module/zfs/parameters/zfs_txg_timeouty compáralo con la cadencia observada. - Confirma si el vdev de log (si existe) muestra mayor espera durante los picos.
- Revisa
dmu_tx_delayu contadores equivalentes; busca crecimiento durante los eventos.
Checklist B: Suavizar la latencia sin romper durabilidad
- Valida hardware primero: reemplaza dispositivos lentos/erróneos; confirma uniformidad de firmware.
- Confirma comportamiento sync: identifica qué datasets/apps son sync-heavy; mantén
sync=standard. - Arregla SLOG si hace falta: en espejo, con PLP y baja latencia; verifica esperas en
zpool iostat -l. - Ajuste por dataset: establece
recordsizeapropiadamente; verificalogbias. - Experimento pequeño con txg_timeout: ajustar 5→3 temporalmente; observa latencia cola y throughput.
- Sensatez con datos sucios: asegura que los límites sucios no creen commits gigantes o throttling constante.
- Revertir si los trade-offs dañan: prioriza throughput estable y cola predecible sobre una media más bonita.
Checklist C: Gestión de cambios que no te perseguirá
- Captura métricas “antes”: espera por vdev, latencia de la app, tasa de escrituras sync.
- Cambia una perilla a la vez con una ventana de observación cronometrada.
- Documenta la hipótesis (“reducir txg_timeout reduce amplitud de ráfagas”).
- Define criterios de éxito (p99 de latencia de escritura, no solo promedio).
- Mantén listo y probado un comando de rollback.
Preguntas frecuentes
1) ¿Es txg_timeout la razón por la que mis escrituras hacen picos cada cinco segundos?
PUEDE ser el metrónomo visible, pero no siempre es la causa. La causa suele ser que el trabajo de sincronización del TXG se concentra en el tiempo, a menudo porque dispositivos o logging sync no pueden absorber la carga suavemente. Verifica correlacionando picos con esperas por vdev y contadores de backpressure relacionados con TXG.
2) ¿Debo reducir txg_timeout para suavizar la latencia?
A veces, modestamente. Bajar de 5 a 3 segundos puede reducir el tamaño de la ráfaga, pero también puede reducir throughput e incrementar overhead. Si tu cuello de botella es un vdev lento o un SLOG lento, bajar txg_timeout solo cambia el tempo del sufrimiento.
3) ¿Un SLOG arreglará las ráfagas de TXG?
Un SLOG ayuda con la latencia de escrituras síncronas (cargas fsync-heavy). No elimina directamente el trabajo de commit del TXG a la pool principal. Muchos entornos tienen ambos problemas: el SLOG arregla el “dolor de fsync” y el ajuste/hardware del TXG arregla el “dolor de ráfagas de commit”.
4) ¿Por qué empeora cuando el pool está casi lleno?
A medida que los pools se llenan, la asignación se complica, la fragmentación sube y las actualizaciones de metadatos pueden ser más costosas. La sincronización del TXG puede tardar más, lo que incrementa el tiempo que los datos sucios esperan para ser durables, y eso incrementa el throttling y la percepción de ráfagas.
5) ¿Está relacionado recordsize con txg_timeout?
Indirectamente. recordsize cambia la forma de I/O y el overhead de metadatos. Registros más pequeños pueden aumentar trabajo de metadatos y demanda de IOPS, haciendo el sync del TXG más intenso. El temporizador no cambia, pero el trabajo por TXG sí.
6) ¿Cuál es la forma más segura de “suavizar la latencia” para bases de datos en ZFS?
Mantén sync=standard, usa un SLOG en espejo y con PLP para cargas WAL/fsync, configura recordsize apropiadamente (a menudo 16K para muchas bases de datos) y luego aborda outliers de latencia por vdev. Considera cambios pequeños en txg_timeout solo después de tener lo básico sólido.
7) Si no me importa la durabilidad, ¿puedo poner sync=disabled?
Puedes, pero deberías considerarlo como quitarte el cinturón de seguridad porque te arruga la camisa. Puede ser aceptable para datos temporales, caches efímeros o entornos de pruebas con tolerancia explícita a pérdida. No lo hagas para nada que te avergüence explicar tras un crash.
8) ¿Cómo sé si me están limitando los límites de datos sucios?
Mira retrasos de escritores crecientes (por ejemplo, dmu_tx_delay aumentando) y latencia a nivel de aplicación que se correlacione con la oscilación de datos sucios y ventanas de sync TXG. A menudo verás estallidos de I/O seguidos por periodos donde los escritores se bloquean más de lo habitual.
9) ¿Puede la compresión ayudar con las ráfagas de TXG?
A veces. La compresión puede reducir bytes escritos, lo que reduce tiempo de dispositivo durante el sync TXG, pero incrementa trabajo de CPU y puede cambiar patrones de I/O. Si estás limitado por dispositivo y tienes margen de CPU, la compresión puede reducir la amplitud de la ráfaga. Si estás limitado por CPU o cerca de saturación, puede empeorar la latencia cola.
10) ¿Este problema es específico de Linux?
No. Los TXGs son fundamentales para ZFS en todas las plataformas. Lo que difiere es la visibilidad (qué contadores se exponen) y los nombres y valores por defecto de los tunables.
Conclusión
txg_timeout no hace que ZFS sea ráfaga; revela el agrupamiento que ZFS usa para ser rápido y correcto. Esas ráfagas de escritura suelen ser normales—hasta que no lo son. Cuando se convierten en un problema de latencia, suele ser porque la fase de sync del TXG se está estirando por un dispositivo lento, presión de escrituras sync o acumulación de datos sucios que activa throttling.
Suavizar la latencia consiste principalmente en respetar la canalización: asegura que el vdev más lento no esté secretamente lento, da a cargas síncronas un camino de log de baja latencia adecuado, afina datasets para ajustar la forma de I/O y solo entonces ajusta el timing de TXG y los límites de datos sucios—con cuidado, midiendo y con rollback listo. El objetivo no es eliminar las ráfagas por completo; es mantenerlas lo bastante pequeñas para que tus aplicaciones dejen de notarlo—y para que tu on-call deje de desarrollar una respuesta pavloviana a intervalos de cinco segundos.