Ошибка QuotaExceededError возникает в IndexedDB в тех случаях, когда браузер не может выделить дополнительное пространство для хранения данных. Поскольку Dexie.js является надстройкой над IndexedDB, данная ошибка автоматически передаётся в приложение через механизмы обработки исключений библиотеки.
На практике QuotaExceededError появляется при:
Типичный пример:
try {
await db.files.add({
name: "video.mp4",
data: hugeBlob
});
} catch (error) {
if (error.name === "QuotaExceededError") {
console.error("Недостаточно места для сохранения");
}
}
В Dexie ошибка также может быть перехвачена через собственную систему исключений:
db.files.add(file)
.catch(Dexie.QuotaExceededError, error => {
console.error("Квота исчерпана");
});
Такой способ делает код более читаемым и позволяет обрабатывать конкретный тип ошибки отдельно от остальных.
IndexedDB не предоставляет бесконечное хранилище. Каждый браузер самостоятельно управляет доступным объёмом памяти.
Существуют несколько уровней ограничений:
Например, современные браузеры могут выделять:
Точные значения постоянно меняются между версиями браузеров и операционными системами, поэтому нельзя рассчитывать на фиксированный объём.
Главное правило проектирования базы данных в Dexie.js заключается в том, что хранилище всегда следует считать ограниченным ресурсом.
Браузеры на основе Chromium используют динамическое квотирование.
Размер доступного пространства зависит от:
Хранилища IndexedDB, Cache API и другие механизмы хранения объединяются в общую систему квот.
Firefox использует собственную систему управления локальными данными.
Особенности:
Safari традиционно отличается наиболее жёсткими ограничениями.
Особенно это заметно:
Из-за этого приложения на Dexie.js должны особенно тщательно контролировать объём сохраняемой информации.
Современные браузеры поддерживают API оценки используемого хранилища.
Для этого используется объект navigator.storage.
const estimate = await navigator.storage.estimate();
console.log("Использовано:", estimate.usage);
console.log("Квота:", estimate.quota);
Результат может выглядеть следующим образом:
{
usage: 52428800,
quota: 2147483648
}
Где:
На основе этих данных можно заранее отслеживать приближение к лимиту.
Полезной практикой является периодическая проверка процента использования квоты.
Пример:
async function getStorageUsagePercent() {
const { usage, quota } =
await navigator.storage.estimate();
return (usage / quota) * 100;
}
Использование:
const percent = await getStorageUsagePercent();
if (percent > 80) {
console.warn("Хранилище почти заполнено");
}
Такой подход позволяет начинать очистку данных ещё до появления QuotaExceededError.
Одной из наиболее частых причин возникновения ошибки становятся крупные бинарные данные.
Примеры:
Нередко приложение сохраняет объекты размером десятки мегабайт:
await db.files.add({
fileName: file.name,
blob: file
});
Несколько сотен подобных записей способны быстро исчерпать доступную квоту.
Эффективным способом экономии пространства является компрессия.
Текстовые данные хорошо поддаются сжатию:
const compressed = compress(JSON.stringify(data));
await db.cache.put({
id: 1,
payload: compressed
});
Преимущества:
Особенно заметен эффект при хранении больших JSON-структур.
Локальная база данных часто содержит временную информацию.
Примеры:
Регулярная очистка предотвращает бесконтрольный рост базы.
Пример:
await db.logs
.where("createdAt")
.below(Date.now() - 30 * 24 * 60 * 60 * 1000)
.delete();
Здесь удаляются записи старше 30 дней.
TTL (Time To Live) позволяет автоматически ограничивать срок хранения данных.
Структура записи:
{
id: 1,
value: data,
expiresAt: Date.now() + 86400000
}
Очистка:
await db.cache
.where("expiresAt")
.below(Date.now())
.delete();
Подобный механизм особенно полезен для:
Кэш без ограничений почти гарантированно приводит к переполнению хранилища.
Распространённый подход — ограничение числа записей.
Например:
const count = await db.cache.count();
if (count > 10000) {
const oldest = await db.cache
.orderBy("createdAt")
.limit(1000)
.toArray();
const ids = oldest.map(x => x.id);
await db.cache.bulkDelete(ids);
}
Таким образом объём данных остаётся контролируемым.
LRU (Least Recently Used) удаляет данные, которые использовались реже всего.
Структура записи:
{
id: 1,
value: data,
lastAccess: Date.now()
}
При чтении:
await db.cache.update(id, {
lastAccess: Date.now()
});
При очистке:
const obsolete = await db.cache
.orderBy("lastAccess")
.limit(500)
.toArray();
После этого записи удаляются.
Такой механизм широко применяется в офлайн-приложениях и системах локального кэширования.
Большие наборы данных не всегда необходимо хранить полностью.
Неэффективный подход:
await db.products.bulkPut(allProducts);
Если каталог содержит сотни тысяч товаров, база быстро разрастётся.
Более рационально сохранять:
Информация может быть классифицирована по критичности.
Хранятся максимально долго:
users
settings
documents
Могут быть удалены при необходимости:
apiCache
imagesCache
searchResults
При нехватке места сначала очищаются именно второстепенные данные.
Некоторые браузеры позволяют запросить постоянное хранилище.
Проверка:
const persistent =
await navigator.storage.persisted();
Запрос:
const granted =
await navigator.storage.persist();
Если браузер разрешит постоянное хранение, вероятность автоматического удаления данных существенно снижается.
Обработка ошибки должна сопровождаться освобождением пространства.
Пример:
try {
await db.files.add(fileData);
}
catch (error) {
if (error.name === "QuotaExceededError") {
await db.cache.clear();
await db.files.add(fileData);
}
}
Однако автоматический повтор следует использовать осторожно, чтобы избежать бесконечных циклов.
В крупных проектах удобно создать единый механизм обработки.
async function saveWithQuotaControl(action) {
try {
return await action();
}
catch (error) {
if (error.name === "QuotaExceededError") {
await cleanupStorage();
return action();
}
throw error;
}
}
Использование:
await saveWithQuotaControl(() =>
db.files.add(file)
);
Такой подход уменьшает дублирование кода.
При работе с большими базами важно понимать, какие таблицы занимают основное пространство.
Можно выполнять приблизительную оценку:
const records = await db.logs.count();
console.log(records);
Либо вычислять средний размер объектов:
const data = await db.logs.limit(100).toArray();
const avgSize =
JSON.stringify(data).length / data.length;
Подобная статистика помогает выявлять источники роста базы.
Вместо удаления информации иногда применяется архивирование.
Схема работы:
Пример:
const oldRecords =
await db.logs
.where("date")
.below(oldDate)
.toArray();
После передачи на сервер выполняется:
await db.logs
.where("date")
.below(oldDate)
.delete();
Такой подход особенно полезен для журналов активности и истории операций.
Наиболее эффективная стратегия заключается не в обработке QuotaExceededError, а в предотвращении её появления.
Основные методы:
При грамотном управлении жизненным циклом данных даже крупные приложения на Dexie.js способны годами работать без возникновения QuotaExceededError, сохраняя высокую производительность и предсказуемое потребление дискового пространства.