Как localForage открывает и версионирует базу данных

Инициализация хранилища в localForage начинается не с непосредственного открытия базы данных, а с построения цепочки выбора драйвера, конфигурации экземпляра и подготовки параметров будущего соединения. Важная особенность архитектуры заключается в том, что момент «открытия базы» откладывается до первого обращения к API, что позволяет минимизировать накладные расходы при создании экземпляра.

Каждый экземпляр localForage связан с набором параметров, определяющих контекст хранения:

  • имя базы данных (name)
  • имя хранилища (storeName)
  • версия API-драйвера
  • список доступных драйверов
  • предпочтительный драйвер

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

Ключевой момент заключается в том, что разные экземпляры localForage изолируются через комбинацию name + storeName. Это позволяет им существовать параллельно без конфликтов ключей, даже если они используют один и тот же драйвер.

Механизм выбора драйвера

Перед открытием базы выполняется последовательный перебор доступных драйверов. Порядок определяется конфигурацией driver:

  1. IndexedDB (приоритет по умолчанию в современных браузерах)
  2. WebSQL (устаревший, но поддерживаемый в некоторых окружениях)
  3. localStorage (fallback-решение)

Каждый драйвер обязан реализовывать метод проверки совместимости _support. Он выполняется синхронно или асинхронно и возвращает подтверждение возможности использования хранилища в текущей среде.

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

Ленивое открытие базы данных

Фактическое открытие происходит при первом вызове операций getItem, setItem или аналогичных методов. В этот момент запускается процедура инициализации драйвера.

Общая последовательность:

  1. Проверка наличия активного драйвера
  2. Если драйвер не инициализирован — запуск _initStorage
  3. Создание внутреннего соединения
  4. Кэширование ссылки на открытое хранилище

После завершения инициализации последующие вызовы используют уже существующее соединение, что исключает повторные затраты на открытие базы.

Открытие IndexedDB и версия базы

Наиболее сложный процесс происходит при использовании IndexedDB-драйвера. Здесь localForage опирается на стандартный механизм indexedDB.open, где версия базы играет ключевую роль.

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

  • формирование имени базы: комбинация config.name
  • установка версии базы (внутренний числовой инкремент)
  • вызов indexedDB.open(name, version)

Если версия базы, указанная в запросе, выше существующей, браузер инициирует событие onupgradeneeded. Именно в этом обработчике происходит создание object store.

Структура upgrade-процесса:

  • проверка наличия store с именем config.storeName
  • создание object store при отсутствии
  • настройка ключевого пути (обычно keyPath: 'key' или аналогичная структура)
  • подготовка индексов (если предусмотрены драйвером)

После завершения upgrade соединение считается готовым к работе и передается в кэш драйвера.

Версионирование и его ограничения

localForage не реализует сложную доменную систему миграций поверх IndexedDB. Версионирование ограничивается уровнем:

  • версии IndexedDB-базы
  • логики драйвера
  • имени хранилища

Изменение структуры данных не сопровождается автоматическими миграциями пользовательских данных. Повышение версии базы влияет только на возможность изменения схемы object store в onupgradeneeded.

Это означает, что:

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

Поведение WebSQL-драйвера

В случае WebSQL открытие базы происходит через openDatabase, где также присутствует версия. Однако механика обновления отличается:

  • версия передается как строковый или числовой параметр
  • отсутствует событие аналогичное onupgradeneeded
  • структура таблиц создается через SQL-инициализацию

localForage использует упрощённый слой абстракции, где таблица создается при первом обращении, если она отсутствует. Версионирование носит декларативный характер и не управляет миграциями.

localStorage и псевдоверсионирование

localStorage не предоставляет встроенной модели баз данных, поэтому версия реализуется косвенно. В localForage это выражается через:

  • префикс ключей (name + storeName)
  • отсутствие структурной схемы
  • отсутствие upgrade-процедуры

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

Инициализация и очередь операций

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

  • помещается в очередь
  • ожидает готовности драйвера
  • выполняется последовательно после открытия соединения

Это предотвращает гонки состояний, особенно в IndexedDB, где операции могут быть отклонены при отсутствии активной транзакции.

Кэширование соединения

После успешного открытия драйвера создается кэш:

  • соединение IndexedDB хранится в памяти экземпляра
  • повторные вызовы не инициируют open
  • операции используют уже открытые транзакции или создают новые поверх соединения

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

Изоляция и независимость экземпляров

Каждый экземпляр localForage имеет собственный контекст:

  • отдельное имя базы
  • отдельный store
  • отдельный драйверный слой

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

Ошибки открытия и деградация

Если ни один драйвер не проходит проверку _support, процесс инициализации завершается ошибкой. Типичные причины:

  • отключенный IndexedDB в приватном режиме
  • ограничения корпоративной политики браузера
  • отсутствие localStorage

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

Поведение при повторной инициализации

Если драйвер уже открыт, повторный вызов инициализации игнорируется. Однако изменение конфигурации после первого обращения не влияет на текущее соединение. Для применения новых параметров требуется создание нового экземпляра.

Версионная логика при этом не пересчитывается, так как она фиксируется в момент первого open.