HEAD FIRST INTO SYMFONY

CACHE, REDIS & REDIS CACHE
André Rømcke (@andrerom)

VP Technical Services & Support @eZSystems
Oct. 18th 2019 - Verona - sfday Italy 2019
https://joind.in/talk/c5187
So what is this talk about?
1. Bit of background on where eZ fits in this
2. Symfony Cache, Cache & Cache tagging
3. Memcached vs Redis
4. Redis & Redis Cluster
5. New Adapters in Symfony 4.3
6. In Practice with symfony/demo & redis-cli
7. BONUS: Edge cases, edge case & more edge
cases
Who?
๏André Rømcke | @andrerom
๏Economics, Consulting, Engineering, Lead, VP Engineering, now Services & Support
๏Contributed to Symfony, FOS, Composer, PHP-FIG, Docker, attempts for PHP…
๏Works for …
๏eZ Systems AS | ez.no
๏Global 75+ people across 7+ countries, with partners & community in many many more
๏Maker of …
๏eZ Platform | ezplatform.com
๏Open Source CMS, a feature rich, flexible Full & Headless, on Symfony since 2012
๏Also exists in commercial flavors: eZ Platform Enterprise & eZ Commerce
๏Uses …
Symfony Cache in eZ Platform
๏Moved over from Stash in 2017
๏Heavily relies on Cache tagging feature
๏Since then contributed improvements on performance issues with Redis/Redis Cluster/…
๏Since spring ships with own optimized TagAware adapters for Redis and FileSystem
➡Contributed these to Symfony 4.3 & Currently improving them for 4.4
Lets get started:
Symfony Cache
ez.no
Symfony Cache Component
๏PSR-6 compliant Cache component
๏Aims to be fast: Among others supports multi get calls to Redis and Memcached
๏Is progressively being used in several places in Symfony Framework, e.g.:
๏PropertyInfo
๏Serializer
๏Validator
๏(…)
๏.. maybe HTTP Cache at some point
ez.no
Symfony Cache Adapters
๏Adapters:
๏APCu (per proces cache)
๏Array (in memory per request, mainly for testing)
๏Chain (chain several adapters after each-other)
๏Doctrine
๏FileSystem (Also PHPFile & PHPArray for immutable opcache cache)
๏Proxy (To reuse other PSR-6 implementations)
๏Redis
๏Memcached
๏And “TagAware”..
ez.no
Hold on, what’s this with Tags?
๏Data/Entities often has secondary indexes
๏Eg. In a CMS it’s things like:
๏content type id
๏section id
๏location id, location path ids, …
๏Sometime operations in your application affect those => affecting bulk of entities
๏So to cache entities, or the result of them (e.g. view):
๏.. you might want a secondary index in the cache to avoid having to iterate database
๏E.g. key: ez-content-66 tags: content-66, type-2, location-44 (…)
ez.no
TagAware: Secondary index for invalidation
/**
* Interface for invalidating cached items using tags.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TagAwareAdapterInterface extends AdapterInterface
{
/**
* Invalidates cached items using tags.
*
* @param string[] $tags An array of tags to invalidate
*
* @return bool True on success
*
* @throws InvalidArgumentException When $tags is not valid
*/
public function invalidateTags(array $tags);
}
ez.no
TagAwareAdapter
๏Wraps your normal adapter, stores item Tags in separate key
๏Does a separate lookup for expiry timestamp for tags => “2-RTT”
Distributed cache backends:

Memcached vs Redis
ez.no
Memcached vs Redis: Overview
๏Vivamus commodo ipsum in hendrerit iaculis.
๏Donec congue erat nibh, ac luctus erat accumsan tempor.
๏Mauris bibendum ac eros eu tempor. Duis libero libero, luctus quis posuere quis, porta vel
turpis.
๏Sed augue dolor, laoreet eget turpis eget, laoreet vehicula neque. Donec sit amet dolor vel
lorem ultrices facilisis ac sit amet velit.
๏Aenean nisi nisi, aliquet in pulvinar mollis, vulputate vitae nulla. Donec non ligula ac diam
volutpat dapibus.
๏Aliquam eleifend turpis id ligula accumsan luctus. Donec sem justo, scelerisque eget
condimentum ut, semper eget nulla.
๏Vivamus ultricies massa lectus, id varius orci sodales quis.
Memcached Redis
Multi operations (get, set, ..) V V
Datatypes String
String, List, Set, Hash, Sorted Set, …
Streams
Control over eviction X V
Persistance X V
Pipeline / Lua X V

Not on Redis Cluster
Multiserver V V

Mainly Redis Cluster OR Redis Sentinel
Multithreaded V X

