Recuperación de pool lleno en ZFS: qué falla primero y cómo volver

¿Te fue útil?

Al 92% de ocupación, ZFS todavía sonríe cortésmente. Al 98%, empieza a responder correos con “según mi última petición de asignación”. Al 100%, no negocia: tu “renombrado rutinario” se convierte en un incidente de producción y de repente todos aprenden qué significa ENOSPC.

Esta es una guía de campo para el momento feo en que un pool ZFS se queda sin espacio (o sin el espacio que ZFS puede realmente usar), además del camino disciplinado de vuelta a un margen operativo saludable. Está escrita para personas que no tienen tiempo de admirar la elegancia del copy-on-write mientras suena el pager.

Qué falla primero: la cadena de fallos cuando ZFS se llena

“Pool lleno” no es un único estado. Es un conjunto en cascada de restricciones que afectan a distintos workloads de forma diferente. ZFS no solo necesita bytes para tus datos; necesita espacio de trabajo para metadatos, contabilidad de asignación, reescrituras copy-on-write y commits de grupos transaccionales (TXG). Cuando el pool se acerca a lleno, las elecciones del asignador empeoran, la fragmentación aumenta y la latencia empieza a comportarse como si quisiera enviarte un mensaje.

1) Lo primero que notarás: latencia, no necesariamente errores

Cuando el espacio libre escasea, ZFS dispone de menos regiones contiguas para asignar. Para escrituras, eso significa más búsquedas de metaslab, más fragmentación y más I/O disperso. Incluso si el pool no está “al 100%”, el acantilado de rendimiento es real. Para muchos pools, el declive comienza alrededor del 80–90% dependiendo de recordsize, workload y la geometría de los vdev. No es superstición; es física del asignador y penalizaciones de seek disfrazadas de stack de almacenamiento.

2) Luego la aplicación empieza a fallar “misteriosamente”

Las aplicaciones con frecuencia fallan de forma indirecta:

  • Bases de datos pueden bloquearse en fsync o fallar al no poder crear archivos temporales, segmentos WAL o nuevas extensiones de tablespace.
  • Contenedores no arrancan porque las capas overlay no pueden escribir, los logs no pueden anexarse o el runtime no puede crear estado bajo /var/lib.
  • Servicios del sistema fallan porque no pueden escribir pidfiles, journald no puede persistir o gestores de paquetes no pueden descomprimir.

3) Borrar archivos no siempre libera espacio (y esa es la parte cruel)

Las instantáneas (snapshots) de ZFS preservan bloques referenciados. Si borras un directorio de 200 GB pero las snapshots todavía referencian esos bloques, el uso del pool no se mueve. En un incidente de “pool lleno”, este es el momento en que los equipos empiezan a culpar a ZFS. ZFS es inocente; simplemente es consistente.

4) Los bordes más afilados: metadatos, special vdevs y reservas

Algunos incidentes de “pool lleno” son, en realidad, incidentes de “el pool tiene espacio, pero no puedes usarlo”:

  • Reservas (reservation, refreservation) pueden acotar espacio de modo que tus borrados no ayuden al dataset que te importa.
  • Cotas (quota, refquota) pueden limitar un dataset y provocar ENOSPC aunque el pool tenga holgura.
  • Special vdev lleno (si usas uno) puede causar fallos de asignación de metadatos mientras los vdevs de datos principales aún tienen espacio.
  • Slop space (espacio reservado a nivel de pool) puede bloquear asignaciones cuando estás cerca del lleno; es una característica de seguridad que maldecirás hasta que te salve.

5) Por qué “añadir un disco” no siempre alivia de inmediato

Ampliar capacidad puede ayudar, pero no rebobina la fragmentación, no arregla un special vdev lleno ni elimina referencias desde snapshots. Además, la actividad de resilver o expansión añade carga en el peor momento posible. Añades capacidad para sobrevivir; aún necesitas limpieza y políticas para recuperarte.

Una idea parafraseada de Werner Vogels (reliability/ops): “Todo falla, todo el tiempo; diseña y opera asumiendo que ocurrirá”. Eso aplica a la saturación de almacenamiento más de lo que cualquiera quiere admitir.

