refquota de ZFS: La única cuota que detiene las “mentiras” del espacio usado

¿Te fue útil?

ZFS es un sistema de archivos que dice la verdad—salvo cuando no lo hace. No porque tenga fallos, sino porque ZFS hace contabilidad a través de un árbol familiar: datasets, snapshots, clones, holds, reservations, vdevs especiales y metadatos que no sabías que eran facturables. Si alguna vez has mirado los números de USED que no coinciden con la realidad, ya conoces las “mentiras” del espacio usado. No son maliciosas. Son simplemente ZFS siendo técnicamente correcto de una manera que igual te despertará a las 02:00.

refquota es la cuota que ancla la factura al dataset que la creó, en lugar de permitir que se socialice entre descendientes e historial de snapshots compartido. Si gestionas ZFS multi-tenant (almacenamiento de VM, directorios home, caches de CI, contenedores, objetivos de backup), refquota es la única cuota que consistentemente evita que los argumentos tipo “yo no usé ese espacio” se conviertan en un incidente de almacenamiento.

El problema: “mentiras” del espacio usado en ZFS

Nombrémoslo con precisión. Cuando la gente se queja de que ZFS miente sobre el espacio usado, normalmente se refiere a una de estas situaciones:

  • Un dataset muestra poco uso, pero el pool está lleno.
  • Un tenant afirma que solo usó 200G, pero el pool perdió 2T.
  • Borrar archivos no libera espacio (porque los snapshots mantienen bloques vivos).
  • Los clones parecen usar “nada” hasta que divergen, y entonces la factura aparece en un lugar inesperado.
  • La cuota del dataset padre parece correcta, pero los hijos todavía pueden causar agotamiento del pool vía historial de snapshots compartido.

Clave: ZFS hace contabilidad a nivel de bloques. Los bloques pueden ser referenciados por múltiples objetos a la vez: un filesystem activo, un snapshot, un clone, un snapshot de un clone, etc. Used no es solo “archivos visibles ahora”. Es “bloques referenciados por algo”. Y ese “algo” puede ser histórico.

Las cuotas clásicas (quota) tratan del espacio total usado por un dataset y sus descendientes. Son excelentes para “esta subárbol no debe exceder X”. Pero no están diseñadas para facturar con justicia sólo por los cambios propios de este dataset cuando snapshots/clones/descendientes comparten bloques. Para eso sirve refquota: limita el espacio referenciado por ese dataset en solitario.

Resumen de una frase que puedes llevar a una reunión: quota es un presupuesto familiar; refquota es un límite de crédito personal.

Primera broma (los ingenieros de almacenamiento merecen al menos una): la contabilidad de ZFS es como un informe de gastos corporativo: todo está desglosado, nada es simple y, de alguna forma, los “costes compartidos” siempre aparecen en la tarjeta equivocada.

Por qué los equipos de operaciones se queman

Los fallos operativos alrededor del espacio en ZFS rara vez son por no conocer comandos. Son por asumir un modelo que no es verdad:

  • Asumir que borrar un archivo libera espacio inmediatamente.
  • Asumir que el USED de un dataset es lo que “le cuesta” al pool ahora mismo.
  • Asumir que las cuotas se comportan como las cuotas de proyecto de ext4 o XFS.
  • Asumir que los snapshots son “gratuitos hasta que crecen”. Son gratuitos hasta que cambias bloques.

refquota no hace que los snapshots sean mágicamente gratis. Lo que hace es impedir que un dataset aumente su huella referenciada más allá de un tope—incluso cuando el uso es difícil de razonar por la historia compartida. Obliga a responder a una pregunta simple: “¿Cuánto puede medir este dataset, pase lo que pase?”

Hechos interesantes y contexto histórico

