CentralNotice/Optimizing banner loading

From Wikitech

Currently the CentralNotice banners load approximately 1 second after the rest of the page loads which is annoying.

Ideas

1. Store country in a cookie

One problem is that the GeoIP look-up has to be done after the rest of the page loads (since it isn't reliably fast) and we can't figure out what banners to choose from (if any) until we know what country they are in. We could address this problem by storing the user's country in a cookie so that we can start the banner loading process immediately (without having to wait for the GeoIP lookup).

We would attach the BannerController loading to the SiteNoticeAfter hook since that is the soonest that the script could be called in the page and still be guaranteed to function. If the country cookie is set, it should go ahead and request the banner. If the cookie isn't set, it should wait until the page is finished loading (i.e. wait for the GeoIP lookup).

Result: Using this method, the time it takes from the Sitenotice div first rendering until the banner is actually displayed drops from 1.1 seconds to 0.9 seconds for all page views after the first one (for a page that is squid cached).

2. Remove Ajax call (frontload the banner lists)

It looks like the main delay for CentralNotice banners is caused by the Ajax call to BannerListLoader which takes about half a second. This Ajax call has to be completed before the request for the banner itself can be made. The BannerListLoader could be combined with the BannerController (which is called directly by the page) by taking the following steps:

  • Lower the cache times for BannerController to 5 minutes/5 minutes
  • Include the banner lists for all 239 countries in the BannerController

This will increase the overhead of the BannerController greatly, but the load time may still be less due to not having to wait for the Ajax call.

Result: This approach lowers the banner loading delay from 1.1 seconds to about 0.8 seconds. If combined with Idea #1, however, the loading delay drops to 0.1 second for all loads after the first (at least in a local test environment), which effectively eliminates the banner "bump". The down side is that page loading is delayed by 0.4 seconds (due to the additional frontloading) and might be delayed even more during times of heavy CentralNotice use, like during the fundraiser. (See r99341 for the code used.)

3. Partial page caching

See also: Partial page caching

If we could server-side cache all of a page except for the Sitenotice div, we would be able to eliminate the BannerListLoader Ajax call and just include the appropriate banner list for the user's country in the BannerController (at least for all page loads after the first one). Many commercial sites use partial page caching in a similar fashion for their banner ads. The end result would be similar to Idea #2, but without the increased overhead for the BannerController.

Support for this would need to be implemented in the core MediaWiki code. See https://bugzilla.wikimedia.org/show_bug.cgi?id=32618.

4. Make the GeoIP lookup reliably fast

This would eliminate the need for the Idea #1 work-around, and would allow us to always request the banner immediately, even on the first page load.

This idea would need to be implemented by Ops.

5. Use JS to detect cookie hiding banners and load correct CSS

This would work, but only if we standardized the height of CentralNotice banners.

From Tim Starling (wikitech-l, 20 Nov 2011):

Maybe we could have a <script> tag in the <head> that modifies the
relevant stylesheet rules based on cookies present.

Followed up by Roan Kattouw (wikitech-l, 21 Nov 2011):

This would be fairly doable but would require that determining the
banner size (and whether there is a banner at all) is as lightweight
as possible. Ideally this would be done without AJAX (because all
banner sizes are equal, or should be) and would only rely on a cookie
to determine whether banners are hidden or not.

Krinkle:

stylesheets rules to be set from the <head>:
* #centralNotice display:none/block
* and (if supposed to be visible) height as well

note that right now there is no standardized height for central notices,
making it still required to know the banner to be loaded in advance and 
as such Geolocation and other dependencies of determining the banner.

6. Load BannerLoader locally

To prevent the extra hostname look-up for meta, we could theoretically load the BannerLoader locally from each wiki. This would require:

  1. Adding some new functionality to the Message class to retrieve messages (i.e. banners) from a different wiki. Not sure how difficult this would be.
  2. Redoing some of the analytics logic to account for the new URL schemes (and possibly relying less on query string parameters).

2012 update

To optimize banner loading, we implemented the following changes:

  • Worked with Ops to make the GeoIP lookup reliably fast (and switched away from using a separate hostname)
  • Moved the GeoIP lookup from the footer to the head
  • Rewrote the BannerController to have no self-executing code and instead provide an initialization function
  • Converted the BannerController into a ResourceLoader module and moved it to the head
  • Used the SiteNoticeAfter hook to insert a call to the BannerController initializer from the SiteNotice div itself (the soonest the function can be called and guaranteed to work).
  • Inserted a DNS prefetch into the head for meta.wikimedia.org (the domain the banners are delivered from)

This solution still allows us to have banners of any size with GeoIP targeting. This is probably as optimized as it can get without either pre-loading the banner lists for every country (and thus slowing initial page load time) or implementing partial-page caching.