Almacenamiento Linux: La opción de montaje que puede corromper tus expectativas

¿Te fue útil?

Algunos incidentes no son ruidosos. El servicio sigue en pie, los gráficos parecen normales y todos felicitan al escalador automático. Entonces ocurre un corte de energía momentáneo o un nodo se reinicia, y de pronto tu base de datos “exitosamente” no tiene lo de ayer por la tarde.

Este es el modo de fallo del almacenamiento en Linux que nadie quiere explicar en un postmortem: tu aplicación hizo exactamente lo que debía, pero tus opciones de montaje cambiaron en silencio lo que significa “escritura completada”. Los datos no están corrompidos en el sentido dramático. Tus expectativas sí.

La opción de montaje que corrompe expectativas: async (y sus amigos)

Si me obligas a elegir una opción de montaje que rutinariamente convierte producción en teatro de improvisación, es async.

async cambia cuándo el sistema informa éxito. Permite que el kernel (o el servidor, en el caso de NFS) reconozca las escrituras antes de que se confirmen en almacenamiento estable. Eso puede estar bien para espacios temporales, cachés, artefactos de compilación o cosas que puedas regenerar. No está bien para tu base de datos, tu cola, tu servicio de identidad, tu “fuente única de verdad” o cualquier cosa por la que luego te vayan a pedir cuentas.

En sistemas de ficheros locales, no suele montarse explícitamente con async porque normalmente es el comportamiento por defecto de la caché de páginas: las escrituras se almacenan en búfer y se vuelcan después. Lo peligroso es cuando la gente combina supuestos de E/S con búfer con opciones que debilitan el orden y la durabilidad, y luego asume que fsync() los salva. A veces lo hace. A veces te salva artísticamente.

La expectativa que se corrompe

La mayoría de los ingenieros—ingenieros razonables y empleados—asumen que una de estas cosas es cierta:

  • “Si mi app recibe un código de retorno exitoso de write(), los datos están seguros.”
  • “Si mi app llama a fsync(), los datos están definitivamente seguros.”
  • “Si el sistema de ficheros usa journaling, no puede perder datos comprometidos.”

En Linux, la única que es defendible a veces es la segunda, y aun así depende de las opciones de montaje, la semántica del sistema de ficheros, el comportamiento de la caché de escritura del dispositivo, el firmware del controlador y si tu “disco” es en realidad un servicio de red disfrazado de dispositivo de bloques.

El pequeño conjunto de perillas que causan más arrepentimiento

Estos son los sospechosos habituales:

  • NFS async en el lado del servidor (y a veces elecciones del cliente) que reconocen antes del commit.
  • ext4 data=writeback que debilita el orden entre el journaling de metadatos y los datos de archivo.
  • barrier=0 / nobarrier que eliminan el comportamiento de flush/FUA que hace cumplir el orden a través de cachés volátiles.
  • Deshabilitar barreras de escritura mediante políticas del dispositivo/controlador (“es más rápido”) mientras confías en fsync() (“es seguro”).
  • commit= aumentar el tiempo máximo que los metadatos pueden quedarse en RAM antes de ser comprometidos en el journal (ventana mayor de pérdida en fallo de energía).

Cada uno se puede justificar en contextos específicos. Juntos, son un proyecto grupal cuya calificación es “irrecuperable.”

Broma 1: El sistema de almacenamiento “más rápido” es el que nunca escribe nada. También es muy duradero, porque no hay nada que perder.

Durabilidad: lo que esperas frente a lo que el kernel realmente garantiza

I/O en Linux tiene capas, y cada capa tiene sus opiniones.

En términos generales: las aplicaciones escriben en la caché de páginas; el kernel programa el writeback; el sistema de ficheros actualiza metadatos y tal vez los journaliza; la capa de bloques encola peticiones; el dispositivo puede mentir con una caché volátil; y luego ocurre la física. O no. Las sorpresas desagradables casi siempre están en el “tal vez”.

Tres afirmaciones que suenan similares y no lo son

  • Los datos son visibles: otro proceso puede leerlos (desde la caché), aunque no estén en disco.
  • Los datos son persistentes: sobrevivirán a un fallo o pérdida de energía.
  • El sistema de ficheros es consistente: después de un fallo, los metadatos son recuperables y las estructuras están sanas.

El journaling trata primero la consistencia de metadatos. Algunos modos añaden garantías más fuertes sobre los datos de archivo. Pero “consistente” no significa “contiene las últimas escrituras exitosas”. Significa “se puede montar sin pasarte el fin de semana en fsck”.

Dónde encaja fsync()—y dónde no

fsync(fd) pide al kernel que vacíe las páginas sucias y los metadatos asociados a ese archivo hacia almacenamiento estable. Ese es el contrato. El problema es qué significa “almacenamiento estable” cuando:

  • un disco tiene una caché de escritura volátil e ignora los flushes,
  • un controlador RAID reordena escrituras y miente sobre la salud de su batería,
  • un hipervisor traduce los flushes en “mejor esfuerzo”,
  • un sistema en red responde antes del commit.

