Open wanderview opened 3 years ago
FYI @jakearchibald @asakusuma @asutherland @annevk as I'm not sure if you watch this repo.
Also, it seems like there could be eviction policies on the bucket that would be relevant to service workers; e.g. expire the bucket after a certain time period.
I like this idea. It would make client - worker interactions simpler if you knew you weren't going to have a really old service worker running. Any other use cases for needing the storage policy applied to the service worker itself?
We may want to revert this part of a recent PR of mine:
Storage policies do not affect the service worker registrations of a given bucket.
I haven't thought much about how to handle multiple service workers (mainly because it's not a tenable setup for us until scope pattern matching lands), but it's unclear to me why you would need to be able to identify the bucket given a ServiceWorkerRegistration
If you called getRegistration() on the navigation.serviceWorker ServiceWorkerContainer, though, it would pull from all buckets
what does "pull from all buckets" mean? is there a global default bucket?
Right, the idea here is that all service worker registrations would still all be mixed together in a common pool even if they are assigned to different logical buckets. This is different than how every other storage type works with buckets. The motivation is so that when a navigation occurs and we need to match the request URL against a scope we need a single collection of registrations to consider. If we partitioned SW in buckets then you could have two registrations with the same scope in different buckets and it would not be clear which one to pick to control the new client.
The current proposal is to put a subset of ServiceWorkerContainer
on the bucket object: https://github.com/WICG/storage-buckets/blob/gh-pages/explainer.md#storage-buckets-and-service-workers
const inboxBucket = await navigator.storage.buckets.open("inbox");
await inboxBucket.serviceWorker.register("/inbox-sw.js", { scope: "/inbox" });
const registrations = inboxBucket.serviceWorker.getRegistrations();
Sounds like the other option is:
await navigator.serviceWorker.register("/inbox-sw.js", {
scope: "/inbox",
bucket: "inbox"
});
const registrations = navigator.serviceWorker.getRegistrations({ bucket: "inbox" });
In a vacuum, I don't have a strong opinion here, but I think the existing proposal feels more consistent with how other bucket APIs work, where you access bucketed objects through the bucket JS object.
For instance, the cache api is:
const attachments = await navigator.storage.buckets.open("inbox").caches.open("attachments");
not
caches.open("attachments", {
bucket: "inbox"
})
I think the existing proposal feels more consistent with how other bucket APIs work, where you access bucketed objects through the bucket JS object.
I guess I find it confusing that the underlying semantics are different, but we try to make the API shape the same. It seems like a different API shape would help communicate the different semantics.
For example, with cache_storage you can do:
const inbox_attachments = await navigator.storage.buckets.open("inbox").caches.open("attachments");
const doc_attachments = await navigator.storage.buckets.open("documents").caches.open("attachments");
And have two different cache objects with the same name "attachments" in different buckets.
But for service workers you can't do this since we need a single global namespace for scopes.
I don't have a strong opinion here. In initial discussions I think there was a shared understanding that there wasn't a perfect answer here because of the issues @wanderview raises of tensions between API consistency and the realities of ServiceWorkers residing in a global scope namespace.
I would tend to favor doing whatever is the least amount of work for whoever writes the spec changes for ServiceWorkers to pose it in terms of buckets and bottles! In the event we think it's pretty much the same either way, then whatever seems to be the least confusing when written up as a "Writing your first ServiceWorker that uses buckets" tutorial seems good.
But in the great spirit of "why not just do both?", how bad it would be to do what @wanderview proposes but also if the myBucket.serviceWorker.register(a, b)
was just defined as a convenience method that amounts to navigator.serviceWorker.register(a, { bucket: myBucket.name, ...b})
and similarly for the getRegistration calls? It seems like this would be the least awkward to explain in documentation.
But in the great spirit of "why not just do both?", how bad it would be to do what @wanderview proposes but also if the myBucket.serviceWorker.register(a, b) was just defined as a convenience method that amounts to navigator.serviceWorker.register(a, { bucket: myBucket.name, ...b}) and similarly for the getRegistration calls? It seems like this would be the least awkward to explain in documentation.
So I think the concern I have with this is that if you registered the same scope in a different bucket using the bucket endpoints it would appear to silently delete the registration in the other bucket. Its strikes me that this would be surprising, but maybe I'm wrong.
Previously I had been assuming service workers would not be part of non-default buckets. I didn't really understand the use case. Recently, however, it was mentioned developers want service workers in buckets so they can ensure the SW registration is evicted along with other related storage in the same bucket. Also, it seems like there could be eviction policies on the bucket that would be relevant to service workers; e.g. expire the bucket after a certain time period.
So the idea would be to associate service workers with buckets for the purposes of eviction and policy. Service workers in different buckets, however, would not be partitioned from one another. For any given scope there would still only be one registration for the entire origin, but it could be in any bucket.
What would the API shape of this be though?
One suggestion was to expose ServiceWorkerContainer on the bucket object just like any other storage type. If accessed that way you would get something that looked partitioned; e.g.
getRegistration()
would only return registrations in that bucket. If you calledgetRegistration()
on thenavigation.serviceWorker
ServiceWorkerContainer, though, it would pull from all buckets. This would be done to maintain the behavior thatgetRegistration()
returns the SW that would control the current page.This proposal seems somewhat weird and magical to me, though. We expose ServiceWorkerContainer on the bucket endpoints as if they are partitioned, but they really are not partitioned.
An alternative would be to:
bucket
orpolicy-bucket
that specifies the bucket name.register()
I think this would make it a bit clearer that the service workers are still all in the same unpartitioned pool of registrations, but that they are associated with buckets for the purposes of policy/eviction.
This alternative solution, though, does raise the question "can a registration move from one bucket to another"? The
register()
option approach supports this but we would have to define exactly what the semantics are.