Ситуации, в которых работник службы может отображать старую версию приложения даже после загрузки новой версии?

Извините, у меня нет воспроизводимого тестового примера этой проблемы, потому что я сам никогда не видел, чтобы это происходило. Я знаю только, что это происходит из-за входа в мое приложение на стороне клиента и жалоб от пользователей.

Проблема в:

  1. Я развертываю новую версию своего приложения
  2. Пользователь заходит на мой сайт, получает новую версию и успешно ее запускает
  3. Пользователь снова заходит на мой сайт и получает старую версию моего приложения

Я использую сервис-воркер, который, как я надеялся, может дать некоторые гарантии того, что этого сценария не произойдет. Это особенно беспокоит, когда новая версия включает миграцию схемы IndexedDB, потому что тогда старая версия моего приложения даже больше не будет работать.

Подробнее:

Я использую Workbox 4.3.1. Мой обслуживающий работник в основном:

importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
workbox.precaching.precacheAndRoute([]);
workbox.routing.registerNavigationRoute("/index.html", {
    blacklist: [
        new RegExp("^/static"),
        new RegExp("^/sw.js"),
    ],
});

workbox.precaching.precacheAndRoute([]); заменяется workboxBuild.injectManifest. Я могу вручную подтвердить, что нужные файлы заполняются. И вообще сервис-воркер работает. Я вижу это в инструментах разработчика браузера. Я могу отключиться от Интернета и по-прежнему пользоваться своим приложением. Вроде все нормально. Как я уже сказал выше, я никогда не видел, чтобы эта проблема возникала, и у меня нет воспроизводимого тестового примера.

Но некоторые из моих пользователей столкнулись с описанной выше проблемой. Я попытался использовать журнал ошибок на стороне клиента для расследования. Я добавил в свое приложение код, чтобы сохранить его номер версии в localStorage, и при начальной загрузке он сравнивает этот номер версии с текущим номером версии. Если версия в localStorage более свежая, чем текущая запущенная версия (т.е. она успешно запускала более новую версию в прошлом, но теперь возвращается к более старой версии), он регистрирует номера версий вместе с некоторой дополнительной информацией:

let registrations = [];
if (window.navigator.serviceWorker) {
    registrations = await window.navigator.serviceWorker.getRegistrations();
}
log({
    hasNavigatorServiceWorker:
        window.navigator.serviceWorker !== undefined,
    registrationsLength: registrations.length,
    registrations: registrations.map(r => {
        return {
            scope: r.scope,
            active: r.active
                ? {
                      scriptURL: r.active.scriptURL,
                      state: r.active.state,
                  }
                : null,
            installing: r.installing
                ? {
                      scriptURL: r.installing.scriptURL,
                      state: r.installing.state,
                  }
                : null,
            waiting: r.waiting
                ? {
                      scriptURL: r.waiting.scriptURL,
                      state: r.waiting.state,
                  }
                : null,
        };
    }),
})

Просматривая свои журналы, я вижу, что эта проблема возникает только у 1% моих пользователей. Firefox обогащен среди этих пользователей (4% общего трафика, но 18% записей журнала для этой проблемы), но это происходит для всех браузеров и операционных систем.

И я вижу, что почти все записи имеют эти значения:

{
    hasNavigatorServiceWorker: true,
    registrationsLength: 1,
    registrations: [{
        "scope": "https://example.com/",
        "active": {
            "scriptURL": "https://example.com/sw.js",
            "state": "activated"
        },
        "installing": null,
        "waiting": null
    }]
}

Насколько мне известно, все это правильные значения.

Я также должен отметить, что мои файлы JavaScript имеют хэш в URL-адресе, поэтому не может быть, чтобы мой сервер каким-то образом возвращал старую версию моего JavaScript, когда пользователь запрашивает новую версию.

Итак, что могло происходить? Как можно объяснить это наблюдаемое поведение? Что еще я мог регистрировать для дальнейшей отладки?

Единственные сценарии, которые я могу придумать, кажутся мне крайне неправдоподобными. Нравится...

  1. Пользователь загружает v1, сервис-воркер по какой-то причине не работает
  2. Пользователь загружает версию 2, сервис-воркер по какой-то причине не работает
  3. Пользователь каким-то образом получает v1 из кеша своего браузера, так как все предыдущие сервисные работники потерпели неудачу, но теперь сервисный воркер работает правильно и сохраняет это как текущую версию.

Но у меня нет свидетельств того, что сервис-воркер когда-либо терпел неудачу. В моих журналах ошибок ничего нет. Ни один пользователь не жалуется, что офлайн-поддержка не работает.

Если это помогает, то на самом деле это происходит на веб-сайте https://play.basketball-gm.com/, сервисный работник находится по адресу https://play.basketball-gm.com/sw.js, а весь код доступен на GitHub.

