Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Optimizing with Cache

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $9.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Yay! The var/cache directory does not need to be writable and our life is simple and all our cache dreams are fulfilled. Well actually, we can do more with caching to make our site screaming fast. Zoom!

APCu for Better Performance

First, let's install apcu - this should be a bit faster than OpCache. I'll do this during provision. Open up playbook.yml. Down a bit, yep! Add a new package: php-apcu:

- hosts: webserver
... lines 3 - 33
... lines 35 - 66
- name: Install PHP packages
become: true
... lines 70 - 71
... lines 73 - 78
- php-apcu
... lines 80 - 172

This package is named a bit different than the others.

Let's get the provision started - use playbook.yml and add -l aws to only provision the aws host:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini --ask-vault-pass -l aws

Use beefpass for the password.

Doctrine Metadata and Query Caching

There's one other way we can boost performance. Open app/config/config_prod.yml:

... lines 1 - 3
# orm:
# metadata_cache_driver: apc
# result_cache_driver: apc
# query_cache_driver: apc
... lines 9 - 26

See those Doctrine caches? Uncomment the first and third, and change them to apcu, which our server will now have! Woo!

... lines 1 - 3
metadata_cache_driver: apcu
# result_cache_driver: apc
query_cache_driver: apcu
... lines 9 - 26

The metadata_cache_driver caches the Doctrine annotations or YAML mapping... this is not stuff we need to be parse on every request. The query_cache_driver is used when querying: it caches the translation from Doctrine's DQL into SQL. This is something that does not need to be done more than once.

So... now... I have the same question as before: do we need to clear this cache on each deploy? Nope! Internally, Symfony uses a cache namespace for Doctrine that includes the directory of our project. Since Ansistrano always deploys into a new releases/ directory, each deploy has its own, unique namespace.

When provisioning finishes, commit the new config changes:

git add -u
git commit -m "Doctrine caching"

Push them!

git push origin master

Now, deploy the site:

ansible-playbook ansible/deploy.yml -i ansible/hosts.ini --ask-vault-pass

While we're waiting, find the server again and run:

php -i | grep apcu

Yes! Now APCu is installed and working. Without doing anything else, Symfony's cache.system service is already using it. And when the deploy finishes, thanks to the Doctrine caching, we should have the fastest version of our site yet.

Except... for one more, fascinating cache issue. Actually, let's not call it a cache issue. Let's call it a cache opportunity!

Leave a comment!

Login or Register to join the conversation

Why we don't activate `result_cache_driver` ? what'is the difference between metadata_cache_driver and query_cache_driver ? For how can't install apcu, do you think using pool cache is a good idea ?


Hey ahmedbhs !

Excellent questions!

> Why we don't activate `result_cache_driver`

This activates the cache on a separate, optional caching system where Doctrine can cache the results of a query. Even if you activate this config, you still need to activate the cache on any queries where you want this - https://doctrine2.readthedo...

I don't use this feature because I just use Symfony's cache service whenever I want to cache anything (whether that's the result of something from the database or something else).

> what'is the difference between metadata_cache_driver and query_cache_driver?

It's subtle. The "metadata cache" basically caches the annotations that you have on your entities. The query cache caches the transformation from the DQL queries (which are also used with the query builder) into SQL. Both are things that it makes 100% sense to cache and not reset that cache until something in your code changes (e.g. on the next deploy).

> For how can't install apcu, do you think using pool cache is a good idea ?

What do you mean by a pool cache? Are you referring to Symfony's cache pools? If so, absolutely. The cache config I show here is a bit out-of-date. It works, but we've made it easier in newer versions of DoctrineBundle to use Symfony's cache instead. You can see a nice example in the recipe for DoctrineBundle - this is the config file for the "prod" environment: https://github.com/symfony/...


Cat in space

"Houston: no signs of life"
Start the conversation!

While the fundamentals of Ansistrano haven't changed, this tutorial is built using Symfony 3, which has significant differences versus Symfony 4 and later.

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=5.5.9",
        "doctrine/doctrine-bundle": "^1.6", // 1.6.8
        "doctrine/orm": "^2.5", // v2.7.2
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "sensio/distribution-bundle": "^5.0.19", // v5.0.20
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "symfony/monolog-bundle": "^3.1.0", // v3.1.0
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "symfony/swiftmailer-bundle": "^2.3.10", // v2.6.3
        "symfony/symfony": "3.3.*", // v3.3.5
        "twig/twig": "^1.0||^2.0", // v1.34.4
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "predis/predis": "^1.1", // v1.1.1
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0", // v3.3.5
        "doctrine/data-fixtures": "^1.1", // 1.3.3
        "hautelook/alice-bundle": "^1.3" // v1.4.1

What Ansible libraries does this tutorial use?

# ansible/requirements.yml
    src: DavidWittman.redis
    version: 1.2.4
    src: ansistrano.deploy
    version: 2.7.0
    src: ansistrano.rollback
    version: 2.0.1