$30 off During Our Annual Pro Sale. View Details »

Demystifying cache in Doctrine ORM (v2)

Demystifying cache in Doctrine ORM (v2)

Applications should be fast regardless of any cache system. However, sometimes we do need to cache data in order to optimise things and deliver a fast response to the clients.
This talk covers the different types of cache offered Doctrine ORM (metadata, query, result set, and second level cache), how they work together with the Unit of Work and what we should know before caching all the things.

Luís Cobucci

May 11, 2018
Tweet

More Decks by Luís Cobucci

Other Decks in Technology

Transcript

  1. Demystifying
    Luís Cobucci

    @lcobucci
    https://goo.gl/oZPuRc
    cache in Doctrine ORM

    View Slide

  2. Reliability
    https://goo.gl/1shwsY

    View Slide

  3. Maintainability
    https://goo.gl/D4DvKC

    View Slide

  4. Scalability
    https://goo.gl/M67mS8

    View Slide

  5. Trade-offs
    https://goo.gl/L7YdSp

    View Slide

  6. Minions of users!
    https://goo.gl/7MAkHZ

    View Slide

  7. Luís Cobucci

    @lcobucci
    https://jobs.usabilla.com

    View Slide

  8. Doctrine ORM?
    https://goo.gl/xCBz84

    View Slide

  9. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *

    View Slide

  10. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *

    View Slide

  11. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *

    View Slide

  12. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    Order
    matters

    View Slide

  13. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    INSERT INTO customer (name, email)

    VALUES (“Luís Cobucci”, “[email protected]”);

    SET @customer_id = LAST_INSERT_ID();

    View Slide

  14. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    INSERT INTO customer (name, email)

    VALUES (“Luís Cobucci”, “[email protected]”);

    SET @customer_id = LAST_INSERT_ID();
    INSERT INTO template (name, body)

    VALUES (“Template 1”, “blah… blah… blah…”);

    View Slide

  15. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    INSERT INTO customer (name, email)

    VALUES (“Luís Cobucci”, “[email protected]”);

    SET @customer_id = LAST_INSERT_ID();
    INSERT INTO template (name, body)

    VALUES (“Template 1”, “blah… blah… blah…”);
    INSERT INTO campaign (title, template_id, message)

    VALUES (“Test”, LAST_INSERT_ID(), “blah… blah… blah…”);

    View Slide

  16. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    INSERT INTO customer (name, email)

    VALUES (“Luís Cobucci”, “[email protected]”);

    SET @customer_id = LAST_INSERT_ID();
    INSERT INTO template (name, body)

    VALUES (“Template 1”, “blah… blah… blah…”);
    INSERT INTO campaign (title, template_id, message)

    VALUES (“Test”, LAST_INSERT_ID(), “blah… blah… blah…”);
    INSERT INTO message (campaign_id, customer_id, sent)

    VALUES (LAST_INSERT_ID(), @customer_id, FALSE);

    View Slide

  17. Template
    Message
    Customer
    Campaign
    1
    1
    1
    *
    *
    *
    INSERT INTO customer (name, email)

    VALUES (“Luís Cobucci”, “[email protected]”);

    SET @customer_id = LAST_INSERT_ID();
    INSERT INTO template (name, body)

    VALUES (“Template 1”, “blah… blah… blah…”);
    INSERT INTO campaign (title, template_id, message)

    VALUES (“Test”, LAST_INSERT_ID(), “blah… blah… blah…”);
    INSERT INTO message (campaign_id, customer_id, sent)

    VALUES (LAST_INSERT_ID(), @customer_id, FALSE);
    Don’t forget to use a
    transaction!

    View Slide

  18. EntityManager
    persist()
    flush()
    find()
    remove()

    View Slide

  19. declare(strict_types=1);
    $customer = new Customer('Luís Cobucci', '[email protected]');

    $template = new Template('Template 1', 'blah… blah… blah…');

    $campaign = new Campaign($template, 'Test', 'blah… blah… blah…');

    $message = new Message($campaign, $customer);


    $entityManager->persist($customer);
    $entityManager->persist($template);
    $entityManager->persist($campaign);
    $entityManager->persist($message);
    $entityManager->flush();

    View Slide

  20. EntityManager UnitOfWork DB

    View Slide

  21. declare(strict_types=1);
    $customer = $entityManager->find('Customer', 2);
    $campaign = $entityManager->find('Campaign', 1);


    $entityManager->persist(new Message($campaign, $customer));
    $entityManager->flush();

    View Slide

  22. http://goo.gl/gH0hsx
    Amazing, right?

    View Slide

  23. “ORMs are slow!”
    https://goo.gl/dRfUyZ

    View Slide

  24. https://goo.gl/91u2rx

    View Slide

  25. https://goo.gl/SAH57F
    Big ball of mud

    View Slide

  26. https://goo.gl/d3DNXm
    Hydration

    View Slide

  27. https://goo.gl/hr22Dx
    Outdated

    View Slide

  28. https://goo.gl/1y3fvm
    Bugs…

    View Slide

  29. https://goo.gl/cuLYT9
    Bugs…

    View Slide

  30. https://goo.gl/D3Gw7e
    Bugs…

    View Slide

  31. Make it fast!
    https://goo.gl/6Y2kQd

    View Slide

  32. “ a hardware or software component
    that stores data so future requests for
    that data can be served faster; the data
    stored in a cache might be the result of
    an earlier computation, or the duplicate
    of data stored elsewhere.
    Cache (computing) - Wikipedia

    View Slide

  33. Our software
    Cache
    External API

    View Slide

  34. Our software
    Cache
    External API
    1. Give me
    “external.result”

    View Slide

  35. Our software
    Cache
    External API
    2. Cache miss
    1. Give me
    “external.result”

    View Slide

  36. Our software
    Cache
    External API
    2. Cache miss
    3. Give me
    data
    1. Give me
    “external.result”

    View Slide

  37. Our software
    Cache
    External API
    2. Cache miss
    3. Give me
    data
    4. There you
    go
    1. Give me
    “external.result”

    View Slide

  38. Our software
    Cache
    External API
    2. Cache miss
    1. Give me
    “external.result”
    3. Give me
    data
    4. There you
    go
    5. Store
    “external.result”

    View Slide

  39. Our software
    Cache
    External API
    2. Cache miss
    1. Give me
    “external.result”
    3. Give me
    data
    4. There you
    go
    5. Store
    “external.result” 6. Stored!

    View Slide

  40. Our software
    Cache
    External API
    1. Give me
    “external.result”

    View Slide

  41. Our software
    Cache
    External API
    2. Cache hit!
    1. Give me
    “external.result”

    View Slide

  42. Our software
    Cache
    External API
    2. Cache hit!
    1. Give me
    “external.result”
    When should we invalidate
    the cache?

    View Slide

  43. “ There are two hard things in
    Computer Science: cache invalidation,
    naming things, and off-by-one errors
    Common sense

    View Slide

  44. Metadata
    https://goo.gl/AhjBFJ

    View Slide

  45. /** @ORM\Entity */
    class Customer

    {

    /** 

    * @ORM\Id

    * @ORM\GeneratedValue 

    * @ORM\Column(type="integer")

    */
    private $id;


    /** @ORM\Column(type="string") */

    private $name;


    /** @ORM\Column(type="string") */

    private $email;
    }

    View Slide

  46. /** @ORM\Entity */
    class Customer

    {

    /** 

    * @ORM\Id

    * @ORM\GeneratedValue 

    * @ORM\Column(type="integer")

    */
    private $id;


    /** @ORM\Column(type="string") */

    private $name;


    /** @ORM\Column(type="string") */

    private $email;
    }
    ClassMetadata

    View Slide

  47. declare(strict_types=1);
    use Doctrine\Common\Cache\RedisCache;
    use Doctrine\ORM\Configuration;
    $metadataCache = new RedisCache();
    $configuration = new Configuration();
    // …
    $configuration->setMetadataCacheImpl($metadataCache);

    View Slide

  48. Query
    https://goo.gl/nuFRba

    View Slide

  49. SELECT customer FROM Customer customer;

    View Slide

  50. SELECT customer FROM Customer customer;
    SELECT

    c0.id AS id_0,

    c0.name AS name_1,
    c0.email AS email_2,

    FROM customer c0;

    View Slide

  51. declare(strict_types=1);
    $builder = $entityManager->createQueryBuilder()

    ->select(‘customer’)
    ->from(Customer::class, ‘customer');
    $query = $builder->getQuery();

    $result = $query->getResult();

    View Slide

  52. declare(strict_types=1);
    use Doctrine\Common\Cache\RedisCache;
    use Doctrine\ORM\Configuration;
    $queryCache = new RedisCache();
    $configuration = new Configuration();
    // …
    $configuration->setQueryCacheImpl($queryCache);

    View Slide

  53. declare(strict_types=1);
    $builder = $entityManager->createQueryBuilder()

    ->select(‘customer’)
    ->from(Customer::class, ‘customer');
    if (isset($params[‘email’])) {
    $builder->where(‘customer.email = :email’)

    ->setParameter(‘email’, $params[‘email’]);
    }
    $query = $builder->getQuery();

    $result = $query->getResult();

    View Slide

  54. Result set
    https://goo.gl/7yah7S

    View Slide

  55. $query = 'SELECT COUNT(m) FROM Message m WHERE m.user = :user';

    $count = $entityManager->createQuery($query)

    ->setParameter('user', 1)

    ->getSingleScalarResult();

    View Slide

  56. declare(strict_types=1);
    use Doctrine\Common\Cache\RedisCache;
    use Doctrine\ORM\Configuration;
    $queryCache = new RedisCache();
    $configuration = new Configuration();
    // …
    $configuration->setQueryCacheImpl($queryCache);

    View Slide

  57. $query = 'SELECT COUNT(m) FROM Message m WHERE m.user = :user';

    $count = $entityManager->createQuery($query)

    ->setParameter('user', 1)

    ->useResultCache(true, 3600)

    ->getSingleScalarResult();

    View Slide

  58. L2C
    https://goo.gl/8pbG3y

    View Slide

  59. EntityManager UnitOfWork DB

    View Slide

  60. declare(strict_types=1);
    use Doctrine\Common\Cache\RedisCache;
    use Doctrine\ORM\Cache as L2C;
    use Doctrine\ORM\Configuration;
    $level2Cache = new RedisCache();
    $level2Config = new L2C\CacheConfiguration();
    $level2Config->setSecondLevelCacheEnabled(
    new L2C\DefaultCacheFactory(
    new L2C\RegionsConfiguration(),
    $level2Cache
    )
    );
    $configuration = new Configuration();
    // …
    $configuration->setSecondLevelCacheEnabled(true);
    $configuration->setSecondLevelCacheConfiguration($level2Config);

    View Slide

  61. EntityManager UnitOfWork L2C
    DB

    View Slide

  62. L2C
    Collection
    Entity Query

    View Slide

  63. /**

    * @ORM\Entity

    * @ORM\Cache(usage="NONSTRICT_READ_WRITE")

    */
    class Customer

    {

    // …

    }

    View Slide

  64. /**

    * @ORM\Entity

    * @ORM\Cache(usage="NONSTRICT_READ_WRITE")

    */
    class Customer

    {

    // …

    }
    READ_ONLY

    NONSTRICT_READ_WRITE

    READ_WRITE

    View Slide

  65. /**

    * @ORM\Entity

    * @ORM\Cache(usage="NONSTRICT_READ_WRITE")

    */
    class Message

    {

    // …


    /**

    * @ORM\ManyToOne(targetEntity="Customer")
    * @ORM\Cache(usage="READ_ONLY")
    */

    private $customer;

    // …

    }

    View Slide

  66. $query = ‘SELECT m FROM Message m WHERE m.customer = :id’;

    $messages = $entityManager->createQuery($query)

    ->setParameter(‘id', 1)

    ->setCacheable(true)

    ->getResult();

    View Slide

  67. https://goo.gl/bGP8u8
    Intense traffic

    View Slide

  68. https://goo.gl/bGP8u8
    Upcoming!

    View Slide

  69. Demystifying
    Luís Cobucci

    @lcobucci
    https://goo.gl/oZPuRc
    cache in Doctrine ORM

    View Slide

  70. Thanks!
    @lcobucci

    View Slide