Hechos e historia interesantes (los que cambian decisiones)

  1. ZFS nació en Sun a mediados de los 2000 con copy-on-write y checksums end-to-end como puntos de diseño centrales, no añadidos.
  2. “Las snapshots son baratas” es verdad—hasta que el pool está lleno y “baratas” se convierte en “¿por qué nadie las podó?”.
  3. ZFS usa un modelo transaccional (TXGs). Tus escrituras no están “hechas” como muchos asumen hasta que el TXG comete; la presión allí aparece como latencia sync.
  4. La contabilidad de espacio es sutil: USED, REFER, USEDDS, uso por snapshot y reservas cuentan verdades distintas.
  5. La “regla del 80%” no la inventó ZFS, pero ZFS hace las consecuencias de altos niveles de ocupación más visibles porque el comportamiento del asignador degrada bruscamente.
  6. Special vdevs (popularizados en OpenZFS) pueden acelerar metadatos y bloques pequeños, pero introducen un nuevo modo de fallo “lleno” si se dimensionan mal.
  7. Recordsize y volblocksize influyen en la fragmentación y en la usabilidad del espacio libre. Bloques grandes pueden ser eficientes—hasta que no encuentran espacio de aterrizaje.
  8. La compresión puede retrasar la crisis, pero también puede ocultar el crecimiento hasta que cruzas un umbral y de repente cada escritura es una pelea por espacio.
  9. Copy-on-write significa que “sobrescribir” necesita espacio libre. Quedarse sin espacio libre puede romper workloads que creen que están “solo actualizando en sitio”.

Broma #1: Un pool ZFS al 99% es como una sala de reuniones reservada “consecutivamente” todo el día: técnicamente disponible, prácticamente inutilizable.

Guía de diagnóstico rápido (primero / segundo / tercero)

Si solo tienes cinco minutos antes de que un VP aparezca en tu Slack, haz esto. El objetivo es identificar cuál de estos es tu cuello de botella: capacidad del pool, cuota/reserva del dataset, snapshots, o una limitación de special vdev/metadatos.

Primero: confirma la restricción real

  • ¿El pool está realmente lleno? Revisa la capacidad y salud con zpool list.
  • ¿El dataset está limitado? Revisa cuotas/refquotas del dataset afectado.
  • ¿El espacio está “atascado” en snapshots? Observa el uso de snapshots y la rotación reciente de snapshots.
  • ¿Reservas están acaparando espacio? Busca valores anómalos de refreservation y reservation.

Segundo: localiza el mayor consumidor en el menor tiempo

  • Desglose por dataset: encuentra qué dataset consume el pool.
  • Desglose por snapshot: encuentra qué conjunto de snapshots está anclando más espacio.
  • Perspectiva de procesos: identifica qué workload está escribiendo y fallando actualmente.

Tercero: elige la acción inmediata más segura

  • Válvula de seguridad inmediata: para el escritor (pausa la ingestión, desactiva logs verbosos, detén jobs descontrolados).
  • Recuperación de bajo riesgo: elimina datos descartables sin requisitos de snapshot; poda snapshots antiguos si la política lo permite.
  • Arreglo estructural: añade capacidad, ajusta cuotas/reservas o corrige el dimensionamiento del special vdev—luego normaliza la política de snapshots.

No empieces con “rm -rf” en rutas aleatorias. Solo crearás un segundo incidente: “borramos lo incorrecto y además no liberamos espacio”.

Tareas prácticas de recuperación (comandos, salidas, decisiones)

Estas son tareas probadas en el campo que puedes ejecutar durante un incidente. Cada tarea incluye un comando, salida realista, lo que significa y la decisión a tomar. Ejecútalas en orden si no estás seguro. Salta entre ellas si ya conoces la forma del problema.

Task 1: Confirmar capacidad y salud del pool

cr0x@server:~$ zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank   27.2T  26.6T   640G        -         -    72%    97%  1.00x  ONLINE  -

Significado: El pool tank está al 97% y muy fragmentado. Estás en la zona de peligro.

Decisión: Trátalo como un outage en curso. Detén escritores no esenciales. Planea recuperar espacio y/o añadir capacidad.

Task 2: Buscar errores del pool y dispositivos lentos

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Determine if the device needs to be replaced, and clear the errors
  scan: scrub repaired 0B in 12:31:44 with 0 errors on Sun Dec 22 03:11:08 2025
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
            sda                     ONLINE       0     0     0
            sdb                     ONLINE       0     0     0
            sdc                     ONLINE       2     0     0
            sdd                     ONLINE       0     1     0

errors: Permanent errors have been detected in the following files:
        tank/data/postgres/base/16384/2609

Significado: Tienes errores en dispositivos y un archivo referenciado con corrupción. Esto es independiente de “pool lleno” pero puede hacerse visible bajo estrés.