Además, debes llamarlo correctamente. Muchos sistemas requieren fsync() en el directorio después de crear/renombrar archivos para garantizar que la entrada de directorio sea durable. El archivo puede estar a salvo mientras que el nombre que lo apunta no lo es. Esa es una forma muy Linux de perder cosas: los datos existen, pero no puedes encontrarlos.

Una cita para tu libreta de on-call

“La esperanza no es una estrategia.” — Gen. Gordon R. Sullivan

Cambia “esperanza” por “opciones de montaje por defecto” y ya estás en gran parte del camino hacia una mejor postura de almacenamiento.

Modos de journaling que cambian tu realidad: ext4 data=ordered, writeback, journal

ext4 sigue por todas partes porque es aburrido, rápido y predecible—hasta que cambias las perillas que lo hacen aburrido.

Qué hacen realmente estos modos

  • data=ordered (por defecto en muchas distribuciones): los metadatos se journalizan. Los datos de archivo no se journalizan, pero ext4 intenta asegurar que los bloques de datos se escriban en disco antes de que los metadatos que los apuntan se comprometan. Después de un fallo, no deberías ver basura antigua en bloques de archivos recién escritos (en la mayoría de los casos). Aún puedes perder escrituras recientes, pero las pierdes de una manera más sensata.
  • data=writeback: los metadatos se journalizan, pero ext4 no hace cumplir el orden entre las escrituras de datos y los commits de metadatos. Tras un fallo, los metadatos pueden apuntar a bloques cuyos contenidos son antiguos. Este es el modo “el sistema de ficheros está consistente pero tus archivos son sorprendentes”.
  • data=journal: tanto los datos como los metadatos se journalizan. Es más fuerte en consistencia tras fallo, normalmente más lento y no es gratis. También aumenta la amplificación de escritura: escribes datos en el journal y luego en su ubicación final.

Por qué data=writeback corrompe expectativas

Porque a menudo pasa pruebas casuales. Tu app escribe, tus tests pasan, tu benchmark luce bien, tu manager asiente. Luego sufres un apagado no limpio y descubres que la última “transacción” exitosa escribió metadatos lo bastante duraderos para sobrevivir al reinicio, pero los bloques de datos debajo son de una versión anterior.

Esto es especialmente brutal para cargas de trabajo de tipo append o registros donde los usuarios esperan crecimiento monótono. Con writeback, después de un fallo puedes obtener “huecos” de contenido antiguo dentro de lo que parece ser un archivo más nuevo.

¿Entonces nunca debería usar data=writeback?

Para servidores de propósito general: sí, básicamente nunca. Si tienes una carga de trabajo muy limitada que nunca lee datos recién escritos tras un fallo (scratch, cachés), ok. Para cualquier cosa con semántica de durabilidad, estás comprando rendimiento con una deuda que no se refinancia.

Barreras, caché de escritura y por qué “almacenamiento estable” es político

Las barreras (y sus equivalentes modernos: flushes y FUA) son las vallas que dicen a los dispositivos “este orden importa”. Sin ellas, la pila de almacenamiento se convierte en una caja de sugerencias.

Cachés de escritura: el gran acelerador y el gran mentiroso

La mayoría de las unidades (y muchos dispositivos de bloque virtuales) usan una caché con write-back. Las escrituras completan rápido porque primero aterrizan en memoria volátil. Si se pierde la energía, esas escrituras se evaporan. Si tu dispositivo reconoce escrituras desde la caché volátil como “hechas” e ignora las solicitudes de flush, el SO no puede garantizar durabilidad sin importar cuán sinceramente llame a fsync().

Qué significa realmente barrier=0 / nobarrier

Deshabilitar barreras puede mejorar el rendimiento y la latencia en algunas pilas, especialmente kernels antiguos y ciertos stacks de controladores. También puede crear la paradoja clásica post-fallo: el journal se reproduce, el sistema de ficheros se monta limpio y tu base de datos descubre que ayer falta aunque los logs afirmen que commitió.

Las barreras no son “seguridad extra”. Son cómo el sistema de ficheros hace cumplir el orden que asumió cuando te prometió consistencia. Quitarlas es como quitar las tuercas porque la rueda gira más rápido.

