ZFS tiene una costumbre peculiar: un pool llega a “90% usado” y de repente se queda sin pista. Las escrituras se ralentizan, las asignaciones fallan y tu monitorización se enciende como una máquina de pinball. Pero zpool list todavía afirma que hay espacio disponible. Si alguna vez has murmurado “ZFS me está mintiendo”, no estás solo —y no estás completamente equivocado. ZFS hace cuentas que tu modelo mental probablemente no contempla.
Esta es la historia del espacio slop, más las otras manos invisibles que hacen que los pools parezcan llenos antes del 100%: copy-on-write (CoW), metadata, alineación de bloques, snapshots, reservas, vdevs especiales y fragmentación. Lo trataremos como una guía de operador, no como un folleto: qué ocurre, cómo medirlo, cómo arreglarlo y cómo no repetir el mismo incidente con otro número de ticket.
Qué es el espacio slop (y qué no es)
El espacio slop es la forma que tiene ZFS de evitar que empujes un sistema de ficheros CoW contra un muro. Cuando un pool se llena demasiado, ZFS es cada vez más propenso a encontrar fallos de asignación en el peor momento posible: al reescribir metadata, al comprometer grupos de transacciones, o al intentar asignar un nuevo bloque para reemplazar uno viejo. CoW significa “no sobrescribir en sitio”; significa “asignar nuevo, luego actualizar punteros.” Esa actualización en sí necesita espacio.
El espacio slop es un colchón reservado que ZFS intenta retener para uso general de forma que el pool aún pueda realizar el “trabajo interno” requerido para mantenerse consistente y avanzar. Piénsalo como el carril de emergencia en una autopista: puedes conducir en él, pero si todos lo hacen, las ambulancias dejan de llegar a los accidentes.
En la operativa, el slop se manifiesta como: “Available” en zfs list siendo menor de lo esperado, y a veces un dataset rechazando escrituras aunque zpool list diga que hay espacio. Los detalles varían según la plataforma (versión de OpenZFS, integración con el SO), pero la lección para el operador es consistente: no planifiques usar los últimos puntos porcentuales de un pool, y no consideres el “100%” como objetivo utilizable de diseño.
Qué no es el espacio slop
- No es una cuota: las cuotas son por dataset y se aplican en el límite del dataset. Slop trata sobre la supervivencia del pool.
- No es “espacio perdido”: sigue formando parte del pool. Puede volverse disponible bajo ciertas condiciones y se usa para asignaciones internas.
- No es espacio de snapshots: los snapshots son bloques ordinarios mantenidos vivos por referencias. Slop es un comportamiento parecido a una reserva aplicado por el asignador.
Chiste #1: Si crees que puedes ejecutar un pool ZFS al 99.9% para siempre, no eres optimista—eres un ingeniero de caos no remunerado.
Por qué los pools parecen llenos antes del 100%
“Espacio slop” es el titular, pero en incidentes reales rara vez es el único actor. Los pools se sienten llenos temprano porque el espacio restante no es igualmente utilizable para todas las escrituras futuras. ZFS necesita espacio de la forma correcta: extensiones libres que coincidan con tamaños de bloque, espacio en los vdevs adecuados y suficiente margen para hacer CoW y actualizar metadata. Aquí están las causas prácticas, en el orden en que más suelen perjudicar sistemas de producción.
1) CoW necesita espacio de trabajo
En un sistema de ficheros CoW, una pequeña escritura de usuario puede desencadenar una cadena de asignaciones:
- Bloques de datos nuevos
- Bloques indirectos nuevos (si el árbol crece o debe reescribirse)
- Bloques de metadata nuevos (punteros de bloques, dnodes, mapas de espacio, clases de asignación)
- Frees diferidos (espacio reclamado más tarde, no inmediatamente)
Por eso “solo estoy escribiendo 1 GB” puede fallar cuando el pool muestra “2 GB libres”. En el tramo final de un pool lleno, el asignador puede necesitar probar muchos candidatos, fragmentando más y consumiendo metadata. ZFS no está dramatizando; intenta no corromperse.
2) Espacio slop: margen de seguridad a nivel de pool
El espacio slop suele calcularse como una fracción del tamaño del pool con un tope, y la mecánica exacta ha cambiado entre implementaciones y versiones. El efecto: una parte del espacio libre se trata como efectivamente no disponible para datasets ordinarios para preservar la operabilidad del pool. Cuando ves un dataset que afirma “0 avail” mientras el pool todavía tiene “algo” libre, a menudo estás viendo slop en acción.
No es un “3%” fijo. En pools grandes puede haber un tope; en pools pequeños puede ser proporcionalmente significativo. En el mundo real, en cajas de laboratorio pequeñas se siente como si ZFS te robara el almuerzo; en pools de cientos de terabytes se siente como un impuesto sensato por estabilidad.
3) Registros de 128 KiB, sectores de 4K y la tiranía de la alineación
La mayoría de los datasets ZFS tienen recordsize por defecto alrededor de 128 KiB. La mayoría de los discos presentan sectores físicos de 4K; algunos presentan 512e; algunos dispositivos flash tienen tamaños de página internos mayores. ZFS debe alinear las escrituras a ashift, el exponente del tamaño de sector del pool. Un pool con ashift=12 usa sectores de 4K; ashift=13 usa 8K, etc.
Si tu carga escribe muchos bloques pequeños, el pool puede “usar” espacio más rápido de lo que sugieren tus tamaños de archivo debido a padding, sobrecarga de paridad y metadata. Y si elegiste un ashift demasiado grande “por rendimiento”, puede que también hayas decidido quemar capacidad de forma permanente. (Llegaremos a la historia donde eso salió mal.)
4) Paridad RAIDZ y el problema de la “última vuelta”
La asignación RAIDZ es inteligente, pero no es magia. A medida que el espacio libre se fragmenta, ZFS tiene menos opciones para montar franjas completas. Eso puede aumentar la amplificación de escritura y reducir el espacio efectivo libre. El último 10–20% de un pool RAIDZ puede sentirse como melaza: no porque ZFS sea perezoso, sino porque las asignaciones “buenas” son más difíciles de encontrar y el asignador tiene que trabajar más para evitar disposiciones patológicas.
Los mirrors suelen degradarse con más gracia bajo fragmentación que RAIDZ, pero no escapan a los requisitos de margen para CoW.
5) La metadata ocupa espacio real
ZFS almacena mucha metadata: punteros de bloques, dnodes, bloques indirectos, spacemaps, sumas de comprobación y (dependiendo de las características) estructuras adicionales. Cuantos más archivos, snapshots, clones y bloques pequeños tengas, más metadata arrastras.
Si añades un vdev especial para metadata y bloques pequeños, la contabilidad cambia: la metadata puede dejar de consumir espacio “normal”, pero ahora puedes chocar contra otra pared—la capacidad del vdev especial. Cuando el vdev especial se llena, el pool puede quedar efectivamente inmóvil incluso si los vdevs principales tienen espacio.
6) Snapshots: el espacio que no ves hasta que es demasiado tarde
Los snapshots no copian datos en el momento de la creación; fijan bloques. Puedes eliminar un directorio de 10 TB y ver que casi no vuelve espacio, porque los snapshots todavía referencian esos bloques. Aquí es donde “el pool parece lleno temprano” se convierte en “el pool está lleno y no sabemos por qué.”
Los snapshots también incrementan la metadata y pueden aumentar la fragmentación porque los bloques liberados no se liberan hasta que todas las referencias desaparecen.
7) Reservas, refreservations y sorpresas con volblocksize
Las reservas (para filesystems) y las refreservations (usadas a menudo para zvols) reservan espacio para que un dataset pueda seguir escribiendo incluso bajo presión. Eso es bueno—pero también puede hacer que otros datasets parezcan llenos temprano porque el espacio restante está prometido a otra parte.
Los zvols añaden otra capa: el tamaño de bloque del volumen (volblocksize) interactúa con la carga y ashift. Un desajuste puede desperdiciar espacio y aumentar la amplificación de escritura, lo que empeora el “tramo final” del pool.
8) Frees diferidos y temporización de grupos de transacciones
ZFS agrupa trabajo en grupos de transacciones (TXGs). Las liberaciones pueden diferirse; el espacio puede estar “lógicamente libre” pero no reutilizable inmediatamente hasta que el TXG relevante se comprometa y la sincronización termine. En un pool con alta carga de escrituras, puedes ver una brecha entre lo que las aplicaciones creen que liberaron y lo que el asignador puede reutilizar ahora mismo.
Chiste #2: “Dice 5% libre” es el equivalente en almacenamiento de “llego en cinco minutos”—puede ser cierto, pero no deberías apostar tu despliegue de producción a ello.
Hechos interesantes e historia
Algunos puntos de contexto que explican por qué ZFS se comporta como lo hace—y por qué el precipicio de “pool lleno” es un compromiso conocido y diseñado en lugar de un bug.
- ZFS nació a mediados de los 2000 con checksumming de extremo a extremo y CoW como principios centrales, sacrificando la simplicidad de sobrescribir en sitio por integridad y semántica de snapshots.
- La “regla del 80%” precede a ZFS: sistemas de ficheros Unix clásicos (como UFS/FFS) reservaban espacio (a menudo ~10%) para root y para reducir la fragmentación. Mecanismo distinto, mismo objetivo: evitar morir por disco lleno.
- RAIDZ no es RAID5: RAIDZ de ZFS integra asignación y paridad dentro del filesystem, evitando el write hole mediante CoW y semántica transaccional.
- ashift es para siempre: una vez creado un pool, la elección del tamaño de sector es efectivamente permanente para ese vdev. La gente aprende esto justo después de aprender qué significa ashift.
- Los vdevs especiales cambiaron las reglas: metadata y bloques pequeños pueden redirigirse a dispositivos más rápidos, pero también introdujeron un nuevo modo de “pool se siente lleno”: agotamiento del vdev especial.
- La proliferación de snapshots es un asesino moderno de capacidad: ZFS hizo que tomar snapshots fuera barato, lo que llevó a la práctica de “snapshotea todo”—y a la realización de que eliminar no significa reclamación.
- La compresión cambió la planificación de capacidad: con lz4 y similares, “usado lógico” y “usado físico” divergen, lo que complica la intuición humana y hace que el “espacio libre” parezca inconsistente.
- Copy-on-write implica amplificación de escritura: especialmente bajo fragmentación y en RAIDZ, escrituras pequeñas aleatorias pueden costar más IO físico y actualizaciones de metadata que su tamaño aparente.
- La contabilidad de espacio evolucionó entre versiones de OpenZFS: campos como usedby* y mejoras en los informes hicieron más fácil atribuir espacio, pero también expusieron cuántos compartimentos pueden existir para el espacio.
Tres micro-historias del mundo corporativo
Micro-historia #1: El incidente por una suposición errónea
Tenían una hoja de cálculo limpia: tamaño del pool, crecimiento previsto y una línea de “alerta al 90%”. El pool era RAIDZ2, copias secuenciales mayormente por la noche y un cluster de virtualización ocupado durante el día. Todos estaban de acuerdo: 90% es agresivo, pero manejable. El sistema había funcionado así durante meses, que es como las malas suposiciones ganan antigüedad.
Llegó el fin de trimestre. Un trabajo por lotes produjo más datos de lo habitual, algunas VMs se migraron en vivo y una ventana de backups coincidió con una tarea de retención de snapshots. El espacio libre cayó a la zona incómoda, pero el panel aún mostraba “unos terabytes libres.” El ingeniero on-call trató de mantener la calma, porque la calma es más barata que el pánico.
A las 02:13, la latencia de escritura subió. A las 02:17, un par de VMs reportaron errores de disco. A las 02:19, el pool empezó a lanzar ENOSPC para un dataset que “debería tener espacio.” No mentía: el dataset había alcanzado el punto donde ZFS ya no entregaría el último trozo de margen del pool para asignaciones ordinarias. Slop más fragmentación significaban que “libre” no era “asignable.”
La suposición errónea no fue “90% está bien.” La suposición errónea fue tratar el espacio libre del pool como un solo número que predice asignabilidad. Recuperaron eliminando snapshots de vida corta, pausando backups y moviendo algunos discos de VM fuera del pool. El postmortem cambió dos cosas: alertas disparadas antes y cada informe de capacidad empezó a incluir fragmentación y atribución de snapshots—no solo porcentaje usado.
Micro-historia #2: La optimización que salió mal
Un equipo de almacenamiento quería “anticiparse” a quejas de rendimiento. Tenían dispositivos NVMe sin usar, así que añadieron un vdev especial para acelerar metadata y IO pequeño. En papel, era perfecto: operaciones de directorio más rápidas, menor latencia para lecturas pequeñas y una plataforma de virtualización más feliz.
Funcionó—hasta que no. Con los meses, el vdev especial se llenó en silencio. No con datos de usuario, no con “archivos grandes”, sino con metadata, bloques pequeños y la acumulación de características “útiles” del filesystem. Nadie vigilaba la asignación del vdev especial por separado; el pool todavía tenía mucho espacio en el RAIDZ principal. La monitorización vio 60% de uso del pool y volvió a dormirse.
Un día una operación de rutina—crear muchos archivos pequeños durante un build CI—empezó a fallar. ZFS necesitaba asignar metadata, y la clase que almacenaba esa metadata estaba efectivamente sin espacio. El pool principal tenía espacio, pero el asignador no podía colocar los bloques requeridos donde la política lo demandaba. El equipo optimizó la ruta caliente y creó accidentalmente un punto único de fallo de capacidad.
La solución no fue glamorosa: añadir más capacidad al vdev especial (y espejarlo correctamente), reequilibrar moviendo algunos bloques pequeños de datasets de vuelta a la asignación normal donde fuera posible, y configurar alertas específicas sobre uso del vdev especial. La lección: toda optimización crea un nuevo recurso que se puede agotar. Si no lo monitorizas, solo estás inventando un nuevo modo de outage.
Micro-historia #3: La práctica aburrida pero correcta que salvó el día
Otra empresa ejecutaba ZFS para bases de datos y logs. Nada exótico. Su responsable de almacenamiento tenía una regla: ningún pool debía pasar tiempo significativo por encima de ~75% salvo que exista un plan escrito de expansión. También tenían otra regla: los snapshots deben tener políticas de retención explícitas, y cada dataset debe declarar si es “snapshot-heavy” o “snapshot-light”.
Una tarde, un desarrollador desplegó por error un build que duplicó el volumen de logs. El dataset de logs se disparó. El sistema de monitorización no solo alertó con “pool al 85%.” También alertó con “pool proyectado a 80% en 6 horas” y “tendencia de espacio de snapshots acelerándose.” Eso no es magia; es aritmética aburrida y etiquetado consistente.
El on-call limitó la ingestión de logs, redujo la frecuencia de snapshots para datasets no críticos e incrementó la retención para el dataset de base de datos solo. También tenían un paso preaprobado en el runbook: aumentar temporalmente la cuota de un dataset crítico mientras se reduce la de otro menos crítico. Sin compras de emergencia. Sin karaoke de culpas a medianoche.
El pool nunca cruzó el precipicio donde la fragmentación y el slop se vuelven existenciales. El informe del incidente ocupó una página, que es el mejor tipo de informe. La lección: tener margen es una característica, y las reglas aburridas suelen ser las únicas que funcionan bajo estrés.
Tareas prácticas: comandos + interpretación
El objetivo aquí no es lanzar comandos al azar. Es construir un modelo mental que refleje la contabilidad de ZFS: realidad a nivel pool, promesas a nivel dataset y “por qué no puedo asignar aunque parezca que debería.” Los ejemplos asumen OpenZFS en un sistema Linux típico, pero los conceptos se traducen.
Tarea 1: Comprobar capacidad del pool y salud básica
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 87.2T 71.4T 15.8T - - 41% 81% 1.00x ONLINE -
Interpretación: CAP es el instrumento burdo. FRAG es una luz de advertencia, no un veredicto, pero una vez que la fragmentación sube, el “último 10–15%” se vuelve menos utilizable. Un pool al 81% con 41% de frag ya puede sentirse apretado para cargas de escritura aleatoria.
Tarea 2: Ver asignación detallada de vdev y ashift
cr0x@server:~$ zdb -C tank | sed -n '1,120p'
MOS Configuration:
version: 5000
name: 'tank'
vdev_tree:
type: 'root'
id: 0
guid: 1234567890123456789
children[0]:
type: 'raidz'
id: 0
ashift: 12
nparity: 2
children[0]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK0'
children[1]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK1'
children[2]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK2'
Interpretación: ashift te dice la unidad mínima de asignación. Si es mayor que tus necesidades físicas reales, has reducido permanentemente la capacidad utilizable. Si es menor que la realidad (raro con defaults modernos), el rendimiento y la amplificación de escritura pueden sufrir.
Tarea 3: Comparar vista de pool vs dataset de espacio disponible
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used tank
NAME USED AVAIL REFER MOUNTPOINT
tank 71.4T 9.82T 96K /tank
tank/vm 52.1T 2.40T 42.0T /tank/vm
tank/backup 18.9T 7.40T 18.9T /tank/backup
Interpretación: El FREE del pool era 15.8T, pero el dataset de nivel superior muestra 9.82T avail. Esa brecha es donde viven slop, reservas y realidades de contabilidad. El número que interesa a las aplicaciones suele ser el AVAIL del dataset, no el FREE del pool.
Tarea 4: Inspeccionar reservas y refreservations
cr0x@server:~$ zfs get -H -o name,property,value,source reservation,refreservation tank tank/vm tank/backup
tank reservation none default
tank refreservation none default
tank/vm reservation 2T local
tank/vm refreservation none default
tank/backup reservation none default
tank/backup refreservation none default
Interpretación: Una reserva reduce lo que los demás pueden usar. Si un dataset está reservado y mayormente vacío, el resto del pool puede sentirse “lleno temprano.” Esto suele ser intencional, pero debe ser visible en los informes de capacidad.
Tarea 5: Atribuir espacio a snapshots, hijos y refreservation
cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation tank/vm
NAME USED USEDBYSNAPSHOTS USEDBYDATASET USEDBYCHILDREN USEDBYREFRESERVATION
tank/vm 52.1T 8.4T 43.7T 0B 0B
Interpretación: 8.4T fijadas por snapshots no es “liberable” hasta que los snapshots se vayan. Si borraste imágenes de VM y no volvió espacio, esto suele ser la razón.
Tarea 6: Listar snapshots y encontrar los más pesados
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used | tail -n 8
tank/vm@auto-2025-12-20 210G 39.8T Sat Dec 20 02:00 2025
tank/vm@auto-2025-12-21 240G 40.1T Sun Dec 21 02:00 2025
tank/vm@auto-2025-12-22 310G 40.5T Mon Dec 22 02:00 2025
tank/vm@auto-2025-12-23 480G 41.0T Tue Dec 23 02:00 2025
tank/vm@auto-2025-12-24 1.2T 42.0T Wed Dec 24 02:00 2025
tank/vm@pre-migration 3.6T 38.2T Wed Dec 24 18:11 2025
tank/vm@quarter-end 6.1T 37.9T Thu Dec 25 01:05 2025
tank/vm@baseline 7.8T 36.4T Fri Dec 12 03:00 2025
Interpretación: USED en un snapshot muestra bloques únicos retenidos por ese snapshot. Si un snapshot eclipsa a los demás, es candidato principal para revisión (no eliminación automática—revisar).
Tarea 7: Comprobar ratio de compresión y uso lógico vs físico
cr0x@server:~$ zfs get -H -o name,property,value compressratio,compression tank/vm
tank/vm compressratio 1.45x -
tank/vm compression lz4 local
Interpretación: La compresión puede enmascarar el crecimiento hasta que deja de hacerlo. Cuando los datos entrantes son menos comprimibles (backups cifrados, medios ya comprimidos, bloques aleatorios), el uso físico puede subir más rápido que tu tendencia histórica.
Tarea 8: Comprobar fragmentación del espacio libre a nivel pool
cr0x@server:~$ zpool get -H -o name,property,value frag,capacity,free tank
tank frag 41% -
tank capacity 81% -
tank free 15.8T -
Interpretación: FRAG subiendo con CAP es normal; FRAG subiendo rápido a CAP moderado suele indicar desajuste de carga (VM random writes en RAIDZ, snapshots intensos o churn constante).
Tarea 9: Inspeccionar asignación del vdev especial (si existe)
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
ata-DISK0 ONLINE 0 0 0
ata-DISK1 ONLINE 0 0 0
ata-DISK2 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme-NVME0 ONLINE 0 0 0
nvme-NVME1 ONLINE 0 0 0
errors: No known data errors
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE
tank 87.2T 71.4T 15.8T
raidz2-0 87.2T 68.9T 18.3T
special 1.8T 2.5T 0B
Interpretación: Si special muestra “0B free”, tienes un problema aunque el vdev principal tenga espacio. El pool puede fallar asignaciones que requieren bloques de clase special (metadata/bloques pequeños dependiendo de propiedades). Este es el patrón de “optimización que salió mal”.
Tarea 10: Identificar datasets que usan clases de asignación special
cr0x@server:~$ zfs get -H -o name,property,value special_small_blocks tank tank/vm tank/backup
tank special_small_blocks 0 default
tank/vm special_small_blocks 16K local
tank/backup special_small_blocks 0 default
Interpretación: Si special_small_blocks está configurado, los bloques pequeños van a special. Eso es genial hasta que special se llena. Conoce qué datasets dependen de ello.
Tarea 11: Comprobar cuotas y refquotas que pueden crear “sensación de lleno” a nivel dataset
cr0x@server:~$ zfs get -H -o name,property,value quota,refquota tank/vm
tank/vm quota 55T local
tank/vm refquota none default
Interpretación: La cuota limita el total usado (incluyendo snapshots según el tipo); refquota limita el espacio referenciado. Si un dataset llega a cuota, parece lleno independientemente del estado del pool.
Tarea 12: Confirmar si los frees se están diferiendo (espacio no reclamado inmediatamente)
cr0x@server:~$ zpool iostat -v tank 2 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 71.4T 15.8T 210 980 82.1M 311M
raidz2-0 68.9T 18.3T 200 940 80.4M 305M
special 2.5T 0B 10 40 1.7M 6.1M
---------- ----- ----- ----- ----- ----- -----
tank 71.4T 15.8T 190 1020 75.2M 340M
raidz2-0 68.9T 18.3T 180 980 73.6M 333M
special 2.5T 0B 10 40 1.6M 6.6M
---------- ----- ----- ----- ----- ----- -----
Interpretación: Escrituras sostenidas fuertes, especialmente junto con churn de snapshots, pueden mantener el pool en un estado donde el espacio está “en movimiento.” Si borras datos e inmediatamente intentas una escritura grande, aún puedes chocar con ENOSPC porque el asignador no puede reutilizar el espacio liberado. Aquí la paciencia (o una pausa controlada en la carga) puede importar.
Tarea 13: Reducir presión de snapshots de forma segura (flujo de trabajo de ejemplo)
cr0x@server:~$ zfs destroy -nvp tank/vm@baseline
would destroy tank/vm@baseline
would reclaim 7.8T
cr0x@server:~$ zfs destroy -vp tank/vm@baseline
will destroy tank/vm@baseline
will reclaim 7.8T
destroyed tank/vm@baseline
Interpretación: Usa -nvp primero: simulación más verboso más salida algo parseable. Si intentas salir de un evento de pool lleno, esta es una de las palancas más limpias—siempre que el snapshot sea seguro de eliminar.
Tarea 14: Detectar “espacio prometido en otra parte” vía logicalused/available
cr0x@server:~$ zfs get -H -o name,property,value logicalused,logicalavailable tank
tank logicalused 102T -
tank logicalavailable 14.2T -
Interpretación: Con compresión, lo lógico y lo físico divergen. Si logicalavailable es bajo mientras crees tener espacio físico, puedes estar encontrando restricciones como slop, reservas o agotamiento de vdev special.
Playbook de diagnóstico rápido
Esta es la secuencia cuando suena el pager. El objetivo es identificar qué tipo de “lleno” tienes: pool lleno, dataset limitado, bloques fijados por snapshots, espacio prometido por reservas, vdev special agotado o fragmentación/fallo de asignación.
Primero: establece qué tipo de fallo estás viendo
- Error de aplicación: ENOSPC? EIO? timeouts? No asumas que ENOSPC es “no bytes libres”; puede significar “no hay espacio asignable bajo la política”.
- Ámbito: ¿un solo dataset o todo? ¿un zvol o todos los filesystems?
- Momento: ¿empezó tras un snapshot, replicación, un borrado grande o añadir un vdev special?
Segundo: comprueba la realidad del pool
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 87.2T 71.4T 15.8T - - 41% 81% 1.00x ONLINE -
cr0x@server:~$ zpool status -x tank
pool 'tank' is healthy
Decisión: Si HEALTH no es ONLINE/healthy, detente y arregla eso primero. Los juegos de capacidad no importan si estás degradado y resilverizando.
Tercero: compara dataset avail con pool free
cr0x@server:~$ zfs list -o name,used,avail,mounted -r tank | head
NAME USED AVAIL MOUNTED
tank 71.4T 9.82T yes
tank/vm 52.1T 2.40T yes
tank/backup 18.9T 7.40T yes
Decisión: Si el pool free es “razonable” pero el dataset avail es pequeño, probablemente estás frente a slop, reservas, cuotas o restricciones de capacidad del vdev special.
Cuarto: atribuye espacio rápidamente
cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation -r tank | head -n 15
NAME USED USEDBYSNAPSHOTS USEDBYDATASET USEDBYCHILDREN USEDBYREFRESERVATION
tank 71.4T 0B 96K 71.4T 0B
tank/vm 52.1T 8.4T 43.7T 0B 0B
tank/backup 18.9T 1.1T 17.8T 0B 0B
Decisión: Si los snapshots dominan, recuperar espacio significa cambiar políticas de snapshots, no borrar ficheros.
Quinto: comprueba vdev special y restricciones de clases de asignación
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE
tank 87.2T 71.4T 15.8T
raidz2-0 87.2T 68.9T 18.3T
special 1.8T 2.5T 0B
Decisión: Si special está lleno, trátalo como un incidente de alta severidad. Liberar espacio “normal” no ayudará necesariamente si el asignador necesita bloques de clase special.
Sexto: comprueba fragmentación y forma de la carga
cr0x@server:~$ zpool get frag tank
NAME PROPERTY VALUE SOURCE
tank frag 41% -
Decisión: Frag alta más CAP alta sugiere que necesitas margen inmediatamente. La solución más rápida suele ser eliminar snapshots, mover datasets o expandir el pool. La desfragmentación no es un botón; es disciplina de capacidad y lifecycle.
Errores comunes (síntomas y soluciones)
Error 1: Tratar “pool FREE” como “dataset puede escribir”
Síntoma: zpool list muestra terabytes libres, pero las escrituras fallan en un dataset o zvol.
Causas probables: umbral de slop alcanzado; cuota del dataset; reserva en otra parte; vdev special lleno.
Solución: Compara AVAIL del dataset objetivo con zfs list; comprueba cuotas/reservas; inspecciona uso del vdev special; libera espacio en el lugar correcto.
Error 2: Datasets con muchos snapshots sin disciplina de retención
Síntoma: Borrar ficheros no libera espacio; usedbysnapshots crece; uso del pool sube “misteriosamente”.
Solución: Usa zfs list -t snapshot -o used para identificar snapshots pesados; podar de forma segura; rediseñar la retención (menos snapshots, retención más corta o estrategia de replicación que no fije para siempre).
Error 3: Añadir vdevs special sin dimensionar y alertar
Síntoma: El pool parece bien, pero operaciones de metadata/archivos pequeños fallan; vdev special muestra casi lleno o lleno.
Solución: Monitoriza la asignación del vdev special; amplía el vdev special (espejado); considera ajustar special_small_blocks para datasets; evita que special sea el cuello de botella de capacidad.
Error 4: Sobre-optimizar ashift o recordsize por “rendimiento”
Síntoma: La capacidad es menor de lo esperado; cargas de escritura pequeña consumen espacio rápido; el rendimiento se degrada cerca del lleno.
Solución: Elige ashift basado en la realidad física, no en folclore. Ajusta recordsize/volblocksize por carga. Si ya elegiste mal, planea una migración; ashift no es un interruptor.
Error 5: Ejecutar pools RAIDZ demasiado calientes con cargas random-write
Síntoma: Picos de latencia a medida que el pool se llena; la fragmentación sube; las asignaciones se ralentizan; “espacio restante” se vuelve inutilizable.
Solución: Mantén más margen; considera mirrors para cargas random-write pesadas (VMs, bases de datos); usa vdev special con cuidado; reduce churn de snapshots.
Error 6: Suponer que los frees son inmediatos durante churn intenso
Síntoma: Borras mucho, pero AVAIL no sube rápido; las escrituras inmediatas siguen fallando.
Solución: Permite que los TXGs sincronicen; reduce temporalmente la presión de escritura; verifica que los snapshots no estén fijando bloques; evita “borrar y reescribir el mundo” inmediatamente durante un incidente.
Listas de comprobación / plan paso a paso
Checklist A: Informe de capacidad que coincida con la realidad
- Informa zpool CAP, zpool FRAG y zpool FREE.
- Informa los datasets principales con USED, AVAIL y usedbysnapshots.
- Informa la asignación del vdev special por separado (si existe).
- Incluye resumen de cuotas/reservas para datasets “críticos”.
- Tendencia de crecimiento usando usado físico (no solo lógico) si la compresión está activada.
Checklist B: Cuando un dataset dice “no queda espacio”
- Comprueba AVAIL del dataset: zfs list dataset.
- Comprueba cuota/refquota: zfs get quota,refquota.
- Comprueba reservation/refreservation: zfs get reservation,refreservation.
- Comprueba pinning por snapshots: zfs list -o usedbysnapshots y lista de snapshots.
- Comprueba salud del pool y espacio libre del vdev special.
- Si necesitas espacio de emergencia: elimina snapshots con dry-run primero; pausa cargas churn; mueve datos fuera del pool si es posible.
Checklist C: Mantenerse fuera de la zona de “riesgo cola”
- Configura umbrales de alerta en pool CAP (temprano) y en tasa de cambio (más temprano).
- Configura umbrales de alerta en FRAG y en uso del vdev special.
- Define una utilización máxima objetivo por pool basada en la carga (las VMs necesitan más margen que los archivos de archivo).
- Mantén políticas de snapshots explícitas, revisadas y aplicadas.
- Prueba escenarios de “borrar datos”: confirma que el espacio vuelve cuando se espera (y aprende cuándo no lo hace).
- Planifica expansiones antes de necesitarlas; las expansiones de emergencia son cómo terminas con geometría de vdev extraña para siempre.
Preguntas frecuentes
1) ¿Es configurable el espacio slop?
A veces, dependiendo de la plataforma y la versión de OpenZFS, hay tunables que afectan el comportamiento. Operativamente, trata el espacio slop como una característica de seguridad, no como una línea presupuestaria. Si “lo ajustas para eliminarlo”, estás cambiando un colchón predecible por modos de fallo impredecibles bajo presión.
2) ¿Por qué zpool list FREE no coincide con zfs list AVAIL?
Porque el espacio libre del pool no es lo mismo que el espacio asignable para un dataset. La diferencia puede incluir espacio slop, reservas retenidas para otros datasets y a veces restricciones como clases de asignación de vdev special. Confía siempre en AVAIL del dataset para la pregunta “¿puedo escribir aquí?”.
3) Borré un directorio enorme. ¿Por qué no volvió espacio?
Lo más común: snapshots. Los bloques todavía están referenciados por snapshots, así que no pueden liberarse. Comprueba usedbysnapshots y lista los snapshots ordenados por USED.
4) ¿La fragmentación realmente importa en ZFS?
Sí, especialmente cerca de alta utilización y en RAIDZ. La fragmentación reduce las opciones del asignador y aumenta el trabajo por asignación. Puede convertir “espacio libre” en “espacio que existe pero no es útil”, particularmente para escrituras grandes o alineadas a franjas.
5) ¿La regla de “mantener pools por debajo del 80%” es real?
Es una regla práctica, no física. Algunas cargas pueden funcionar felizmente por encima del 80% por largos periodos (archivos fríos con pocas reescrituras). Otras se vuelven miserables al 70% (VMs con escrituras aleatorias y snapshots intensos). El número correcto depende de la carga, tipo de vdev y tolerancia operativa al riesgo.
6) ¿Puede un vdev special lleno romper todo el pool?
Puedes. Si metadata o bloques pequeños están dirigidos a special y special se llena, las operaciones que requieren esas asignaciones pueden fallar incluso si los vdevs principales tienen espacio. Por eso los vdev special deben dimensionarse con prudencia y monitorizarse agresivamente.
7) ¿Las reservas ayudan con problemas de slop?
Las reservas ayudan a garantizar que un dataset pueda escribir cuando el pool está apretado, pero no crean espacio. Cambian quién falla primero. En pools multi-tenant, eso a menudo es la decisión correcta—solo entiende el intercambio.
8) ¿Por qué las escrituras se ralentizan dramáticamente cerca del lleno?
Porque la asignación se vuelve más difícil, las actualizaciones de metadata aumentan y el asignador debe buscar extensiones utilizable. En RAIDZ, montar buenas franjas se vuelve más restringido. CoW significa que cada escritura es “asignar nuevo y luego confirmar”, lo que es más costoso cuando el pool está lleno.
9) Si añado más discos, ¿desaparecerá el problema del “slop”?
Añadir capacidad ayuda porque restaura margen y da al asignador mejores opciones. Pero si la causa raíz es proliferación de snapshots, mala configuración de cuotas o agotamiento del vdev special, añadir discos puede solo retrasar el próximo incidente.
10) ¿Cuál es el mejor hábito para evitar sorpresas de “se siente lleno”?
Monitorea snapshots y margen como métricas de primera clase. “Usado” es un indicador rezagado; “qué está fijando espacio” y “a qué velocidad nos acercamos al precipicio” son indicadores líderes.
Conclusión
Los pools ZFS parecen llenos antes del 100% porque ZFS no es un cubo de bloques tonto. Es un sistema CoW transaccional que necesita espacio para maniobrar: espacio para bloques nuevos, espacio para metadata, espacio para evitar trampas por fragmentación y espacio para mantener sus promesas (reservas) sin colapsar bajo carga. El espacio slop es la versión explícita de esa realidad; los snapshots, los vdevs special y el comportamiento de RAIDZ son las versiones implícitas.
Si extraes una lección operativa de esto: la capacidad no es un solo número. Los runbooks que solo comprueban “porcentaje usado” son cómo terminas depurando un incidente de almacenamiento con la caja de herramientas emocional de un pronóstico meteorológico. Comprueba el espacio asignable a nivel dataset, atribuye qué está fijando bloques, vigila la fragmentación y los vdevs special, y mantén suficiente margen para que ZFS haga lo que contrataste: mantener tus datos intactos mientras todo lo demás arde.