Decisión: No comiences una limpieza agresiva hasta snapshotear datasets críticos (si es posible) y planear la remediación de los discos fallando. Si el pool está demasiado lleno para snapshotear, prioriza liberar un poco de espacio de forma segura primero, luego haz snapshots.

Task 3: Identificar qué datasets consumen el pool

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used tank
NAME                 USED   AVAIL  REFER  MOUNTPOINT
tank                 26.6T   512G   192K  /tank
tank/data            19.8T   512G  19.8T  /tank/data
tank/backups          4.9T   512G  3.1T   /tank/backups
tank/vm               1.6T   512G  1.6T   /tank/vm
tank/home             310G   512G   310G  /tank/home

Significado: El mayor consumidor es tank/data, luego tank/backups.

Decisión: Concéntrate primero en los datasets más grandes. Liberar 50 GB en un pool de 27 TB puede comprarte minutos, no estabilidad.

Task 4: Ver si las snapshots están anclando espacio

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -S used tank/data | head
NAME                                 USED  REFER  CREATION
tank/data@autosnap_2025-12-26_0000     620G  19.8T  Fri Dec 26 00:00 2025
tank/data@autosnap_2025-12-25_0000     410G  19.6T  Thu Dec 25 00:00 2025
tank/data@autosnap_2025-12-24_0000     395G  19.5T  Wed Dec 24 00:00 2025
tank/data@autosnap_2025-12-23_0000     380G  19.2T  Tue Dec 23 00:00

Significado: Las snapshots están consumiendo cientos de GB cada una en “bloques únicos” (la columna USED). Los borrados en tank/data no liberarán esos bloques hasta destruir las snapshots.

Decisión: Si las snapshots no son requeridas por cumplimiento o puntos de restauración, poda. Si son necesarias, añade capacidad primero o mueve datos fuera del pool.

Task 5: Encontrar rápidamente los mayores consumidores por snapshot en el pool

cr0x@server:~$ zfs list -t snapshot -o name,used -S used tank | head -n 10
NAME                                  USED
tank/backups@weekly_2025-12-22          1.2T
tank/data@autosnap_2025-12-26_0000      620G
tank/data@autosnap_2025-12-25_0000      410G
tank/vm@hourly_2025-12-26_0900          210G
tank/vm@hourly_2025-12-26_0800          205G
tank/home@daily_2025-12-26              18.4G
tank/home@daily_2025-12-25              17.9G

Significado: Una snapshot de backup consume 1.2 TB; probablemente retención fuera de control o un dataset de backup que cambia demasiado.

Decisión: Apunta primero a las snapshots de mayor impacto, pero verifica que sea seguro eliminarlas (política de restauración, retención legal, etc.).

Task 6: Verificar cuotas y refquotas en el dataset que falla

cr0x@server:~$ zfs get -o name,property,value,source quota,refquota tank/data
NAME       PROPERTY  VALUE  SOURCE
tank/data  quota     none   default
tank/data  refquota  20T    local

Significado: tank/data está limitado a 20 TB de uso referenciado. Puede alcanzar ENOSPC aunque el pool tenga espacio libre.

Decisión: Si el dataset es el que falla, considera elevar refquota temporalmente—después de confirmar que no dejará sin espacio a otros datasets críticos.

Task 7: Revisar reservas y refreservations (acaparamiento de espacio)

cr0x@server:~$ zfs get -o name,property,value,source reservation,refreservation -r tank | egrep -v 'none|0B|default'
tank/vm  refreservation  2T  local
tank/db  reservation     1T  local

Significado: Dos datasets reservan 3 TB. Ese espacio es efectivamente intocable por otros datasets.

Decisión: En una emergencia, reduce o elimina reservas si puedes tolerar el riesgo (existen por una razón). Documenta y restáuralas después.

Task 8: Confirmar si los borrados están “atascados” porque un proceso mantiene archivos abiertos

cr0x@server:~$ lsof +L1 | head
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NLINK     NODE NAME
nginx      1421 root    9w   REG  0,113  1048576     0  5329156 /tank/data/logs/access.log
postgres   2198 postgres 11w REG  0,113  1073741824   0  5329901 /tank/db/pg_wal/000000010000002A000000B3

Significado: Archivos fueron borrados (NLINK=0) pero siguen abiertos por procesos. El espacio no se liberará hasta que esos descriptores sean cerrados.

Decisión: Reinicia o envía la señal adecuada a los servicios afectados para que reabran logs (o rota correctamente). Esto puede recuperar espacio sin tocar snapshots.

Task 9: Identificar qué está escribiendo ahora mismo (y pararlo con intención)