Datos interesantes y contexto histórico (el almacenamiento tiene recibos)

  1. ext3 introdujo el journaling al Linux mainstream a principios de los 2000 extendiendo ext2, principalmente para reducir los largos tiempos de fsck tras fallos.
  2. La asignación retrasada de ext4 mejoró el rendimiento pero también cambió el comportamiento ante fallos; se volvió más importante que las apps llamen correctamente a fsync().
  3. Las barreras fueron opcionales y caras en algunas pilas antiguas; administradores a veces las desactivaban siguiendo guías de tuning escritas para hardware distinto.
  4. Algunos SSDs de consumo iniciales eran famosos por ignorar comandos de flush, haciendo que el comportamiento “sync” fuera más aspiracional que real.
  5. XFS priorizó escalabilidad y paralelismo y históricamente recomendó “usar hardware con protección contra pérdida de energía” en lugar de pretender arreglar dispositivos que mienten.
  6. El debate NFS sync vs async es antiguo; administradores han estado intercambiando durabilidad por velocidad desde antes de que “cloud” fuera una profesión.
  7. POSIX permite escrituras en búfer; un write() exitoso no implica persistencia, lo cual sorprende a la gente exactamente una vez en su carrera.
  8. Las bases de datos añadieron conmutadores de durabilidad (como deshabilitar fsync) porque los usuarios pedían velocidad—y luego culpaban a la base de datos por las consecuencias.

Peculiaridades por sistema de ficheros (ext4, XFS, btrfs)

ext4: valores seguros por defecto, ajustes inseguros

ext4 con configuraciones relativamente por defecto (data=ordered, barreras activadas) suele ser una buena base para discos locales. La mayoría de las historias de terror empiezan con “cambiamos opciones de montaje para alcanzar un objetivo de latencia.” El segundo acto empieza con “pérdida de energía”.

También presta atención a:

  • commit=: aumenta el intervalo entre commits del journal. Valores grandes aumentan la ventana de pérdida de metadatos tras un fallo. Genial para benchmarks de throughput; menos genial para líneas de tiempo de incidentes.
  • errors=remount-ro: valor por defecto común; bueno para seguridad, pero puede convertir un problema latente de disco en un sistema raíz en solo lectura. Eso es una característica, no una traición.

XFS: robusto, pero aún debes respetar la física

XFS es excelente en cargas paralelas y sistemas de ficheros grandes. Usa journaling para metadatos; los datos no se journalizan. Depende del orden de escrituras y del comportamiento de flush para mantener la consistencia.

La “corrupción de expectativas” común con XFS tiene menos que ver con banderas de montaje y más con asumir que el dispositivo subyacente respeta los flushes. Si ejecutas XFS sobre una pila que miente sobre flushes, puedes obtener inconsistencias post-fallo que parecen “XFS se comió mis datos” cuando el verdadero culpable es “el controlador reconoció una escritura que no pudo conservar”.

btrfs: checksums, copy-on-write y otros trade-offs

btrfs aporta checksums de extremo a extremo y semántica copy-on-write, lo que cambia cómo aparece la corrupción. Puede detectar corrupción silenciosa, lo cual es estupendo. También puede amplificar escrituras y comportarse de manera diferente bajo ciertas cargas. Los fallos de expectativa aquí suelen relacionarse con tuning de rendimiento (compresión, CoW en bases de datos) más que con semánticas crudas de durabilidad. Aun así: las opciones de montaje importan.

Almacenamiento en red: NFS y la seductora mentira de async

Si el almacenamiento local es complicado, el almacenamiento en red es complicado con latencia.

Servidor NFS async: reconocimientos rápidos, remordimientos lentos

En NFS, la opción de montaje más peligrosa a menudo no es una opción de cliente: es la opción de exportación del servidor async. Con async, el servidor puede responder “OK” a una escritura del cliente antes de comprometerla en almacenamiento estable. Si el servidor NFS falla, el cliente cree que la escritura fue durable, pero la realidad discrepa.

A veces los equipos justifican async con “pero el servidor tiene UPS”. Vale. Un UPS es un plan de energía, no una prueba de corrección. Kernel panic, bug de firmware, fallos en la alimentación dual, error humano: no le piden permiso a tu UPS.

Broma 2: “Lo montamos async por rendimiento” es el equivalente en almacenamiento a “quité el detector de humo porque hacía ruido”.

sync en NFS no es gratis, pero es honesto

sync obliga al servidor a comprometer las escrituras en almacenamiento estable antes de responder. Es más lento, sí. Pero alinea los reconocimientos con la durabilidad. Ese es todo el propósito de un sistema de almacenamiento: almacenar cosas.

Tres mini-historias corporativas desde las trincheras del almacenamiento

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

Una empresa mediana gestionaba un repositorio interno de artefactos: binarios, logs de build y manifiestos de despliegue. Nada “crítico”, hasta que lo fue. El equipo lo migró a un nuevo clúster de VM y conectó un dispositivo de bloque rápido en red. El plan de migración incluía un ajuste rápido del sistema de ficheros: data=writeback en ext4 porque un benchmark mostró una mejora notable durante cargas paralelas de subida.

La suposición fue simple y muy humana: “El repositorio es append-only, así que la consistencia no importa mucho.” Sus pipelines CI escribían artefactos, recibían HTTP 200 y seguían. El servicio pareció estable durante meses.

