Удаление устаревших ключей и хранилищ

В основе очистки данных в localForage лежит операция удаления конкретного ключа. Метод removeItem работает асинхронно и одинаково в разных драйверах (IndexedDB, WebSQL, localStorage), что делает его универсальным механизмом для устранения устаревших записей.

await localforage.removeItem('user:session');

Ключевая особенность удаления в localForage — отсутствие блокировки хранилища. Операция не гарантирует мгновенного физического удаления данных из внутреннего движка (например, IndexedDB может выполнять удаление транзакционно и отложенно), но для логики приложения запись считается исчезнувшей сразу после завершения промиса.

При удалении данных важно учитывать:

  • отсутствие синхронного состояния после вызова;
  • возможные ошибки драйвера (например, при повреждённой базе);
  • необходимость обработки результата через catch.
localforage.removeItem('cache:temp')
  .then(() => {
    // ключ удалён
  })
  .catch(err => {
    // ошибка удаления
  });

Массовая очистка устаревших данных

Метод clear полностью очищает текущее хранилище, удаляя все ключи, относящиеся к выбранному экземпляру localForage.

await localforage.clear();

Такой подход применяется при:

  • сбросе пользовательского состояния;
  • смене версии схемы данных;
  • полной инвалидизации кэша.

Особенность заключается в том, что clear действует только в рамках текущей конфигурации storeName и driver. При наличии нескольких логических хранилищ (через разные storeName) очистка одного экземпляра не затрагивает остальные.

const cacheStore = localforage.createInstance({
  name: 'app',
  storeName: 'cache'
});

await cacheStore.clear();

Удаление целых хранилищ через dropInstance

Когда требуется удалить не только данные, но и сам контейнер хранения, используется dropInstance. Этот метод работает на уровне IndexedDB/WebSQL и позволяет уничтожать целые базы или object store.

await localforage.dropInstance({
  name: 'app',
  storeName: 'cache'
});

Этот механизм важен при:

  • полной миграции на новую схему хранения;
  • удалении устаревших пространств данных;
  • очистке после деинсталляции функциональных модулей приложения.

Поведение dropInstance зависит от драйвера:

  • IndexedDB: удаляется object store или база;
  • WebSQL: удаляется таблица;
  • localStorage: выполняется массовое удаление ключей по префиксу.

Стратегии удаления устаревших ключей

TTL (Time-To-Live)

Одним из наиболее распространённых подходов является добавление метаданных времени жизни к каждому значению. Вместо хранения «голого» объекта сохраняется структура:

const record = {
  value: data,
  expires: Date.now() + 1000 * 60 * 60 // 1 час
};

await localforage.setItem('cache:item1', record);

При чтении выполняется проверка:

const item = await localforage.getItem('cache:item1');

if (item && item.expires < Date.now()) {
  await localforage.removeItem('cache:item1');
}

Такой подход превращает хранилище в самоподчищающуюся систему, но требует дисциплины чтения данных через единый слой доступа.


Ленивая очистка (lazy cleanup)

Удаление происходит только при обращении к ключу. Это снижает нагрузку на хранилище и избегает массовых операций.

Алгоритм:

  1. Получение значения.
  2. Проверка актуальности.
  3. Удаление при необходимости.
  4. Возврат null.
async function getWithTTL(key) {
  const item = await localforage.getItem(key);

  if (!item) return null;

  if (item.expires && item.expires < Date.now()) {
    await localforage.removeItem(key);
    return null;
  }

  return item.value;
}

Фоновая очистка (garbage collection)

При большом объёме данных применяется периодический обход всех ключей через iterate или keys.

await localforage.iterate(async (value, key) => {
  if (value.expires && value.expires < Date.now()) {
    await localforage.removeItem(key);
  }
});

Особенность подхода заключается в стоимости операции: обход всего пространства хранения может быть дорогим в IndexedDB при больших объёмах.


Работа с набором ключей

Метод keys позволяет получить список всех ключей текущего хранилища. Это основа для построения механизмов анализа устаревших данных.

const keys = await localforage.keys();

Использование:

  • поиск префиксов;
  • выборочная очистка;
  • аудит состояния хранилища.

Пример фильтрации по пространству имён:

const keys = await localforage.keys();

const staleKeys = keys.filter(k => k.startsWith('temp:'));

await Promise.all(
  staleKeys.map(k => localforage.removeItem(k))
);

Пространства имён и префиксное удаление

localForage не навязывает строгую схему имён ключей, поэтому управление устаревшими данными часто строится через префиксы.

Типичная схема:

  • user:* — пользовательские данные
  • cache:* — временный кэш
  • session:* — сессии

Удаление устаревшего сегмента:

const keys = await localforage.keys();

for (const key of keys) {
  if (key.startsWith('cache:')) {
    await localforage.removeItem(key);
  }
}

Такая стратегия особенно полезна при отсутствии поддержки отдельных storeName для каждой подсистемы.


Устаревшие данные при миграции схемы

При изменении структуры хранения часто возникают несовместимые ключи. Проблема решается через версионирование схемы.

Пример хранения версии:

await localforage.setItem('schema:version', 2);

При старте приложения:

const version = await localforage.getItem('schema:version');

if (version === 1) {
  await migrateFromV1();
}

Внутри миграции выполняется очистка старых ключей:

async function migrateFromV1() {
  const keys = await localforage.keys();

  const oldKeys = keys.filter(k => k.startsWith('old_schema:'));

  await Promise.all(oldKeys.map(k => localforage.removeItem(k)));

  await localforage.setItem('schema:version', 2);
}

Такой подход предотвращает накопление «мёртвых» структур после обновлений.


Очистка через изоляцию экземпляров

localForage поддерживает создание независимых экземпляров через createInstance. Это позволяет логически разделять данные и удалять их без влияния на другие подсистемы.

const logsStore = localforage.createInstance({
  name: 'app',
  storeName: 'logs'
});

await logsStore.removeItem('log:123');

Полная очистка такого сегмента:

await logsStore.clear();

Удаление экземпляра как сущности:

await localforage.dropInstance({
  name: 'app',
  storeName: 'logs'
});

Удаление при переполнении (eviction policy)

При ограниченном объёме хранения применяется стратегия принудительного удаления устаревших или наименее используемых данных.

Простейшая реализация LRU-подобного механизма:

await localforage.setItem(key, {
  value,
  lastAccess: Date.now()
});

Очистка старых записей:

const keys = await localforage.keys();

const items = await Promise.all(
  keys.map(async key => {
    const val = await localforage.getItem(key);
    return { key, lastAccess: val?.lastAccess || 0 };
  })
);

items.sort((a, b) => a.lastAccess - b.lastAccess);

const toRemove = items.slice(0, 10);

await Promise.all(
  toRemove.map(i => localforage.removeItem(i.key))
);

Обработка ошибок при удалении

Операции удаления могут завершаться неудачей по причинам:

  • переполнения транзакции IndexedDB;
  • блокировки базы другим контекстом;
  • повреждения хранилища;
  • ограничений приватного режима браузера.

Рекомендуется централизованная обработка:

async function safeRemove(key) {
  try {
    await localforage.removeItem(key);
  } catch (e) {
    // логирование или повторная попытка
  }
}

При массовом удалении важно учитывать частичное выполнение операций и не полагаться на атомарность групповых Promise.all.


Очистка при закрытии сессии

Сессионные данные часто требуют удаления при завершении работы приложения. Используется комбинация session:* ключей и обработчиков жизненного цикла.

window.addEventListener('beforeunload', async () => {
  const keys = await localforage.keys();

  await Promise.all(
    keys
      .filter(k => k.startsWith('session:'))
      .map(k => localforage.removeItem(k))
  );
});

В браузерах с ограничениями на асинхронные операции при выгрузке страницы такая логика может выполняться частично, поэтому критичные данные лучше очищать заранее, а не в момент закрытия.


Очистка как часть жизненного цикла данных

Удаление устаревших ключей в localForage становится частью общей архитектуры управления состоянием. Оно связывает:

  • версионирование схем;
  • TTL-механизмы;
  • сегментацию хранилищ;
  • стратегию кэширования;
  • контроль переполнения.

Каждый подход к удалению данных формирует отдельный слой ответственности, а устойчивость системы определяется тем, насколько согласованно эти слои взаимодействуют между собой.