cr0x@server:~$ zpool iostat -v tank 2 3
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        26.6T   640G    420   3800   82M   610M
  raidz2-0  26.6T   640G    420   3800   82M   610M
    sda         -      -     70    640   14M    98M
    sdb         -      -     66    640   13M    99M
    sdc         -      -     73    940   14M   155M
    sdd         -      -     71    580   14M    93M
----------  -----  -----  -----  -----  -----  -----

Significado: Las escrituras son intensas y desiguales en sdc. Alguien sigue vertiendo datos en un pool casi lleno.

Decisión: Pausa la ingestión, desactiva logs de depuración, detén jobs por lotes o pon el sistema en modo solo lectura para el servicio afectado hasta que se restaure la holgura.

Task 10: Comprobar si un special vdev es el verdadero “lleno”

cr0x@server:~$ zpool list -v tank
NAME         SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank        27.2T  26.6T   640G        -         -    72%    97%  1.00x  ONLINE  -
  raidz2-0  27.0T  26.1T   900G        -         -    70%    97%      -  ONLINE
  special    200G   198G   2.0G        -         -    15%    99%      -  ONLINE

Significado: El special vdev está al 99%. Si metadatos y bloques pequeños van allí, puedes obtener fallos de asignación aun con algo de espacio libre en otros lados.

Decisión: Trata esto como urgente. Probablemente necesites añadir capacidad al special vdev (mirroring de otro dispositivo y attach, o reconstruir un special vdev más grande) y/o ajustar la estrategia de special_small_blocks. Las soluciones rápidas son limitadas.

Task 11: Liberar espacio destruyendo snapshots (de forma segura y en orden)

cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-23_0000
cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-24_0000
cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-25_0000

Significado: La destrucción de snapshots es asíncrona en efecto; puede que no veas alivio inmediato si el sistema está ocupado, pero debería tender en la dirección correcta.

Decisión: Borra primero las más antiguas cuando la retención lo permita. Para cuando recuperes holgura operativa (más abajo explicamos “cuánto”), corrige la política de snapshots para no repetir esto a las 3 a.m.

Task 12: Confirmar espacio recuperado a nivel pool

cr0x@server:~$ zpool list tank
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  27.2T  25.9T  1.30T        -         -    69%    95%  1.00x  ONLINE  -

Significado: Has ganado ~660 GB. Todavía apretado, pero te alejaste del borde.

Decisión: Sigue hasta obtener holgura operativa (a menudo 15–20% en pools activos). Si no puedes llegar mediante eliminación, necesitas expansión de capacidad o migración de datos.

Task 13: Comprobar si un dataset sigue bloqueado por cuota tras mejorar la holgura del pool

cr0x@server:~$ zfs list tank/data
NAME       USED   AVAIL  REFER  MOUNTPOINT
tank/data  19.9T   100G  19.9T  /tank/data

Significado: El pool ahora tiene más espacio libre, pero el dataset solo tiene 100 GB disponibles—coherente con un límite de refquota.

Decisión: Eleva refquota si procede, o redistribuye datos a otro dataset con más holgura.

Task 14: Elevar temporalmente un refquota (controlado, registrado, reversible)

cr0x@server:~$ sudo zfs set refquota=22T tank/data
cr0x@server:~$ zfs get -o name,property,value,source refquota tank/data
NAME       PROPERTY  VALUE  SOURCE
tank/data  refquota  22T    local

Significado: El dataset puede ahora referenciar hasta 22 TB, asumiendo que el pool tiene espacio.

Decisión: Usa esto como medida temporal. Luego implementa planificación de capacidad y guardarraíles para evitar la cultura de “simplemente lo subimos otra vez”.

Task 15: Encontrar los mayores consumidores dentro de un dataset (cuando necesitas borrar algo real)

cr0x@server:~$ sudo du -xhd1 /tank/backups | sort -h | tail -n 10
120G    /tank/backups/tmp
540G    /tank/backups/staging
1.8T    /tank/backups/daily
2.4T    /tank/backups/weekly

Significado: /tank/backups/weekly domina. Ahí es donde la poda importará.

Decisión: Elimina desde la capa de retención menos crítica para el negocio primero. Luego ajusta el job de backup para que no cree crecimiento ilimitado (a menudo está “conservado para siempre” por accidente).

Task 16: Verificar que la herramienta de retención de snapshots no recree el problema inmediatamente

cr0x@server:~$ systemctl status zfs-auto-snapshot.timer
● zfs-auto-snapshot.timer - ZFS auto-snapshot timer
     Loaded: loaded (/lib/systemd/system/zfs-auto-snapshot.timer; enabled; preset: enabled)
     Active: active (waiting) since Thu 2025-12-26 09:05:12 UTC; 4h 12min ago
    Trigger: Thu 2025-12-26 14:00:00 UTC; 42min left