Entonces un host hypervisor se cayó durante una ventana de parches. La VM se reinició limpiamente. ext4 reprodujo el journal. El sistema de ficheros se montó sin quejas. El servicio de artefactos arrancó. Todos respiraron aliviados.

Dos días después, un equipo intentó redeplegar una versión antigua. El manifiesto estaba, pero un binario referenciado era sutilmente incorrecto: nombre correcto, tamaño correcto, contenido incorrecto. No basura aleatoria: un chunk de una build anterior. La depuración fue miserable porque parecía un mal cache o un release fallido. Finalmente lo rastrearon a un archivo que había sido sobrescrito durante una actualización de metadatos sin orden impuesto entre datos y metadatos. Su suposición de “append-only” era errónea: el servicio hacía compactación periódica y reescrituras de metadatos.

La solución no fue heroica. Remontaron con data=ordered, forzaron el comportamiento correcto de fsync en la ruta de la aplicación y añadieron una verificación de integridad post-fallo para artefactos críticos. La lección caló: las semánticas de almacenamiento no son negociables solo porque tu servicio no sea “cliente-facing”.

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

Una plataforma analítica tenía una vía de ingest caliente que escribía en XFS local sobre NVMe. Durante horas pico, la latencia se volvía errática. Alguien hizo el movimiento clásico: ajustar opciones de montaje y parámetros del scheduler de I/O en producción porque los gráficos pedían a gritos. Deshabilitaron barreras (la perilla exacta varió según kernel y sistema de ficheros) y ajustaron el comportamiento de commit para empujar commits de metadatos con menos frecuencia.

Funcionó. La latencia mejoró. Los dashboards se calmaron. El cambio se codificó en una imagen, porque el éxito es contagioso.

Semanas después, un rack perdió energía. Los hosts volvieron. La mayoría de servicios se recuperaron. Un subconjunto de nodos arrancó con mensajes de “replayed journal” y luego sirvió datos de ingest parcial en silencio. La plataforma no cayó; degradó en una forma peor que un crash: produjo analíticas incompletas que parecían plausibles.

El postmortem fue incómodo porque nadie “rompió” nada. La optimización hizo lo que debía: aceleró el sistema debilitando suposiciones de orden. El problema fue que su canal de datos dependía de comportamiento similar a fsync de un componente que no fsyncaba en todos los lugares correctos. El tweak de montaje amplió la ventana donde la falta de flushes importaba.

Revirtieron los cambios de montaje, añadieron límites explícitos de durabilidad en los componentes de ingest y—esto importa—introdujeron una prueba de caos que simulaba pérdida abrupta de energía para un clúster de staging. El tuning de rendimiento no paró. Simplemente dejó de ser un salto de fe.

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

Un servicio cercano a pagos (no procesaba tarjetas, pero lo bastante ligado para ser auditado) tenía una política sosa: todos los volúmenes de bases de datos debían montarse con valores seguros, barreras activadas y cualquier desviación requería una evaluación de riesgo escrita y un plan de rollback. Los ingenieros se quejaban porque eso ralentizaba experimentos de rendimiento “simples”.

También tenían otro hábito aburrido: pruebas trimestrales de “tirar el enchufe” en una réplica no productiva usando configuraciones de almacenamiento similares a producción. Nada sofisticado. Directo y brutal. Ejecutaban una carga representativa, provocaban un apagado no limpio, reiniciaban y verificaban que la base de datos se recuperara sin perder transacciones reconocidas.

Una vez, la prueba falló. La base de datos arrancó, pero un pequeño conjunto de transacciones recientemente comprometidas había desaparecido. Ese es el tipo de bug que se convierte en un evento regulatorio si se despliega.

Lo rastrearon hasta una actualización de firmware del almacenamiento que cambió el comportamiento de flush bajo ciertas profundidades de cola. Nada en los logs del SO gritó. El sistema de ficheros parecía limpio. Solo la prueba de durabilidad lo detectó. Bloquearon el despliegue de firmware, cambiaron a dispositivos con protección contra pérdida de energía para ese nivel y mantuvieron la política aburrida.

Cuando el centro de datos real tuvo un evento de energía, su servicio fue el que no necesitó una sala de guerra de emergencia. No porque fueran más listos. Porque fueron más tercos con las semánticas.

Guía de diagnóstico rápido

Cuando se sospecha de durabilidad o corrupción, necesitas rapidez y disciplina en la priorización. Esta es la lista de “no te pierdas en los detalles” que uso.

Primero: confirma qué montaste y exportaste realmente

  • Revisa opciones de montaje actuales (findmnt, /proc/mounts).
  • Si hay NFS, revisa opciones de export del servidor (exportfs -v) y opciones de montaje del cliente (nfsstat -m).

Segundo: valida la cadena de durabilidad (flushes y caché de escritura)

  • ¿Está habilitada la caché de escritura del dispositivo? ¿Está protegida?
  • ¿Se soportan y respetan los flushes (tanto como puedas inferir)?
  • ¿Están deshabilitadas las barreras/flushes en el sistema de ficheros o en device-mapper?

