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

Add flag to indicate delay-tolerant Request, to enable request coalescing. #184

Open
igrigorik opened this issue Dec 15, 2015 · 5 comments
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: api

Comments

@igrigorik
Copy link
Member

Some requests initiated both by the browser and the application are delay tolerant (for relatively small values of "delay") - e.g. beacons, polling for updates, background sync requests, error reports, etc.

I think we should allow both the browser and applications to signal this when initializing the Request, via an additional flag. When such flag is present, the browser can coalesce network access and improve energy + network efficiency... Waking up the radio incurs a lot of overhead regardless of transfer size (due to timeout logic in the controller that keeps the radio active for some period of time), and coalescing multiple requests to fire at once would help amortize that cost and reduce overall energy footprint.

Implementing this kind of coalescing functionality in app-space is hard / impossible: it requires explicit coordination between all actors that initiate fetches (third party scripts, iframes, plus other browser processes, etc); there isn't sufficient information to infer whether radio is active or not.


In terms of implementation, the browser can do the following:

  • If radio is already active then dispatch request immediately
  • If radio is not active, append request to buffer
    • Whichever happens soonest:
      • Start X second timer, which, if invoked takes all requests in buffer and dispatches them.
      • If network interface becomes active (e.g. another process wakes it up), then take all requests in buffer and dispatch them + cancel timer.
      • If a non-coalesced request needs to be sent, queue it and all requests in the buffer + cancel timer.

In other words, delay-tolerant requests would be sent immediately within at most X seconds from being queued, and sooner if network interface becomes - or is, already - active.

Nothing needs to change in terms of processing on the client: the promise would get resolved when response is received; rejected if request fails. The extra "delay" due to coalescing would look as if there is a long RTT to the server.

In terms of consumers that could leverage this: Beacon API, Reporting API, background sync, and application code that's emitting delay tolerant requests.

@sicking
Copy link

sicking commented Dec 16, 2015

As I mentioned in the other issue, we should think about what parts of the delaying we want to expose to the web page which initiated the request.

If we let the webpage see the timing of when the request goes out, that will severely limit our flexibility in choosing that timing. And we'll have to precisely define what that timing should be so that it's consistent across browsers.

@sicking
Copy link

sicking commented Dec 16, 2015

On a separate note, what is the plan for if the user leaves the page while these requests are still pending?

  • Do they still go out in the background (which is what I believe happens with sendBeacon)?
  • Are they "flushed" and go out right away?
  • Are then simply dropped on the floor and never sent?
  • Do we fire the reject-handler before leaving the page?
  • Do we let the webpage choose?

@igrigorik
Copy link
Member Author

If we let the webpage see the timing of when the request goes out, that will severely limit our flexibility in choosing that timing. And we'll have to precisely define what that timing should be so that it's consistent across browsers.

The webpage will be able to observe when the fetch goes out by checking the fetchStart timestamp on the associated ResourceTiming object. The delta between startTime and fetchStart is the time the request was "blocked" on the client due to unavailable connection (e.g. due to all http/1 connections being occupied), or coalescing delay.

The above doesn't limit us in any way. I do think we should provide some suggested values for how often the UA should flush coalesced requests, but this can be purely advisory.

On a separate note, what is the plan for if the user leaves the page while these requests are still pending?

Do they still go out in the background (which is what I believe happens with sendBeacon)?

If the pending request was queued via sendBeacon then yes, they'll be dispatched whenever visibilityChange transitions to 'hidden'.

Are they "flushed" and go out right away?
Are then simply dropped on the floor and never sent?
Do we fire the reject-handler before leaving the page?
Do we let the webpage choose?

Dropped on the floor and never sent, I think.

  • If you need stronger guarantees then you have sendBeacon.
  • AFAIK, we don't automatically reject active fetch requests when page is being unloaded.
  • We cancel active requests when unloading, firing them doesn't make sense.

@annevk annevk added the addition/proposal New features or enhancements label Jan 7, 2016
igrigorik added a commit to w3c/beacon that referenced this issue Jan 13, 2016
Current implementations do not defer or coalesce beacon requests, and
the definition of Beacon-Age allows it to be omitted when age is 0. As
such, this is backwards compatible change.

The right place to enable this is probably in Fetch API:
whatwg/fetch#184

Assuming above is resolved, Beacon API would simply set a flag to
indicate to fetch that the request is delay tolerant. Fetch would handle
the coalescing logic and append the ~Request-Age header. This would
allow different request types to be batched together (e.g. background
sync, beacons, and so on).
@annevk annevk added the needs implementer interest Moving the issue forward requires implementers to express interest label Aug 23, 2016
@dvoytenko
Copy link

I'd like to pick this thread back up in the context, at the very least, of analytics pings.

I see this solution being instrumental to minimize the loss of analytics pings, while also minimizing negative effects on performance. I did some limited testing on different devices and it showed that simple batching can improve battery utilization by 33-38%. The benefit would be even better if it was possible to align batching with radio "on" times.

The @igrigorik 's description above sounds great. But couple of nuances:

  1. In Fetch API, this feature has to be coupled with keep-alive to avoid data loss.
  2. It's possible to also extend sendBeacon API to explicitly allow batching, but it seems that overall direction is to make Fetch spec stronger, so this is probably optional.
  3. This API has to be opt-in, e.g. via RequestInit.allowBatchingDelay-sort of parameter that would indicate maximum allowed batching delay in milliseconds.
  4. Delayed requests should include additional age-style HTTP header, such as Queue-Delay: milliseconds or similar to allow receiving server service to reconstruct the correct timeline. This header has to be CORS-friendly.

/cc @annevk @tyoshino

@tyoshino
Copy link
Member

Great. Looks the list of requirements is enough for starting spec work.

Given the configurable timeout, we need to define a request pool and timer logic in the spec for batching. I'll give it a try once get some time.

/cc @yutakahirano as he's working on keepalive impl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: api
Development

No branches or pull requests

5 participants