Post on 10-Apr-2017
Going to production ona Raspberry Pi with
varnish tags
Who am I?
Jérémy DERUSSÉ
Web Technical Leader
AptusHealth
@jderusse
How to get fast responsesfrom a Symfony application?
you can't
How to get fast responsesfrom a Symfony application?your backend
you can't
Solution
using http shared cachedescribe in RFC-2616 RFC-7234
Integration in Symfonyuse Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; /** * @Cache(smaxage="3600") */ public function indexAction() { // ... }
Advanced usage with FriendsOfSymfony/FOSHttpCacheBundle
Issue #1Unsharable resources
private resources (ie. invoice, shopping cart, ...)per role representations
Solutionvary cache on user/role/othersee SfLive 2015 Jérôme Vieilledent & David Buchmann Repousser les limites : HTTP cache et utilisateurs connectés
Issue #2Cache invalidation
“ There are only two hard things in Computer Science: cacheinvalidation and naming things.
Phil Karlton
Cache models
Validation modeletaglast-modified
drawbacks
application is bootedhard to implement
Expiration modelexpirescache-control
drawback
no control on invalidation
cache modelIn a real world
Backend can't validate every cache HITLife time is not predictable
BUT
Varnish can be requested to partially invalidate responsesBackend knows when resources changeResponses are build on top of resources
curl -I "http://varnish.myapp.com/c"
curl -I "http://varnish.myapp.com/b"
curl -I "http://varnish.myapp.com/a"
HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo,Bar
HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo
Varnish tags
curl \ -X "BAN" \ -H "X-Cache-Tags: Foo" \ "http://varnish.myapp.com"
HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Bar,Qux
Varnish tagscurl "http://varnish.myapp.com/posts/42"
HTTP/1.1 200 OK Cache-Control: public, s-maxage=86400 X-Cache-Tags: Post:42,Author:12,Comment:314,Comment:1337 { "id": 42, "title": "My blog post.", "body": "Lorem Ipsum.", "author": { "id": 12, "username": "jderusse" }, "comments": [ { "id": 314, "message": "Wow such post" }, { "id": 1337, "message": "much performance" } ] }
Automate Tagging
Tagging Response1. Collect displayed resources2. Generate resource identifier3. Tag response
Automate TaggingTagging Response - 1. Collect displayed resources
namespace App\EventListener; use JMS\Serializer\EventDispatcher\Events; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { public function onPostSerialize(ObjectEvent $event) { $resource = $event->getObject(); // TODO } public static function getSubscribedEvents() { return [ [ 'event' => Events::POST_SERIALIZE, 'format' => 'json', 'method' => 'onPostSerialize', ], ]; } }
Automate TaggingTagging Response - 2. Generate resource identifier
namespace App\EventListener; use App\Tag\TagExtractorInterface; use FOS\HttpCacheBundle\Handler\TagHandler; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onPostSerialize(ObjectEvent $event): void { //... $tags = $this->tagExtractor->extract($event->getObject()); } //... }
Automate TaggingTagging Response - 3. Tag response
namespace App\EventListener; use App\Tag\TagExtractorInterface; use FOS\HttpCacheBundle\Handler\TagHandler; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; private $tagHandler; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onPostSerialize(ObjectEvent $event): void { $tags = $this->tagExtractor->extract($event->getObject()); $this->tagHandler->addTags($tags); } //... }
Automate Tagging
Tagging Response1. Collect displayed resources2. Generate resource identifier3. Tag response
Invalidate cache1. Listen changes2. Generate resource identifier3. Call varnish
Automate TaggingInvalidate Cache - 1. Listen changes
namespace App\EventListener; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Events; class DoctrineInvalidationTagListener implements EventSubscriber { public function getSubscribedEvents() { return [Events::onFlush]; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); foreach ($uow->getScheduledEntityUpdates() as $resource) { // TODO } foreach ($uow->getScheduledEntityDeletions() as $resource) { // TODO } } }
Automate TaggingInvalidate Cache - 2. Generate resource identifier
namespace App\EventListener; use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); $tags = []; foreach ($uow->getScheduledEntityUpdates() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } foreach ($uow->getScheduledEntityDeletions() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } // TODO } }
Automate TaggingInvalidate Cache - 3. Call varnish
namespace App\EventListener; use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; use FOS\HttpCache\Handler\TagHandler; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onFlush(OnFlushEventArgs $eventArgs) { // ... $this->tagHandler->invalidateTags($tags); } }
Automate Tagging
Tagging Response1. Collect displayed resources2. Generate resource identifier3. Tag response
Invalidate cache1. Listen changes2. Generate resource identifier3. Call varnish
Enjoy
Silver bullet?
Works well whenHIT >> MISSRead >> WriteApplication knows resources used to buildresponse
DrawbackOperations are not AtomicB ackend handles writesBackend knows infrastructureIt slows writes
Demo
Software - env=dev
- fetch=lazy
symfony/symfonydoctrine/doctrine-bundle
friendsofsymfony/rest-bundlejms/serializer-bundle
friendsofsymfony/http-cache-bundle
marmelab/admin-on-rest
Demo
HardwareRaspberry Pi Orange Pidocker
MySQLNGINXPHP7-FPMVarnish
$9.59
Demo
ab -n 8000 -c 26 192.168.1.17:81/comments/1This is ApacheBench, Version 2.3 <$Revision: 1757674 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.netLicensed to The Apache Software Foundation, http://www.apache.org/
Server Software: nginx/1.10.3Server Hostname: 192.168.1.16Server Port: 80
Document Path: /comments/1Document Length: 576 bytes
Concurrency Level: 26Time taken for tests: 26.912 secondsComplete requests: 200Failed requests: 0Total transferred: 193400 bytesHTML transferred: 115200 bytesRequests per second: 7.43 [#/sec] (mean)Time per request: 3498.553 [ms] (mean)Time per request: 134.560 [ms] (mean, across all concurrentTransfer rate: 7.02 [Kbytes/sec] received
Connection Times (ms) min mean[+/-sd] median maxConnect: 1 2 1.4 1 6Processing: 546 3342 1156.7 3020 7204Waiting: 546 3342 1156.7 3020 7204Total: 550 3344 1156.5 3021 7205
in numbers
ab -n 8000 -c 26 192.168.1.17/comments/1This is ApacheBench, Version 2.3 <$Revision: 1757674 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.netLicensed to The Apache Software Foundation, http://www.apache.org/
Server Software: nginx/1.10.3Server Hostname: 192.168.1.17Server Port: 80
Document Path: /comments/1Document Length: 576 bytes
Concurrency Level: 26Time taken for tests: 2.340 secondsComplete requests: 8000Failed requests: 0Total transferred: 8948504 bytesHTML transferred: 4608000 bytesRequests per second: 3418.52 [#/sec] (mean)Time per request: 7.606 [ms] (mean)Time per request: 0.293 [ms] (mean, across all concurrentTransfer rate: 3734.21 [Kbytes/sec] received
Connection Times (ms) min mean[+/-sd] median maxConnect: 0 2 0.8 2 7Processing: 2 5 3.3 5 55Waiting: 2 5 3.3 4 55Total: 2 8 3.3 7 56
app varnish
Thank You
Questions?
Credits
http://linuxgizmos.com/10-dollar-orange-pi-one-pits-quad-core-cortex-a7-against-pi-zero/http://toutsurlesbisounours.centerblog.net/rub-bisounours-3eme-generation-.htmlhttps://de.wikipedia.org/wiki/Carambarhttps://rcgo.com.br/recursos.html