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

buckets and service workers #37

Open
wanderview opened this issue Jun 10, 2021 · 8 comments
Open

buckets and service workers #37

wanderview opened this issue Jun 10, 2021 · 8 comments

Comments

@wanderview
Copy link

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 called getRegistration() on the navigation.serviceWorker ServiceWorkerContainer, though, it would pull from all buckets. This would be done to maintain the behavior that getRegistration() 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:

  1. Do not expose ServiceWorkerContainer on the bucket endpoints.
  2. Add ServiceWorkerRegistration attribute like bucket or policy-bucket that specifies the bucket name.
  3. Allow the attribute to be specified during registration via a dictionary option to 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.

@wanderview
Copy link
Author

FYI @jakearchibald @asakusuma @asutherland @annevk as I'm not sure if you watch this repo.

@asakusuma
Copy link
Contributor

asakusuma commented Jun 10, 2021

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?

@wanderview
Copy link
Author

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.

@asakusuma
Copy link
Contributor

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.

@asakusuma
Copy link
Contributor

asakusuma commented Jun 10, 2021

For instance, the cache api is:

const attachments = await navigator.storage.buckets.open("inbox").caches.open("attachments");

not

caches.open("attachments", {
  bucket: "inbox"
})

@wanderview
Copy link
Author

wanderview commented Jun 10, 2021

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.

@asutherland
Copy link
Collaborator

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.

@wanderview
Copy link
Author

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.

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

3 participants