Cuota vs reserva en ZFS: la pareja de control de espacio que debes comprender

¿Te fue útil?

ZFS te ofrece dos mandos para controlar el espacio que se parecen, suenan parecido y se comportan sólo lo suficientemente diferente como para causar incidentes en producción: quota y reservation. Una es un techo. La otra es un suelo. Y ambas las hace cumplir la propia contabilidad de ZFS —no lo que du piensa, no lo que siente tu aplicación y definitivamente no lo que prometía la presentación de tu presupuesto de almacenamiento.

Si gestionas sistemas multi-inquilino, constructores de CI, granjas de VM, flotas de bases de datos o cualquier cosa donde “alguien terminará llenando el pool”, cuota y reserva son la diferencia entre un on-call tranquilo y una sala de guerra a las 02:00. Este artículo está escrito de la forma en que realmente operas ZFS: con comandos, síntomas y la realidad desordenada de instantáneas, refquota y “¿por qué no hay espacio si df dice que sí?”

1. Cuota vs reserva: las definiciones reales

Cuota: “No puedes crecer más allá de esto.”

Una quota en un dataset es un tope rígido sobre cuánto espacio ese dataset (y sus descendientes) pueden consumir. Una vez que el uso del dataset alcanza la cuota, las escrituras que requieran más espacio fallan con ENOSPC (o variantes específicas de la aplicación de “no hay espacio disponible”).

Comportamiento clave: ZFS aplica la cuota según su contabilidad interna del espacio lógico referenciado (con matices que debes respetar, especialmente cuando existen instantáneas). Una cuota no es espacio “reservado”. Es permiso para consumir hasta un límite.

Reserva: “Esta cantidad es mía aunque los demás tengan hambre.”

Una reservation es una garantía: ZFS apartará espacio del pool para que un dataset pueda seguir escribiendo hasta esa cantidad reservada, incluso si otros datasets compiten por el espacio libre.

Comportamiento clave: las reservas reducen el espacio disponible del pool para los demás, inmediatamente, incluso si el dataset no está usando actualmente ese espacio. Esta es la garantía de “suelo”.

Dos frases que te mantienen cuerdo

  • La cuota es un límite de velocidad. Puedes conducir hasta ese límite, pero no te compra combustible.
  • La reserva es un vale de combustible. No te dice a dónde ir, pero te garantiza que puedes ir a algún lugar.

Broma #1 (corta y relevante): Una cuota es el CFO diciendo “no gastes más que esto”. Una reserva es el CFO realmente poniendo el dinero en tu centro de costos—rara, hermosa y aún así de alguna forma confusa.

2. Modelo mental: techo, suelo y quién paga

La mayoría de los equipos aciertan la cuota el primer día: “Cada inquilino recibe 500G”. Luego ocurre el primer incidente porque la cuota no protege el pool. Solo protege a los demás datasets de que ese dataset crezca demasiado. Si repartes cuotas cuya suma es el 200% de la capacidad del pool (sobreaprovisionamiento), estás apostando al comportamiento. A veces eso está bien. A veces es como aprender el significado de “amplificación de escritura durante la compactación”.

Las reservas son el modo de fallo opuesto: sí protegen el dataset, pero pueden dejar en silencio al resto sin espacio. Una reserva es entrar al frigorífico compartido y poner tu nombre en la mitad de las estanterías “por si acaso”.

Piénsalo en tres números, no en uno

Cuando depuras espacio, necesitas tres conceptos distintos:

  • Usado: lo que ZFS contabiliza como en uso por datasets e instantáneas.
  • Disponible: lo que ZFS dice que se puede asignar (después del slop space, reservas, etc.).
  • Referenciado vs lógico vs físico: cuánto datos “son tuyos”, cuánto está compartido por instantáneas y cuánto está realmente en disco tras la compresión.

Las cuotas y reservas se aplican en el límite del dataset

Los datasets de ZFS son la unidad de ejecución. Las cuotas/reservas no se aplican a “un directorio” a menos que uses cuotas de proyecto (más adelante). Para zvols de VM existen otros mandos (volsize, refreservation) y la confusión se multiplica.

¿Qué se bloquea cuando alcanzas la cuota?

Escrituras que necesitan bloques nuevos. Los sobrescrituras también pueden necesitar bloques nuevos porque ZFS es copy-on-write. Eso significa que “estoy editando un archivo en su lugar” aún puede asignar, y las cuotas pueden morderte.

¿Qué pasa cuando el pool está lleno pero tienes una reserva?

Si un dataset tiene una reserva y el pool se queda apretado, ZFS intenta preservar esa cantidad reservada para el dataset. Eso puede significar que otros datasets vean ENOSPC antes de lo esperado, porque desde su perspectiva el espacio reservado nunca fue realmente “disponible”.

3. Datos interesantes y contexto histórico

