Добавление и удаление хранилищ при апгрейде

В Dexie.js структура базы данных определяется версионной схемой, где каждая версия описывает набор object stores и индексов. Любое изменение структуры — добавление, удаление или изменение таблиц — выполняется исключительно через механизм версионирования, поскольку IndexedDB не допускает прямой модификации схемы без открытия транзакции на обновление версии базы.

Модель версий и роль схемы

Каждая версия базы в Dexie.js фиксирует состояние схемы через метод:

db.version(n).stores({...})

Где:

  • n — номер версии базы данных;
  • stores — строковое описание object stores и их индексов.

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


Добавление новых хранилищ

Добавление новой таблицы реализуется через расширение схемы в новой версии базы. При этом все ранее существующие хранилища должны быть повторно объявлены, поскольку схема в Dexie.js не наследуется автоматически между версиями.

Пример добавления нового object store:

db.version(1).stores({
  users: "++id,name,email"
});

db.version(2).stores({
  users: "++id,name,email",
  orders: "++id,userId,createdAt"
});

Особенности добавления:

  • новая таблица orders создаётся только в версии 2;
  • старая таблица users сохраняет свою структуру без изменений;
  • при апгрейде Dexie автоматически создаёт новый store.

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

db.version(2).stores({
  users: "++id,name,email",
  orders: "++id,userId,createdAt"
}).upgrade(tx => {
  return tx.table("orders").add({
    userId: 1,
    createdAt: Date.now()
  });
});

Удаление хранилищ

Удаление таблицы в Dexie.js не выполняется явно через отдельный метод. Исключение object store из схемы новой версии приводит к его автоматическому удалению при апгрейде базы.

Пример удаления хранилища:

db.version(1).stores({
  users: "++id,name,email",
  logs: "++id,message,createdAt"
});

db.version(2).stores({
  users: "++id,name,email"
});

В данном случае:

  • logs существует в версии 1;
  • в версии 2 он отсутствует;
  • IndexedDB удаляет logs при обновлении версии.

Важные ограничения удаления

Удаление store сопровождается рядом особенностей:

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

Одновременное добавление и удаление

Часто миграции комбинируют добавление новых таблиц и удаление старых:

db.version(1).stores({
  users: "++id,name,email",
  sessions: "++id,userId,token"
});

db.version(2).stores({
  users: "++id,name,email",
  authTokens: "++id,userId,token,expiresAt"
});

В этом случае происходит:

  • добавление authTokens;
  • удаление sessions;
  • сохранение users без изменений.

Миграция данных при замене хранилища

При замене одного store другим часто требуется перенос данных. Dexie.js предоставляет транзакционный контекст через upgrade, позволяющий читать старые данные и записывать новые.

Пример миграции:

db.version(2).stores({
  users: "++id,name,email",
  authTokens: "++id,userId,token,expiresAt"
}).upgrade(async tx => {
  const oldSessions = await tx.table("sessions").toArray();

  const transformed = oldSessions.map(s => ({
    userId: s.userId,
    token: s.token,
    expiresAt: Date.now() + 3600 * 1000
  }));

  await tx.table("authTokens").bulkAdd(transformed);
});

Особенности транзакции:

  • выполняется в рамках upgrade-транзакции;
  • доступ к старым и новым store возможен одновременно;
  • при ошибке миграция откатывается полностью.

Переименование хранилищ

Прямого механизма переименования object store в IndexedDB не существует. В Dexie.js переименование реализуется как комбинация удаления и добавления:

db.version(1).stores({
  sessions: "++id,userId,token"
});

db.version(2).stores({
  authSessions: "++id,userId,token"
}).upgrade(async tx => {
  const oldData = await tx.table("sessions").toArray();
  await tx.table("authSessions").bulkAdd(oldData);
});

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


Синхронизация схемы между версиями

Каждая новая версия должна включать полное описание всех актуальных хранилищ. Частичная декларация схемы недопустима, поскольку Dexie.js рассматривает stores() как полный снимок структуры на момент версии.

Нарушение этого правила приводит к:

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

Изменение структуры хранилищ

Изменение индексов или первичных ключей также требует новой версии. Например:

db.version(1).stores({
  users: "++id,name,email"
});

db.version(2).stores({
  users: "++id,email,username"
});

При этом:

  • изменение индексов приводит к пересозданию object store;
  • данные старой структуры могут быть потеряны без миграции;
  • безопасный путь — перенос данных через upgrade.

Последовательные миграции и цепочка версий

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

db.version(1).stores({
  users: "++id,name,email"
});

db.version(2).stores({
  users: "++id,name,email",
  logs: "++id,message"
});

db.version(3).stores({
  users: "++id,name,email",
  logs: "++id,message",
  settings: "++id,key,value"
});

Каждая версия представляет атомарный шаг миграции. Dexie выполняет их по порядку, гарантируя целостность данных между переходами.


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

Upgrade-транзакция обладает особыми характеристиками:

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

Это обеспечивает согласованное состояние схемы после завершения обновления.


Типичные ошибки при работе с хранилищами

Часто встречающиеся проблемы при изменении структуры:

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

Принципы безопасной миграции структуры

Корректная стратегия изменения хранилищ строится на нескольких принципах:

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