Page MenuHomePhabricator

Top-queue stylesheets should be versioned for improved caching
Open, Stalled, MediumPublic

Description

Right now our top-queue stylesheets load without a version parameter, and thus are not cached for long.

This is the case because our page output is cached for upto 30 days and in order to allow rapid deployment following ResourceLoader's design we mustn't hardcode version numbers in the page html.

Instead we have a module metadata manifest. Our top and bottom queue for javascript bundles uses this to form the url for the load request. However the stylesheet request must not depend on JavaScript and as-such can't reference those version number.

The only way around this in the current set-up would be to embed the version with ESI. However that's not currently supported and while ResourceLoader has ESI logic in place for some methods, it doesn't for this one.

Roan (@Catrope) and I have considered doing the request in JavaScript and then falling back to an unversioned one (like now) inside <noscript>.

We did some research in browser support for <link rel=stylesheet> inside noscript in 2011 and concluded that it's supported everywhere except in Opera 11.0. It worked in Opera 10, and the regression was fixed in Opera 11.10. Opera 12 also supported it fine. After Opera 12, Opera switched to Chromium (with the Opera 15 release). So the Opera 12.x legacy branch supports it fine.

Previous comments about this:

See also:

Event Timeline

Krinkle raised the priority of this task from to Medium.
Krinkle updated the task description. (Show Details)
Krinkle added subscribers: Krinkle, ori, Catrope.
Krinkle raised the priority of this task from Medium to High.Jul 3 2015, 12:14 AM

The proposed solution of collapsing top-queue stylesheets into the regular top queue bypasses the need for ESI, and accomplishes the result of having these styles be versioned (strongly cacheable) requests. We'd maintain no-js compatibility through <noscript>

However that would go at the cost of request no longer being scannable by the browser's lookahead parser (by being dynamically generated).

In addition, with the current work on making the top queue asynchronous, this would break the first paint experience by having no CSS at all (not even core skin styles).

Ori and I decided we don't want to go that route and instead deprioritise this in favour of making the top queue asynchronous. This will be blocked on ESI for now.

Krinkle lowered the priority of this task from High to Medium.Oct 2 2015, 10:06 PM

Our currently scheme involves embedding stylesheet urls in the HTML without a version parameter. This means users have a consistent experience within their session. Both from the browser perspective, and site-wide as a whole, given that the stylesheet is cached separately. In addition, fragmentation is minimal (e.g. page A with a different combo url than page B) given the short expiry for the stylesheet.

In order to have both 1) versioned/immutable stylesheet urls and 2) still have a fairly consistent interface within a page view session, requires the general principle of all page views (cached or not) going through a lightweight edge service that embeds a piece of content that is separately cached. In other words, having the versioned url embedded at the end and replaced universally.

This was task was originally aimed at solving the problem with Squid ESI or Varnish ESI. The idea being that we'd replace <link rel="stylesheet" href="load.php?modules=..."> with <esi:include href="load.php?modules...&esi-include=1"/> or something like that, where the esi-include=1 request would be responded with a versioned url for the current version. It seems like ESI isn't gonna happen, but a different (perhaps, better) approach has been suggested.

This idea of using a page-composition service is gaining popularity (T116126, T106099). In a nutshell: A server-side Node.js service implementing a Service Worker-compatible API. It's main job would be simple string manipulation, using basic pre-compiled HTML templates that can be streamed to the client using one or more backend responses. For example, it could easily on-demand craft a versioned stylesheet url based on cached copy of the ResourceLoader manifest.

Krinkle changed the task status from Open to Stalled.May 13 2019, 8:24 PM

Just want to chime in some information for community use cases outside of WMF.

As a third-party wiki admin, having a versioned stylesheet (and startup module, but it's not a part of this ticket) is essential to our caching layers. There are currently no way to invalidate edge and browser cache when modules are updated (e.g. extension and skin updates). Any duration of edge or browser cache becomes problematic when ResourceLoader modules don't match with the HTML. For example, if an extension introduced changes in its DOM element, MW will notify CDN to purge the page accordingly, but not for the stylesheet and scripts. As a result, the page can be broken for users until both the edge and browser cache expire.

Even when we invalidate edge cache manually, stylesheet are still specially painful to hunt down because pages have different stylesheet URLs because of the different combinations of RL modules. After that we have to notify and guide users to clear their browser cache after encountering a broken page, which is not a great experience.

Moving to Radar as this isn't currently actionable, nor is it currently considered urgent in our current perf budget and architecture. The stylesheets are cached long enough to avoid blocking rendering during subsequent page views, whilst also allowing quick deployments and propagation of new changes (at the same rate as the startup module, which is our baseline for deployment propagation).

Also, while it has a limited expiry in TTL for blind/offline/unconditional re-use, browsers and our CDN do both keep it longer around for "free" reuse after a check E-Tag/HTTP-304 validation roundtrip, thus still saving re-transferring of the stylesheet data for well beyond the configured TTL.