Algunos puntos de contexto que importan cuando diseñas políticas y predices comportamientos:

  1. La unidad básica de contabilidad de ZFS es el puntero de bloque, no el nombre de archivo. Por eso los snapshots pueden mantener espacio vivo sin “tener archivos” que puedas ver.
  2. Los snapshots no son copias. Son referencias inmutables a bloques existentes y solo “cuestan” espacio conforme el dataset activo diverge.
  3. Los clones son snapshots escribibles. Comparten bloques con su origen y tu factura depende de cuánto divergen del historial compartido.
  4. “USED” es una métrica en capas. Incluye espacio referenciado por el dataset más espacio referenciado por hijos y snapshots dependiendo de la propiedad que mires (used, refer, usedby*).
  5. ZFS tiene nociones separadas de espacio “lógico” y “físico”. La compresión y las copias hacen que el tamaño lógico pueda diferir mucho de la asignación del pool.
  6. Las cuotas en ZFS evolucionaron para coincidir con el modelo de dataset, no con las cuotas POSIX por usuario. Son límites por dataset primero; las cuotas por usuario/grupo son una capa adicional.
  7. “Referenced” es lo más cercano a una huella facturable. Aproxima lo que se liberaría si destruyeras el dataset (sin contar bloques aún necesarios en otro lugar).
  8. Las reservations pueden causar ENOSPC “misterioso”. Un pool con bastante espacio raw puede negar escrituras si el espacio libre no está disponible para ese dataset debido a reservations y quotas.
  9. Históricamente, muchos incidentes de ZFS vienen de políticas de retención de snapshots, no de tasas de escritura. La gente dimensiona para churn diario y olvida que “semanal guardado por un año” se acumula.

Familia de cuotas: quota, refquota, reservation, refreservation

ZFS te da cuatro mandos que parecen similares hasta que arruinan tu fin de semana. Definámoslos con intención operacional.

quota

quota limita la cantidad de espacio que un dataset y todos sus descendientes pueden usar. Si pones quota=1T en tank/prod, entonces tank/prod más tank/prod/db más tank/prod/vm colectivamente no pueden exceder 1T.

Úsalo cuando quieras un techo duro para una subárbol.

refquota

refquota limita la cantidad de espacio que un dataset en sí referencia (su “huella personal”), excluyendo descendientes. Esta es la que actúa como “limitar este dataset, sin importar lo que hagan sus hijos”.

Úsalo para datasets de tenants, discos de VM, raíces de contenedores, directorios home—cualquier cosa donde necesites límites por entidad que no se conviertan en discusiones sobre snapshots compartidos.

reservation

reservation garantiza espacio a un dataset y todos sus descendientes. Es la promesa de “esta subárbol siempre tiene al menos X disponible”.

Úsalo con cuidado. Las reservations son un instrumento contundente. Son cómo construyes “esta carga de trabajo nunca debe quedarse sin espacio porque otro tenant tuvo un pico”. También son cómo creas un pool que parece medio vacío y aun así devuelve ENOSPC a la carga que no obtuvo reservation.

refreservation

refreservation garantiza espacio al dataset en sí (excluyendo descendientes). Es la versión personal de reservation, tal como refquota es la versión personal de quota.

Úsalo cuando un dataset necesite headroom garantizado para ráfagas, crecimiento de metadatos o picos transaccionales (bases de datos, WAL, algunos patrones de VM). Pero trátalo como una asignación presupuestaria: reduce el espacio del pool “libre para todos”.

Un modelo mental rápido

Si solo recuerdas una cosa:

  • quota: limitar la familia.
  • refquota: limitar la persona.
  • reservation: garantizar la familia.
  • refreservation: garantizar la persona.

Cómo funciona realmente refquota (y lo que no hace)

Referenced vs used: la factura que puedes defender

La propiedad que importa aquí es referenced (a menudo mostrada como REFER en zfs list). Esto es la cantidad de espacio que se liberaría si el dataset fuera destruido, excluyendo bloques referenciados en otra parte (como por snapshots fuera del conjunto de snapshots del dataset, o por clones que dependan de relaciones).

refquota se aplica contra el espacio referenciado del dataset. Cuando fijas refquota, ZFS detiene las escrituras cuando el espacio referenciado de ese dataset excedería el tope.

Esto tiene dos consecuencias operacionales:

  • Proporciona una “señal de parada” por dataset que no se expande solo porque exista un dataset hijo.
  • No evita que el pool se llene por snapshots retenidos en otro lugar u otros datasets. No es una salvaguarda a nivel de pool; es una herramienta de equidad.

Lo que refquota no resuelve

