Page MenuHomePhabricator

Preload base modules request
Closed, ResolvedPublic

Description

Mentioned in T131117.


Similar to the preloading of the wiki logo (T100999), it would be interesting to consider preloading for the base modules request.

By design (https://www.mediawiki.org/wiki/ResourceLoader/Features) the base modules url should not be embedded in the HTML document. This is among the reasons why we have a startup module. The startup module is supposed to be the only unversioned/mutable response, for which the url can be safely embedded in cached HTML. From that script we then bootstrap all other scripts.

As such, for the same reason we can't put the <script src> directly in the HTML, we can't emit the Link preload header from the HTML response, ether.

I read through the preload spec (https://w3c.github.io/preload/) to see whether it supports emitting preload headers from a sub resource (e.g. the startup module response could emit the url via a Preload header). While this would be discovered slightly later than if it were on the HTML Document, it is still much earlier than having to wait for the browser to download and parse the whole script, and wait for a good moment to execute it procedurally (especially given it's async/deferred).

I couldn't find in the Preload whether it supports being specified from a non-HTML response or from a sub resource, so I filed an upstream issue about it: https://github.com/w3c/preload/issues/92.

Initial response there indicates that, regardless of current spec wording, the only current implementation (Chrome) does actually support it, and that was intentionally so.

Event Timeline

To make this work, we'll need to extend the concept of "module content" (scripts, styles, messages, templates) to also account for headers (or, at least, Link headers; or perhaps specifically "Resource URLs to preload").

This meta information will need to be supported at a few different levels:

  • ResourceLoaderModule: A new method that returns the information. And expose either as part of getModuleContent, or use directly in ResourceLoader::response.
  • ResourceFileCache: We may need to change the file format for this if we want to store the meta data here too. Alternatively, if producing this information is "fast enough", we could just not cache this (or use object cache).
  • ResourceLoader::sendResponseHeaders: To include the new Link headers.

Then to use it, we would declare in StartupModule that the getStartupModulesUrl() is a candidate for preloading.

Questions:

  • Does a "304 Not Modified" response need to contain the Link headers?
  • Should FileCache mode store these headers somehow? If we want to use these for preloading urls from a stylesheet (T120984), getLinkHeaders() will likely involve as much work as getStyles() would so it wouldn't make sense *not* to cache. On the other hand, I'd really like to avoid having to refactor FileCache to support headers instead of just a response body.

Questions:

  • Does a "304 Not Modified" response need to contain the Link headers?

Hopefully not. But this should be easy to validate experimentally by making the preloaded resource must-revalidate, because then you'll be able to see exactly when the browser fetches it even if it's already got it in cache. (It makes little sense to do this in production though, I only suggest this for testing how preload works.)

Krinkle raised the priority of this task from Medium to High.
Krinkle moved this task from Accepted Enhancement to Assigned on the MediaWiki-ResourceLoader board.

Confirmed with an isolated example that browsers will use the Link headers from their cached response when receiving a 304 Not Modified response without re-sending the Link headers on the 304 response. Test case: https://gist.github.com/Krinkle/d39cd3d4bd30a10fad5e1ae7f60b4c11

Change 365888 had a related patch set uploaded (by Krinkle; owner: Krinkle):
[mediawiki/core@master] [WIP] resourceloader: Add support for modules sending preload headers

https://gerrit.wikimedia.org/r/365888

Change 365889 had a related patch set uploaded (by Krinkle; owner: Krinkle):
[mediawiki/core@master] [WIP] resourceloader: Preload base modules request from startup module

https://gerrit.wikimedia.org/r/365889

Change 365888 merged by jenkins-bot:
[mediawiki/core@master] resourceloader: Add support for modules sending preload headers

https://gerrit.wikimedia.org/r/365888

Change 365889 had a related patch set uploaded (by Krinkle):

[mediawiki/core@master] resourceloader: Preload base modules request from startup module

https://gerrit.wikimedia.org/r/365889

Timeline capture from Chrome (Network: Slow 3G) with and without this patch applied:

Screen Shot 2017-09-01 at 05.03.11.png (379×2 px, 126 KB)

Screen Shot 2017-09-01 at 04.56.01.png (357×2 px, 123 KB)

Change 365889 merged by jenkins-bot:
[mediawiki/core@master] resourceloader: Preload base modules request from startup module

https://gerrit.wikimedia.org/r/365889

Krinkle moved this task from Not yet to Published on the Wikimedia-Performance-publish board.
Krinkle removed a project: Patch-For-Review.

If we have time at the offsite we could sit down and check how we test this locally with WebPageTest or Browsertime to get more reliable metrics (running X number of tests, taking the median etc) and write about it at Wikitech, it would be nice to have a work flow for testing things locally. Or maybe this should actually be task for us?

Using sitespeedio/browsertime (in Docker on Mac, with throttled connectivity), I did a couple of runs against local MediaWiki-Vagrant with git checkouts of mediawiki-core from before and after https://gerrit.wikimedia.org/r/365889.

I confirmed the same impact as from the manual comparison in Chrome. The JS startup/base requests now nicely overlap, and overall page load end is reduced by about 1.1s on slow connections. In case of the slow-3g throttle via browsertime, it went from ~30s to ~28s.