Tercero: identifica si ves problemas de consistencia de metadatos o de orden de datos

  • Problemas de metadatos: fallos al montar, bucles de replay del journal, inodos huérfanos, errores del sistema de ficheros.
  • Problemas de orden: el sistema se monta limpio, pero las invariantes a nivel de aplicación están rotas (transacciones faltantes, contenidos de archivo incorrectos bajo nombres correctos).

Cuarto: reproduce con una prueba controlada de crash (si puedes)

  • Ejecuta una pequeña carga de write+fsync.
  • Forza un apagado no limpio.
  • Verifica qué sobrevivió y qué no.

Tareas prácticas: comandos, salidas y decisiones

Estos son comandos reales que puedes ejecutar durante un incidente, una migración o una semana de “por qué esto va lento”. Para cada uno: qué ejecutar, qué significa la salida y qué decisión tomar.

Tarea 1: Ver qué está realmente montado (no lo que crees que está en fstab)

cr0x@server:~$ findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /var/lib/postgresql
/dev/nvme0n1p2 /var/lib/postgresql ext4 rw,relatime,errors=remount-ro,data=ordered

Significado: Esta es la verdad en el kernel en ejecución. Aquí, ext4 está en data=ordered, que generalmente es el valor por defecto más seguro.

Decisión: Si ves data=writeback, nobarrier o un commit= inusualmente grande en una ruta que requiere durabilidad, márcalo y planifica un cambio.

Tarea 2: Contrastar con /proc para detectar bind mounts y sorpresas

cr0x@server:~$ grep -E ' /var/lib/postgresql ' /proc/mounts
/dev/nvme0n1p2 /var/lib/postgresql ext4 rw,relatime,errors=remount-ro,data=ordered 0 0

Significado: Mismo relato; también útil cuando la salida de findmnt está siendo filtrada por herramientas.

Decisión: Si una ruta está inesperadamente montada desde overlayfs, tmpfs o una capa de runtime de contenedor, detente y mapea el verdadero límite de persistencia.

Tarea 3: Inspeccionar superblock de ext4 y características por defecto (estado del journal, etc.)

cr0x@server:~$ sudo tune2fs -l /dev/nvme0n1p2 | egrep 'Filesystem features|Default mount options|Filesystem state|Journal'
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Default mount options:    user_xattr acl
Filesystem state:         clean
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3

Significado: Confirma que hay journaling (has_journal) y que las sumas de comprobación de metadatos están habilitadas (metadata_csum), lo que ayuda a detectar ciertas corrupciones.

Decisión: Si el sistema de ficheros no está limpio después de un incidente, planifica una ventana de fsck offline. Si falta journaling en un volumen que asumías journalizado, trátalo como un fallo de diseño.

Tarea 4: Revisar la vista del kernel sobre replays de journal y advertencias ext4

cr0x@server:~$ dmesg -T | egrep -i 'ext4|jbd2|xfs|btrfs' | tail -n 20
[Tue Feb  4 10:42:12 2026] EXT4-fs (nvme0n1p2): mounted filesystem with ordered data mode. Quota mode: none.
[Tue Feb  4 10:42:12 2026] EXT4-fs (nvme0n1p2): re-mounted. Opts: (null)

Significado: Muestra si el sistema de ficheros reprodujo un journal, se montó en solo lectura o registró errores.

Decisión: Si ves replays repetidos del journal o “Errors detected”, asume problemas de I/O subyacente o cachés inseguros. Escala al equipo de hardware/virtualización y reduce las opciones de riesgo de escritura.

Tarea 5: Verificar ajustes de caché de escritura del disco (y no asumir que son seguros)

cr0x@server:~$ sudo hdparm -W /dev/nvme0n1
/dev/nvme0n1:
 write-caching =  1 (on)

Significado: La caché de escritura está activada. Es normal, pero plantea la pregunta: ¿está protegida por protección ante pérdida de energía, caché con batería o un controlador que respete los flushes?

Decisión: Si es hardware de consumo sin PLP y te importa la durabilidad, asegúrate de que estén activadas barreras/flushes y considera cambios de hardware para niveles críticos.

Tarea 6: Comprobar caché volátil y soporte de flush en la capa de bloques

cr0x@server:~$ lsblk -D -o NAME,ROTA,DISC-GRAN,DISC-MAX,DISC-ZERO
NAME    ROTA DISC-GRAN DISC-MAX DISC-ZERO
nvme0n1    0      512B       2G         0

Significado: Soporte de discard y granularidad. No es una señal directa de durabilidad, pero ayuda a entender la clase de dispositivo y comportamiento.

Decisión: Usa esto como pista: SSD/NVMe típicamente implica caché de escritura y comportamiento FTL; ajusta y valida en consecuencia, especialmente en semánticas de flush.