A los ingenieros de almacenamiento les encanta lo “simple”. A ZFS le encanta lo “correcto”. La brecha entre esos dos es donde viven cuotas y reservas. Aquí hay algunos puntos de contexto que ayudan a explicar por qué el sistema se comporta así:

  1. ZFS se construyó alrededor de copy-on-write, lo que significa que los sobrescrituras asignan nuevos bloques. La contabilidad de espacio debe considerar los bloques “antiguos” retenidos por instantáneas, no solo el sistema de archivos vivo.
  2. Las primeras versiones de ZFS enfatizaron la integridad de extremo a extremo (checksums, autocuración) mucho antes de que fuera popular; la aplicación de cuotas tuvo que funcionar con semántica transaccional, no “mejor esfuerzo”.
  3. Existen los conceptos de refquota y refreservation porque las instantáneas complicaron la idea ingenua de “un dataset usa X”. El espacio referenciado y el espacio total son facturas diferentes.
  4. Existe el “slop space” de ZFS (una reserva pequeña no asignable a nivel de pool) para mantener el sistema funcionando cuando está casi lleno. Esto hace que “¿por qué faltan 5G?” sea un misterio recurrente para los recién llegados.
  5. La compresión cambia la percepción humana del uso: una cuota se aplica sobre la contabilidad lógica, mientras que el consumo físico puede ser menor. A los usuarios no les gusta que les digan “no hay espacio” cuando los discos no están llenos.
  6. El aprovisionamiento delgado se volvió mainstream, y ZFS lo abrazó con datasets y cuotas—pero las reservas son el contrapeso cuando necesitas espacio garantizado.
  7. El almacenamiento para VM popularizó los zvols, y muchos errores operativos vienen de tratar los zvols como sistemas de archivos. volsize no es una cuota; es el tamaño del dispositivo.
  8. La contenedorización normalizó la disposición multi-inquilino. Los datasets de ZFS se volvieron un límite limpio para cuotas y delegación, pero solo si entiendes descendientes y comportamiento de instantáneas.
  9. Las cuotas por proyecto llegaron para responder “necesito límites por directorio” sin proliferar datasets. Son potentes, pero añaden otra capa de contabilidad que debe monitorizarse.

4. Propiedades que realmente usarás (y sus trampas)

ZFS expone una pequeña constelación de propiedades de control de espacio. Apréndelas en parejas, porque así es como se comportan en la práctica.

quota y reservation: aplican al dataset + descendientes

quota limita el espacio total consumido por un dataset y todos sus hijos. Lo mismo para reservation: reserva para el dataset y sus descendientes.

Esto es genial cuando asignas “un inquilino” como dataset de primer nivel y pones todo debajo. No es genial cuando pretendes limitar solo el dataset padre pero alguien crea hijos y se pregunta por qué la cuota del padre “no hace nada”. Está haciendo exactamente lo que prometió: gobernar todo el subárbol.

refquota y refreservation: aplican solo al dataset (no a descendientes ni instantáneas)

refquota es una cuota sobre el espacio referenciado del dataset—típicamente significando “datos vivos”, sin incluir instantáneas ni descendientes. Es la perilla de “quiero limitar lo que este dataset en sí mismo puede referenciar”.

refreservation es el suelo emparejado para el espacio referenciado.

Operativamente, refquota es cómo evitas que flujos de trabajo con muchas instantáneas castiguen a los inquilinos por historia que no pidieron (o que te castiguen a ti porque mantienes siete días de historial).

Por qué importa “incluir instantáneas” y “excluir instantáneas”

El espacio de instantáneas es espacio “real” en el pool, pero no necesariamente “propiedad” de la forma en que tus inquilinos piensan. Un dataset puede estar en su cuota y aun así necesitar asignar espacio debido al churn copy-on-write, especialmente cuando las instantáneas anclan bloques antiguos. Así es como obtienes el clásico: “eliminé archivos pero el uso no bajó.” No eliminaste los bloques; eliminaste referencias. Las instantáneas mantuvieron vivas las referencias antiguas.

Las reservas pueden ser mayores que lo usado (y ese es el punto)

Cuando estableces una reserva, estás preasignando disponibilidad del pool, no escribiendo ceros. Si reservas 200G y solo usas 20G, el pool seguirá comportándose como si esos 200G no estuvieran disponibles para otros datasets. Esto es intencional. También es causa común de “espacio libre misteriosamente bajo”.

Delegación y realidad multi-inquilino

En entornos corporativos, es habitual delegar la gestión de datasets a equipos de plataforma o incluso a inquilinos (equipos de infraestructura de CI, operaciones de servidores de juegos, etc.). Si permites que los inquilinos creen instantáneas, pueden hacer que la aplicación de cuotas parezca injusta a menos que elijas refquota correctamente. Y si permites que los inquilinos establezcan reservas, efectivamente les permites preemptar la capacidad del pool. Eso no es un problema técnico. Es un problema de organigrama.