refquota no te salvará de:

  • Fragmentación del pool y espacio de trabajo (slop). ZFS necesita espacio de trabajo. Si mantienes pools al límite experimentarás colapso de rendimiento y patrones extraños de ENOSPC sin importar las cuotas.
  • Explosiones de retención de snapshots. Un dataset puede mantenerse por debajo de su refquota mientras snapshots antiguos mantienen enormes cantidades de espacio referenciado a nivel de pool. El dataset no puede “ver” esa factura como su propio uso referenciado si los bloques están atribuidos en otra parte.
  • Objetivos de replicación sin política consistente. Un stream send/receive puede recrear snapshots y holds que hagan que el uso parezca diferente que en el origen si no tienes cuidado con la retención y los tags de holds.

Segunda broma, porque la necesitarás cuando la gráfica se vaya vertical: un refquota es como un limitador de velocidad en un camión—no te impedirá conducir hacia un río, pero sí te impedirá culpar al motor después.

Por qué solo quota conduce a “mentiras” del espacio usado

Con snapshots y clones, “quién usó el espacio” se convierte en una cuestión de propiedad de bloques. quota estándar es basada en subárbol y interactúa con descendientes. Si tu estructura de tenants es “un dataset padre con muchos hijos”, una cuota en el padre puede ser correcta pero operacionalmente inútil para facturación y control del radio de impacto.

Este es el patrón que pega:

  • Pones quota en tank/tenants.
  • Cada tenant es un dataset hijo tank/tenants/acme, tank/tenants/zephyr, etc.
  • Se toman snapshots en el padre o se replican de manera que cruzan expectativas.
  • Un tenant churnea datos; los snapshots retienen bloques antiguos.
  • El pool se llena, pero el dataset del tenant aparece “pequeño” dependiendo de qué métrica mires.

refquota en cada dataset de tenant cambia la conversación de “el espacio es complicado” a “has alcanzado tu límite; limpia o compra más”.

Tres micro-historias del mundo corporativo

1) Incidente causado por una suposición incorrecta: “Borrar archivos libera espacio”

La situación era familiar: un pool ZFS compartido soportando una flota de runners de CI y algunos caches de build efímeros. Cada equipo tenía un dataset, y el equipo de plataforma puso quota en un dataset padre. Se tomaban snapshots cada hora porque alguien una vez perdió un cache y declaró la guerra a la pérdida de datos.

El incidente comenzó como una alerta pequeña: espacio libre del pool descendiendo más rápido de lo habitual. Luego fue una alerta real: latencias de escritura disparadas, colas acumulándose, trabajos aleatorios fallando con ENOSPC. Cuando el canal de incidentes se encendió, la primera respuesta fue la clásica: “Simplemente borrad archivos viejos de los caches.” Los equipos cumplieron. Borrarón decenas o centenas de gigabytes. El pool no se movió.

¿Por qué? Snapshots. Los snapshots retenían los bloques antiguos, así que borrar los archivos en vivo no los liberó. Pero el error operacional mayor fue asumir que las cuotas del dataset impedirían que un solo equipo causara impacto amplio. No lo hicieron, porque la cuota estaba en el padre y la contabilidad que estaban mirando era USED de zfs list, no una vista consciente de snapshots.

La solución no fue heroica. Fue aburrida y correcta: refquota por equipo, más una política de retención que encajara con el caso de uso real (snapshots horarios por 24 horas, diarios por una semana, no “horarios para siempre”). Después de eso, cuando un equipo golpeaba el límite, era su muro. El pool dejó de ser una tragedia compartida.

2) Optimización que salió mal: “Clones para ahorrar espacio”

Un equipo de virtualización quería aprovisionamiento más rápido para VMs de desarrollo. Tenían imágenes base. Descubrieron el cloning de ZFS y decidieron usarlo por doquier: crear un snapshot de la plantilla, clonarlo para cada VM nueva y disfrutar de creación instantánea y bloques compartidos.

Durante un tiempo fue hermoso. El crecimiento de almacenamiento se ralentizó, los tiempos de despliegue mejoraron y el dashboard parecía sano. Entonces llegó el día de parches. Todos aplicaron actualizaciones del SO en docenas de VMs y de repente las escrituras explotaron. La divergencia de la imagen base hizo que cada VM empezara a asignar sus propios bloques. Eso es normal, pero la sorpresa vino del lado humano: los equipos asumieron “la plantilla es compartida así que es básicamente gratis” y nadie puso refquotas por VM.

