(c) 2009 Facebook, Inc. or its licensors. "Facebook" is a registered trademark of Facebook, Inc.. All rights reserved. 1.0
May 15, 2015
(c) 2009 Facebook, Inc. or its licensors. "Facebook" is a registered trademark of Facebook, Inc.. All rights reserved. 1.0
Improving Facebook's Performance with AJAX and Browser Caching
The AJAX Experience 2009Sep 14-16, 2009 Boston, MA
Changhao Jiang and David Wei
Site speed is critical to Facebook
▪ Facebook site:
▪ More than 250 million users around the world
▪ Billions of requests every day
▪ Site speed has a huge impact
▪ Affects user engagement, user growth and business metrics
▪ One of the highest priority tasks at Facebook.
Front end performance
▪ Most important performance metric: TTI (Time to interact) latency
▪ From the time user clicks on a link to he/she is able to interact with the page.
▪ Front end latency accounts for more than 70% of total TTI latency:
▪ Front end latency: > 70%
▪ Include network latency and browser render time (CSS, JavaScript, images)
▪ Back end latency: < 30%
▪ Include page generation time (PHP, DB, Memcache, etc)
Front end engineering at Facebook
▪ Performance measuring/monitoring/debugging tools
▪ Static resource (JavaScript, CSS, images) optimizations:
▪ Packaging, spriting, Dynamic JavaScript loading, and CSS reuse
▪ Software abstractions
▪ Enables algorithmic / system level optimization of page rendering process
Two software abstractions at Facebook
▪ Dramatically improve Facebook’s TTI latency:
▪ Quickling: transparently ajaxifies the whole web site
▪ PageCache: caches user-visited pages in browser
Quickling: Ajaxify the Facebook site
Motivation
▪ Goal: make the site faster by removing redundant work across pages
▪ Approach: use AJAX to render pages
▪ Requirement: transparent to users and developers
▪ Simulate browser behavior
▪ Allow developers to focus on product features (fast moving)
Redundant work
▪ JavaScript / CSS library shared across pages
▪ Product features common to all pages:
▪ e.g. Top navigation bar, Facebook Chat.
load unload load unloa
d load unload load unloa
d
load unload
Full page load
Ajax call
Remove redundant work via Ajax
Page 1 Page 2 Page 3 Page 4
Page 1 Page 2 Page 3 Page 4
Use session
Use session
How Quickling works?1. User clicks a link or back/forward button
2. Quickling sends an ajax to server
4. Quickling blanks the content area
3. Response arrives
5. Download javascript/CSS
6. Show new content
Architecture components
▪Link Controller
▪HistoryManager
▪Bootloader
▪Busy Indicator
LinkControllerIntercept user clicks on links▪ After DOM content is ready, dynamically attach a handler to all link clicks
(could also use event delegation)
$(‘a’).click(function() {
// ‘payload’ is a JSON encoded response from the server $.get(this.href, function(payload) { // Dynamically load ‘js’, ‘css’ resources for this page. bootload(payload.bootload, function() {
// Swap in the new page’s content $(‘#content’).html(payload.html)
// Execute the onloadRegister’ed js code execute(payload.onload) }); }});
HistoryManagerEnable ‘Back/Forward’ buttons for AJAX requests▪ Set target page URL as the fragment of the URL
▪ http://www.facebook.com/home.php
▪ http://www.facebook.com/home.php#/cjiang?ref=profile
▪ http://www.facebook.com/home.php#/friends/?ref=tn
▪ Detect back/forward button-click:
▪ W3C browsers: periodically poll the “location.hash” to see if value changes
▪ IE: inject an invisible iframe, whose ‘src’ attribute will be updated by browser.
BootloaderLoad static resources via ‘script’, ‘link’ tag injection▪ Before entering a new page:
▪ Dynamically download CSS/Javascript for the next page
▪ Javascript:
▪ CSS
var script = document.createElement('script'); script.src = source; script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script);var link = document.createElement('link'); link.rel = "stylesheet"; link.type = "text/css"; link.media = "all" ; link.href = source; document.getElementsByTagName('head')[0].appendChild(link);
Bootloader (cont.)Load static resources via ‘script’, ‘link’ tag injection▪ Reliable callbacks to ensure page content shown after depended
resources arrive:
▪ JavaScript resources:
▪ ‘bootloader.done(resource_name)’ injected at the end of all JS packages.
▪ CSS resources
▪ ‘#bootloader_${resource_name} {height: 42px}’ injected in all CSS packages.
▪ Bootloader polls the corresponding invisible div height to determine if the CSS package arrives.
Busy Indicator
▪ Set link cursor to hourglass immediately
▪ Set document.body’s cursor to hourglass
▪ Use iframe transport instead of XHR.
Simulate browser events
▪ Simulate browser events for Quickling response
▪ DomContentReady
▪ onload
▪ onbeforeunload
▪ onunload
▪ Stub out setTimeout, setInterval:
▪ Make sure timer functions are uninstalled upon leaving a page
▪ Occasionally, developers explicitly clean up JS states before leaving a page
CSS issue
▪ CSS rules accumulated over time
▪ Render time is roughly proportional to the # of CSS rules
▪ Automatically unload CSS rules before leaving a page
Memory consumption
▪ Browser memory consumption increase overtime
▪ Periodically force full page load to allow browsers to reclaim memory
Permanent link
▪ Problem: Allow users to save/send browser URL:
▪ For example:
▪ http://www.facebook.com/home.php#/profile.php
▪ Browser will request ‘/home.php’ instead of ‘/profile.php’,
▪ How to make browser displays ‘/profile.php’ instead of ‘/home.php’?
▪ Solution:
▪ Prelude JavaScript code in every page that checks for ‘location.hash’, redirect if it contains a target page URL.
Current status
▪ Turned on for FireFox and IE users: (>90% users)
▪ ~60% of page hits to Facebook site are Quickling requests
Render time improvement
50% 75%
Full page load Quickling
50% 75%
40% ~ 50% reduction in render time
profile.php home.php
PageCache: Cache visited pages at client side
PageCache
▪ Motivation: temporal locality
▪ Data accessed now is likely to be accessed again soon.
▪ Cache the data at the client:
▪ Improve latency
▪ Reduce server load
PageCacheCache user visited pages in browsers
▪ Some pages are likely to be revisited soon
▪ A typical user session:
▪ home -> profile -> photo -> home -> notes -> home -> photo -> photo
▪ Home page visited every 3 ~ 5 page views
▪ Back/Forward button
Possible solution 1
▪ Modify HTTP headers
▪ ‘Cache-Control’, ‘Expires’ headers
▪ Cache on disk, managed by browser
▪ Pros:
▪ Easy to implement
▪ Cons:
▪ Only good for static content that rarely changes
Possible solution 2
▪ Cache DOM tree
▪ Simulate tab-switching
▪ Cache in memory, managed by application with JavaScript
▪ Pros:
▪ Flexible (full control over cache with JavaScript)
▪ Restore a page from cache is super fast.
▪ Cons:
▪ Difficult to be transparent to developers
▪ Hard to isolate cache pages, global variables clobbering across pages
Possible solution 3 (adopted approach)
▪ Cache server response
▪ Cache server’s response
▪ Cached in memory, managed by application with JavaScript
▪ Pros:
▪ Flexible (full control over cache with JavaScript)
▪ Almost transparent to developers
▪ Cons:
▪ Have to keep track of changes made to cached response
▪ Reconstruct DOM and global JavaScript states when restoring a page
How PageCache works?1. User clicks a link or back button
2. Quickling sends ajax to server
4. Quickling blanks the content area
3. Response arrives
5. Download javascript/CSS
6. Show new content
2. Find Page in the cache
3.5 Save response in cache
Incremental updates
Cached version
Restored version
Incremental updatesPoll server for incremental updates via AJAX calls
▪ Provides an ‘onpagecacheRegister’ API to developers:
▪ Invoked when a page is restored from cache.
▪ Used to send AJAX to server for incremental updates, refresh ads, etc.
▪ Blank the content area before incremental updates arrive to avoid flash of stale contents.
In-page writes
Cached version
Restored version
In-page writesRecord and replay
▪ Automatically record all state-changing operations in a cached page
▪ All AJAX calls using ‘POST’ method, or those explicitly specified ‘replayable’ will be added to cached content.
▪ Automatically replay those operations when cached page is restored.
▪ Cached AJAX handler callback functions already contain all the contextual information about the recorded operations due to JavaScript closure.
Cross-page writes
Cached version
Restored versionState-changing op
Cross-page writesServer side invalidation
▪ Instrument server-side database API, whenever a write operation is detected, send a signal to the client to invalidate the cache.
▪ The signal (a.k.a. cache invalidation message) contain information about what persistent data has changed.
▪ Upon receiving cache messages, the client side can flush the whole cache or refresh only those affected page components via AJAX.
Current status
▪Deployed on production
▪Cache in memory
▪Turned on for home page
20%
~20% savings on page hits to home page
Render time improvement
3X ~ 4X speedup in render time vs Quickling
50% 75%
Full page load Quickling PageCache
Summary
▪ Quickling:
▪ Dramatically improves render time by eliminating redundant work.
▪ PageCache:
▪ improves user latency and reduces server load.