5. Instantáneas: la tercera parte en cada discusión

Las instantáneas son la razón por la que ZFS es una alegría—y la razón por la que las conversaciones sobre espacio se vuelven raras.

Las instantáneas no “ocupan espacio” al crearlas, pero pueden retener espacio para siempre

Una instantánea es inicialmente metadata. El coste en espacio viene después, cuando el dataset vivo cambia y la instantánea conserva referencias a bloques antiguos. Eliminar 100G de archivos del dataset vivo no libera esos bloques si las instantáneas aún los referencian.

Cómo interactúan las instantáneas con las cuotas

Aquí está la sutileza que engaña a la gente: dependiendo de qué tipo de cuota uses, las instantáneas pueden o no contarse contra la cuota.

  • quota cuenta el dataset y sus descendientes; el espacio retenido por instantáneas puede aún causar que las asignaciones fallen porque el dataset no puede asignar nuevos bloques sin superar la cuota.
  • refquota se centra en el espacio referenciado (datos vivos). El espacio de instantáneas no está “referenciado” por el dataset de la misma manera, por lo que es más adecuado cuando gestionas instantáneas de forma centralizada.

El churn de instantáneas hace que las cuotas parezcan “pegajosas”

En la práctica, la sensación de pegajosidad es copy-on-write más retención. Bases de datos que reescriben archivos grandes, trabajos de compactación, imágenes de VM, cachés de compilación—todos estos son cargas de “churn” de espacio. Pueden requerir espacio doble temporal durante una reescritura. Si fijas cuotas demasiado cerca del uso en estado estacionario, creas un sistema que funciona… hasta que necesita mantenimiento.

Broma #2 (corta y relevante): Las instantáneas son como tomar fotos de tu armario. Eliminar los calcetines después no hace las fotos más pequeñas, y a ZFS no le impresiona tu nuevo minimalismo.

6. Tres micro-historias del mundo corporativo

Micro-historia 1: El incidente causado por una suposición errónea (cuota como red de seguridad del pool)

El equipo de plataforma gestionaba un pool ZFS compartido para runners de CI. Cada proyecto tenía su propio dataset bajo tank/ci, y cada dataset tenía una cuota. Todos se sentían responsables. Todos se sentían seguros. El pool estaba dimensionado para carga típica y builds “estallido”, y las cuotas tenían la intención de evitar que un equipo se descontrolara.

Entonces llegó un refactor grande en un monorepo, y un nuevo paso de build empezó a producir artefactos por duplicado: una vez sin comprimir, otra comprimida, y luego subiendo ambas. La cuota del dataset impidió crecimiento infinito, claro—pero el build aún tenía suficiente margen para expandirse rápidamente en muchos proyectos a la vez. Las cuotas no evitaron que el pool se llenara porque el pool era compartido y la suma del “crecimiento permitido” estaba muy por encima de la capacidad real.

Al mismo tiempo, se tomaban instantáneas cada 15 minutos para “rollback rápido” de imágenes de runner. Nadie había conectado esa política con los artefactos de CI. El churn de escritura más instantáneas frecuentes creó muchos bloques anclados. Los directorios de build se eliminaban tras completarse, pero las instantáneas mantenían vivo el churn hasta que la retención expiró.

El incidente no fue dramático al principio. Empezó como builds inestables. Luego fallos al descomprimir paquetes. Luego algunos hosts de runner se pusieron en modo solo lectura de formas extrañas porque las aplicaciones se comportaron mal bajo ENOSPC. La primera reacción del equipo fue aumentar cuotas, porque “los proyectos alcanzan cuota”. Eso hizo que el pool se llenara más rápido.

La solución eventual fue aburrida y correcta: establecer una política a nivel de pool (monitorizar el espacio libre del pool), mover los artefactos de CI a un dataset con retención de instantáneas corta (o sin instantáneas) y mantener las cuotas como equidad entre inquilinos—no protección del pool. También introdujeron una pequeña reserva para datasets del sistema (logs, caches de paquetes) para que los hosts pudieran seguir funcionando en emergencias.

Micro-historia 2: La optimización que salió mal (reservas por todas partes “por fiabilidad”)

Un ingeniero con mentalidad de almacenamiento se unió a un equipo que había sufrido outages por “pool lleno”. Su instinto fue razonable: garantizar capacidad para datasets críticos. Creó reservas para bases de datos, logs y un puñado de servicios que “nunca deben fallar”. La idea era evitar vecinos ruidosos y el temido comportamiento de pool casi lleno.

Funcionó por un tiempo. Luego llegó crecimiento. Nuevos servicios aparecieron. Cada uno pidió “solo una pequeña reserva” porque sonaba responsable. Nadie quería ser el servicio que no reservó y luego causó un outage. Así es como las buenas intenciones escalan hasta convertirse en mala aritmética.

