Doctrine ORM2 cache layer

It is not a boomerang

SymfonyDay 2015

This is a boomerang

Who am I?

Gianluca Arbezzano

@gianarb

from CorleyCloud

I'm the Enemy

Member of:

Zend Framework Integration

Doctrine Team

Caching

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

Doctrine

An incubator for persistence-oriented libraries

ORM is one of this libraries

ORMs are another layer of your application

ORMs are SLOW

ORM's weren’t born to speed up applications

This is why doctrine is built with cache layers.

doctrine\cache

it is a standalone projec to manage cache

fetch($id) - fetches an entry from the cache

contains($id) - test if an entry exists in the cache

save($id, $data, $lifetime) - puts data into the cache

delete($id) - deletes a cache entry

doctrine\cache

it supports different adapters

APC

Memcache

Memcached

Redis

Your implementation

ORM structure

EntityManager

UnitOfWork

Metadata Drivers

DQL

Repositories

Second Level Cache

Caches metadata

data about your data structure


# app/config/config.yml
doctrine_cache:
  providers:
    metadata_cache:
      aliases: [doctrine.orm.default_metadata_cache]
      file_system:
        directory: "%kernel.cache_dir%/doctrine/metadata"
    orm:
        metadata_cache_driver:
            cache_provider: metadata_cache
                    

Query Cache

Caches the transformation of a DQL query to its SQL counterpart.


# app/config/config.yml
doctrine_cache:
  providers:
    query_cache:
      aliases: [doctrine.orm.default_metadata_cache]
      file_system:
        directory: "%kernel.cache_dir%/doctrine/metadata"
    orm:
        query_cache_driver:
            cache_provider: query_cache
                    

Result Cache


$usersCount = $entityManager
    ->createQuery('SELECT COUNT(u) FROM User u')
    ->useResultCache(true)
    ->setResultCacheId('count_user');
    ->setResultCacheLifeTime(7200)
    ->getResult();
                    

Cache ID dependes from query and PARAMS!

Second Level Cache

Doctrine >= 2.5

Second Level Cache

The Second Level Cache is designed to reduce the amount of necessary database access

Tries to reduce the gap between SQL and NoSQL database

Second Level Cache

Reduce gap?!


App\FetchSingleEvent
    Method Name   Iterations   Average Time      Ops/second
    ------------ ------------ ----------------- -------------
    withMongoDB: [     1,000] [0.0002602758408] [3,842.07769]
    withMySQL  : [     1,000] [0.0003527779579] [2,834.64422]
    withMySqli : [     1,000] [0.0003824789524] [2,614.52295]
                    

Second Level Cache

The ORM tries to load an entity from the cache first

If it is not there, it fetches from the database, then caches the entity

Second Level Cache

Region

Second level cache is organized into regions - each region has its own namespace and lifetime parameters

Each entity is assigned to a region

Second Level Cache

Caching mode

Defines how your entities are cached

READ_ONLY: Perfect for immutable data, simple to manage.

READ_WRITE: doctrine locks the cache internally

NONSTRICT_READ_WRITE: perfect for sites with rare updates, no lock

Result cache row

it something like this


[
    "from Person as p where p.parent.id=? and p.firstName=?",
    [ 1 , "Joey"] ] -> [  2 ]
]
                    

Slc row

it caches only entity identifier and values


[
  'region_name:entity_1_hash' => [
    'id'=> 1,
    'name' => 'FooBar',
    'associationName'=>null
  ]
];
                    

This is our use case

This is our use case

  • Total time: 152 ms
  • 7 queries
  • doctrine time 16ms
  • with 20 users your db should support 140 queries

there are worst cases

You can try to call it 200 times

+49% for free

In dev Mode 98% pages load in ~744ms

Metadata and queries in cache (apc) 98% pages load in ~366ms

Internal PHP server

I can not control my strength

Are you sure that is all a problem of time?

not all that glitters is gold

it's not magic

cache configuration is your responsability, it doesn't know the application flow

Thanks!

github.com/gianarb