Хранение версии схемы в idb-keyval

idb-keyval — это минималистичная библиотека для работы с IndexedDB в браузере через простой интерфейс ключ–значение. Она абстрагирует сложность нативного API IndexedDB, предоставляя методы get, set, del, clear и keys. Одним из ключевых аспектов при использовании IndexedDB в приложениях является управление версией схемы базы данных. Даже в упрощённой модели idb-keyval хранение версии схемы позволяет безопасно обновлять данные и контролировать совместимость.


Структура хранения версии схемы

Версия схемы обычно хранится в виде отдельной записи в IndexedDB. В контексте idb-keyval это реализуется как обычная пара ключ–значение:

import { get, set } from 'idb-keyval';

const SCHEMA_VERSION_KEY = 'schema_version';
const CURRENT_SCHEMA_VERSION = 2;

async function getSchemaVersion() {
    const version = await get(SCHEMA_VERSION_KEY);
    return version || 0;
}

async function setSchemaVersion(version) {
    await set(SCHEMA_VERSION_KEY, version);
}

Ключевые моменты:

  • SCHEMA_VERSION_KEY — строка, однозначно идентифицирующая версию схемы.
  • getSchemaVersion возвращает текущую версию схемы или 0, если версия ещё не установлена.
  • setSchemaVersion обновляет версию схемы после выполнения миграций.

Контроль изменений схемы

При каждом запуске приложения необходимо проверять текущую версию схемы и при необходимости запускать процедуры миграции:

async function migrateIfNeeded() {
    const currentVersion = await getSchemaVersion();
    
    if (currentVersion < 1) {
        // Миграция с версии 0 на 1
        await migrateToVersion1();
        await setSchemaVersion(1);
    }

    if (currentVersion < 2) {
        // Миграция с версии 1 на 2
        await migrateToVersion2();
        await setSchemaVersion(2);
    }
}

async function migrateToVersion1() {
    const oldData = await get('user_data');
    const transformedData = { ...oldData, createdAt: Date.now() };
    await set('user_data', transformedData);
}

async function migrateToVersion2() {
    const settings = await get('app_settings');
    const upd atedSettings = { ...settings, theme: 'light' };
    await se t('app_settings', upd atedSettings);
}

Особенности подхода:

  • Каждая миграция изолирована и описывает конкретное преобразование данных.
  • Хранение версии схемы в отдельном ключе позволяет безопасно повторно запускать проверку без потери данных.
  • idb-keyval обеспечивает атомарность операций get и set для отдельных ключей, что упрощает контроль состояния.

Обработка ошибок и откат

При работе с миграциями важно предусмотреть обработку ошибок, чтобы не нарушить целостность данных:

async function migrateWithSafety() {
    try {
        await migrateIfNeeded();
    } catch (error) {
        console.error('Ошибка при миграции схемы:', error);
        // Возможен откат или уведомление пользователя
    }
}

Советы по надёжности:

  • Хранить резервные копии критичных данных перед выполнением миграции.
  • Проверять существование ключей перед модификацией.
  • Использовать структурированные объекты для версии схемы и данных, чтобы облегчить преобразования.

Расширение функционала хранения схемы

Для сложных приложений можно внедрять следующие улучшения:

  1. История версий Вместо хранения одной текущей версии можно хранить массив объектов с отметкой времени и описанием изменений:

    await se t('schema_history', [
        { version: 1, date: 1680000000000, description: 'Добавлен createdAt' },
        { version: 2, date: 1685000000000, description: 'Добавлена тема' }
    ]);
  2. Групповые миграции Если приложение использует множество связанных ключей, можно создавать миграции, которые обрабатывают сразу набор ключей атомарно.

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


Практические рекомендации

  • Использовать целочисленное значение версии для упрощения сравнения (0, 1, 2 …).
  • Не полагаться на порядок ключей в IndexedDB — каждая запись должна быть автономной.
  • Для больших наборов данных рассматривать возможность пакетной обработки, чтобы миграции не блокировали UI.
  • Тестировать миграции на разных версиях данных перед развертыванием.

Хранение версии схемы в idb-keyval обеспечивает упрощённое управление обновлениями базы данных, минимизирует риски потери данных и упрощает миграции при росте и усложнении приложения.