Tras un trimestre, el pool parecía medio vacío en términos de uso bruto, sin embargo el “available” era bajo. Los equipos seguían abriendo tickets: “df muestra espacio libre pero las escrituras fallan”. No estaban equivocados; simplemente miraban la capa de contabilidad equivocada. Las reservas habían precomprometido la mayor parte del pool en silencio.

El contragolpe fue operativo: durante un pico de tráfico, la canalización de logs necesitó crecimiento temporal y no pudo obtenerlo. Los datos no eran grandes a largo plazo, pero la oleada necesitaba capacidad de ráfaga. El pool tenía espacio físico; no tenía espacio asignable porque estaba reservado. El resultado fueron logs perdidos justo cuando más importaba depurar.

El postmortem terminó con una regla: las reservas son para supervivencia de infraestructura (datasets OS, WAL/headroom crítico) y para cargas con SLA estrictos. Todo lo demás recibe cuotas y monitorización. El equipo también aprendió a documentar las reservas como “deuda de capacidad” que debe pagarse con discos reales.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día (refquota + límites de instantáneas)

Una empresa gestionaba una plataforma analítica multi-inquilino. Los inquilinos subían datos, trabajos los procesaban y los resultados se almacenaban por inquilino. El equipo de plataforma quería dos cosas: límites previsibles para inquilinos y rollback fuerte para seguridad operativa. También sabían que las instantáneas serían innegociables porque los “oops” ocurren semanalmente en sistemas de datos.

En lugar de usar quota por todas partes, usaron un patrón: los datasets de inquilinos recibieron refquota para imponer límites de “datos vivos”. Las instantáneas se gestionaron de forma central por el equipo de plataforma, con políticas de retención ajustadas al tipo de carga (corta para scratch, más larga para resultados curados).

Crearon un subárbol de datasets separado para datos scratch/intermedios, con poda agresiva de instantáneas y recordsize afinado. Lo más importante: alinearon los límites de instantáneas con la propiedad: los inquilinos no eran cargados (vía cuota) por el historial de seguridad de la plataforma, y los datasets scratch no podían conservar instantáneas más allá de una ventana corta.

Meses después, un despliegue malo desencadenó una oleada de reintentos de trabajos que reescribieron agresivamente archivos intermedios. El churn fue real. Pero el sistema permaneció operativo porque: (1) los datasets scratch tenían retención que no ancló el churn por mucho tiempo, y (2) existía una pequeña reserva para datasets críticos del sistema para que logs y servicios esenciales pudieran seguir escribiendo mientras el equipo estabilizaba los trabajos.

Sin heroicidades. Sin “magia de almacenamiento”. Solo límites claros, aplicación conservadora y la humildad de asumir que alguien hará algo estúpido con el disco.

7. Tareas prácticas: comandos e interpretación (12+)

Estos son los comandos que realmente uso cuando un pool está apretado, un dataset alcanza límites o alguien afirma “ZFS miente”. Los comandos se muestran con salida típica. Ajusta nombres de pool/dataset para tu entorno.

Tarea 1: Listar capacidad y salud del pool

cr0x@server:~$ zpool list
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  7.25T  5.61T  1.64T        -         -    22%    77%  1.00x  ONLINE  -

Interpretación: El pool está al 77% de uso. Eso no es una emergencia por sí mismo, pero si sube a los altos 80s/90s, las asignaciones se vuelven frágiles (y el rendimiento puede tambalear). Esta salida no muestra reservas directamente.

Tarea 2: Comprobar el espacio “available” del pool con slop considerado

cr0x@server:~$ zfs get -H -o name,property,value available tank
tank	available	1.52T

Interpretación: ZFS puede reportar menos que FREE debido al slop space y otra contabilidad. Si zpool list muestra free pero zfs get available es bajo, te estás dirigiendo a la zona donde ENOSPC aparece “temprano”.

Tarea 3: Mostrar cuotas y reservas en un árbol de datasets

cr0x@server:~$ zfs get -r -o name,property,value -s local quota,reservation,refquota,refreservation tank/tenants
NAME                  PROPERTY        VALUE
tank/tenants          quota           -
tank/tenants          reservation     -
tank/tenants          refquota        -
tank/tenants          refreservation  -
tank/tenants/acme     quota           2T
tank/tenants/acme     reservation     -
tank/tenants/acme     refquota        1.5T
tank/tenants/acme     refreservation  200G
tank/tenants/zephyr   quota           1T
tank/tenants/zephyr   reservation     -
tank/tenants/zephyr   refquota        -
tank/tenants/zephyr   refreservation  -

Interpretación: Esto te dice quién tiene límites y garantías. Observa la mezcla: quota limita el subárbol; refquota limita los datos vivos de ese dataset; refreservation garantiza margen para datos referenciados.

