wandres.dev
CUOTAS Y DESALOJO · cuando el navegador te borra

Estimar y vigilar: cuánto ocupas y cuánto te queda

Consultar cuánto ocupas y cuánto te queda es posible y conveniente, pero el número que obtienes es una estimación deliberadamente imprecisa que sirve para avisar a tiempo y nunca como garantía de que la próxima escritura quepa.

⏱ 16 min

Si el presupuesto es dinámico y el desalojo depende de decisiones ajenas, queda al menos una pregunta que sí puedes formular en tiempo de ejecución: cuánto estoy ocupando y cuánto me queda. La plataforma responde, y la respuesta es útil, pero está construida con una honestidad que conviene entender antes de apoyarse en ella. Los dos números que recibes no son una lectura del disco: son una estimación redondeada a propósito, calculada con criterios que incluyen consideraciones de privacidad, y ninguno de los dos constituye una promesa sobre si la siguiente escritura cabrá. La disciplina de esta lección consiste en extraer todo el valor posible de un número aproximado sin caer nunca en la tentación de tratarlo como exacto.

🎯 Al terminar esta lección sabrás
  • Usar la estimación del StorageManager para obtener uso y cuota, y componer con ellos una razón de ocupación.
  • Entender por qué esos valores son aproximados y qué clase de errores comete quien los toma al pie de la letra.
  • Convertir el número en un aviso al usuario con umbrales propios, elegidos por criterio y no copiados del navegador.
  • Establecer una cadencia de vigilancia razonable y combinarla con el manejo del fallo real de escritura.

La estimación y lo que devuelve

El punto de entrada es una única llamada asíncrona sobre navigator.storage. Devuelve un objeto con dos campos relevantes: el espacio que el origen ocupa actualmente y el que el navegador estima que tiene disponible en total. La resta de ambos es lo que suele interesarte, aunque la magnitud verdaderamente informativa no es la diferencia absoluta sino la proporción, porque un mismo margen libre significa cosas muy distintas según el tamaño del presupuesto.

async function medirAlmacenamiento() {
  if (!navigator.storage || !navigator.storage.estimate) return null;

  const { usage, quota } = await navigator.storage.estimate();
  if (!quota) return null; // sin cuota estimada no hay razon que calcular

  return {
    usadoMB: Math.round(usage / 1_048_576),
    totalMB: Math.round(quota / 1_048_576),
    razon: usage / quota, // entre 0 y 1: la magnitud realmente util
  };
}

Conviene también contemplar el caso de que la llamada no pueda responder. Hay entornos donde el StorageManager no existe y situaciones donde la cuota estimada llega vacía, y en ninguno de los dos tiene sentido inventarse un valor de reserva. La ausencia de estimación no es un cero ni un infinito: es la ausencia de información, y la política correcta ante ella es la que ya tenías —escribir, capturar el fallo y degradar— sin ningún aviso preventivo que estaría basado en un número imaginario.

Recuerda que ambos números se refieren al origen completo y no a la API que estés usando. El uso que te devuelven suma tus bases de datos de IndexedDB, tus entradas de Cache Storage, tus ficheros de OPFS y todo lo demás que el origen haya escrito, incluida la sobrecarga interna del propio motor de almacenamiento. Algunos navegadores ofrecen además un desglose por API, pero no forma parte del estándar y no conviene construir lógica sobre él: sirve para diagnosticar durante el desarrollo, no para decidir en producción.

La llamada está disponible tanto en la ventana como en un worker, lo cual importa más de lo que parece: si tu aplicación ya hace el trabajo pesado de almacenamiento fuera del hilo principal, la vigilancia puede vivir en el mismo sitio y comunicar sus conclusiones por mensajes, sin añadir ni una operación asíncrona a la ruta que el usuario percibe. Es la ubicación natural para una tarea que no tiene ninguna urgencia de interfaz.

Por qué es una estimación, y no una medición