Tarea 7: Buscar barreras/flushes deshabilitados en opciones de montaje

cr0x@server:~$ findmnt -no TARGET,OPTIONS | egrep -i 'barrier|nobarrier|data=writeback|commit='
/mnt/fast rw,relatime,data=writeback,commit=60

Significado: Este montaje está debilitando explícitamente el orden (data=writeback) y aumentando el intervalo de commit.

Decisión: Si /mnt/fast contiene algo que no puedes regenerar, programa un remount con opciones más seguras y planifica una auditoría de durabilidad a nivel de aplicación.

Tarea 8: Confirmar comportamiento de montaje del cliente NFS (qué cree que está haciendo el cliente)

cr0x@server:~$ nfsstat -m
/var/lib/shared from nfs01:/export/shared
 Flags: rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.0.2.10,local_lock=none

Significado: Muestra la versión de NFS y opciones del cliente. Nota: las opciones del cliente no anulan los reconocimientos async del servidor.

Decisión: Si esto sustenta una base de datos o una cola, trata NFS como un ítem de revisión de arquitectura. Verifica las opciones de export del servidor y la compatibilidad de la aplicación.

Tarea 9: Comprobar en el servidor NFS exports con async (la verdadera trampa)

cr0x@server:~$ sudo exportfs -v | sed -n '1,120p'
/export/shared
	10.0.0.0/16(rw,async,wdelay,hide,no_subtree_check,sec=sys,secure,root_squash)

Significado: La exportación es async. El servidor puede reconocer escrituras antes de commit.

Decisión: Para cargas durables, cámbialo a sync, luego mide el impacto en rendimiento honestamente. Si el rendimiento es inaceptable, la respuesta es arquitectura, no negación.

Tarea 10: Validar presión de writeback y límites de dirty (dimensionar la ventana de riesgo)

cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000

Significado: Las páginas sucias pueden acumularse hasta 20% de la RAM antes de throttling; la expiración es ~30 segundos. Esto afecta cuánto dato puede ser “reconocido” por el sistema sin estar en disco.

Decisión: Para sistemas críticos, mantén estos valores razonables. No “optimices” dejando que el sistema almacene minutos de escrituras a menos que aceptes perder esos minutos en un fallo.

Tarea 11: Observar qué piensa tu sistema de ficheros sobre delayed allocation y writeback (ejemplo ext4)

cr0x@server:~$ cat /proc/mounts | grep ' ext4 ' | head -n 2
/dev/nvme0n1p2 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
/dev/nvme0n1p3 /var ext4 rw,relatime,data=ordered 0 0

Significado: Confirma modo de datos y manejo de errores.

Decisión: Si necesitas garantías más fuertes para rutas específicas, considera moverlas a un sistema de ficheros separado con ajustes conservadores en vez de hacer globalmente el sistema más lento.

Tarea 12: Medir si tu app realmente emite flushes (chequeo de realidad)

cr0x@server:~$ sudo strace -f -e trace=fdatasync,fsync,openat,rename -p 1423 -s 80
strace: Process 1423 attached
fsync(17)                                = 0
rename("tmpfile", "current")             = 0
fsync(5)                                 = 0

Significado: El proceso está llamando fsync() y también sincronizando un descriptor de directorio (a menudo necesario después de rename() para metadatos durables). Esto es lo que “correcto” parece para muchas cargas.

Decisión: Si no ves fsync/fdatasync donde lo esperas (bases de datos, escritores WAL, colas), tu durabilidad probablemente sea ilusoria. Arregla la app/config antes de culpar al sistema de ficheros.

Tarea 13: Revisar errores de I/O y timeouts en la capa de bloques (el primo de la corrupción silenciosa)

cr0x@server:~$ sudo journalctl -k -b | egrep -i 'I/O error|blk_update_request|nvme|reset|timed out' | tail -n 20
Feb 04 10:38:21 server kernel: nvme nvme0: I/O 42 QID 7 timeout, aborting
Feb 04 10:38:21 server kernel: nvme nvme0: Abort status: 0x371

Significado: Tienes timeouts a nivel de dispositivo. Esto puede causar replays de journal, remount-ro y escrituras parciales dependiendo del manejo de errores.

Decisión: Trátalo como un problema de fiabilidad de hardware/disco virtual. Deja de tunear opciones de montaje y empieza a planificar reemplazo, fixes de firmware o cambios de infraestructura.

Tarea 14: Inspeccionar ajustes de cola para entender picos de latencia (no es directamente durabilidad, pero sí relacionado)

cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] kyber none

Significado: Muestra el scheduler en uso. El tuning de latencia a veces lleva a la gente a desactivar comportamientos relacionados con flush; no confundas estas capas.

Decisión: Ajusta el scheduler separadamente de las semánticas de durabilidad. Mantén correctos los comportamientos de barreras/flush, luego optimiza throughput/latencia dentro de esas restricciones.

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