Tarea 4: Encontrar los datasets más grandes rápido

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used | head -n 12
NAME                 USED  AVAIL  REFER  MOUNTPOINT
tank                 5.61T 1.52T   128K  /tank
tank/tenants         4.90T 1.52T    96K  /tank/tenants
tank/tenants/acme    1.92T  800G  1.44T  /tank/tenants/acme
tank/tenants/zephyr  1.31T 1.52T  1.05T  /tank/tenants/zephyr
tank/vm              420G  1.52T    96K  /tank/vm
tank/logs            180G  1.52T   160G  /tank/logs

Interpretación: USED incluye instantáneas y descendientes. REFER es “datos vivos” referenciados por ese dataset. Cuando USED es mucho mayor que REFER, normalmente las instantáneas/children son la razón.

Tarea 5: Identificar datasets con muchas instantáneas (brecha USED vs REFER)

cr0x@server:~$ zfs list -t filesystem -o name,used,refer -S used | head -n 10
NAME                 USED  REFER
tank/tenants/acme    1.92T 1.44T
tank/tenants/zephyr  1.31T 1.05T
tank/logs            180G  160G
tank/ci              140G   18G
tank/home            110G   45G

Interpretación: tank/ci es una señal de alarma: 140G usados, solo 18G referenciados. Eso suele ser instantáneas reteniendo churn, o muchos datasets hijos.

Tarea 6: Inspeccionar instantáneas y su espacio

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -S used | head -n 8
NAME                                   USED  REFER  CREATION
tank/ci@autosnap_2025-12-24_0100        22.4G    0B  Wed Dec 24 01:00 2025
tank/ci@autosnap_2025-12-24_0045        18.1G    0B  Wed Dec 24 00:45 2025
tank/tenants/acme@daily_2025-12-23      12.7G    0B  Tue Dec 23 02:00 2025
tank/home@hourly_2025-12-24_0100         6.2G    0B  Wed Dec 24 01:00 2025

Interpretación: USED de una instantánea es el espacio único retenido por esa instantánea (espacio que se liberaría si se destruyera, asumiendo que ninguna otra instantánea referencia esos bloques). Una sucesión de instantáneas con gran USED indica churn.

Tarea 7: Mostrar desglose de espacio para un dataset

cr0x@server:~$ zfs get -o property,value -p used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation tank/ci
PROPERTY             VALUE
used                 150323855360
usedbysnapshots      126406688768
usedbydataset        19327352832
usedbychildren       0
usedbyrefreservation 0

Interpretación: Las instantáneas consumen ~126G de espacio único. Aquí es donde se fue tu pool.

Tarea 8: Establecer una cuota (límite) en un dataset

cr0x@server:~$ sudo zfs set quota=500G tank/tenants/zephyr
cr0x@server:~$ zfs get -H -o name,property,value quota tank/tenants/zephyr
tank/tenants/zephyr	quota	500G

Interpretación: zephyr (y cualquier hijo bajo él) no puede consumir más de 500G en total. Si ya está por encima de 500G, las escrituras fallarán hasta que el uso baje.

Tarea 9: Establecer una reserva (garantía) para margen crítico

cr0x@server:~$ sudo zfs set reservation=50G tank/logs
cr0x@server:~$ zfs get -H -o name,property,value reservation tank/logs
tank/logs	reservation	50G

Interpretación: 50G se elimina del “available” para los demás y se reserva para tank/logs (y sus descendientes). Esto ayuda a que los logs sigan escribiendo cuando el pool está presionado.

Tarea 10: Usar refquota para limitar solo datos vivos (límites seguros frente a instantáneas)

cr0x@server:~$ sudo zfs set refquota=300G tank/tenants/acme
cr0x@server:~$ zfs get -H -o name,property,value refquota tank/tenants/acme
tank/tenants/acme	refquota	300G

Interpretación: Esto limita el dato referenciado en vivo de acme. Si las instantáneas de plataforma inflan USED, refquota es menos probable que castigue al inquilino por la política de retención.

Tarea 11: Usar refreservation para garantizar margen de escritura en vivo

cr0x@server:~$ sudo zfs set refreservation=20G tank/tenants/acme
cr0x@server:~$ zfs get -H -o name,property,value refreservation tank/tenants/acme
tank/tenants/acme	refreservation	20G

Interpretación: Garantiza 20G de espacio referenciado para las propias escrituras de ese dataset. Útil para cosas como WAL de bases de datos o scratch que no deben detenerse durante presión del pool.

Tarea 12: Confirmar por qué un dataset muestra poco “avail” (presión de cuotas/reservas)

cr0x@server:~$ zfs get -o name,quota,refquota,reservation,refreservation,used,avail,refer tank/tenants/acme
NAME               QUOTA  REFQUOTA  RESERV  REFRESERV  USED  AVAIL  REFER
tank/tenants/acme     2T     300G       -      20G   1.92T  0B   1.44T