Y conviene interiorizar que ninguna de las dos cifras es tuya en el sentido en que lo sería un contador que tú llevaras. Ambas las produce el navegador con sus criterios, para todos los orígenes por igual y sin obligación de explicarse. La palabra del nombre de la API es literal, y hay al menos tres razones acumuladas para ello. La primera es de privacidad: un valor exacto del espacio libre en disco es una señal muy discriminante para identificar un dispositivo, de modo que los navegadores lo redondean, lo agrupan en escalones o le añaden ruido deliberado. La segunda es de contabilidad: lo que ocupa un registro en disco no es el tamaño de sus datos, sino ese tamaño más índices, metadatos, páginas parcialmente llenas y espacio pendiente de compactar. Y la tercera es de temporalidad: la cuota depende del espacio libre del sistema, así que el valor que leíste hace un minuto describe un mundo que ya cambió.

flowchart LR
E[estimate del StorageManager] --> U[uso aproximado del origen]
E --> Q[cuota aproximada del origen]
U --> R[razon de ocupacion]
Q --> R
R --> A[umbral de aviso decidido por ti]
A --> M[mensaje al usuario y purga de lo prescindible]
style E fill:#89b4fa,color:#11111b
style R fill:#cba6f7,color:#11111b
style M fill:#a6e3a1,color:#11111b

Una manifestación concreta de la segunda razón desconcierta a mucha gente la primera vez que la observa: borras un volumen apreciable de registros, vuelves a consultar la estimación y el uso apenas ha bajado. No es un fallo de la API ni de tu código. Los motores de almacenamiento marcan el espacio como libre pero no lo devuelven al sistema de inmediato, sino cuando compactan, y ese momento lo eligen ellos. La conclusión práctica es que no debes encadenar un borrado con una medición inmediata para verificar que ha surtido efecto, ni mucho menos condicionar la siguiente decisión a que el número haya cambiado.

De estas tres razones se sigue la regla que gobierna todo uso legítimo de la API: la estimación sirve para detectar tendencias y proximidades, no para autorizar operaciones concretas. Preguntar si quedan cien megas libres antes de escribir un fichero de noventa es un uso incorrecto, porque el margen de error puede ser mayor que la diferencia y porque entre la consulta y la escritura el sistema ha podido cambiar. Preguntar si llevas ocupado el 90% del presupuesto para avisar al usuario y empezar a purgar caché es un uso correcto, porque una imprecisión de unos pocos puntos porcentuales no altera la conclusión.

⚠️
No conviertas la estimación en una comprobación previa

El antipatrón más extendido consiste en envolver cada escritura en una consulta de espacio y abortar preventivamente si el número parece justo. Falla en las dos direcciones a la vez: rechaza escrituras que habrían cabido perfectamente, porque la estimación es conservadora, y deja pasar otras que fallarán igualmente, porque nada garantiza que el estado no cambie entre la consulta y la transacción. Además convierte cada escritura en dos operaciones asíncronas donde antes había una. La verificación real de si algo cabe no es una consulta: es el intento, y su respuesta es el QuotaExceededError que debes capturar.

Hay un uso más de la estimación que rara vez se menciona y que rinde mucho durante el desarrollo: como detector de fugas. Si registras la razón de ocupación al arrancar y al cerrar cada sesión de trabajo, una tendencia monótonamente creciente en un uso que debería ser estacionario delata que algo acumula sin liberar —una caché que nunca caduca, un histórico que jamás se poda, una tabla de eventos que crece con cada acción—. Ahí la imprecisión del valor no molesta en absoluto, porque lo que se observa es la pendiente y no el punto.

Del número al aviso

Antes de redactar ningún mensaje conviene recordar a quién va dirigido. El usuario no tiene un modelo mental del almacenamiento del navegador, no distingue entre su disco y el presupuesto de tu origen, y desde luego no sabe qué es una cuota. Traducir el número a su mundo —lo que ocupa, lo que le queda, lo que puede hacer— es la mitad del trabajo, y omitir esa traducción convierte un aviso oportuno en una alarma incomprensible que solo genera ansiedad.

La utilidad genuina de la estimación es dar al usuario tiempo de reaccionar antes de que el problema se vuelva agudo. Para eso necesitas umbrales, y aquí conviene una precisión que evita mucha confusión: esos umbrales son decisiones tuyas de diseño, no cifras que el navegador publique ni políticas de la plataforma. Escalonarlos en dos o tres niveles suele bastar, y el escalonamiento importa más que los valores concretos: un aviso informativo cuando la ocupación empieza a ser alta, una acción de limpieza automática de lo prescindible cuando sube más, y un mensaje inequívoco cuando el margen es crítico.