Significado: Las snapshots automáticas están programadas y habilitadas.

Decisión: No desactives esto a ciegas. Ajusta la retención, excluye datasets que no necesitan snaps frecuentes y asegura que la poda funcione. Si debes pausarlo durante la recuperación, pon un recordatorio para reactivarlo.

Task 17: Confirmar feature flags y contexto de versión del pool (útil al llamar soporte)

cr0x@server:~$ zpool get all tank | egrep 'feature@|ashift|autoreplace|autoexpand'
tank  ashift                   12                     local
tank  autoexpand               off                    default
tank  autoreplace              off                    default
tank  feature@spacemap_histogram  enabled             local
tank  feature@allocation_classes  active              local

Significado: Tienes clases de asignación activas (común con special vdevs), ashift=12 y autoexpand deshabilitado.

Decisión: Al planear cambios de capacidad, sabe si autoexpand está desactivado y si las clases de asignación podrían estar involucradas en comportamientos de “espacio disponible pero no utilizable”.

Tres microhistorias corporativas desde el campo

Microhistoria #1 (suposición equivocada): “Borrar los datos liberará espacio.”

Una compañía SaaS mediana ejecutaba un clúster analítico sobre ZFS. El on-call recibió un incidente por outage en la API. Síntoma: escrituras fallando, base de datos quejándose de “no hay espacio en el dispositivo”. El pool mostraba 99% usado. El on-call hizo lo que la mayoría haría bajo estrés: borró un directorio lleno de exportaciones antiguas.

El uso del pool no cambió. Ni siquiera un punto porcentual. El on-call borró más. Aún nada. Ahora tenían un servicio roto y además datos eliminados que un cliente podría pedir. El canal del incidente se llenó de conjeturas confiadas, lo cual siempre es una mala señal.

La suposición equivocada fue simple: que los borrados del filesystem liberan espacio inmediatamente. En ZFS con snapshots frecuentes, esos bloques seguían referenciados. Tenían una política de snapshots que tomaba snapshots horarios y las retenía más tiempo del ciclo de vida de los datos. Excelente para puntos de restauración. Terrible para limpieza de emergencia.

La recuperación fue aburrida pero estricta: identificar los mayores consumidores de snapshots, confirmar requisitos de retención con el dueño de datos, destruir las snapshots más antiguas primero y solo entonces borrar más datos del filesystem. Recuperaron holgura y restauraron el servicio. El postmortem creó una regla: “No borrar hasta contabilizar snapshots”. Añadieron un dashboard que muestra uso de snapshots por dataset, para que el siguiente on-call no necesite un doctorado en teología ZFS para hacer triage.

Microhistoria #2 (optimización que salió mal): el special vdev subdimensionado

Un equipo de plataforma interno grande añadió un special vdev para acelerar workloads pesados en metadatos: millones de archivos pequeños, muchas operaciones de directorio y capas de contenedor. Funcionó de maravilla. La latencia bajó. Todos celebraron y siguieron con sus vidas.

Seis meses después, tuvieron un incidente de “pool lleno” donde el pool aún tenía espacio libre medible—cientos de gigabytes. Sin embargo, la creación de archivos nueva fallaba esporádicamente. Renombrados se atascaban. Algunos datasets se comportaban normalmente; otros se colapsaban. Clásico outage parcial: el peor tipo.

El special vdev era el culpable. Estaba dimensionado de forma optimista, basado en estimaciones de metadatos y un umbral de “bloques pequeños” que acabó capturando más de lo previsto. Llegó al 99% y entonces la asignación de metadatos se convirtió en un punto de estrangulamiento. Los vdevs de datos principales estaban bien, lo que hacía que los síntomas parecieran paranormales.

La solución no fue un truco de CLI ingenioso. Fue capacidad: reconstruir el special vdev con dispositivos más grandes y corregir la política para que special_small_blocks reflejara la realidad. También aprendieron una verdad operativa: los special vdevs son potentes, pero no son infraestructura opcional. Los monitorizas como monitorizas el pool y planificas su crecimiento como el de una base de datos—porque, efectivamente, lo es.

Microhistoria #3 (práctica aburrida pero correcta): cuotas + retención + holgura salvaron el día