Interpretación: AVAIL 0B es clave. Significa que desde la perspectiva de ZFS este dataset no puede asignar más espacio, probablemente debido a que el impuesto refquota es menor que el actual REFER. En otras palabras: los límites son inconsistentes con la realidad; acabas de ponerte el cinturón después del choque.

Tarea 13: Reducir la presión de instantáneas borrando instantáneas antiguas (con cuidado)

cr0x@server:~$ zfs list -t snapshot -o name,used -S used | grep '^tank/ci@' | head
tank/ci@autosnap_2025-12-24_0100   22.4G
tank/ci@autosnap_2025-12-24_0045   18.1G
tank/ci@autosnap_2025-12-24_0030   15.9G
cr0x@server:~$ sudo zfs destroy tank/ci@autosnap_2025-12-24_0030

Interpretación: Destruir una instantánea libera espacio solo si los bloques son únicos para ella. Confirma siempre políticas y dependencias de replicación antes de destruir instantáneas en producción.

Tarea 14: Revelar “tragones” de espacio ocultos con “written” (churn reciente)

cr0x@server:~$ zfs get -H -o name,property,value written@autosnap_2025-12-24_0045 tank/ci
tank/ci	written@autosnap_2025-12-24_0045	41234597888

Interpretación: Esto muestra bytes escritos desde esa instantánea. Números altos + instantáneas frecuentes son receta para crecimiento retenido por instantáneas.

Tarea 15: Comparar la contabilidad de ZFS con la vista del sistema de archivos

cr0x@server:~$ df -h /tank/ci
Filesystem      Size  Used Avail Use% Mounted on
tank/ci         1.6T   19G  0B  100% /tank/ci

Interpretación: El montaje muestra 0B disponible porque ZFS aplica la disponibilidad del dataset tras cuotas/reservas. Si los usuarios dicen “df dice lleno”, créelos, y luego mira las propiedades de ZFS para entender por qué.

Tarea 16: Encontrar reservas que están “robando” disponibilidad del pool

cr0x@server:~$ zfs get -r -H -o name,property,value reservation,refreservation tank | egrep -v '\t-\s*$' | head -n 20
tank/logs	reservation	50G
tank/system	reservation	30G
tank/tenants/acme	refreservation	20G

Interpretación: Cualquier valor distinto de - reduce el espacio asignable del pool. Si el “available” se siente bajo, esta lista suele ser la razón.

8. Guion de diagnóstico rápido

Esta es la secuencia de triaje que te lleva a la causa raíz rápidamente cuando alguien reporta “dataset lleno”, “pool lleno” o “escrituras fallando”. El objetivo es evitar el clásico bucle de subir cuotas a ciegas o borrar datos al azar bajo presión.

Paso 1: ¿Es el pool, el dataset o un límite?

cr0x@server:~$ zpool list tank
NAME   SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  7.25T  6.98T   270G        -         -    29%    96%  1.00x  ONLINE  -
cr0x@server:~$ zfs get -H -o name,property,value available tank
tank	available	120G

Interpretación: Si la CAP del pool es >90% y tank available es bajo, tienes un evento de capacidad de pool. Si el pool parece bien pero un dataset está sin espacio, suele ser cuotas, reservas o anclaje por instantáneas.

Paso 2: Identificar qué dataset está constreñido

cr0x@server:~$ zfs list -o name,used,avail,refer -S used | head -n 15
NAME                 USED  AVAIL  REFER
tank                 6.98T  120G   128K
tank/tenants         5.80T  120G    96K
tank/tenants/acme    2.40T    0B  1.90T
tank/tenants/zephyr  1.70T  120G  1.65T
tank/ci              650G     0B   40G

Interpretación: Los datasets con AVAIL 0B son donde las aplicaciones fallarán primero.

Paso 3: Comprobar si son cuotas/reservas o instantáneas

cr0x@server:~$ zfs get -o name,quota,refquota,reservation,refreservation,used,usedbysnapshots,refer,avail tank/ci
NAME     QUOTA  REFQUOTA  RESERV  REFRESERV  USED  USEDBYSNAPSHOTS  REFER  AVAIL
tank/ci  200G   -         -       -          650G  590G             40G    0B

Interpretación: Aquí, la cuota es 200G pero el dataset está usando 650G (probablemente por descendientes o instantáneas; también es posible que la cuota se aplicara después o al nivel equivocado). USEDBYSNAPSHOTS es enorme, así que las instantáneas son la palanca inmediata.

Paso 4: Decidir la remedio adecuado para el momento

  • Emergencia de pool lleno: elimina primero las instantáneas más seguras de borrar (las que tienen mayor USED), o poda la retención. Evita “rm -rf” a menos que entiendas que las instantáneas ya están reteniendo el espacio.
  • Inquilino alcanzando cuota: decide si subir la cuota o reducir uso; comprueba si la retención de instantáneas está causando crecimiento “invisible”.
  • Todo está hambriento pero el pool no está lleno: audita reservas/refreservation y deshaz el sobrecompromiso de garantías.

