Пакетные операции: bulkAdd(), bulkPut(), bulkGet(), bulkDelete()

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

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


Архитектурная основа пакетных операций

IndexedDB работает через транзакции, каждая из которых имеет фиксированный набор object store и ограниченное время жизни. Частые мелкие операции создают значительные накладные расходы: открытие транзакции, сериализация запросов, обработка событий успеха и ошибок.

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

Ключевые принципы:

  • минимизация количества транзакций
  • последовательная обработка массивов данных
  • управление частичными ошибками
  • оптимизация записи через bulk-алгоритмы браузера

bulkAdd(): пакетная вставка данных

Метод bulkAdd() используется для массовой вставки новых записей в таблицу. Он предназначен для сценариев первичного импорта данных или добавления больших наборов уникальных объектов.

Сигнатура

table.bulkAdd(items, keys?)
  • items — массив объектов для добавления
  • keys — (опционально) массив ключей, если не используется autoIncrement

Поведение метода

bulkAdd() строго ориентирован на добавление новых записей:

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

Особенности обработки ошибок

Dexie позволяет получить подробную информацию о сбойных элементах:

try {
  await db.users.bulkAdd(usersArray);
} catch (err) {
  console.error(err.failures); // массив ошибок по элементам
}

Ошибки могут возникать из-за:

  • дублирования primary key
  • нарушения уникальных индексов
  • некорректной структуры данных
  • превышения лимитов браузера

Производительность

bulkAdd() значительно быстрее последовательных add() благодаря:

  • одной транзакции
  • минимизации commit-операций
  • оптимизированному batch-writing внутри IndexedDB

bulkPut(): массовое добавление и обновление

Метод bulkPut() является более универсальным аналогом bulkAdd(). Он выполняет вставку новых записей и обновление существующих в рамках одной операции.

Сигнатура

table.bulkPut(items, keys?)

Логика работы

bulkPut() выполняет upsert-операцию:

  • если запись с ключом отсутствует → вставка
  • если запись существует → полная замена объекта

Это делает метод ключевым инструментом синхронизации данных.


Отличия от put()

Обычный put() работает с одной записью, тогда как bulkPut():

  • обрабатывает массив данных
  • использует одну транзакцию
  • снижает overhead вызовов
  • оптимизирует запись через batch-процедуры

Поведение при ошибках

При массовых операциях возможны частичные сбои:

  • часть записей может быть успешно применена
  • часть — отклонена
  • Dexie возвращает структуру с деталями ошибок
try {
  await db.products.bulkPut(products);
} catch (e) {
  console.log(e.failures);
}

Использование в синхронизации

bulkPut() часто применяется при:

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

bulkGet(): пакетное чтение данных

Метод bulkGet() позволяет извлекать несколько записей по списку ключей за одну операцию.

Сигнатура

table.bulkGet(keys)
  • keys — массив первичных ключей

Возвращаемое значение

Метод возвращает массив, где:

  • элементы соответствуют порядку ключей
  • отсутствующие записи представлены undefined
const users = await db.users.bulkGet([1, 2, 3]);

Результат:

[
  { id: 1, name: "A" },
  undefined,
  { id: 3, name: "C" }
]

Производительность

bulkGet() эффективнее множественных get() благодаря:

  • минимизации транзакционных переключений
  • внутренней оптимизации чтения IndexedDB
  • уменьшению числа асинхронных операций

Сценарии применения

  • загрузка списка сущностей по ID
  • восстановление связей между объектами
  • построение кэшированных представлений
  • работа с графовыми структурами данных

bulkDelete(): массовое удаление записей

Метод bulkDelete() предназначен для удаления множества записей по ключам.

Сигнатура

table.bulkDelete(keys)

Поведение операции

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

Особенности выполнения

Удаление выполняется максимально эффективно за счёт:

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

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

Ошибки могут возникнуть в редких случаях:

  • повреждение базы
  • блокировка транзакции
  • ограничения браузера

Dexie возвращает информацию о неудачных удалениях через исключения или структуру ошибок транзакции.


Транзакционная модель пакетных операций

Все bulk-методы выполняются внутри IndexedDB транзакций. Dexie автоматически:

  • определяет нужный scope object store
  • объединяет операции в один commit
  • управляет очередностью запросов

Гарантии:

  • атомарность на уровне транзакции
  • отсутствие промежуточных состояний для bulk-операции
  • последовательное выполнение внутри batch

Ограничения и поведение при больших объёмах данных

При работе с массивами в десятки тысяч элементов возникают особенности:

  • возможны ограничения памяти браузера
  • увеличивается время удержания транзакции
  • повышается риск блокировки UI-thread (в зависимости от окружения)

Рекомендуется учитывать:

  • размер batch (оптимально 1000–5000 записей)
  • структуру объектов (глубина сериализации)
  • наличие индексов

Сравнение bulk-методов

Метод Назначение Перезапись Чтение Удаление
bulkAdd массовая вставка нет нет нет
bulkPut вставка + обновление да нет нет
bulkGet массовое чтение да нет
bulkDelete массовое удаление нет да

Взаимодействие с индексами

При использовании bulk-операций индексы обновляются автоматически:

  • primary key индекс
  • уникальные индексы
  • составные индексы

Однако стоимость обновления индексов может существенно влиять на производительность при bulkPut больших объёмов данных.


Влияние хуков Dexie

Dexie поддерживает hooks (creating, updating, deleting), которые также срабатывают при bulk-операциях.

Особенности:

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

Типичные ошибки при использовании bulk-операций

Дублирование ключей в bulkAdd

Массовая вставка без предварительной фильтрации часто приводит к конфликтам primary key.

Чрезмерно большие массивы

Передача десятков тысяч объектов за один вызов может вызвать:

  • блокировку UI
  • превышение лимитов транзакции

Игнорирование структуры ошибок

bulk-методы могут частично завершаться успешно, поэтому отсутствие обработки failures приводит к неконсистентному состоянию данных.


Оптимизационные практики

  • разбиение данных на батчи
  • использование bulkPut вместо множественных put
  • предварительная дедупликация данных
  • минимизация глубины объектов
  • отключение ненужных hooks при массовых операциях

Роль bulk-операций в архитектуре приложений

Пакетные методы Dexie.js формируют основу для высокопроизводительных offline-first приложений. Они позволяют:

  • синхронизировать локальные кэши с сервером
  • выполнять миграции данных
  • загружать большие датасеты без деградации UX
  • снижать нагрузку на IndexedDB API

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