Una empresa del ámbito financiero (fuerte cumplimiento, mucho papeleo) usaba ZFS para servicios de archivos y backups. No eran del tipo “moverse rápido”. Tenían cuotas en datasets de usuario, refquotas en apps ruidosas, reservas para la base de datos y una política de snapshots con niveles de retención explícitos. La mayoría de ingenieros por dentro puso los ojos en blanco.

Entonces, una herramienta de un proveedor se volvió loca y comenzó a escribir logs de depuración a gran velocidad. El pool empezó a subir. Pero el radio de impacto se contuvo: el dataset de la herramienta alcanzó su refquota y empezó a fallar sus propias escrituras sin consumir todo el pool. La reserva de la base de datos sostuvo. Los directorios home siguieron siendo escribibles. El incidente se limitó a “esa herramienta está rota”, no a “todo está roto”.

Lo limpiaron a plena luz del día. Podaron el dataset ofensivo, arreglaron la rotación de logs y ajustaron alertas sobre espacio disponible a nivel de dataset, no solo de pool. Nadie tuvo que negociar borrados de datos críticos a las 2 a.m.

Esta es la parte donde el equipo aburrido gana. No evitaron incidentes; los hicieron locales. Las cuotas y las políticas de retención no son excitantes, pero son armadura operativa.

Broma #2: El mejor momento para ajustar la retención de snapshots fue hace seis meses. El segundo mejor momento es antes de que tu borrado “no haga nada” y empieces a regatear con el filesystem.

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

1) “Borramos datos pero el uso del pool no bajó”

Síntoma: rm termina; df y zpool list cambian apenas.

Causa raíz: Las snapshots siguen referenciando los bloques, o los archivos borrados siguen abiertos por procesos.

Solución: Revisa uso de snapshots (zfs list -t snapshot) y archivos borrados abiertos (lsof +L1). Destruye snapshots innecesarias; reinicia procesos que mantienen archivos borrados; luego verifica cambios en el espacio libre del pool.

2) “El pool tiene espacio libre pero la app dice ENOSPC”

Síntoma: El pool muestra cientos de GB libres; un dataset o aplicación aún falla al escribir.

Causa raíz: Límite de refquota/quota del dataset, o una reserva en otro lugar está acotando espacio, o el slop space evita la asignación.

Solución: Inspecciona cuotas/reservas (zfs get quota,refquota,reservation,refreservation). Ajusta la restricción deliberadamente y mantén suficiente holgura de pool para evitar colisiones con slop space.

3) “Las operaciones con archivos pequeños fallan primero”

Síntoma: Crear archivos pequeños falla; lecturas grandes todavía funcionan; algunos datasets se comportan peor.

Causa raíz: Special vdev lleno (presión en asignación de metadatos) o fragmentación severa.

Solución: Revisa la asignación del special vdev (zpool list -v). Añade capacidad o reconstruye el special vdev más grande; revisa special_small_blocks. Reduce el nivel de ocupación y la fragmentación restaurando holgura.

4) “Todo está lento, incluso las lecturas”

Síntoma: Alta latencia en general cerca del 95–100% de uso; stalls relacionados con TXG; usuarios reportan “el sistema está congelado”.

Causa raíz: Contención de asignación y fragmentación; escrituras sync pesadas; cuellos de botella a nivel de dispositivo amplificados bajo presión.

Solución: Para escritores pesados, recupera espacio y considera reducir temporalmente la tasa de escritura (throttling a nivel de aplicación). Tras la recuperación, aplica política de holgura y monitoriza tendencias de fragmentación.

5) “Destruimos snapshots pero el espacio libre aún no se recupera”

Síntoma: La lista de snapshots se reduce, pero el espacio libre del pool no aumenta como se esperaba.

Causa raíz: El espacio está retenido por otras snapshots/clones; o escrituras en curso consumen el espacio reclamado de inmediato; o reservas/cotas confunden expectativas.

Solución: Revisa clones (zfs get origin), verifica los escritores principales (zpool iostat) y comprueba reservas. Pausa escritores durante la limpieza para poder recuperar realmente holgura.

6) “Añadimos capacidad pero el rendimiento no mejoró”

Síntoma: El pool ya no está lleno, pero la latencia sigue siendo mala.

Causa raíz: La fragmentación persiste; el special vdev sigue constreñido; el workload es sync-heavy; o hay desequilibrio entre dispositivos.

Solución: Restaura holgura significativa (no solo 1–2%). Revisa fragmentación y uso del special vdev. Considera cambios a nivel de workload (batching de logs, alineación de recordsize) en lugar de esperar que solo la capacidad sea la solución mágica de rendimiento.

Listas de comprobación / plan paso a paso