9. Errores comunes, síntomas y soluciones

Error 1: Tratar la cuota como “protección del pool”

Síntoma: El pool se llena aunque cada dataset tenga cuota. Varios inquilinos alcanzan cuota simultáneamente durante un evento de churn.

Por qué ocurre: Las cuotas limitan individuos, no la suma. Si sobrecommitías cuotas, el pool aún puede llenarse cuando todos crecen a la vez.

Solución: Monitorea el espacio libre del pool y establece guardarraíles operativos (alertas al 80/85/90%). Usa reservas solo para datasets críticos. Mantén cuotas para equidad, no para seguridad.

Error 2: Poner reservas “por si acaso” en todos lados

Síntoma: El pool parece tener espacio libre, pero muchos datasets muestran AVAIL bajo o las escrituras fallan de forma impredecible. Los equipos ven números contradictorios entre herramientas.

Por qué ocurre: Las reservas preconsumen espacio asignable del pool. Demasiadas reservas hacen que el pool sea funcionalmente lleno aun cuando no lo esté físicamente.

Solución: Lista reservas recursivamente, justifica cada una y elimínalas/ajústalas. Prefiere refreservation para margen dirigido en lugar de reservas amplias de subárbol.

Error 3: Usar quota cuando querías refquota (las instantáneas lo hacen doler)

Síntoma: Los inquilinos se quejan de que el uso no baja tras eliminaciones; alcanzan cuota pese a “limpiar”.

Por qué ocurre: Las instantáneas retienen bloques antiguos. quota no separa datos vivos del historial de instantáneas como los inquilinos esperan.

Solución: Usa refquota para límites de inquilinos si las instantáneas son gestionadas por la plataforma. O mueve el snapshotting a un dataset padre y deja los datasets de inquilinos sin instantáneas, según tu modelo de gobernanza.

Error 4: Aplicar cuota en el nivel equivocado del árbol de datasets

Síntoma: Una cuota “no hace nada” o tiene alcance sorprendente. Un dataset hijo llena inesperadamente la cuota del padre.

Por qué ocurre: quota se aplica al dataset + descendientes. Ponerla en el padre equivocado cambia quién está incluido.

Solución: Visualiza el árbol de datasets. Aplica cuotas en la raíz del inquilino. Usa refquota si solo quieres limitar el uso referenciado de un único dataset.

Error 5: Confundir el tamaño de zvol con cuotas

Síntoma: El disco de la VM “se queda sin espacio” aunque la cuota del dataset parezca generosa, o el pool se llena inesperadamente por suposiciones de aprovisionamiento delgado.

Por qué ocurre: El volsize de un zvol define el tamaño del dispositivo; el espacio se asigna cuando se escriben bloques, y las instantáneas pueden anclar bloques antiguos también. El comportamiento de cuota/reserva puede diferir según cómo provisionas.

Solución: Para VMs respaldadas por zvol, rastrea el uso de zvol y políticas de instantáneas cuidadosamente. Considera refreservation para zvols críticos si el sobreaprovisionamiento es arriesgado.

Error 6: Fijar cuotas demasiado ajustadas para cargas copy-on-write

Síntoma: Compactación de base de datos, actualizaciones de imagen de VM o pasos de build fallan aunque el uso en estado estable esté por debajo de la cuota.

Por qué ocurre: Las reescrituras necesitan asignación extra temporal; las instantáneas amplifican esto. Necesitas margen para reescritura transaccional.

Solución: Reserva espacio para ráfagas. Usa refreservation o simplemente fija cuotas con holgura. Reduce la frecuencia de instantáneas en datasets con mucho churn.

10. Listas de verificación / plan paso a paso

Checklist A: Diseñar almacenamiento por inquilino (el plan “no me molestes”)

  1. Crea un dataset por inquilino (o por entorno) como límite de gestión.
  2. Decide si las instantáneas son propiedad del inquilino o de la plataforma. No rompas la propiedad accidentalmente.
  3. Si las instantáneas son gestionadas por la plataforma, prefiere refquota para límites de inquilinos.
  4. Establece quota solo cuando quieras incluir explícitamente a los hijos (común para “raíz de inquilino”).
  5. Añade pequeña reservation o refreservation solo para cargas que deben seguir escribiendo bajo presión del pool.
  6. Alerta sobre capacidad del pool, no solo uso de datasets. Las cuotas no te salvan del crecimiento agregado.
  7. Documenta: qué datasets pueden tener reservas, y por qué.