El pool alcanzó un umbral donde el rendimiento se degradó. No solo “más lento”, sino “las operaciones de metadatos se sienten como si las hiciera una paloma mensajera”. El equipo intentó mitigar borrando VMs, pero algunos de los bloques pesados todavía estaban referenciados debido a clones y relaciones de snapshots. El espacio no volvió donde esperaban. Estaban mirando las métricas equivocadas.

La recuperación implicó dos cambios de política: (1) poner cada dataset de VM bajo una refquota alineada a su tamaño previsto, y (2) tratar los clones como una herramienta de aprovisionamiento, no como una estrategia de ciclo de vida—promover clones cuando sea necesario y no mantener una cadena de dependencias compleja a través de meses de snapshots. La optimización no estaba mal; solo le faltaban guardarraíles.

3) Práctica aburrida pero correcta que salvó el día: “Presupuestos de espacio + auditorías semanales”

Otro equipo operaba un appliance ZFS multi-tenant para equipos internos. No era llamativo: horarios de snapshot predecibles, nombrado consistente y un pequeño ritual semanal. Cada lunes por la mañana, un ingeniero pasaba 15 minutos revisando un informe corto: top datasets por usedbysnapshots, top por referenced, datasets cerca de refquota y pools por debajo del 20% libre.

Esta práctica parecía innecesaria—hasta que una integración de un proveedor empezó a volcar logs grandes y compressibles en el dataset equivocado. La compresión hacía que el tamaño lógico pareciera terrorífico, pero la asignación física se mantenía moderada… hasta que dejó de hacerlo. La rotación de logs churneó bloques diariamente y los snapshots retenían versiones antiguas. La línea de tendencia del pool comenzó a subir.

La auditoría del lunes lo detectó antes de que fuera un incidente. El equipo vio un dataset con usedbysnapshots aumentando rápidamente y un refquota acercándose. Arreglaron la ruta de ingestión, expiraron snapshots de ese dataset únicamente y aumentaron ligeramente el refquota para no romper una tubería crítica mientras desplegaban cambios.

No hubo pager nocturno. No hubo expansión de emergencia. Solo un hábito pequeño y aburrido y cuotas que significaban lo que creían. En producción, lo aburrido es una característica.

Tareas prácticas: comandos y interpretaciones

Estas son tareas que puedes ejecutar hoy. Cada una incluye qué buscar y cómo interpretar los resultados. Los ejemplos asumen un pool llamado tank.

Tarea 1: Ver la tabla de la verdad: used vs referenced

cr0x@server:~$ zfs list -o name,used,refer,usedbysnapshots,usedbychildren,usedbydataset -r tank
NAME                      USED  REFER  USEDBYSNAPSHOTS  USEDBYCHILDREN  USEDBYDATASET
tank                     3.12T   192K             0B            3.12T            192K
tank/tenants             3.12T   128K           540G            2.58T            128K
tank/tenants/acme         900G   600G           220G             80G            600G
tank/tenants/zephyr       780G   760G            10G             10G            760G

Interpretación: USED incluye snapshots y children. REFER es la “huella personal” del dataset. Si un tenant dice “solo tengo 600G”, comprueba si los snapshots están reteniendo 220G y si los hijos usan más.

Tarea 2: Revisar quotas y refquotas en un solo comando

cr0x@server:~$ zfs get -o name,property,value,source quota,refquota,reservation,refreservation tank/tenants/acme
NAME               PROPERTY        VALUE  SOURCE
tank/tenants/acme  quota           none   default
tank/tenants/acme  refquota        700G   local
tank/tenants/acme  reservation     none   default
tank/tenants/acme  refreservation  none   default

Interpretación: Si refquota está fijado y quota no, estás aplicando límites por dataset sin restringir descendientes. Eso suele ser correcto para datasets de tenants que no deben ser castigados por hijos (o que no los usan).

Tarea 3: Fijar un refquota de forma segura (y confirmarlo)

cr0x@server:~$ sudo zfs set refquota=500G tank/tenants/acme
cr0x@server:~$ zfs get -o name,property,value refquota tank/tenants/acme
NAME               PROPERTY  VALUE  SOURCE
tank/tenants/acme  refquota  500G   local

Interpretación: Las escrituras a tank/tenants/acme fallarán una vez que REFER alcance ~500G (sujeto a recordsize, metadatos, espacio de slop). Esta es la “señal de parada”.