But multi process with Redis Cluster *
* Redis 6 is adding partly threading with background thread handling slow operations
ez.no
Memcached vs Redis: “Greatly Simplified”
Massive concurrent multi threaded, simple Key-Value store, that
can take advantage of lots of CPU cores, and scales up across
many servers
VS
Datastore for different advance data types to do sophisticated
operations on, which is single threaded, but can be scaled up
with Redis Cluster with some limitations to capabilities.
Topics around:

Redis & Redis Cluster
Datatypes
๏ Strings
๏ Lists
๏ Sets
๏ SortedSets
๏ Hashes
๏ Bitmaps
๏ HyperLogLogs
๏ Streams (As of Redis 5.0)
Commands
๏There are 227 commands and counting
๏There are generic key, cluster, connection, Pub/Sub, Scripting, Transaction commands
๏And Datatypes allows for specific operations, aka commands as well
๏For String it goes beyond flavors of GET / SET, there is e.g. APPEND, BITPOS, DECR, …
๏E.G. for "SET”:
๏SADD, SREM, SPOP
๏SCARD - Returns the set cardinality (number of elements) of the set stored at key
๏SDIFF, SINTER, SUNION, SMOVE
๏SMEMBERS, SSCAN
Eviction
maxmemory-policy:
๏noeviction
๏volatile-random
๏volatile-ttl
๏volatile-lru
๏volatile-lfu (As of Redis 4.0)
๏allkeys-random
๏allkeys-lru
๏allkeys-lfu (As of Redis 4.0)
Redis Cluster: processe
๏Allows you to scale up Redis by running several instances
๏Multi process on same server and/or across servers
๏Coordinates cache across “cache slots", deals with replication, …
๏Unlike Memcached, several operations has limitations on Cluster
Redis Cluster: limitations
๏Does not support “pipline”: capability to perform several operations in one call
๏PHPRedis mainly supports multi operations on MGET and MSET with cluster
๏Examples of affected operations:
๏RENAME won’t work if the new key ends up in another “Cache Slot”
๏EVAL (Lua Script) likewise can only be given keys that maps to same node
ERR	CROSSSLOT	Keys	in	request	don't	hash	to	the	same	slot
New in Symfony 4.3:

FilesystemTagAwareAdapter &
RedisTagAwareAdapter
ez.no
Tags storage
๏Moves tags to be a “relation” to cache key, instead of expiry time
๏Avoids the lookup on getItem(s), instead does it on invalidation
๏FilesystemTagAwareAdapter:
๏4.3: Symlinks for tag “relation”, 4.4: uses a file to list “relations”
๏RedisTagAwareAdapter:
๏Uses “Set” for tag “relation”
๏Forces a default expiry on cache which does not have expiry
ez.no
Why the efforts on 1-RTT lookups?
๏AWS ElasticCache Redis instances has latency of:
๏On larger EC2 instances: 0.2-0.5ms
๏On micro/small instances: Apparently more
๏When hitting backend time it takes to just wait for Redis:
๏Simple page with 20 items shown:
๏~40 lookups x latency = 5-20 ms
๏News site large landing page with 1000 items shown:
๏~2000 lookups x latency = 0.4 - 1 seconds
ez.no
In Symfony Cache:
๏Pipeline used instead of MGET => affecting Redis Cluster
๏Cache versioning lookups on Redis cluster
๏TagAwareAdapter micro ttl cache for tag lookups

Lookup optimizations done over the last year
ez.no
On Application side (eZ Platform):
๏Changs take better advantage of Multi Get
๏Introduce optimized RedisTagAwareAdapter
๏Introduced Application specific in-memory cache

Lookup optimizations done over the last year
ez.no
End result example:
๏Had 17.000 lookups in Symfony Cache on our Admin dashboard
๏Due to Symfony Cache logic this was ~40-60k lookups on Redis Cluster
๏30 seconds in worst case, just waiting for Redis Cluster
๏After all fixes it went down to 63 lookups in Symfony Cache
Lookup optimizations done over the last year
Let’s change symfon/demo to:
Cache index posts
Edit something in admin
Look to cache commands to clear
Add invalidation in Admin
Let’s also connect to redis using redis-cli
Play around and emulate SymfonyCache
Edge cases, edge cases & Edge cases:

Some things to be aware of
ez.no
Some edge cases
๏Race conditions
๏When caching data: Transactions
๏Async / Stale cache
๏RedisTagAwareAdapter: Sets never expire so use memory
The End, Questions?
Talk + Slides: https://joind.in/talk/c5187
Other talks: http://www.slideshare.net/andreromcke
Twitter: @andrerom



eZ Platform: https://ezplatform.com/
Redis: https://redis.io/
Memcached: https://memcached.org