1) “El sistema de ficheros se monta limpio, pero mi base de datos perdió transacciones comprometidas”

Síntomas: Tras un fallo/reinicio, la BD arranca; los logs muestran commits exitosos; faltan datos o hay rollback más allá de lo que espera la lógica de replicación/WAL.

Causa raíz: Las escrituras reconocidas no fueron durables: servidor NFS con async, barreras deshabilitadas, caché de escritura que miente o la aplicación no hace fsync del WAL/datos correctamente. ext4 data=writeback puede amplificar lo raro.

Solución: Asegura que las exportaciones NFS sean sync para cargas durables; mantén barreras/flush activados; valida que el dispositivo respete flush; configura el sistema de ficheros en un modo de journaling más seguro; corrige la configuración de la app para fsync en los límites correctos.

2) “Tras un corte de energía, los archivos existen pero contienen contenido antiguo”

Síntomas: Nombres y timestamps parecen correctos; contenidos son antiguos o parcialmente revertidos; checksums fallan en una ventana estrecha antes del fallo.

Causa raíz: ext4 data=writeback o problemas de barrera/flush que permiten que los metadatos se comprometan antes que los bloques de datos.

Solución: Usa data=ordered (o data=journal cuando se justifique); mantén flushes activados; verifica semánticas de flush del controlador/dispositivo; reduce tuning arriesgado.

3) “Todo se volvió de solo lectura en mitad de la operación”

Síntomas: Las apps empiezan a fallar con “Read-only file system”; los logs del kernel muestran errores del sistema de ficheros; ocurre un remount.

Causa raíz: Errores de I/O subyacentes o corrupción de metadatos; errors=remount-ro hizo su trabajo. No es un bug de opciones de montaje: el kernel te está protegiendo.

Solución: Investiga salud del dispositivo; recoge logs; ejecuta checks del sistema de ficheros en mantenimiento; reemplaza hardware sospechoso; restaura desde backups conocidos si es necesario.

4) “Habilitamos una opción de rendimiento y ahora la recuperación tras crash tarda una eternidad”

Síntomas: Reproducción larga del journal; arranque lento; servicios esperando recuperación del sistema de ficheros; I/O alto en inicio.

Causa raíz: commit= grande, backlog pesado de writeback o carga que genera muchos cambios de metadatos no comprometidos; en NFS, backlog del servidor.

Solución: Reduce el intervalo de commit; asegura capacidad I/O adecuada; evita acumular enormes conjuntos de páginas sucias; para NFS, mantén exports síncronos para datos críticos y escala el almacenamiento apropiadamente.

5) “Solo cambiamos opciones de montaje, ¿por qué la integridad de datos ahora es ‘aleatoria’?”

Síntomas: Heisenbugs tras reinicios; fallos de checksum intermitentes; no reproducible en carga normal.

Causa raíz: Cambiaste semánticas de orden/durabilidad. La carga dependía accidentalmente del comportamiento anterior (timing, orden, flushes) y ahora viola sus propias invariantes.

Solución: Revierte a semánticas seguras; luego define y aplica explícitamente límites de durabilidad en diseño de aplicación y almacenamiento.

Listas de verificación / plan paso a paso

Lista 1: Antes de tocar opciones de montaje en producción

  1. Clasifica los datos: caché/regenrable vs durable/autoritativo.
  2. Escribe la promesa de durabilidad en lenguaje claro: “Después de que la app reporte éxito, los datos sobreviven a pérdida de energía.”
  3. Confirma el comportamiento de la app: ¿llama a fsync/fdatasync correctamente (incluyendo fsync de directorios tras rename/create)?
  4. Confirma comportamiento del almacenamiento: ¿el dispositivo/controlador/hipervisor respeta los flushes?
  5. Prueba el cambio en staging bajo carga parecida a producción y ejecuta un test de apagado no limpio.
  6. Tener rollback: remount rápido o redeploy de la configuración previa.

Lista 2: Valores seguros que recomiendo (y cuándo desviarse)

  • ext4: preferir data=ordered, barreras/flush activados (por defecto), evitar data=writeback para datos durables.
  • XFS: mantener por defecto salvo que tengas una razón probada. Emplea esfuerzo en validar el comportamiento de flush del dispositivo.
  • NFS para cargas durables: preferir export sync en el servidor. Si necesitas rendimiento tipo async, replantea la arquitectura (WAL local + replicación, o almacenamiento diseñado para ese propósito).
  • Intervalos de commit: no los infles a la ligera. Ventanas mayores implican pérdidas mayores.

Lista 3: Verificación post-incidente (tras apagado no limpio)

  1. Captura logs del kernel sobre replay/errors del sistema de ficheros.
  2. Verifica que las opciones de montaje sean las esperadas.
  3. Ejecuta verificaciones de integridad a nivel de aplicación (no solo checks del sistema de ficheros).
  4. Valida transacciones comprometidas o marcadores de estado conocidos.
  5. Si hay discrepancias, asume fallo en la cadena de durabilidad y escala en consecuencia.

