Библиотека idb-keyval предоставляет удобный интерфейс для работы с IndexedDB, скрывая сложность низкоуровневого API. Однако при работе с этой библиотекой могут возникать ошибки, связанные с блокировкой базы данных. Понимание их природы и способов обработки критически важно для надежной работы приложения.
База данных IndexedDB может оказаться заблокированной по нескольким причинам:
Одновременное открытие нескольких версий базы
При попытке открыть базу с другой версией в то время, как другая
транзакция ещё активна, IndexedDB может заблокировать новую сессию до
завершения текущей. В idb-keyval это проявляется как отказы в операциях
get, set, del или
clear.
Длительные или зависшие транзакции Транзакции
IndexedDB ограничены временем жизни. Если одна транзакция слишком долго
удерживает блокировку на объектном хранилище, новые операции оказываются
в состоянии ожидания или получают ошибку
DOMException: The database connection is blocked.
Закрытие вкладки или перезагрузка браузера Если база была открыта в нескольких вкладках, закрытие одной из них во время активной транзакции другой может вызвать сбои при последующих операциях.
Конфликты версий базы данных При обновлении
схемы IndexedDB через изменение версии, idb-keyval автоматически
вызывает событие onupgradeneeded. Если другая транзакция в
этот момент работает с базой, попытка изменения версии приведёт к
блокировке.
При блокировке базы данных чаще всего встречаются следующие ошибки:
DOMException: The database connection is blocked
Возникает при попытке обновления схемы базы, когда открытые соединения
еще не закрыты.
DOMException: Version change transaction blocked
Появляется, если происходит попытка обновления версии базы через
idb-keyval в момент активных транзакций.
AbortError Транзакция прерывается
из-за блокировки другой транзакцией или ошибки браузера.
QuotaExceededError Иногда
блокировка базы сопровождается этой ошибкой, если попытка записи
превышает доступное пространство.
idb-keyval возвращает промисы для всех операций
(get, set, del,
clear). Это позволяет использовать стандартные методы
обработки ошибок:
import { set, get } from 'idb-keyval';
set('user', { name: 'Alice' })
.catch(err => {
if (err.name === 'AbortError' || err.name === 'QuotaExceededError') {
console.error('Ошибка транзакции или блокировка базы', err);
} else {
throw err;
}
});
get('user')
.then(value => console.log(value))
.catch(err => console.error('Не удалось получить значение:', err));
Важно различать временные блокировки, которые исчезнут после завершения других транзакций, и постоянные ошибки, такие как неправильная версия базы или превышение квоты. Для временных блокировок стоит реализовать повтор попытки с небольшим таймаутом:
async function safeSet(key, value, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await set(key, value);
return;
} catch (err) {
if (err.name === 'AbortError') {
await new Promise(res => setTimeout(res, 100));
continue;
}
throw err;
}
}
throw new Error('Не удалось записать данные после нескольких попыток');
}
Store.versionchange.
Если обнаружено событие изменения версии базы, следует корректно закрыть
соединения.Создание собственного хранилища с обработкой ошибок выглядит следующим образом:
import { Store, set, get } from 'idb-keyval';
const myStore = new Store('my-database', 'keyval-store');
async function writeData(key, value) {
try {
await set(key, value, myStore);
} catch (err) {
console.warn('Ошибка записи в базу:', err.name);
// Возможна повторная попытка или логирование
}
}
async function readData(key) {
try {
return await get(key, myStore);
} catch (err) {
console.warn('Ошибка чтения из базы:', err.name);
return null;
}
}
Такой подход позволяет изолировать работу с конкретной базой и централизованно обрабатывать возможные блокировки и ошибки транзакций.