Стратегии восстановления после ошибок

Ошибки в работе с localForage возникают на нескольких уровнях: хранилище браузера, драйверы (IndexedDB, WebSQL, localStorage), слой сериализации данных и ограничения среды выполнения. Каждая категория требует собственной стратегии восстановления, поскольку поведение отказов принципиально различается.

Основные группы ошибок:

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

Ключевая особенность localForage заключается в абстракции над несколькими backend-хранилищами. Это создаёт дополнительный уровень неопределённости: ошибка может быть связана как с конкретным драйвером, так и с логикой выбора драйвера.


Принципы устойчивого восстановления

Архитектура восстановления строится на нескольких базовых принципах:

1. Идемпотентность операций Повторное выполнение операции записи или удаления не должно приводить к дублированию или порче состояния.

2. Деградация вместо отказа При невозможности записи система продолжает работу в упрощённом режиме, сохраняя критически важные функции.

3. Многоуровневая стратегия fallback Каждый уровень хранения рассматривается как потенциально ненадёжный.

4. Разделение ошибок на временные и постоянные Временные ошибки обрабатываются повторными попытками, постоянные — переключением стратегии хранения или отказом от части функциональности.


Повторные попытки и экспоненциальная задержка

Одним из базовых механизмов восстановления является retry-механика. В контексте localForage она особенно важна для IndexedDB, где операции могут временно блокироваться транзакциями.

Типовая стратегия retry:

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

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

Пример логики:

  • первая попытка — сразу;
  • вторая — через 100–200 мс;
  • третья — через 400–800 мс;
  • дальнейшие — с ростом до фиксированного потолка.

Переключение между драйверами хранения

localForage поддерживает несколько драйверов, и стратегия восстановления часто строится вокруг их приоритетного переключения.

Типичный порядок устойчивости:

  1. IndexedDB — основной и наиболее стабильный для современных браузеров;
  2. WebSQL — устаревший, но иногда используемый fallback;
  3. localStorage — последний резервный вариант.

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

  • при падении IndexedDB происходит переход на WebSQL;
  • при отсутствии поддержки WebSQL используется localStorage;
  • при невозможности использования всех драйверов активируется режим in-memory или отключённого кэша.

Ключевая проблема переключения — несовместимость форматов данных между драйверами при разных лимитах и механизмах сериализации.


Деградация функциональности при частичном отказе

Вместо полного отключения хранилища применяется функциональная деградация:

  • отключение некритичных ключей данных;
  • перевод части данных в volatile cache (память);
  • отключение истории или временных данных;
  • уменьшение частоты записи.

Деградация позволяет избежать каскадного отказа системы при нестабильном storage backend.


Обработка превышения квоты хранилища

Ошибка quota exceeded является одной из наиболее частых и требует отдельного сценария восстановления.

Стратегии обработки:

1. Очистка некритичных данных

  • удаление временных ключей;
  • очистка кэшей;
  • удаление устаревших записей по TTL.

2. LRU-эвикция Применение стратегии Least Recently Used:

  • сортировка ключей по времени доступа;
  • удаление наименее используемых записей.

3. Сжатие данных

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

4. Перенос в альтернативный storage

  • миграция части данных в IndexedDB при отказе localStorage;
  • распределение данных по нескольким хранилищам.

Восстановление после повреждения данных

Повреждение данных чаще всего связано с:

  • некорректной сериализацией JSON;
  • несовместимостью версий схемы;
  • частичной записью при прерывании операции;
  • ошибками драйвера.

Стратегии восстановления:

1. Safe parse wrapper Все операции чтения проходят через защищённый слой:

  • попытка JSON.parse;
  • при ошибке — возврат fallback значения;
  • логирование ключа как повреждённого.

2. Версионирование данных Каждая запись содержит мета-информацию версии схемы:

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

3. Резервные копии

  • хранение shadow-copy критичных данных;
  • восстановление из последней валидной версии.

Миграция схемы и восстановление консистентности

Изменения структуры данных требуют контролируемой миграции.

Подходы:

Инкрементальная миграция

  • преобразование данных при чтении;
  • отложенное обновление при записи.

Batch migration

  • массовое обновление всех ключей при инициализации приложения;
  • использование фонового процесса.

Lazy migration

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

Ошибки миграции рассматриваются как частичный отказ данных и требуют fallback на предыдущую схему.


Circuit breaker для storage операций

Механизм circuit breaker применяется для предотвращения повторяющихся дорогостоящих ошибок хранилища.

Состояния:

  • closed — нормальная работа;
  • open — временная блокировка операций;
  • half-open — тестирование восстановления.

При серии ошибок записи storage переводится в open-состояние, и операции:

  • либо кэшируются в памяти;
  • либо отклоняются с немедленным fallback.

Через заданный интервал выполняется проверка восстановления.


Очереди операций и оффлайн-устойчивость

При нестабильном storage операции записи могут быть помещены в очередь.

Модель очереди:

  • FIFO структура;
  • асинхронная обработка;
  • повтор при неудаче;
  • персистентность очереди в fallback storage.

Очередь позволяет:

  • сгладить пики нагрузки;
  • избежать потери операций при временной недоступности IndexedDB;
  • обеспечить eventual consistency.

Конкурентный доступ и защита от гонок

localForage не предоставляет полноценного транзакционного слоя, поэтому требуется внешняя координация.

Стратегии:

  • mutex на уровне ключей;
  • оптимистичные блокировки;
  • версионирование записей (ETags);
  • compare-and-swap логика через read-modify-write цикл.

При конфликте изменений применяется стратегия:

  • повтор операции;
  • слияние данных;
  • откат одной из версий.

Диагностика и восстановление состояния хранилища

Система восстановления опирается на телеметрию состояния storage:

  • процент ошибок записи;
  • время ответа драйвера;
  • частота fallback-переходов;
  • количество повреждённых ключей;
  • объём освобождённого пространства.

На основе этих данных строится динамическая адаптация стратегии:

  • увеличение retry delay при деградации;
  • усиление эвикции при росте quota ошибок;
  • отключение проблемного драйвера.

Паттерн комплексного recovery pipeline

Полноценный pipeline восстановления включает последовательные этапы:

  1. попытка операции в основном драйвере;
  2. retry с экспоненциальной задержкой;
  3. переключение на альтернативный драйвер;
  4. очистка или эвикция данных при quota error;
  5. восстановление из резервной копии при повреждении;
  6. помещение операции в очередь при временной недоступности;
  7. активация circuit breaker при массовых сбоях;
  8. последующая синхронизация состояния при восстановлении.

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