Preguntas frecuentes

1) ¿Es data=writeback siempre “corrupción”?

No. El sistema de ficheros puede seguir estructuralmente consistente. La “corrupción” suele ser semántica: los metadatos pueden referenciar datos que no se escribieron en el orden que asumiste. Si esperabas “el nuevo archivo apunta a bytes nuevos”, writeback puede violar eso tras fallos.

2) Si mi app usa fsync(), ¿estoy a salvo?

Más seguro, no automáticamente a salvo. También necesitas que la pila de almacenamiento respete los flushes, y debes fsync las cosas correctas (incluyendo directorios tras rename/create, según el patrón). Y si estás en NFS con servidor async, los reconocimientos aún pueden adelantarse al commit durable.

3) ¿Por qué la gente usa opciones inseguras?

Porque los benchmarks son persuasivos y los incidentes son raros—hasta que no lo son. También porque algunos entornos (clusters de scratch, cachés, granjas de build) realmente no necesitan durabilidad, y las opciones son válidas allí. El error es aplicar por copia esos ajustes en sistemas que requieren durabilidad.

4) ¿Cuál es la diferencia entre “consistencia del sistema de ficheros” y “durabilidad de datos”?

Consistencia significa que las estructuras del sistema de ficheros están sanas tras la recuperación. Durabilidad significa que las escrituras reconocidas sobreviven a fallos. El journaling se centra en consistencia; la durabilidad requiere orden correcto y comportamiento de flush a través de toda la pila.

5) ¿NFS es aceptable alguna vez para bases de datos?

A veces, con las opciones de servidor correctas (sync), almacenamiento estable abajo y una base de datos que lo soporte. Pero es un diseño de alto riesgo si no controlas toda la cadena. Muchos equipos eligen almacenamiento local para WAL/escrituras primarias y replican para redundancia.

6) ¿Debería deshabilitar la caché de escritura del disco por seguridad?

Deshabilitar la caché puede reducir riesgo en dispositivos no protegidos, pero también puede hundir el rendimiento y no siempre está soportado o es efectivo. La mejor respuesta es: usa dispositivos con protección contra pérdida de energía para niveles críticos y mantén habilitadas las semánticas de flush/barrera.

7) ¿Qué opción de montaje mejora más la seguridad?

Usualmente, “no deshabilites las características de seguridad” es lo que más ayuda: no uses data=writeback para datos durables, no desactives barreras/flushes y evita async en servidores NFS donde importa la durabilidad. La seguridad suele ser la ausencia de “optimizaciones”.

8) ¿Cómo demuestro que mi almacenamiento es durable?

Lo pruebas con inyección de fallos de estilo pérdida de energía bajo carga realista. Ejecuta una carga que haga escrituras reconocidas con límites de fsync, crashea de forma no limpia, reinicia y valida las invariantes. Cualquier otra cosa es teoría.

9) Mi sistema de ficheros es ext4 con data=ordered. ¿Aún puedo perder datos?

Sí. Puedes perder las escrituras más recientes que aún estaban en caché, y puedes perder escrituras reconocidas si el dispositivo miente sobre flushes o la aplicación no fsyncea correctamente. ordered reduce clases específicas de sorpresas post-fallo; no anula la física.

10) ¿Es data=journal la solución para todo?

No. Puede ser más lento y aumenta la amplificación de escritura. Es una herramienta para casos concretos, no una configuración universal. Muchos sistemas van mejor con logging a nivel de aplicación (WAL) y valores seguros del sistema de ficheros.

Conclusión: próximos pasos que puedes hacer esta semana

Si te llevas una idea de esto: las opciones de montaje no solo afinan rendimiento. Definen qué significa “hecho”. Cuando las cambias, cambias el contrato que las aplicaciones creían tener.

Haz estos pasos:

  1. Inventario de montajes en rutas críticas con findmnt. Anota cualquier cosa no por defecto y justíficala.
  2. Prohíbe data=writeback para datos durables salvo que tengas una excepción documentada y probada.
  3. Audita exportaciones NFS buscando async. Si lo encuentras en una carga durable, trátalo como un riesgo de sev.
  4. Valida la cadena de flush: comportamiento de caché del dispositivo, políticas del controlador, semánticas de la capa de virtualización.
  5. Añade una prueba de apagado no limpio a staging para al menos un servicio crítico. Hazla rutinaria, no heroica.

No necesitas convertirte en desarrollador de sistemas de ficheros. Sí necesitas dejar de permitir que ajustes de rendimiento reescriban tu historia de durabilidad a tus espaldas.

← Anterior
IOMMU explicado: el interruptor del BIOS que hace (o rompe) el passthrough de GPU
Siguiente →
Bases de datos en Linux: PostgreSQL en VPS pequeño — Ajustes que previenen OOM

Deja un comentario