Checklist B: Respondiendo a “no hay espacio” en producción

  1. Comprueba la capacidad del pool: zpool list.
  2. Comprueba el espacio asignable del pool: zfs get available tank.
  3. Encuentra datasets con AVAIL 0B: zfs list -o name,used,avail,refer -S used.
  4. Para el dataset afectado, inspecciona límites y uso de instantáneas: zfs get usedbysnapshots,quota,refquota,reservation,refreservation.
  5. Si las instantáneas son la causa, elimina/poda según la política; no hagas “rm” esperando milagros.
  6. Si las reservas están asfixiando el pool, reduce/elimina reservas no críticas.
  7. Sólo entonces considera subir cuotas (y trátalo como planificación de capacidad, no como una solución rápida).

Checklist C: Higiene trimestral (la práctica aburrida que funciona)

  1. Inventaría todas las quota/refquota/reservation/refreservation no por defecto.
  2. Confirma que las reservas siguen coincidiendo con criticidad y proyecciones de crecimiento.
  3. Revisa políticas de instantáneas en datasets con mucho churn (CI, scratch, DBs temporales).
  4. Detecta datasets donde USED es mucho mayor que REFER; investiga por qué.
  5. Verifica monitorización: alertas de capacidad del pool, alertas por crecimiento de instantáneas y “datasets con AVAIL cerca de cero”.

11. FAQ

Q1: Si pongo una cuota, ¿ZFS “asigna” ese espacio?

No. Una cuota es un límite, no una preasignación. Otros datasets pueden consumir el pool hasta que el dataset limitado intente escribir y encuentre que el pool (o su cuota) no lo permite.

Q2: Si pongo una reserva, ¿escribe ceros en disco?

No. Reserva espacio asignable en la contabilidad del pool. Es una promesa, no un prellenado. Aunque el espacio “available” para otros baja inmediatamente.

Q3: ¿Debería usar quota o refquota para inquilinos?

Si los inquilinos deben ser responsables de sus instantáneas y descendientes, usa quota. Si las instantáneas las gestiona el equipo de plataforma (o quieres que los límites reflejen principalmente datos vivos), usa refquota. La respuesta correcta trata tanto de propiedad y expectativas como de bytes.

Q4: ¿Por qué eliminar archivos no liberó espacio?

Porque las instantáneas (o clones) aún referencian los bloques antiguos. Revisa usedbysnapshots y lista instantáneas por USED. El espacio se libera cuando se destruye la última referencia, no cuando un archivo desaparece de la vista viva.

Q5: ¿Por qué df discrepa con zpool list?

df informa disponibilidad a nivel de dataset tras cuotas/reservas. zpool list informa la asignación bruta a nivel de pool. Ambos tienen razón; solo responden preguntas diferentes. Cuando discrepan, busca cuotas, reservas y slop space.

Q6: ¿Las reservas pueden causar un outage?

Sí—al dejar sin recursos a cargas no reservadas incluso cuando existe espacio físico. Las reservas son poderosas y deben tratarse como compromisos de capacidad. Si no lo firmarías como contrato, no lo pongas como reserva.

Q7: ¿Las cuotas tienen en cuenta la compresión?

Las cuotas se basan en la contabilidad lógica de ZFS, no en “cuántos sectores de disco se usaron tras la compresión” como esperan los usuarios. La compresión puede bajar el uso físico, pero las cuotas aún pueden alcanzarse según el espacio lógico referenciado. Esto es bueno para previsibilidad, frustrante para explicaciones.

Q8: ¿Cuál es el patrón más simple y seguro para datasets del sistema críticos?

Da a los datasets del sistema (logs, servicios centrales) una reserva modesta para que el SO pueda seguir escribiendo durante un evento de presión del pool. Mantén la reserva pequeña, revisada y justificada—suficiente para sobrevivir incidentes, no lo suficiente para convertirse en sobrecompromiso silencioso.

Q9: Si tengo reservas, ¿puedo sobrecomprometer el pool con cuotas?

Sí. Las cuotas pueden sumar más que la capacidad restante del pool. Las reservas reducen lo que realmente está disponible. Trata la suma de cuotas como “demanda potencial”, no como “suministro asignado”.

Q10: ¿Cómo sé rápido si las instantáneas son el culpable principal?

Compara USED vs REFER para el dataset, luego comprueba usedbysnapshots. Si las instantáneas dominan, podar instantáneas (según la política) suele liberar más espacio y más rápido.

12. Conclusión

Cuota y reserva no son funciones en competencia. Son una pareja: quota evita que un dataset ocupe demasiado, y reservation evita que un dataset sea estrangulado. En sistemas reales de producción, normalmente necesitas ambas—simplemente no en todas partes y no sin entender las instantáneas.

Si te llevas una regla operativa: no discutas sobre “espacio libre” hasta que hayas comprobado la propia contabilidad de ZFS para usedbysnapshots, cuotas y reservas. ZFS rara vez miente; normalmente está respondiendo una pregunta que no te habías dado cuenta de que estabas haciendo.

← Anterior
Hyper-Threading desmitificado: hilos mágicos o trucos del planificador?
Siguiente →
IA en la CPU: Qué son las NPUs y por qué existen

Deja un comentario