SfDay 2019: Head first into Symfony Cache, Redis & Redis Cluster

  • 1.
    HEAD FIRST INTOSYMFONY
 CACHE, REDIS & REDIS CACHE André Rømcke (@andrerom)
 VP Technical Services & Support @eZSystems Oct. 18th 2019 - Verona - sfday Italy 2019 https://joind.in/talk/c5187
  • 2.
    So what isthis talk about? 1. Bit of background on where eZ fits in this 2. Symfony Cache, Cache & Cache tagging 3. Memcached vs Redis 4. Redis & Redis Cluster 5. New Adapters in Symfony 4.3 6. In Practice with symfony/demo & redis-cli 7. BONUS: Edge cases, edge case & more edge cases
  • 3.
    Who? ๏André Rømcke |@andrerom ๏Economics, Consulting, Engineering, Lead, VP Engineering, now Services & Support ๏Contributed to Symfony, FOS, Composer, PHP-FIG, Docker, attempts for PHP… ๏Works for … ๏eZ Systems AS | ez.no ๏Global 75+ people across 7+ countries, with partners & community in many many more ๏Maker of … ๏eZ Platform | ezplatform.com ๏Open Source CMS, a feature rich, flexible Full & Headless, on Symfony since 2012 ๏Also exists in commercial flavors: eZ Platform Enterprise & eZ Commerce ๏Uses …
  • 4.
    Symfony Cache ineZ Platform ๏Moved over from Stash in 2017 ๏Heavily relies on Cache tagging feature ๏Since then contributed improvements on performance issues with Redis/Redis Cluster/… ๏Since spring ships with own optimized TagAware adapters for Redis and FileSystem ➡Contributed these to Symfony 4.3 & Currently improving them for 4.4
  • 5.
  • 6.
    ez.no Symfony Cache Component ๏PSR-6compliant Cache component ๏Aims to be fast: Among others supports multi get calls to Redis and Memcached ๏Is progressively being used in several places in Symfony Framework, e.g.: ๏PropertyInfo ๏Serializer ๏Validator ๏(…) ๏.. maybe HTTP Cache at some point
  • 7.
    ez.no Symfony Cache Adapters ๏Adapters: ๏APCu(per proces cache) ๏Array (in memory per request, mainly for testing) ๏Chain (chain several adapters after each-other) ๏Doctrine ๏FileSystem (Also PHPFile & PHPArray for immutable opcache cache) ๏Proxy (To reuse other PSR-6 implementations) ๏Redis ๏Memcached ๏And “TagAware”..
  • 8.
    ez.no Hold on, what’sthis with Tags? ๏Data/Entities often has secondary indexes ๏Eg. In a CMS it’s things like: ๏content type id ๏section id ๏location id, location path ids, … ๏Sometime operations in your application affect those => affecting bulk of entities ๏So to cache entities, or the result of them (e.g. view): ๏.. you might want a secondary index in the cache to avoid having to iterate database ๏E.g. key: ez-content-66 tags: content-66, type-2, location-44 (…)
  • 9.
    ez.no TagAware: Secondary indexfor invalidation /** * Interface for invalidating cached items using tags. * * @author Nicolas Grekas <p@tchwork.com> */ interface TagAwareAdapterInterface extends AdapterInterface { /** * Invalidates cached items using tags. * * @param string[] $tags An array of tags to invalidate * * @return bool True on success * * @throws InvalidArgumentException When $tags is not valid */ public function invalidateTags(array $tags); }
  • 10.
    ez.no TagAwareAdapter ๏Wraps your normaladapter, stores item Tags in separate key ๏Does a separate lookup for expiry timestamp for tags => “2-RTT”
  • 11.
  • 12.
    ez.no Memcached vs Redis:Overview ๏Vivamus commodo ipsum in hendrerit iaculis. ๏Donec congue erat nibh, ac luctus erat accumsan tempor. ๏Mauris bibendum ac eros eu tempor. Duis libero libero, luctus quis posuere quis, porta vel turpis. ๏Sed augue dolor, laoreet eget turpis eget, laoreet vehicula neque. Donec sit amet dolor vel lorem ultrices facilisis ac sit amet velit. ๏Aenean nisi nisi, aliquet in pulvinar mollis, vulputate vitae nulla. Donec non ligula ac diam volutpat dapibus. ๏Aliquam eleifend turpis id ligula accumsan luctus. Donec sem justo, scelerisque eget condimentum ut, semper eget nulla. ๏Vivamus ultricies massa lectus, id varius orci sodales quis. Memcached Redis Multi operations (get, set, ..) V V Datatypes String String, List, Set, Hash, Sorted Set, … Streams Control over eviction X V Persistance X V Pipeline / Lua X V
 Not on Redis Cluster Multiserver V V
 Mainly Redis Cluster OR Redis Sentinel Multithreaded V X
 But multi process with Redis Cluster * * Redis 6 is adding partly threading with background thread handling slow operations
  • 13.
    ez.no Memcached vs Redis:“Greatly Simplified” Massive concurrent multi threaded, simple Key-Value store, that can take advantage of lots of CPU cores, and scales up across many servers VS Datastore for different advance data types to do sophisticated operations on, which is single threaded, but can be scaled up with Redis Cluster with some limitations to capabilities.
  • 14.
  • 15.
    Datatypes ๏ Strings ๏ Lists ๏Sets ๏ SortedSets ๏ Hashes ๏ Bitmaps ๏ HyperLogLogs ๏ Streams (As of Redis 5.0)
  • 16.
    Commands ๏There are 227commands and counting ๏There are generic key, cluster, connection, Pub/Sub, Scripting, Transaction commands ๏And Datatypes allows for specific operations, aka commands as well ๏For String it goes beyond flavors of GET / SET, there is e.g. APPEND, BITPOS, DECR, … ๏E.G. for "SET”: ๏SADD, SREM, SPOP ๏SCARD - Returns the set cardinality (number of elements) of the set stored at key ๏SDIFF, SINTER, SUNION, SMOVE ๏SMEMBERS, SSCAN
  • 17.
    Eviction maxmemory-policy: ๏noeviction ๏volatile-random ๏volatile-ttl ๏volatile-lru ๏volatile-lfu (As ofRedis 4.0) ๏allkeys-random ๏allkeys-lru ๏allkeys-lfu (As of Redis 4.0)
  • 18.
    Redis Cluster: processe ๏Allowsyou to scale up Redis by running several instances ๏Multi process on same server and/or across servers ๏Coordinates cache across “cache slots", deals with replication, … ๏Unlike Memcached, several operations has limitations on Cluster
  • 19.
    Redis Cluster: limitations ๏Doesnot support “pipline”: capability to perform several operations in one call ๏PHPRedis mainly supports multi operations on MGET and MSET with cluster ๏Examples of affected operations: ๏RENAME won’t work if the new key ends up in another “Cache Slot” ๏EVAL (Lua Script) likewise can only be given keys that maps to same node ERR CROSSSLOT Keys in request don't hash to the same slot
  • 20.
    New in Symfony4.3:
 FilesystemTagAwareAdapter & RedisTagAwareAdapter
  • 21.
    ez.no Tags storage ๏Moves tagsto be a “relation” to cache key, instead of expiry time ๏Avoids the lookup on getItem(s), instead does it on invalidation ๏FilesystemTagAwareAdapter: ๏4.3: Symlinks for tag “relation”, 4.4: uses a file to list “relations” ๏RedisTagAwareAdapter: ๏Uses “Set” for tag “relation” ๏Forces a default expiry on cache which does not have expiry
  • 22.
    ez.no Why the effortson 1-RTT lookups? ๏AWS ElasticCache Redis instances has latency of: ๏On larger EC2 instances: 0.2-0.5ms ๏On micro/small instances: Apparently more ๏When hitting backend time it takes to just wait for Redis: ๏Simple page with 20 items shown: ๏~40 lookups x latency = 5-20 ms ๏News site large landing page with 1000 items shown: ๏~2000 lookups x latency = 0.4 - 1 seconds
  • 23.
    ez.no In Symfony Cache: ๏Pipelineused instead of MGET => affecting Redis Cluster ๏Cache versioning lookups on Redis cluster ๏TagAwareAdapter micro ttl cache for tag lookups
 Lookup optimizations done over the last year
  • 24.
    ez.no On Application side(eZ Platform): ๏Changs take better advantage of Multi Get ๏Introduce optimized RedisTagAwareAdapter ๏Introduced Application specific in-memory cache
 Lookup optimizations done over the last year
  • 25.
    ez.no End result example: ๏Had17.000 lookups in Symfony Cache on our Admin dashboard ๏Due to Symfony Cache logic this was ~40-60k lookups on Redis Cluster ๏30 seconds in worst case, just waiting for Redis Cluster ๏After all fixes it went down to 63 lookups in Symfony Cache Lookup optimizations done over the last year
  • 26.
    Let’s change symfon/demoto: Cache index posts Edit something in admin Look to cache commands to clear Add invalidation in Admin Let’s also connect to redis using redis-cli Play around and emulate SymfonyCache
  • 27.
    Edge cases, edgecases & Edge cases:
 Some things to be aware of
  • 28.
    ez.no Some edge cases ๏Raceconditions ๏When caching data: Transactions ๏Async / Stale cache ๏RedisTagAwareAdapter: Sets never expire so use memory
  • 29.
    The End, Questions? Talk+ Slides: https://joind.in/talk/c5187 Other talks: http://www.slideshare.net/andreromcke Twitter: @andrerom
 
 eZ Platform: https://ezplatform.com/ Redis: https://redis.io/ Memcached: https://memcached.org