Tarea 4: Simular “¿por qué mi borrado no liberó espacio?”

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation tank/tenants/acme
NAME                              USED  REFER  CREATION
tank/tenants/acme@hourly-2025...   24G   600G  Mon Dec 23 01:00 2025
tank/tenants/acme@hourly-2025...   30G   620G  Mon Dec 23 02:00 2025

Interpretación: USED del snapshot es cuánto espacio único ese snapshot mantiene vivo comparado con el estado actual del dataset. Si el USED del snapshot es grande, los borrados no liberarán mucho hasta que los snapshots expiren o se destruyan.

Tarea 5: Encontrar datasets cerca de su refquota

cr0x@server:~$ zfs list -o name,refer,refquota -r tank/tenants
NAME                REFER  REFQUOTA
tank/tenants         128K      none
tank/tenants/acme    498G      500G
tank/tenants/zephyr  310G      800G

Interpretación: acme está a punto de alcanzar un tope duro. Debes esperar errores de aplicación pronto (fallos de escritura), no una advertencia suave.

Tarea 6: Diagnosticar ENOSPC: verificar salud del pool y espacio libre

cr0x@server:~$ zpool list -o name,size,alloc,free,cap,health
NAME  SIZE  ALLOC  FREE  CAP  HEALTH
tank  7.25T  6.60T  660G  90%  ONLINE

Interpretación: 90% de capacidad es territorio de peligro para muchos pools, especialmente con vdevs HDD. Incluso si hay quotas, el pool puede llenarse por snapshots, reservations u otros datasets.

Tarea 7: Mostrar espacio retenido por snapshots (el impuesto silencioso)

cr0x@server:~$ zfs list -o name,usedbysnapshots -r tank/tenants | sort -h -k2
tank/tenants               540G
tank/tenants/acme          220G
tank/tenants/zephyr         10G

Interpretación: Si usedbysnapshots domina, tu “problema de espacio” es realmente un problema de retención. Refquota no deshará eso; solo impide que un tenant aumente indefinidamente su huella referenciada.

Tarea 8: Identificar holds que impiden eliminar snapshots

cr0x@server:~$ zfs holds -r tank/tenants/acme@hourly-2025-12-23-0200
NAME                               TAG          TIMESTAMP
tank/tenants/acme@hourly-2025-12-23-0200  keep         Tue Dec 23 02:10 2025

Interpretación: Un tag de hold (aquí keep) bloquea la eliminación. Si el espacio no se libera después de “hemos borrado snapshots viejos”, revisa holds antes de asumir que ZFS está embrujado.

Tarea 9: Liberar un hold y borrar el snapshot

cr0x@server:~$ sudo zfs release keep tank/tenants/acme@hourly-2025-12-23-0200
cr0x@server:~$ sudo zfs destroy tank/tenants/acme@hourly-2025-12-23-0200

Interpretación: El espacio no necesariamente volverá instantáneamente si otros snapshots/clones aún referencian los mismos bloques, pero has eliminado un ancla.

Tarea 10: Auditar relaciones de clones (espacio compartido a través de la línea)

cr0x@server:~$ zfs get -o name,property,value origin tank/vm/dev-42
NAME           PROPERTY  VALUE
tank/vm/dev-42  origin   tank/vm/template@golden-2025-12-01

Interpretación: Si un dataset es un clone, destruir el snapshot de origen puede estar bloqueado y la contabilidad puede sorprenderte. El espacio puede “ser propiedad” de una cadena de dependencias.

Tarea 11: Promover un clone para romper la dependencia del origen

cr0x@server:~$ sudo zfs promote tank/vm/dev-42
cr0x@server:~$ zfs get -o name,property,value origin tank/vm/dev-42
NAME           PROPERTY  VALUE
tank/vm/dev-42  origin   -

Interpretación: La promoción cambia la dependencia para que el clone sea independiente en la línea. Es una decisión de ciclo de vida; puede simplificar la limpieza y hacer el comportamiento futuro del espacio más predecible.

Tarea 12: Confirmar lo que un dataset liberaría (el informe defensivo)

cr0x@server:~$ zfs get -o name,property,value referenced,logicalreferenced,used,logicalused tank/tenants/acme
NAME               PROPERTY           VALUE
tank/tenants/acme  referenced         498G
tank/tenants/acme  logicalreferenced  1.02T
tank/tenants/acme  used               900G
tank/tenants/acme  logicalused        1.60T