const UMBRAL_AVISO = 0.8;    // decision de diseno propia, no una cifra del navegador
const UMBRAL_CRITICO = 0.95;

async function vigilar() {
  const m = await medirAlmacenamiento();
  if (!m) return;

  if (m.razon >= UMBRAL_CRITICO) {
    await purgarCachesPrescindibles();
    avisar("Queda muy poco espacio. Exporta o sincroniza tus datos ahora.");
  } else if (m.razon >= UMBRAL_AVISO) {
    avisar(`Estas usando ${m.usadoMB} MB de unos ${m.totalMB} MB disponibles.`);
  }
}

Una advertencia sobre los valores concretos del ejemplo anterior: son ilustrativos y no deben copiarse sin pensar. El umbral adecuado depende del tamaño típico de tus escrituras, porque lo que un aviso debe garantizar es que quede margen para varias operaciones más antes del agotamiento. Una aplicación que guarda notas de texto puede permitirse apurar mucho; una que importa adjuntos de decenas de megabytes necesita avisar bastante antes, porque entre el aviso y el problema puede caber una sola acción del usuario.

El contenido del aviso es tan importante como el momento. Un mensaje que diga que el almacenamiento está al 92% no le sirve de nada a quien lo lee: no describe una consecuencia ni ofrece una acción. Un mensaje útil nombra lo que está en juego con las palabras del usuario y propone exactamente un movimiento —exportar, sincronizar, borrar documentos antiguos, liberar espacio en el dispositivo—, preferiblemente con el botón que lo ejecuta al lado. La estimación es el disparador; el diseño de la comunicación es lo que convierte el número en algo aprovechable.

Hay también una economía del aviso que conviene respetar. Un mensaje que reaparece en cada sesión sin que la situación haya cambiado deja de leerse a las dos veces, y a partir de la tercera enseña al usuario a descartar sin mirar precisamente el tipo de aviso que un día será importante. Registra que ya avisaste en cada nivel de umbral, no vuelvas a hacerlo hasta que el nivel cambie, y reserva la interrupción modal para el escalón crítico. En los demás, una marca discreta en la interfaz que el usuario pueda consultar cuando quiera comunica lo mismo sin gastar atención.

ℹ️
Purgar lo prescindible es tu palanca más eficaz

Antes de pedirle nada al usuario, mira si puedes resolverlo tú. La mayoría de los orígenes acumulan material que no es dato del usuario sino conveniencia: versiones antiguas de la caché de recursos, miniaturas regenerables, respuestas de red que podrías volver a pedir, resultados intermedios de un cálculo. Ese material es exactamente lo que debe desaparecer primero, y borrarlo no requiere permiso ni disculpas porque su pérdida no es pérdida. Un origen que sabe distinguir su caché de sus datos tiene una palanca de recuperación que se activa sola; uno que lo mezcla todo en la misma base de datos solo puede pedir ayuda.

Vale la pena además registrar la razón de ocupación en tu telemetría, si la tienes, agregada y sin identificar a nadie. La distribución de ese valor entre tus usuarios reales es la única información honesta sobre si tu aplicación cabe de verdad en el mundo, y suele desmentir las suposiciones del equipo: el percentil que importa no es el de la máquina de desarrollo, sino el de quienes conviven con el disco al límite y son los primeros en encontrarse el fallo.

Una cadencia razonable

Queda decidir cada cuánto mirar, y la respuesta empieza por descartar los dos extremos. Mirar continuamente añade coste y no aporta información, porque el valor no cambia entre dos escrituras pequeñas. Mirar una sola vez y confiar en el resultado durante horas ignora que la cuota puede reducirse por causas ajenas a tu aplicación. Entre ambos vicios hay un punto cómodo que se define por eventos y no por relojes. Consultar la estimación es una operación asíncrona con un coste no trivial, así que no tiene sentido llamarla en cada escritura ni con un temporizador de alta frecuencia. Una cadencia sensata combina tres disparadores complementarios: una medición al arrancar, para conocer el terreno; otra tras operaciones que muevan volumen de forma apreciable, como una importación o una descarga de datos para trabajar sin conexión; y otra de fondo, muy espaciada, que capture la deriva lenta.