Fase 0: Estabilizar (detener la hemorragia)

  1. Congelar al mayor escritor. Pausa pipelines de ingestión, desactiva logs de depuración desenfrenados, detén jobs por lotes o reduce temporalmente escritores.
  2. Confirma que estás resolviendo la restricción correcta. Pool lleno vs cuota de dataset vs special vdev lleno vs archivos borrados abiertos.
  3. Comunica un estado simple. “Estamos con restricción de espacio; estamos recuperando X; ETA para retorno de escrituras es Y.” Manténlo factual.

Fase 1: Obtener holgura inmediata (victorias rápidas)

  1. Cierra archivos borrados pero abiertos. Usa lsof +L1, reinicia servicios limpiamente.
  2. Poda las snapshots peores primero. Elimina las más antiguas y con mayor USED que no sean requeridas.
  3. Borra datos desechables sin snapshots. Directorios temporales, datos de staging, caches, artefactos de build antiguos—solo después de verificar que las snapshots no los están anclando.
  4. Apunta a un umbral real de holgura. En pools ocupados, 10% suele ser incómodo. Apunta a 15–20% cuando sea factible.

Fase 2: Restaurar operaciones normales (hacer que las escrituras sean seguras otra vez)

  1. Rehabilita escritores gradualmente. Observa zpool iostat y tasas de error de la aplicación; no pases de cero a toda la capacidad de golpe.
  2. Revisa cuotas y reservas. Revierte cambios de emergencia con cuidado; confirma que los datasets tienen límites apropiados.
  3. Ejecuta un scrub si el hardware fue sospechoso. Si viste errores de dispositivo, programa un scrub y planifica reemplazos si es necesario.

Fase 3: Arreglar el sistema (para que no vuelva a ocurrir)

  1. Implementa retención de snapshots que puedas explicar. Horario: horario por horas para un día, diario por una semana, semanal por un mes—lo que encaje con tu negocio. Hazlo explícito, automatizado y auditado.
  2. Alerta tanto en dataset como en pool sobre holgura. Pool en 80/85/90% con urgencias escalonadas; dataset avail por debajo de umbrales específicos al workload.
  3. Planificación de capacidad basada en tasa de crecimiento, no en impresiones. Rastrea el cambio semanal en zfs list used, deltas de snapshots y churn de backups.
  4. Si usas special vdev, dimensiona como si la producción dependiera de él. Porque depende.

Prevención: hacer que “pool lleno” sea aburrido otra vez

Política de holgura: elige un número y aplícalo

Si tu pool aloja cualquier cosa sensible a la latencia o con muchas escrituras, trata 80–85% como “amarillo”, 90% como “rojo” y 95% como “detener todo”. Estos umbrales no son juicios morales; son guardarraíles operativos. La fragmentación y la presión del asignador no se preocupan por tu roadmap trimestral.

Diseño de datasets: localizar la falla

Pon workloads ruidosos en sus propios datasets con refquota. Pon sistemas críticos (bases de datos, imágenes VM) en datasets con reservas deliberadas solo si realmente necesitas espacio garantizado. Así evitas que una tormenta de logs tumbe tu base de datos.

Disciplina de snapshots: la retención es una característica de producto

Las snapshots no son “backups gratis”. Son un sistema de retención con un perfil de coste que se dispara durante churn alto. Define:

  • Qué snapshotear (no todo merece la misma política)
  • Con qué frecuencia (tiers hourly/daily/weekly)
  • Cuánto tiempo mantenerlos (y quién aprueba aumentos)
  • Cómo podar (automatizado, verificado, monitorizado)

Special vdevs: monitorízalos como un pool separado

Si has adoptado special vdevs, añade alertas sobre su capacidad y vigila el efecto de special_small_blocks. Muchos equipos los tratan como “algo tipo caché”. No lo es. Es una clase de asignación que puede convertirse en tu primer límite duro.

Higiene operativa que compensa

  • Rotación de logs que esté probada (no solo configurada). Incluye el caso de “proceso mantiene fd antiguo”.
  • Backups que no exploten las snapshots. Algunos workflows de backup causan churn masivo; ajústalos.
  • Dashboards que muestren: capacidad del pool, fragmentación, uso de snapshots por dataset y avail por dataset.
  • Runbooks que incluyan las reglas de retención/compliance de tu organización para que el on-call no tenga que negociar políticas en medio del incidente.

Preguntas frecuentes

1) ¿Qué ocurre exactamente cuando ZFS está completamente lleno?