Interpretación: La compresión (y a veces las copias) hace que los tamaños lógicos sean mayores que los físicos. Si vas a cobrar, decide si facturas por físico (referenced) o por lógico (logicalreferenced). Para planificación de capacidad importa lo físico; para “cuánta información tienes” el lógico puede coincidir con la expectativa del usuario.

Tarea 13: Ver la aplicación de la política en acción (detectar hits de refquota)

cr0x@server:~$ zfs get -o name,property,value refquota,refer tank/tenants/acme
NAME               PROPERTY  VALUE
tank/tenants/acme  refquota  500G
tank/tenants/acme  refer     498G

cr0x@server:~$ sudo -u acmeuser dd if=/dev/zero of=/tank/tenants/acme/bigfile bs=1M count=4096
dd: failed to open '/tank/tenants/acme/bigfile': Disc quota exceeded

Interpretación: El error a menudo será “Disc quota exceeded” (EDQUOT), no “No space left on device”. Eso es bueno: señala una política de dataset, no agotamiento del pool.

Tarea 14: Encontrar datasets con reservations que pueden causar ENOSPC sorpresa

cr0x@server:~$ zfs get -o name,property,value -r tank reservation,refreservation | egrep -v ' none$'
tank/prod         reservation     2T
tank/prod/db      refreservation  300G

Interpretación: Las reservations requieren espacio. Si un pool parece tener espacio libre pero las cargas fallan, las reservations son uno de los primeros sospechosos.

Guía rápida de diagnóstico

Este es el orden que encuentra el cuello de botella rápidamente en incidentes reales. No empieces debatiendo ratios de compresión. Empieza por localizar el límite de aplicación y el consumidor real.

1) Determinar el modo de fallo: EDQUOT vs ENOSPC

Revisa los logs de la aplicación y los mensajes del kernel. Si ves “Disc quota exceeded”, estás golpeando quota o refquota (o cuotas de usuario/grupo). Si ves “No space left on device”, probablemente el pool está fuera de espacio asignable, o restringido por reservations/slop.

cr0x@server:~$ dmesg | tail -n 20
... 

2) Comprobar capacidad y salud del pool primero (siempre)

cr0x@server:~$ zpool list -o name,size,alloc,free,cap,health
NAME  SIZE  ALLOC  FREE  CAP  HEALTH
tank  7.25T  6.60T  660G  90%  ONLINE

cr0x@server:~$ zpool status -x
all pools are healthy

Decisión: Si CAP es alto (comúnmente >80–85% en pools HDD, a veces menos según la carga), trátalo como un incidente de capacidad aun cuando exista “free”. ZFS necesita margen para asignación y rendimiento.

3) Identificar si los snapshots son el consumidor real

cr0x@server:~$ zfs list -o name,usedbysnapshots -r tank | sort -h -k2 | tail -n 10
tank/tenants               540G
tank/backup                1.40T

Decisión: Si los snapshots dominan, ve directo a retención, holds y comportamiento de replicación. No pierdas tiempo borrando archivos en vivo.

4) Si el error es EDQUOT, comprueba refquota/refer primero

cr0x@server:~$ zfs get -o name,property,value refquota,quota,refer,used tank/tenants/acme
NAME               PROPERTY  VALUE
tank/tenants/acme  refquota  500G
tank/tenants/acme  quota     none
tank/tenants/acme  refer     498G
tank/tenants/acme  used      900G

Decisión: Si refer está cerca de refquota, la solución es borrar/compactar datos en ese dataset, expirar snapshots que inflen bloques referenciados (raro pero posible según relaciones de clones) o aumentar intencionalmente el refquota.

5) Si el error es ENOSPC, revisa reservations a continuación

cr0x@server:~$ zfs get -o name,property,value -r tank reservation,refreservation | egrep -v ' none$'
tank/prod         reservation     2T
tank/prod/db      refreservation  300G

Decisión: Las reservations pueden consumir el espacio asignable. O reduces reservations, añades capacidad o mueves cargas—preferiblemente no durante el incidente a menos que ya estés en la fase de “elegir dolor”.

6) Encontrar los mayores consumidores por espacio referenciado

cr0x@server:~$ zfs list -o name,refer -r tank | sort -h -k2 | tail -n 15
tank/backup/job-17     1.10T
tank/tenants/acme       498G
tank/prod/db            420G

