Потеря данных при смене драйвера

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

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


Причины потери данных при смене драйвера

Несовместимость физических хранилищ

Каждый драйвер использует независимое хранилище:

  • IndexedDB хранит данные в виде объектов внутри object store
  • localStorage хранит только строки
  • WebSQL использует SQL-таблицы

При смене драйвера localForage не выполняет автоматическую миграцию данных между этими системами. Фактически происходит переключение на другое «пространство данных», где прежние ключи могут отсутствовать.


Отсутствие автоматической миграции

При вызове:

localforage.setDriver([
  localforage.INDEXEDDB,
  localforage.LOCALSTORAGE
]);

или:

localforage.setDriver(localforage.LOCALSTORAGE);

библиотека лишь перенастраивает слой доступа. Содержимое предыдущего драйвера:

  • не копируется
  • не синхронизируется
  • не преобразуется

Это приводит к эффекту «пустого хранилища» после переключения.


Различия в сериализации данных

localForage сериализует данные в зависимости от драйвера:

  • IndexedDB может хранить объекты напрямую (через structured clone algorithm)
  • localStorage всегда требует строкового представления (JSON.stringify / JSON.parse)

Если данные были записаны в одном формате, а затем происходит смена драйвера, возможны следующие проблемы:

  • невозможность корректного парсинга
  • потеря типов (Date, Map, Set)
  • получение null или undefined при чтении

Сценарии, приводящие к потере данных

Переключение драйвера после записи данных

Типичный сценарий:

  1. Данные записаны в IndexedDB
  2. Происходит fallback на localStorage (например, в приватном режиме браузера)
  3. Приложение продолжает работу
  4. После восстановления IndexedDB происходит повторная инициализация с другим драйвером

В этом случае данные, записанные в fallback-драйвере, остаются в localStorage и не переносятся обратно.


Инкогнито-режим и ограничения IndexedDB

В некоторых браузерах приватный режим:

  • ограничивает IndexedDB
  • блокирует WebSQL
  • уменьшает квоты localStorage

localForage автоматически переключается на доступный драйвер. При выходе из инкогнито режим может измениться, но данные останутся разрозненными между хранилищами.


Асинхронная инициализация драйвера

localForage работает асинхронно. Возможна ситуация:

  • часть кода выполняется до завершения setDriver
  • часть — после переключения

Это приводит к состоянию гонки, когда:

  • запись идёт в один драйвер
  • чтение происходит из другого

Переинициализация конфигурации

Вызовы вида:

localforage.config({
  driver: localforage.INDEXEDDB
});

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

  • «потере» ранее записанных ключей
  • созданию нового namespace в другом драйвере
  • расхождению состояния приложения и хранилища

Особенности поведения fallback-цепочки

localForage поддерживает цепочку драйверов:

localforage.setDriver([
  localforage.INDEXEDDB,
  localforage.WEBSQL,
  localforage.LOCALSTORAGE
]);

При этом:

  • выбирается первый доступный драйвер
  • остальные не используются до следующей инициализации

Проблема возникает, если:

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

Несовпадение пространств ключей

Каждый драйвер имеет собственное изолированное пространство ключей. Даже при одинаковой конфигурации:

  • ключ "user" в IndexedDB не связан с "user" в localStorage
  • переключение драйвера не создаёт мост между ними

Это приводит к эффекту «исчезновения данных», хотя фактически они остаются в старом хранилище.


Потеря данных при падении IndexedDB

IndexedDB может быть недоступен в следующих случаях:

  • повреждение базы
  • превышение квоты
  • сброс браузера
  • корпоративные политики безопасности

localForage при этом автоматически переключается на fallback. Если запись происходила до падения, данные оказываются в IndexedDB, но чтение идёт из localStorage, создавая видимость потери.


Проблемы миграции между версиями приложения

При изменении логики приложения:

  • меняется структура данных
  • меняется драйвер по умолчанию
  • изменяется стратегия fallback

Старые данные могут:

  • остаться в предыдущем драйвере
  • стать недоступными из нового контекста
  • конфликтовать по ключам при ручной миграции

Механизмы, которые не предотвращают потерю данных

localForage не включает автоматические механизмы:

  • кросс-драйверной синхронизации
  • миграции схем
  • дедупликации ключей между драйверами
  • унифицированного storage layer поверх всех backend одновременно

Единственный уровень абстракции — единый API, а не единое физическое хранилище.


Стратегии минимизации потерь данных

Фиксация драйвера на этапе инициализации

После выбора стабильного драйвера переключение в runtime создаёт риск рассинхронизации состояния. Использование фиксированного драйвера снижает вероятность расхождения данных между средами выполнения.


Явная миграция данных между драйверами

При необходимости смены драйвера требуется ручной процесс:

  • чтение всех ключей из старого драйвера
  • запись их в новый драйвер
  • подтверждение целостности

localForage предоставляет методы:

  • iterate
  • keys
  • getItem
  • setItem

которые позволяют реализовать перенос данных на уровне приложения.


Проверка доступности драйвера до записи

Инициализация:

localforage.setDriver([...])

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


Изоляция окружений хранения

Разделение логики хранения по средам:

  • production
  • incognito / restricted mode
  • legacy browser

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


Поведение при частичной потере данных

При переходе на другой драйвер типичны следующие эффекты:

  • getItem(key) возвращает null
  • length() резко уменьшается
  • keys() возвращает пустой массив
  • ранее сохранённые значения остаются физически, но становятся недоступными

Это состояние часто ошибочно интерпретируется как удаление данных, хотя фактически происходит смена контекста доступа.


Внутренние причины архитектурного разрыва

Основной источник проблемы — различие в моделях хранения:

  • IndexedDB — объектная база данных
  • localStorage — синхронное key-value хранилище строк
  • WebSQL — реляционная модель

localForage не унифицирует их физически, а только логически. Поэтому смена драйвера равносильна смене базы данных без миграции схемы.


Особенности поведения setDriver в runtime

Вызов setDriver:

  • не блокирует текущие операции
  • может не отменять уже начатые транзакции
  • не гарантирует атомарность переключения

Это создаёт окно состояния, в котором часть операций выполняется в старом драйвере, а часть — в новом, что усиливает риск расхождения данных.


Итоговая модель риска

Потеря данных при смене драйвера возникает из комбинации факторов:

  • изоляция физических хранилищ
  • отсутствие автоматической миграции
  • асинхронность переключения
  • различие форматов сериализации
  • fallback-поведение браузера
  • runtime-конфигурация драйвера

Эта совокупность формирует состояние, при котором данные не уничтожаются, но становятся недоступными в новом контексте доступа.