Las escrituras empiezan a fallar con ENOSPC, pero no todas las escrituras fallan igual. Las asignaciones de metadatos, las actualizaciones copy-on-write y las escrituras sync pueden fallar o bloquearse antes de lo que esperas. El rendimiento suele degradarse antes de la falla total.

2) ¿Por qué ZFS se vuelve lento al acercarse al lleno incluso antes de errores?

El espacio libre se fragmenta. El asignador trabaja más para encontrar bloques adecuados y el I/O se vuelve más disperso. En discos giratorios esto es especialmente doloroso; en SSDs también aumenta la amplificación de escritura y la latencia.

3) ¿Por qué borrar archivos no liberó espacio?

Lo más común: las snapshots siguen referenciando esos bloques, o los archivos fueron borrados pero siguen abiertos por un proceso. Usa zfs list -t snapshot y lsof +L1 para confirmar cuál es el caso.

4) ¿Debo destruir snapshots durante un outage?

Si las snapshots impiden liberar espacio y la política de retención lo permite, sí—destruir snapshots suele ser la forma más limpia de recuperar espacio. Pero hazlo con intención: identifica las snapshots con mayor USED, confirma requisitos de negocio y borra primero las más antiguas.

5) ¿Es seguro aumentar quota/refquota para arreglar ENOSPC?

Mecánicamente es seguro, pero operativamente riesgoso. Estás cambiando quién puede consumir el espacio compartido del pool. Úsalo como medida temporal y acompáñalo con una solución de capacidad o retención.

6) ¿Cuánto espacio libre debo mantener en un pool ZFS?

Para workloads mixtos en general: apunta a 15–20% de holgura. Para workloads mayormente append con bajo churn puedes operar más ajustado, pero compras riesgo y latencia. Si no puedes mantener holgura, necesitas más discos o menos datos—elige uno.

7) ¿Puedo “defragmentar” un pool ZFS después de que se llene demasiado?

No directamente como en filesystems heredados. El método práctico es restaurar holgura y permitir que nuevas escrituras se alojen mejor, o migrar datos (send/receive) a un pool nuevo para un reinicio real. Planifica capacidad para no necesitar heroísmos.

8) ¿Qué pasa si el special vdev está lleno pero el pool no?

Entonces tienes un punto de estrangulamiento en asignación de metadatos. Necesitarás tratar la capacidad del special vdev (a menudo reconstruyéndolo/expandiéndolo) y revisar qué bloques estás enviando allí. No es una situación de “borrar unos archivos”.

9) ¿Las reservas ayudan o perjudican en situaciones de “pool lleno”?

Ambas cosas. Las reservas protegen datasets críticos de vecinos ruidosos, lo que puede salvar el día. Pero también reducen la flexibilidad durante emergencias. Úsalas con moderación y con propiedad y monitorización explícitas.

10) ¿Debo añadir capacidad primero o borrar primero?

Si puedes borrar de forma segura y recuperar espacio rápidamente, borra primero. Si las snapshots/cumplimiento impiden borrar, o la recuperación es más lenta que el coste del outage, añade capacidad primero. A menudo haces ambas cosas: añades capacidad para sobrevivir y luego borras para restaurar márgenes saludables.

Próximos pasos

Un pool ZFS que se llena rara vez es una sorpresa; suele ser sorpresa solo para el on-call que no recibió la señal correcta a tiempo. Cuando sucede, la jugada ganadora es dejar de adivinar. Confirma la restricción (pool vs dataset vs snapshots vs special vdev), recupera espacio con las acciones menos irreversibles primero y restaura holgura a un nivel donde ZFS pueda asignar de forma sensata.

Pasos prácticos que puedes hacer esta semana:

  • Añadir alertas sobre capacidad del pool, avail de datasets y uso de snapshots por dataset.
  • Escribir y aplicar una política de retención que coincida con la realidad del negocio (no con el optimismo).
  • Colocar workloads ruidosos detrás de refquotas para que puedan fallar de forma aislada.
  • Si usas special vdevs, monitorízalos y planifícalos como almacenamiento de primera clase, no como un accesorio.
  • Hacer un game day: simula un pool al 95%, recorre el runbook y observa dónde se rompe tu proceso antes de que lo haga producción.

Porque lo único peor que un pool lleno es un pool lleno durante una ventana de migración que prometiste sería “de bajo riesgo”.

← Anterior
PPTP es una trampa: por qué debes evitarlo y qué usar en su lugar
Siguiente →
Passthrough de GPU en Proxmox: pantalla negra, UEFI/OVMF, problemas de ROM, fallos de reinicio y soluciones comprobadas

Deja un comentario