Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple service worker updates do not result in multiple waiting events #3285

Open
michaelowolf opened this issue Jan 9, 2024 · 0 comments

Comments

@michaelowolf
Copy link

Library Affected:
workbox-window@7.0.0

Browser & Platform:
Brave Version 1.60.118 Chromium: 119.0.6045.163 (Official Build) (x86_64)
(Probably all browsers)

Issue or Feature Request Description:
After the first update to a service worker is found, further updates that supersede it do not have waiting events fired for them (although the registration does fire updatefound). It's not clear to me whether this is intended or not based on the documentation, but there doesn't seem to be any indication that this should only ever happen once.

There seems to be a general assumption in workbox-window that once one service worker update is found, no more need to be handled. This is my guess based on the fact that as soon as an external service worker (which I understand to mean an updated version) is found during an update, the updatefound listener is removed:

registration.removeEventListener('updatefound', this._onUpdateFound);

This means we don't get _onUpdateFound called for new updates, and so never add the listener for their statechange event:

installingSW.addEventListener('statechange', this._onStateChange);

I don't have a reproduceable link but can create one if helpful.

Some rough logs for this (where I've added extra app logs when we call registration.update and handle events):

# Load page with an existing service worker
workbox Successfully registered service worker. /sw.js
workbox A service worker with the same script URL is already controlling this page.

# First update attempt
[app] Calling `registration.update`

# Update found and `waiting` event dispatched
[app] Registration fired `updatefound`
workbox An external service worker has installed. You may want to suggest users reload this page.
[app] `waiting` (external) event fired
workbox An external service worker has installed but is waiting for this client to close before activating...

# Second update attempt
[app] Calling `registration.update`

# Update found
[app] Registration fired `updatefound`
# but no further events triggered

This assumption is acceptable in a "happy path" scenario (at least in our use case), because once an update is available for the user we show a permanent banner to accept it. Even if new updates are found and installed, the absence of the waiting event doesn't matter because once the user accepts the banner then the latest waiting service worker will be told to skip waiting:

void messageSW(this._registration.waiting, SKIP_WAITING_MESSAGE);

I suspect that some state internal to the Workbox instance may become out of sync with the registration when this happens, but I haven't seen any specific effects of that.

Where this does become a problem is recovering from update installation failures. If the first found update fails to install, that service worker never switches state to installed and so, correctly, no waiting event is fired. Then when a new update is found that installs correctly, still no waiting event is fired. This has meant we cannot ever then show the user an update prompt in response to the waiting event if the first update fails to install.

I've managed to reliably reproduce this while making use of the subresource integrity feature, by having a precached resource in the first update fail the integrity check, causing installation to fail. Trying to update again after integrity is fixed will correctly install the service worker update and put it into the waiting state, but no waiting event is fired for it.

Some rough logs for this occurrence:

# Load page with an existing service worker
workbox Successfully registered service worker. /sw.js
workbox A service worker with the same script URL is already controlling this page.

# First update attempt
[app] Calling `registration.update`

# Update found but integrity check fails for precached resources, so no `waiting` event fired
[app] Registration fired `updatefound`
Failed to find a valid digest in the 'integrity' attribute for resource '<URL>' with computed SHA-512 integrity '<HASH>'. The resource has been blocked.
Unknown error occurred while trying to verify integrity.
StrategyHandler.js:160 Uncaught (in promise) TypeError: Failed to fetch
    at l.fetch (StrategyHandler.js:160:35)
    at u.st (PrecacheStrategy.js:143:40)
    at u._handle (PrecacheStrategy.js:71:31)
    at async u.Rt (Strategy.js:144:13)
fetch @ StrategyHandler.js:160
st @ PrecacheStrategy.js:143
_handle @ PrecacheStrategy.js:71

# Second update attempt
[app] Calling `registration.update`

# Update found
[app] Registration fired `updatefound`
# but no further events triggered

In terms of workarounds or solutions, I suspect some combination of the redundant event and the creation & registeration of a new Workbox instance may work, but it doesn't seem like an ideal solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant