Откат к предыдущей версии

Управление версиями кэша

Workbox позволяет эффективно управлять версиями кэша с помощью стратегий precache и runtime caching, что обеспечивает возможность отката к предыдущей версии приложения при необходимости. Основной механизм — это указание уникального revision для каждого ресурса. Например, при сборке приложения с использованием Workbox CLI или Workbox Webpack Plugin автоматически генерируется манифест ресурсов с хэшами:

workbox.precaching.precacheAndRoute([
  { url: '/index.html', revision: 'a1b2c3d4' },
  { url: '/styles/main.css', revision: 'e5f6g7h8' },
  { url: '/scripts/app.js', revision: 'i9j0k1l2' },
]);

Ключевой момент: изменение содержимого файла изменяет revision, что позволяет Workbox определить, что нужно обновить кэш, сохраняя при этом предыдущие версии других ресурсов.

Механизм удаления старых кэшей

Workbox автоматически удаляет устаревшие версии кэшей, если используется precacheAndRoute. Однако для случаев отката к предыдущей версии можно сохранять несколько версий ресурсов с разными ревизиями и управлять ими через собственную логику:

const CURRENT_CACHE = 'my-app-cache-v3';
const PREVIOUS_CACHE = 'my-app-cache-v2';

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) =>
      Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CURRENT_CACHE && cacheName !== PREVIOUS_CACHE) {
            return caches.delete(cacheName);
          }
        })
      )
    )
  );
});

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

Создание fallback для отката

Для случаев, когда новая версия приложения некорректно работает, Workbox поддерживает стратегию staleWhileRevalidate или ручной fallback на старую версию:

workbox.routing.registerRoute(
  ({request}) => request.destination === 'script',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: CURRENT_CACHE,
    plugins: [
      {
        fetchDidFail: async ({originalRequest}) => {
          const cache = await caches.open(PREVIOUS_CACHE);
          const response = await cache.match(originalRequest);
          return response || Response.error();
        }
      }
    ]
  })
);

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

Управление откатом через манифест

Для более сложных сценариев можно хранить версии ресурсов в JSON-манифесте, который загружается через runtime caching. Этот манифест содержит информацию о текущей и предыдущей стабильной версии:

workbox.routing.registerRoute(
  '/manifest.json',
  async ({event}) => {
    const response = await fetch(event.request);
    const manifest = await response.json();

    const cache = await caches.open(CURRENT_CACHE);
    await cache.addAll(manifest.current);
    
    return response;
  }
);

Такой подход позволяет динамически управлять доступными версиями и откатом без необходимости пересборки Service Worker.

Использование Background Sync для безопасного отката

При обновлении ресурса, который критически важен для работы приложения, можно использовать Background Sync. Это гарантирует, что попытка обновления новой версии будет завершена, а в случае ошибки откат произойдет автоматически:

const bgSyncPlugin = new workbox.backgroundSync.BackgroundSyncPlugin('rollbackQueue', {
  maxRetentionTime: 24 * 60 // минуты
});

workbox.routing.registerRoute(
  /\/api\/critical-endpoint/,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin]
  }),
  'POST'
);

Это обеспечивает надежное хранение запросов и возможность повторной попытки с предыдущей версии, если новая версия API недоступна.

Итоговые рекомендации по откату

  1. Использовать уникальные revision для каждого ресурса — это основа корректного управления версиями.
  2. Сохранять предыдущий кэш для критически важных ресурсов, чтобы была возможность мгновенного отката.
  3. Реализовать fallback через fetchDidFail или стратегии Workbox для безопасного восстановления работы приложения.
  4. Динамический манифест версий позволяет переключаться между версиями без пересборки Service Worker.
  5. Background Sync гарантирует, что критические обновления не приведут к потере данных или недоступности приложения.

Такой подход обеспечивает контроль над версиями ресурсов, минимизирует риски некорректной работы после обновления и упрощает откат к стабильной версии приложения.