Decisión: refer te da la vista de “si borro este dataset, ¿qué recupero?”. Es una forma rápida de clasificar candidatos para limpieza.

Errores comunes, síntomas y soluciones

Error 1: Poner quota cuando querías refquota

Síntoma: Un dataset de tenant parece limitado, pero los datasets hijos aún pueden crecer de formas no previstas, o el límite se aplica a todo el subtree y causa peleas internas.

Solución: Aplica refquota a cada dataset de tenant que represente una unidad facturable. Usa quota solo cuando quieras explícitamente un tope de subárbol.

cr0x@server:~$ sudo zfs set refquota=200G tank/tenants/zephyr

Error 2: Leer USED como “lo que cuesta este dataset”

Síntoma: Borras datos de un dataset pero su USED apenas cambia. O un dataset parece enorme porque tiene hijos.

Solución: Usa refer para la huella propia del dataset; usa usedbysnapshots para ver el impuesto de snapshots; usa usedbychildren para entender crecimiento del subtree.

Error 3: No tener en cuenta la retención de snapshots

Síntoma: El pool se llena lenta pero implacablemente, aunque los datasets activos estén estables y la “limpieza” no ayude.

Solución: Inspecciona uso y retención de snapshots. Quita holds. Ajusta horarios. Considera políticas por dataset en lugar de un plan global.

Error 4: Cadenas de dependencia de clones que impiden limpieza

Síntoma: No puedes destruir un snapshot antiguo porque “el snapshot tiene clones dependientes”, o el espacio no vuelve tras borrar “la cosa grande”.

Solución: Audita relaciones origin. Promueve clones cuando sea apropiado. No mantengas clones atados a un snapshot de plantilla de hace meses a menos que disfrutes de la arqueología.

Error 5: Confundir tamaños físicos y lógicos

Síntoma: Usuarios reportan “solo tengo 300G de datos”, pero ves 800G lógicos. O finanzas quiere facturación y los números no cuadran.

Solución: Decide qué número es política: físico (referenced) para capacidad, lógico (logicalreferenced) para volumen percibido. Comunícalo. Ponlo en los runbooks.

Error 6: Abusar de las reservations

Síntoma: El pool tiene espacio libre pero escrituras fallan para algunos datasets; o una carga parece “inmune” mientras otras se quedan sin nada.

Solución: Audita reservation/refreservation. Elimina o ajusta tamaños. Usa reservations con moderación e intención.

Error 7: Esperar que refquota proteja el pool

Síntoma: Todos los tenants tienen refquota, pero el pool aún se llena y el equipo de plataforma se sorprende.

Solución: Refquota es aplicación por dataset, no un plan de capacidad del pool. Aún necesitas objetivos de headroom del pool, controles de retención de snapshots y monitorización de consumidores a nivel de pool como backups.

Listas de verificación / plan paso a paso

Checklist A: Implementar refquota para datasets multi-tenant

  1. Definir unidades facturables. Un dataset por tenant/VM/contenedor es ideal.
  2. Elegir base de cuota. Lo físico (refquota en referenced) es lo más fácil de aplicar; documéntalo.
  3. Fijar refquota en cada dataset de tenant.
  4. Opcionalmente fijar una quota en el padre como disyuntor. Usa quota en el padre para capear todo el programa.
  5. Construir política de snapshots por clase de dato. CI caches no son bases de datos; no las trates igual.
  6. Monitorizar datasets near-limit. Alertar en refer/refquota > 85–90% y en tasa de crecimiento de usedbysnapshots.
cr0x@server:~$ sudo zfs set refquota=300G tank/tenants/acme
cr0x@server:~$ sudo zfs set refquota=500G tank/tenants/zephyr
cr0x@server:~$ sudo zfs set quota=5T tank/tenants

Checklist B: Respuesta a incidentes por “el pool se está llenando”

  1. Comprobar CAP y salud del pool (zpool list, zpool status).
  2. Ordenar datasets por usedbysnapshots y refer.
  3. Verificar holds en snapshots grandes que deberían haber expirado.
  4. Verificar dependencias de clones que impiden borrar snapshots.
  5. Ajustar retención y borrar snapshots quirúrgicamente (no globalmente, salvo que ya estés en modo emergencia).
  6. Si el pool está caliente (>85–90%), planificar margen inmediato: borrar, mover o ampliar. Las cuotas no crearán espacio.

