w3c / ServiceWorker

Service Workers
https://w3c.github.io/ServiceWorker/
Other
3.63k stars 314 forks source link

Worker-As-A-Service service workers #744

Open flaki opened 8 years ago

flaki commented 8 years ago

After revealing the BPG image-format-decoding usecase for service workers both @jakearchibald and @domenic pointed out that the use case misses workers-in-service workers gravely. And that's entirely true, even with ASM.js enabled, and both decompressing and recompressing accomplished in the same Emscriptened (=fast) script, we are still blocking the SW thread for these operations.

There is also bit of an issue with the current implementation. Imagine BPG becoming a big hit in the near future, and suddenly everyone starts using a (more sophisticated) version of this polyfill to add support for older browsers. That would mean all sites on the internet including in their potentially slim service worker script a ~200KByte JavaScript blob for image file transcoding. That is, the same JS in every single worker.

AFAIK the importScripts call supports off-origin script files, so we could theoretically host the lib off a CDN, including it in every service worker from the same location so we could at least save some data traffic and latency on the cached script, but this doesn't feel to me an ideal choice either.

After giving this a little more thought I realized what this service worker actually needs is just a service - image-transpilation-as-a-service. Give me a large blob of BPG data, I'll give you back a JPEG. This idea brings us back to cross-origin service workers/navigator.connect, as we could have, say bpg2jpg.github.io/api/ which has a service worker that accepts post requests with image data, and responds with JPEG images. After being referenced for the first time, the service worker would work as a locally running service, in his own thread, accepting requests from all local clients/websites and serving those asynchronously.

I'm not entirely familiar with the current state of cross-origin service workers (I know fall-through requests are happening but I'm not sure that helps the above usecase), but I remember navigator.connect had a prospect something among these lines.

If anyone could chime in regarding the above that would be great.

Also the question that emerges from the above: do we really need workers in service workers or what we need is some kind of cross-service worker communication (and reimagining our existing worker-based architecture as service-based architectures)? Is there a usecase or limitation that would work with workers-in-service-workers, but could not be realistically transposed into a locally run service.

Two issues that come to my mind:

Even if we couldn't throw away workers-in-service workers just yet, I imagine a lot of the apparent usecases could be solved with above approach, and thus workers could be pushed back to a later point in time (I'd certainly imagine that the Mozilla e-mail case could work this way as well, maybe @jrburke can back me up or correct me in this).

jrburke commented 8 years ago

I see service workers as fast response, low load entry point routers, with regular and shared workers as the intensive processing centers. Service worker is to nginx as regular/shared workers are to backend application/API/database servers.

I can see a bpg2jpg.github.io/api or a navigator.connect working in a similar way: its service worker passes off to a worker to do the heavy lifting. If the service worker does the heavy lifting, then that just sounds like a regular worker to me. But if that heavy lifting also includes fetch URL routing, that will likely lead to slow responses for those URL requests.

I can even see a fancy far future where the browser could decide to put a service worker into a sleep mode, evict it from memory and then rehydrate it once the regular/shared worker finishes its intensive work and gives the response back to the service worker. That is really far away, and has some serious challenges, but by strictly partitioning the responsibility of service worker to just fast response routing, it allows considering those types of optimizations since the range of use is constrained.

So I do not believe cross-origin service worker communication inherently solves the problem we are encountering in the Mozilla Gaia email case. I still prefer to see service workers using regular/shared workers over cross origin service worker communication, if that choice has to be made.

In the email case, the main issue is that browser windows showing email UI need to all use the shared worker already, and it is more efficient from a memory and code location/development reasons for the service worker to communicate with that shared worker. In that case, all the browser windows, shared worker and service worker are on the same domain.

mkruisselbrink commented 8 years ago

I don't think you'd need either navigator.connect or foreign fetch for this usecase. Nothing stops you from having arbitrary many service worker registrations at arbitrary scopes (using the scope solely as an identifier for a particular worker). The existing navigator.serviceWorker.getRegistration API allows you to look up a registration by that identifier, and you can then use the existing postMessage API to communicate between the multiple service workers.

Of course as you point out that still leaves the versioning and synchronized update problem, and maybe sometimes you really do need to run tasks that last longer than a service worker is allowed to run.

Maybe some way shared workers could be made to work is to tie its lifetime to some new kind of UI surface like system tray icons or maybe even better notifications with progress indicators? Allowing service workers to spawn workers that can continue to work even after the service worker (and any clients/UI surfaces) have been closed doesn't seem like the best idea, but if there is at least some UI that shows the user that some background work is being done, that might be enough?

jakearchibald commented 8 years ago

I think the solution here is foreign fetch.

There we'll figure out the versioning etc, then your serviceworker can send a POST containing the BPG to the other serviceworker, and receive a PNG in return.

slightlyoff commented 8 years ago

Agree that foreign fetch is a good first-pass at a solution.

mkruisselbrink commented 8 years ago

Foreign fetch would definitely be (part of) a solution for a BPG-decoding-as-a-service shared service worker. A service worker on some particular origin that does the recoding when it receives a fetch. So yes, I agree that foreign fetch is a good first-pass at a solution for "worker-as-a-service" service workers. And versioning would hopefully not be much of a problem there, since dependencies would pretty much be one-way only.

But that also exposes the problem that now all BPG-decoding requests from all websites run through a single single-threaded service workers. With no ability for service workers to spin up shared and/or dedicated workers, such a service worker can really only process one request at a time.

So I guess my answer indeed misread the original question, and I was more answering the "how could service workers deal with having multiple threads of execution". For that question that maybe wasn't asked, existing postMessage between cooperating same-origin service workers could be one part of the answer; maybe workers that are tied to the lifetime of a FetchEvent (for the BPG decoding example), and/or workers that are tied to the lifetime of a notification-with-progress-bar could be another part for more long running background tasks a service worker might want to perform.

rektide commented 8 years ago

But that also exposes the problem that now all BPG-decoding requests from all websites run through a single single-threaded service workers

756 could address this