40
DIY Scriptable Cache Guy Podjarny, CTO [email protected] twi;er: @guypod

Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Embed Size (px)

DESCRIPTION

In this webinar, we’ll describe how you can build your own Scriptable Cache based on HTML5 localStorage, making Mobile cache work and giving Desktop Cache a boost. We’ll discuss the value of a Scriptable Cache, show the key elements you’ll need to create, and mention some of the pitfalls you need to beware of.

Citation preview

Page 1: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

DIY Scriptable Cache Guy  Podjarny,  CTO  [email protected]  twi;er:  @guypod  

Page 2: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Agenda

•  Caching 101 •  Mobile & Desktop Scriptable Cache

– Concept – 6 Steps to Building a Scriptable Cache – Advanced Optimizations

•  Q&A

2  

Page 3: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

The Value of a Scriptable Cache

•  A dedicated cache, not affected by other sites •  A robust cache, not cleared by power cycles •  Better file consolidation

– Works in more cases – Cache Friendly –  Less requests without more bytes

•  Enable Advanced Optimizations – Robust Prefetching, Async CSS/JS…

•  The Secret to Eternal Youth

3  

Page 4: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Not For The Faint of Heart!

•  DIY Scriptable Cache isn’t simple – No magic 3 lines of code

•  Requires HTML & Resource modifications – Some of each

•  The code samples are pseudo-code – They don’t cover all edge cases – They’re not optimized – They probably have syntax errors

4  

Page 5: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Caching 101

Page 6: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

What is a Cache?

•  Storage of previously seen data

•  Reduces costs •  Accelerates results

•  Sample savings: – Computation costs (avoid regenerating

content) – Network costs (avoid retransmitting content)

6  

Page 7: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Cache Types

7  

Browser  -­‐  Eliminates  network  Hme  -­‐  Shared  by  one  user  

Gateway  -­‐  Server  resources  from  the  faster  intranet  -­‐  Shared  per  organizaHon  

CDN  Edge  -­‐  reduces  roundtrip  Hme  –  latency  -­‐  Shared  by  all  users  

Server-­‐Side  -­‐  Reduces  server  load  -­‐  Faster  turnaround  for  response  -­‐  Shared  by  all  users  

Page 8: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Caching - Expiry

•  Cache Expiry Controlled by Headers –  HTTP/1.0: Expires –  HTTP/1.1: Cache-Control

•  ETAG/Last-Modified Enables Conditional GET –  Fetch Resource “If-Modified-Since”

•  CDN/Server Cache can be manually purged

8  

Page 9: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Stale Cache

•  Outdated data in cache – Affects Browser Cache the most

•  Versioning – Add a version number to the filename – Change the version when the file changes – Unique filename = long caching – stale cache

9  

file.v1.js  

var  today  =  “11/10/26”   var  today  =  “11/10/27”  

file.v2.js  

Page 10: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Cache Sizes - Desktop •  Ranges from 75MB to 250MB •  Fits about 90-300 pages

– Average desktop page size is ~800 KB •  Cycles fully every 1-4 days

– Average user browses 88 pages/day

10  

Page 11: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Cache Sizes - Mobile •  Ranges from 0 MB to 25MB •  Fits about 0-60 pages (Average size ~400KB) •  Memory Cache a bit bigger, but volatile

11  

Page 12: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Conclusion

•  Caching is useful and important •  Cache sizes are too small

– Especially on Mobile •  Cache hasn’t evolved with the times

– Stopped evolving with HTTP/1.1 in 2004 •  Browser Cache evolved least of all

– Browsers adding smart eviction only now – Still no script interfaces for smart

caching

12  

Page 13: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Scriptable Cache

Page 14: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Scriptable Browser Cache - Concept

•  A cache accessible via JavaScript – Get/Put/Delete Actions

•  What is it good for? – Cache parts of a page/resource – Adapt to cache state –  Load resources in different ways

•  Why don’t browsers support it today? – Most likely never saw the need – Useful only for advanced websites – Not due to security concerns (at least not good

ones)

14  

Page 15: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Intro to HTML5 localStorage

•  Dedicated Client-Side Storage –  HTML5 standard –  Replaces hacky past solutions

•  Primarily used for logical data –  Game high-score, webmail drafts…

•  Usually limited to 5 MB •  Enables simple get/put/remove commands •  Supported by all modern browsers

–  Desktop: IE8+, Firefox, Safari, Chrome, Opera –  BB 6.0+, most others (http://mobilehtml5.org/)

15  

Page 16: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 0: Utilities

16  

var  sCache  =  {  …          //  Short  name  for  localStorage          db:  localStorage,          //  Method  for  fetching  an  URL  in  sync          getUrlSync:  funcHon  (url)  {                  var  xhr  =  new  XMLH;pRequest();                  xhr.open(  ‘GET’,  url,  false);                  xhr.send(null);                  if  (xhr.status==200)  {  

 return  xhr.responseText;                  }  else  {  

 return  null;                  }          }  …}  

Page 17: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 1: Store & Load Resources

17  

var  sCache  =  {  …          //  Method  for  running  an  external  script          runExtScript:  funcHon  (url)  {                  //  Check  if  the  data  is  in  localStorage                  var  data  =  db.getItem(url);                  if  (!data)  {  

 //  If  not,  fetch  it      data  =  getUrlSync(url);  

   //  Store  it  for  later  use    db.setItem(url,  data);  

               }                  //  Run  the  script  dynamically                  addScriptElement(data);          }  …}  

Page 18: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 2: Recover on error

18  

var  sCache  =  {  …          runExtScript:  funcHon  (url)  {                  //  Check  if  the  data  is  in  localStorage                  var  data  =  db  &&  db.getItem(url);                  if  (!data)  {  

 //  If  not,  fetch  it      data  =  $.get(url);    //  Store  it  for  later  use    try  {  db.setItem(url,  data)  }  catch(e)  {  }  

               }                  //  Run  the  script  dynamically                  addScriptElement(data);          }  …}  

Page 19: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 3: LRU Cache – Cache State

19  

var  sCache  =  {  …          //  Meta-­‐Data  about  the  cache  capacity  and  state            dat:  {size:  0,  capacity:  2*1024*1024,  items:  {}  },          //  Load  the  cache  state  and  items  from  localStorage          load:  funcHon()  {                  var  str  =  db  &&  db.getItem(“cacheData”);                  if  (data)  {  dat  =  JSON.parse(x);  }          },            //  Persist  an  updated  state  to  localStorage          save:  funcHon()  {                  var  str  =  JSON.stringify(dat);                  try  {db.setItem(“cacheData”,  str);  }  catch(e)  {  }          },  …  }  

Page 20: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 3: LRU Cache – Storing items

20  

var  sCache  =  {  …          storeItem:  funcHon(name,  data)  {                  //  Do  nothing  if  the  single  item  is  greater  than  our  capacity                  if  (data.length  >  dat.capacity)  return;                  //  Make  room  for  the  object                  while(dat.items.length  &&  (dat.size  +  data.length)  >  dat.capacity)  {                          var  elem  =  dat.pop();  //  Remove  the  least  recently  used  element                          try  {  db.removeItem(elem.name);  }  catch(e)  {  }                          dat.size  -­‐=  elem.size;                    }                  //  Store  the  new  element  in  localStorage  and  the  cache                  try  {    

 db.setItem(name,  data);    dat.size  +=  data.length;    dat.items.push  ({name:  name,  size:  data.length});  

               }  catch(e)  {  }          }  …    

Page 21: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 3: LRU Cache – Getting items

21  

var  sCache  =  {  …          getItem:  funcHon(name)  {                  //  Try  to  get  the  item                  var  data  =  db  &&  db.getItem(name);                  if  (!data)  return  null;                  //  Move  the  element  to  the  top  of  the  array,  marking  it  as  used                  for(var  i=0;i<dat.items.length;i++)  {  

 if  (dat.items[i].name  ===  name)  {              dat.items.unshiw(dat.items.splice(i,-­‐1));              break;    }  

               }                  return  data;          }  …}  

Page 22: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Post Step 3: Revised Run Script

22  

var  sCache  =  {  …          runExtScript:  funcHon  (url)  {                  //  Check  if  the  data  is  in  the  cache                  var  data  =  getItem(url);                  if  (!data)  {  

 //  If  not,  fetch  it      data  =  $.get(url);    //  Store  it  for  later  use    storeItem(url,  data);  

               }                  //  Run  the  script                  addScriptElement(data);          }  …}  

Page 23: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 4: Versioning

23  

//  Today:  File  version  1  sCache.load();  sCache.runExtScript(‘res.v1.js’);  sCache.save();    //  Tomorrow:  File  version  2  sCache.load();  sCache.runExtScript(‘res.v2.js’);  sCache.save();    //  Old  files  will  implicitly  be  pushed  out  of  the  cache    //  Also  work  with  versioning  using  signature  on  content  

Page 24: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

What Have We Created So Far? •  Scriptable LRU Cache

– Enforces size limits – Recovers from errors

•  Dedicated Cache – Not affected by browsing other sites

•  Robust Cache – Not affected by Mobile Cache Sizes – Survives Power Cycle and Process Reset

•  Still Has Limitations: – Only works on same domain – Resources fetched sequentially

24  

Page 25: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 5: Cross-Domain Resources

•  Why Cross Domain? –  Enables Domain Sharding –  Various Architecture Reasons

•  Solution: Self-Registering Scripts –  Scripts load themselves into the cache –  Added to the page as standard scripts –  Note that one URL stores data as another URL

25  

h;p://1.foo.com/res.v1.js  

alert(1);   sCache.storeItem(  ‘h;p://1.foo.com/res.v1.js’,  ’alert(1)’);  

h;p://1.foo.com/store.res.v1.js  

Page 26: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 6: Fetching Resources In Parallel

26  

<script>sCache.load()</script>    <script>  //  Resources  downloaded  in  parallel  doc.write(“<scr”+”ipt  src=‘h;p://foo.com/store.foo.v1.js’></scr”+”ipt>”);  doc.write(“<scr”+”ipt  src=‘h;p://bar.com/store.bar.v1.js’></scr”+”ipt>”);  </script>    <!-­‐-­‐    Scripts  won’t  run  unHl  previous  ones  complete,  and  data  is  cached  -­‐-­‐>  <script>sCache.runExtScript(‘h;p://foo.com/foo.v1.js’);  </script>  <script>sCache.runExtScript(‘h;p://bar.com/bar.v1.js’);  </script>  <!-­‐-­‐    Note  the  different  URLs!  -­‐-­‐>    <script>sCache.save();</script>  

Page 27: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 6: Parallel Resources + Cache Check

27  

var  sCache  =  {  …          loadResourceViaWrite:  funcHon  (path,  file)  {                  //  Check  if  the  data  is  in  the  cache                  var  data  =  getItem(url);                  if  (!data)  {  

 //  If  not,  doc-­‐write  the  store  URL    doc.write(“<scr”+”ipt  src=‘”  +  path  +              “store.”  +  file  +  //  Add  the  “store.”  prefix            “’></scr”+”ipt>”);  

               }          }  …}  

Page 28: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Step 6: Parallel Downloads, with Cache

28  

<script>sCache.load()</script>    <script>  //  Resources  downloaded  in  parallel,  only  if  needed  sCache.loadResourceViaWrite("h;p://foo.com/”,”foo.v1.js”);  sCache.loadResourceViaWrite("h;p://bar.com/”,”bar.v1.js”);  </script>    <!-­‐-­‐    Scripts  won’t  run  unHl  previous  ones  complete,  and  data  is  cached  -­‐-­‐>  <script>sCache.runExtScript(‘h;p://foo.com/foo.v1.js’);  </script>  <script>sCache.runExtScript(‘h;p://bar.com/bar.v1.js’);  </script>  <!-­‐-­‐    Note  the  different  URLs!  -­‐-­‐>    <script>sCache.save();</script>  

Page 29: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

What Have We Created?

•  Scriptable LRU Cache –  Enforces size limits –  Recovers from errors

•  Dedicated Cache –  Not affected by browsing other sites

•  Robust Cache –  Not affected by Mobile Cache Sizes –  Survives Power Cycle and Process Reset

•  Works across domains •  Resources downloaded in parallel

29  

Page 30: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Understanding localStorage Quota

•  Many browsers use UTF-16 for characters –  Effectively halves the storage space –  Safest to limit capacity to 2 MB

•  Best value: Cache CSS & JavaScript –  Biggest byte-for-byte impact on page load –  Lowest variation allows for longest caching –  Images are borderline too big for capacity

•  Remember: Quotas are per top-level-domain –  *.foo.com share the same quota

30  

Page 31: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Advanced Optimizations

Page 32: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Adaptive Consolidation

•  Fetch Several Resources with One Request –  Store them as Fragments

•  Adapt to Browser Cache State –  If resources aren’t in cache, fetch them as one file –  If some resources are in cache, fetch separate files –  Optionally consolidate missing pieces

32  

h;p://1.foo.com/foo.v1.js  

alert(1);   sCache.storeItem(‘/foo.v1.js’,  ’alert(1)’);  sCache.storeItem(‘/bar.v1.js’,  ’alert(2)’);    

h;p://1.foo.com/store.res.v1.js  

h;p://1.foo.com/bar.v1.js  

alert(2);  

Page 33: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Adaptive vs. Simple Consolidation - #2

•  User browsers Page A, then Page B – Assume each JS file is 20KB in Size

33  

Page  A  

<script  src=“a.js”></script>  <script  src=“b.js”></script>  <script  src=“c.js”></script>  

Page  B  

<script  src=“a.js”></script>  <script  src=“b.js”></script>  

OpGmizaGon   Total  JS  Requests   Total  JS  Bytes  

None   3   60KB  

Simple  ConsolidaHon   2   100KB  

AdapHve  ConsolidaHon   1   60KB  

Page 34: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Adaptive vs. Simple Consolidation - #2

•  User browsers Page A, then Page B – Assume each JS file is 20KB in Size

34  

Page  A  

<script  src=“a.js”></script>  <script  src=“b.js”></script>  <script  src=“c.js”></script>  

Page  B  

<script  src=“a.js”></script>  <script  src=“b.js”></script>  <script  src=“c.js”></script>  <script  src=“d.js”></script>  

OpGmizaGon   Total  JS  Requests   Total  JS  Bytes  

None   4   80KB  

Simple  ConsolidaHon   2   140KB  

AdapHve  ConsolidaHon   2   80KB  

Page 35: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Adaptive vs. Simple Consolidation - #3

•  External & Inline Scripts are often related •  Breaks Simple Consolidation •  Doesn’t break Adaptive Consolidation

35  

Page:   <script  src=“a.js”></script>  <script>  var  userType  =  “user”;  If  (mode==1)  userType  =  “admin”;  </script>  <script  src=“b.js”></script>  

<script>sCache.runExtScript(‘a.js’)</script>  <script>  var  userType  =  “user”;  If  (mode==1)  userType  =  “admin”;  </script>  <script>sCache.runExtScript(‘b.js’)</script>  

a.js   var  mode=1;  

b.js   alert(userType);  

StoreAll.js  sCache.storeItem(‘a.js’,’var  mode=1;’)  sCache.storeItem(‘b.js’,’alert(userType);’)  

OpHmized  Page:  

Page 36: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Robust Prefetching

•  In-Page Prefetching – Fetch CSS/JS Resources at top of page, to be

used later •  Next-Page Prefetching

– Fetch resources for future pages •  Robust and Predictable

– Not invalidated due to content type change in FF – Not invalided by cookies set in IE – Not reloaded when entering same URL in Safari – …

36  

Page 37: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Async JS/CSS

•  Async JS: Run scripts without blocking page – Doable without Scriptable Cache – Scriptable Cache allows script prefetching – Eliminates need to make fetch scripts block

•  Async CSS: Download CSS without blocking – CSS ordinarily delay resource download & render – You can’t always know when a CSS file loaded – Scriptable Cache enables “onload” event – Can still block rendering if desired

37  

Page 38: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Summary

•  Caching is good – you should use it! •  Scriptable Cache is better

– More robust – More reasonably sized on Mobile – Enables important optimizations

•  The two aren’t mutually exclusive –  “store” files should be cacheable –  Images should likely keep using regular cache

38  

Page 39: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

Or… Use the Blaze Scriptable Cache! •  Blaze automates Front-End Optimization

–  No Software, Hardware or Code Changes needed –  All the pitfalls and complexities taken care of

•  Blaze optimizes Mobile & Desktop Websites –  Applying the right optimizations for each client

See how much faster Blaze can make your site with our Free Report: www.blaze.io Contact Us: [email protected]

39  

Page 40: Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache

DIY Scriptable Cache Guy Podjarny, CTO

[email protected] twitter: @guypod

QuesGons?