Checklist C: Higiene continua que mantiene honesto al refquota

  1. Informe semanal: top usedbysnapshots, top refer, cerca de refquota.
  2. Revisión mensual de reservations.
  3. Revisión del ciclo de vida de snapshots tras cambios de producto importantes (nuevo logging, nuevos artefactos de build, nueva replicación).
  4. Política de ciclo de vida de plantillas/clones: cuándo promover, cuándo reconstruir desde cero.

Preguntas frecuentes

1) ¿refquota se aplica sobre espacio lógico o físico?

refquota se aplica sobre el espacio referenciado tal como lo cuenta ZFS (efectivamente la asignación física después de compresión). Por eso un dataset comprimido puede almacenar más datos lógicos de lo que el refquota podría sugerir.

2) Si fijo refquota, ¿los snapshots aún pueden llenar mi pool?

Sí. Los snapshots pueden retener bloques antiguos y consumir espacio de pool. Refquota limita cuánto referencia el dataset activo, no cuánto historial retiene tu política de snapshots en todo el pool.

3) ¿Por qué un dataset muestra bajo REFER pero alto USED?

Porque USED puede incluir espacio de snapshots y datasets hijos. REFER es “lo que este dataset referencia por sí mismo”. Siempre desgloza con usedbydataset, usedbysnapshots y usedbychildren.

4) ¿Qué error verán las aplicaciones cuando se alcanza refquota?

Comúnmente Disc quota exceeded (EDQUOT). Esa es una pista de que alcanzaste un límite de dataset, no agotamiento del pool.

5) ¿Debo usar quota o refquota para directorios home?

Si cada usuario tiene su propio dataset, usa refquota. Si los usuarios son directorios dentro de un dataset, las cuotas por dataset no ayudarán; necesitarías cuotas por usuario/grupo (un mecanismo diferente) o reestructurar a datasets por usuario.

6) ¿refquota incluye descendientes?

No. Ese es el punto. refquota limita el dataset en sí, excluyendo hijos. Si quieres un tope del subtree, usa quota.

7) ¿Cómo interactúa refquota con reservations?

Una reservation garantiza disponibilidad de espacio; un refquota limita crecimiento. Puedes tener ambos: garantizar 50G con refreservation y poner un tope de 500G con refquota. El uso indebido puede crear comportamientos confusos de espacio libre, así que monitoriza ambos.

8) ¿Por qué aumentar refquota no arregló ENOSPC?

Porque ENOSPC suele ser agotamiento a nivel de pool (o restricciones por reservations), no un límite de dataset. Si el pool está muy lleno, subir un refquota solo cambia quién falla a continuación.

9) ¿Puedo confiar en refquota para chargeback?

Puedes confiar en él para aplicación y control del radio de impacto. Para facturación, decide si cobras por espacio referenciado físico, por espacio lógico o por límites provisionados. Lo más defendible operativamente es facturar sobre límites aplicados (refquota) más excepciones por sobreuso.

10) ¿Cuál es la política más sencilla que funciona?

Un dataset por tenant/carga, refquota por dataset, un objetivo conservador de headroom del pool y una política de retención de snapshots ligada a la necesidad de negocio—no a la ansiedad.

Conclusión

ZFS no miente sobre el espacio. Informa el espacio como debe hacerlo un filesystem copy-on-write con snapshots y clones: bloques y referencias, no solo archivos y carpetas. El problema es que los humanos seguimos haciendo preguntas a ZFS en el dialecto equivocado y luego nos sorprenden las respuestas.

refquota es la cuota que traduce la realidad a nivel de bloques de ZFS en un límite de política que puedes aplicar por dataset. No evitará que tu pool se llene si tu política de snapshots es temeraria, y no hará que los clones sean “gratis para siempre”. Pero detendrá la discusión operativa más común—“ese espacio no es mío”—al convertir “mío” en una huella medible y aplicable. En almacenamiento multi-tenant, eso no es solo conveniente. Es la diferencia entre una plataforma estable y un ritual semanal de culpas por espacio.

← Anterior
MySQL vs MariaDB Replicación y Failover: Qué falla en la vida real (y cómo evitarlo)
Siguiente →
Contenedores huérfanos de Docker: por qué aparecen y cómo purgarlos de forma segura

Deja un comentario