И эта проблема возникла с тех пор, как я начал использовать сервис-воркер около года назад. Я только что наконец приступил к написанию вопроса о переполнении стека, потому что я отказался от надежды, что смогу разобраться в нем самостоятельно или даже создать воспроизводимый тестовый пример.


person dumbmatter    schedule 20.09.2019    source источник


Ответы (3)


Каким-то образом пользователь получает старую версию из своего кеша. Удаление устаревших кешей должно помочь. Как только новый сервис-воркер установлен, а предыдущая версия не используется, новая активируется, и вы получаете событие activate. Поскольку старая версия больше не используется, самое время удалить неиспользуемые кеши.

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // Return true if you want to remove this cache,
          // but remember that caches are shared across
          // the whole origin
        }).map(function(cacheName) {
          return caches.delete(cacheName);
        })
      );
    })
  );
});

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

person Ömürcan Cengiz    schedule 29.09.2019
comment
Но зачем им брать старую версию из кеша, если они уже загрузили более новую версию? - person dumbmatter; 29.09.2019
comment
Если объем кэшированных данных превышает лимит хранилища браузера, браузер начнет удалять все данные, связанные с источником, по одному источнику за раз, пока объем хранилища снова не станет ниже лимита. - person Ömürcan Cengiz; 29.09.2019
comment
Это правда ... но не следует ли начинать с самых старых данных, а не с самых новых? И все равно Workbox автоматически не удаляет свои старые данные? - person dumbmatter; 30.09.2019
comment
Он уже начинается с самых старых данных до самых новых. И нет, он не удаляется автоматически. Вы можете использовать этот код для удаления вручную. - person Ömürcan Cengiz; 30.09.2019
comment
Но если он переходит от самого старого к самому новому, то поведение, которое я наблюдал в своем вопросе, не произойдет. И Workbox утверждает, что автоматически удаляет старые кэшированные данные developers.google.com/ web / tools / workbox / modules /, который, похоже, работает, когда я его тестирую - я не говорю о необработанном API-интерфейсе Service Worker. - person dumbmatter; 30.09.2019
comment
Workbox удаляет старый кеш, когда вы используете мой код. Этот новый сервис-воркер не будет использоваться для ответа на запросы, пока не будет запущено его событие активации. Именно в событии активации предварительное кэширование рабочего окна будет проверять любые кэшированные ресурсы, которых больше нет в списке текущих URL-адресов, и удалять их из кеша. Кроме того, из-за того, что он переходит от самого старого к самому новому, браузер загружается Старый. Каждый раз, когда вы устанавливаете что-то новое, оно помещается в кеш. А из-за превышения лимита хранилища браузера новые данные не будут попадать в кеш, а браузер отобразит старые. - person Ömürcan Cengiz; 30.09.2019
comment
Но если лимит хранилища превышен, поэтому он не может сохранить нового Service Worker, зачем вообще будет отображаться новая версия моего приложения? Разве он не будет продолжать использовать старый Service Worker, и в этом случае у меня никогда не будет этой проблемы, когда он отображает старую версию, затем новую версию, а затем снова старую версию? - person dumbmatter; 30.09.2019
comment
Просто прочтите Очистить старые предварительные кеши на сайте разработчиков. .google.com / web / tools / workbox / modules /. Очевидно, поведение, которое вы наблюдаете, иногда случается, что является редкой ситуацией. - person Ömürcan Cengiz; 30.09.2019
comment
Это также кажется неуместным, потому что это происходит, даже когда я не меняю версию Workbox, а мой код уже вызывает workbox.precaching.cleanupOutdatedCaches () (который я добавил при обновлении Workbox с v3 до v4) - person dumbmatter; 30.09.2019
comment
Извини, дружище, я не понимаю, почему это происходит, даже если ты не меняешь версию ... - person Ömürcan Cengiz; 30.09.2019

На мой взгляд, это может быть скорее ответом «техподдержки» для проблемных пользователей.

Я лично сталкивался с подобными проблемами при попытке повторно загрузить мои собственные приложения React локально во время разработки. (Клиентский код) Я использовал службу туннелирования ngrok.

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

(Техническая поддержка) Ответ:

Может потребоваться выполнить «Жесткую перезагрузку», иногда «Очистить кеш и жестко перезагрузить», это должно очистить браузер. (Это действительно зависит от структуры файлов, загружаемых с сайтом) .

Вы можете получить доступ к этим параметрам, пока открыта консоль разработчика.

Для получения дополнительной информации о жестком обновлении вы можете ссылаться на этот пост SO: какая-разница-между-нормальной-перезагрузкой-жесткой-перезагрузкой-и-пустым-кешем

person Cullen Bond    schedule 09.10.2019

Год спустя, и я наконец понял это.

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

person dumbmatter    schedule 04.06.2020