// Tres disparadores, ninguna consulta en la ruta caliente
window.addEventListener("load", vigilar);
emisor.on("importacion:terminada", vigilar);
emisor.on("descarga:terminada", vigilar);
setInterval(vigilar, 30 * 60 * 1000); // deriva lenta: media hora basta

Observa lo que no aparece en esa lista: ninguna llamada dentro del bucle de escritura. La vigilancia es una tarea de fondo cuyo propósito es anticiparse, y anticiparse no requiere frecuencia sino oportunidad. Consultar tras los eventos que de verdad mueven volumen captura casi toda la información relevante a una fracción del coste de mirar constantemente.

Y una advertencia sobre el temporizador de fondo: apágalo cuando la pestaña no esté visible. Una aplicación que sigue consultando el almacenamiento en segundo plano durante horas gasta batería sin ganar nada, porque nadie va a leer el aviso que produzca. Reanudarlo al volver a primer plano, con una medición inmediata, da el mismo resultado a coste cero.

🚀

Al arrancar

Una sola medición al inicio sitúa la sesión y permite decidir de entrada si conviene purgar o advertir antes de que el usuario empiece a trabajar.

📦

Tras mover volumen

Importaciones, descargas masivas y adjuntos son los eventos que de verdad cambian la razón de ocupación. Mide después de ellos, no antes.

🐢

De fondo y espaciada

Un intervalo largo basta para detectar la deriva lenta. La cuota puede bajar sin que escribas, y esa bajada solo se ve mirando de vez en cuando.

🧯

Y siempre, capturar el fallo

La vigilancia reduce la probabilidad del QuotaExceededError, no lo elimina. El manejo de la escritura fallida sigue siendo obligatorio.

Un número aproximado bien usado vale más que uno exacto mal interpretado

Hay una lección de ingeniería aquí que trasciende el almacenamiento del navegador y que merece la pena extraer con cuidado, porque es la clase de idea que separa a quien programa contra una API de quien programa contra un sistema. La reacción habitual ante un valor deliberadamente impreciso es la decepción, seguida del intento de recuperar la precisión perdida: escribir un contador propio de bytes, cotejar tamaños serializados, construir un modelo paralelo del consumo que crees más fiable que el del navegador. Es tiempo perdido y, peor, es una fuente de errores nueva, porque tu contador ignora los índices, la sobrecarga del motor y el espacio pendiente de compactar, y diverge del real en una dirección que no puedes acotar. La actitud productiva es la contraria: preguntarse qué decisiones puede sostener honestamente un número con ese margen de error, y tomar solo esas. Un valor con una incertidumbre de unos pocos puntos porcentuales no puede autorizar una escritura concreta, pero sí puede disparar un aviso, decidir una purga o modular la agresividad con que sincronizas, porque ninguna de esas tres conclusiones cambia si el número se equivoca dentro de su margen. Esa es exactamente la operación intelectual que el software local-first exige una y otra vez en cada uno de sus frentes: trabajar con información parcial, sin un árbitro que dictamine la verdad, y construir decisiones cuya corrección no dependa de una precisión que nadie puede darte. La estimación de almacenamiento es el primer sitio del track donde ese hábito se practica en pequeño, y no será el último.

⚔️ Construye tu vigilancia de almacenamiento
  1. Implementa la función que consulta la estimación y devuelve uso, total y razón, contemplando el caso de que el navegador no ofrezca la API.
  2. Mide tu aplicación en dos máquinas distintas y compara las cuotas obtenidas: comprueba con datos que el número no es una constante.
  3. Elige tus umbrales y justifícalos por escrito en función de lo que tu aplicación guarda, sin copiar cifras de ningún navegador.
  4. Escribe los mensajes de aviso en lenguaje de usuario, cada uno con exactamente una acción concreta al lado.
  5. Separa en tu almacenamiento lo prescindible de lo irreemplazable e implementa la purga automática del primer grupo antes de molestar a nadie.
  6. Comprueba que, aun con toda esta vigilancia activa, cada escritura significativa sigue capturando el QuotaExceededError.