Comments (13)
Hi, @jkinger. Thanks for reporting this.
Looks like Chrome has changed the way it deactivates idle service workers.
We have a ping/pong messaging in place to make sure the client constantly messages the worker so it never goes idle:
msw/src/browser/setupWorker/start/createStartHandler.ts
Lines 85 to 88 in ad8ebac
Lines 42 to 47 in ad8ebac
Previously, the worker was considered idle if it hasn't handled any messages in the N period of time. Now, it looks like the criteria for that changed.
from msw.
Extension Service Worker termination policy change
I can see that Google has changed the Service Worker termination rules for Service Workers in extensions:
The service worker has not received an event for over thirty seconds and there are no outstanding long running tasks in progress. If a service worker received an event during that time, the idle timer was removed.
This should have no effect on MSW as it's not an extension but a Service Worker registered by your app.
from msw.
Chrome FAQ on Service Worker termination
I've found another mention of this behavior that doesn't seem to be related to extension Service Workers but generally to Service Workers in Chrome:
The browser can terminate a running SW thread at almost any time. Chrome terminates a SW if the SW has been idle for 30 seconds. Chrome also detects long-running workers and terminates them. It does this if an event takes more than 5 minutes to settle, or if the worker is busy running synchronous JavaScript and does not respond to a ping within 30 seconds. When a SW is not running, Developer Tools and chrome://serviceworker-internals show its status as STOPPED.
If this is true, then the idle period has shortened from 5 minutes to 30 seconds.
It's worth mentioning this is a behavior specific only to the Manifest V3 API. Perhaps that's why it's not surfacing for everyone?
from msw.
I wonder why Chrome doesn't do this to MSW's Service Worker:
Service workers don't live indefinitely. While exact timings differ between browsers, service workers will be terminated if they've been idle for a few seconds, or if they've been busy for too long. If a service worker has been terminated and an event occurs that would start it up, it will restart.
https://web.dev/learn/pwa/service-workers#service_worker_lifespan
From what I understand, the worker goes idle, then the browser terminates it, and then your requests fail. Is that correct, @jkinger?
from msw.
A good thread on why Service Workers are killed after becoming idle: w3c/ServiceWorker#980
from msw.
I wonder if instead of trying to keep the worker alive and playing a guess game with the browser and its internal policies, we could detect that the browser has unregistered the worker (I do believe the worker's state change event should be emitted in that case) and if that happens, try to register and activate the worker again?
from msw.
Yes, all that you explained above sounds accurate as this just started happening within 6 months or so and only happens on Chromium-based browsers. Firefox doesn't have this issue.
Your last post about "..try to register and activate the worker again" is what I've been thinking about too. Could I re-instantiate the MSW service again somehow (like below) when the service worker is de-registered or starts throwing errors?
const { worker } = await import('./mocks/browser')
return worker.start()
Or would there be more to it? Or maybe something like a worker.restart()
?
from msw.
I think it's not something you should be doing manually. I wonder if that unregistration that Chrome does trigger the state change event on the worker.
@jkinger, can you please try this out in your scenario?
navigator.serviceWorker.controller.addEventListener('statechange', (event) => {
console.log(event.target.state)
})
What does this print when Chrome terminates the idle worker? Does it print anything at all?
I hope Chrome doesn't go around the worker's lifecycle and the termination that it initiates still causes the worker to go into something like "redundant". If we can detect that via a listener, we can call something akin to worker.start()
when that state transition happens.
from msw.
I've taken your recommendation and code to see if I can get notified when Chrome stops the service worker but unfortunately not getting any events for "'statechange'" when Chrome stops it. I know what you're getting at so trying to explore other listeners as well.
FWIW another observation I've noticed is Chrome doesn't seem to unregister the Service Worker but only stops it. When I return to the Browser / Tab and start interacting with the page again Chrome starts the Server Worker again and it's still under the same Registration ID it was first registered with.
Thanks for your help thus far.
from msw.
FWIW another observation I've noticed is Chrome doesn't seem to unregister the Service Worker but only stops it.
Yeah, this makes sense. Aligns with what Chrome is saying they do.
When I return to the Browser / Tab and start interacting with the page again Chrome starts the Server Worker again and it's still under the same Registration ID it was first registered with.
Does this "solve" the issue then? Can you describe to me the scenario when Chrome kills the worker and that causes issues?
from msw.
Does this "solve" the issue then?
Yeah, you would think, right? But it doesn't, I've been monitoring the Service Worker via chrome://serviceworker-internals/?devtools and I see when Chrome stops it after the set time of inactivity and then I see it being started again when I start interacting with the page but nothing is being intercepted as before.
There is a disconnect happening somewhere during the Stop / Restart of this Worker that's keeping MSW inoperable and that is what I'm trying to pin down. If Chrome re-starts it again then why doesn't MSW start responding to requests again? That's the part that doesn't make sense. It's like the Starting, Stopping then Starting again throws it off somehow.
The only way I'm able to get MSW to start responding again is by doing a Refresh.
from msw.
There is a disconnect happening somewhere during the Stop / Restart of this Worker that's keeping MSW inoperable and that is what I'm trying to pin down.
I can imagine if terminating the worker also removes any listeners attached to it, MSW will lose any connection with the worker. We rely on a MessageChannel to talk to the worker. If that gets shutdown and doesn't revive, MSW won't work.
The only way I'm able to get MSW to start responding again is by doing a Refresh.
This is a strong indication that's precisely what's happening.
from msw.
I finally identified the root cause: when Chrome stops and then restarts the service worker, it creates a new instance of mockServiceWorker.js
. This action removes any previously registered Client IDs under activeClientIds
. To complicate matters, when Chrome restarts the service worker, it provides no indication (that I'm aware of) that a new instance was created. Consequently, no MOCK_ACTIVATE
message is posted to re-register a new active client ID.
Fortunately, all the listeners remain connected and active, so fetch
will still fire. However, since there are no activeClientIds
, the activeClientIds.size === 0
check will prevent it from proceeding.
I'm not yet sufficiently familiar with the MSW code to suggest a comprehensive solution, but I managed to devise a quick, albeit makeshift, fix. If implemented in the App before a fetch, it should temporarily resolve the issue. Here's what the fix looks like:
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage('MOCK_ACTIVATE');
// Small wait for changes to propagate
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
}
await fetch('endpoint', {...});
By calling MOCK_ACTIVATE
manually it registers a new client ID in activeClientIds
and allows the rest of the Service Worker's "fetch" to continue.
I've updated my Github repo (as well as the live site) with this fix, so anyone interested can see it in action.
Itβs not the most elegant solution, but it should suffice until a more definitive fix is developed.
from msw.
Related Issues (20)
- "InvalidStateError: The object is in invalid state" when mocking rest api
- Unable to use msw/node for testing solid-js due to `resolve.conditions` set to `browser` HOT 5
- drop CommonJS support HOT 2
- quiet: true should supress RESPONSE LISTENER logs HOT 4
- Narrowing the response body type in `HttpResponse.json` HOT 16
- Error: No known conditions for "./browser" specifier in "msw" package HOT 5
- "Cannot read properties of undefined (reading 'get')" when using generators/async generators as resolvers HOT 2
- `TypeError: crypto.randomUUID is not a function` with msw 2.2.12 HOT 3
- When running Cypress tests in Electron, MSW activation occurs after test completion, causing tests to fail
- First-class Server-Sent Events (SSE) API
- The currently registered Service Worker has been generated by a different version of MSW (undefined) HOT 1
- disable prettier as well HOT 1
- Issue with response resolver types after recent upgrade HOT 2
- "TypeError: response.body.getReader is not a function" for HTTPResponse of JSON type HOT 5
- Preserve the original request URL in "onUnhandledRequest" warnings HOT 3
- The cookies object recevied in the handlers varies depending on the placement of the handler within the `setupServer` function. HOT 5
- Calls to worker.use() remove handlers to same endpoint with different method HOT 1
- Unable to use HttpResponse.error() with TypeScript HOT 3
- How do I implement MSW in a NextJS backend?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from msw.