Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cache Me If You Can: HTTP-Caching and ESI with Symfony

Cache Me If You Can: HTTP-Caching and ESI with Symfony

We all know we should be caching to reduce our application's workload and make it run more efficiently. The HTTP protocol lets us solve this problem quite elegantly, but doing it properly isn't exactly a walk in the park. How do you prevent stale caches? Which HTTP headers should you be using? And what on earth is ESI? This talk will focus on the caching mechanisms HTTP offers us as Symfony developers, and how we can utilize them for maximum benefit.

Marco Petersen

February 27, 2019
Tweet

More Decks by Marco Petersen

Other Decks in Programming

Transcript

  1. WHAT WE'LL COVER ✅ HTTP Caching Basics ✅ Expiration And

    Validation ✅ Caching With Symfony ✅ Edge side includes
  2. OUT OF SCOPE ❌ Varnish, Squid etc. ❌ Application Caching

    ❌ Specific Techniques (eg Russian Doll Caching)
  3. HOW TO MAKE A WEB APPLICATION FASTER > Optimize database

    queries > Use more efficient algorithms > Rewrite Your Backend in a faster language > . . .
  4. TYPES OF CACHES > Browser Caches > Chrome, Firefox >

    Proxy Caches > Squid > Gateway Caches > Varnish, Nginx, Squid, Cloudflare
  5. CACHING MODELS > Expiration > Expires > Cache-Control > Validation

    > ETag / If-None-Match > Last-Modified / If-Modified-Since
  6. THE GAME PLAN 1. Client requests a resource and receives

    a response. 2. Cache intercepts and stores resouce with expiration date. 3. Client requests same resource. 4. Cached resource is returned if it is still fresh. > The server is not hit.
  7. /** * @Cache(expires="+600 seconds") */ public function bar() { return

    $response; } // or public function foo() { $response->setExpires(new \DateTime('+600 seconds')); return $response; }
  8. CACHE-CONTROL THE MULTI-TALENT! > defines several caching directives > for

    now, we're only interested in max-age and s-maxage
  9. MAX-AGE AND S-MAXAGE ARE LIKE EXPIRES, BUT THEY SPECIFY THE

    NUMBER OF SECONDS UNTIL A RESOURCE BECOMES STALE
  10. /** * @Cache(smaxage=600) */ public function bar() { return $response;

    } // or public function foo() { $response->setSharedMaxAge(600); return $response; }
  11. MAX-AGE AND S-MAXAGE > Browser caches only look for max-age.

    > Shared caches look for s-maxage if it exists, otherwise it follows max-age.
  12. THE GAME PLAN 1. Client requests a resource. 2. Server

    returns a response with validation information. 3. Client requests same resource with the validation information. 4. 304 (Not Modified) is returned if the resource is still valid. > The server is hit, but the response is much smaller.
  13. /** * @Cache(Etag="post.getId() ~ post.getUpdatedAt().getTimestamp()") */ public function bar(Post $post)

    { return $response; } // or public function foo(Request $request) { $reponse = new Response(); // Compute the ETag $response->setEtag($etag) if ($response->isNotModified($request)) { return $response; } return $this->render('foo.html.twig', [], $response); }
  14. // server responds HTTP/1.1 200 OK ETag: "qwerasdfyxcv" Lorem ipsum

    dolor sit amet // client requests same resource again GET /foo HTTP/1.1 If-None-Match: "qwerasdfyxcv" // server responds HTTP/1.1 304 NOT MODIFIED
  15. /** * @Cache(lastModified="post.getUpdatedAt()") */ public function bar(Post $post) { return

    $response; } // or public function foo(Request $request, Post $post) { $reponse = new Response(); $response->setLastModified($post->getUpdatedAt()) if ($response->isNotModified($request)) { return $response; } return $this->render('foo.html.twig', [], $response); }
  16. // server responds HTTP/1.1 200 OK Last-Modified: Wed, 21 Oct

    2015 07:28:00 GMT Lorem ipsum dolor sit amet // client requests same resource again GET /foo HTTP/1.1 If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT // server responds HTTP/1.1 304 NOT MODIFIED
  17. BOTH WORK WELL TOGETHER, ALTHOUGH EXPIRATION TRUMPS VALIDATION. // max_age

    > etag $response->setCache([ 'max_age' => 20, 'etag' => $etag, ]);
  18. // public/index.php use App\Kernel; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; // ... $kernel =

    new Kernel($env, $debug); if ('prod' === $env) { $kernel = new HttpCache($kernel); }
  19. // public/index.php use App\Kernel; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; // ... $kernel =

    new Kernel($env, $debug); if ('prod' === $env) { $kernel = new HttpCache($kernel); }
  20. // src/CacheKernel.php namespace App; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; final class CacheKernel extends

    HttpCache { protected function getOptions(): array { return [ 'default_ttl' => 0, ]; } }
  21. // src/CacheKernel.php namespace App; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; final class CacheKernel extends

    HttpCache { protected function getOptions(): array { return [ 'default_ttl' => 0, ]; } }
  22. RECAP > Expiration asks "When does it become stale?" >

    Expires specifies a future point in time > Cache-Control is like Expires, but relative > Validation asks "Is it still fresh?" > Etag specifies an identifier to compare > Last-Modified specifies a point in time in the past > All work well with Symfony!
  23. FINAL RECAP > Cache to bypass your application. > Expiration

    - "When does this expire?" > Validation - "Is this still valid?" > ESI caches fragments of pages separately.
  24. WHERE TO GO FROM HERE > Set up your own

    gateway cache > Read the HTTP/1.1 spec (RFC-2616) > Check out HTTP/2's new features (RFC-7540) > Server Push > Multiplexing