Cache-warming in Symfony2 is a funny one. Unlike most of the Symfony components, it doesn’t seem to have very much documentation, and what does exist is slightly hidden away in a list of possible Dependency Injection tags.
But it’s probably something that everyone who uses Symfony has seen in action. After all, the cache:clear console command warms the cache by default, and there’s a separate command cache:warmup, which you’ve probably come across in deploy scripts and the like. And you’ll have seen the benefits of it:
The purpose is to initialize any cache that will be needed by the application and prevent the first user from any significant “cache hit” where the cache is generated dynamically.
In your own app, you might use cache-warmup for something like pre-calculating values. For example, if your admin dashboard displays historical statistics about your app, you don’t want the first view of the dashboard to be really slow whilst they’re calculated. You could pre-calculate them, stick the answers in the cache on warmup, and then only recalculate the full set every time you do a new deployment. (Obviously there are other ways to solve this problem — this is just an example!)
But what does cache warmup actually do?
Perhaps the reason that documentation on cache warmup is sparse is the fact that exactly what it does will vary depending on the bundles you are using.
It might seem paradoxical, but the best way to understand what happens at cache warmup is actually to learn how to write your own cache-warmer! You can find a detailed walkthrough in the Symfony documentation. For the purposes of this explanation, I’ll just give a brief overview:
Essentially, a cache-warmer should be a PHP class which implements the CacheWarmerInterface interface or extend the abstract class CacheWarmer. The interface specifies two methods: warmUp (which will actually do your warming actions) and isOptional (which should return true if your app will work without the warmer having run, and false otherwise).
You then register your cache warmer as a service and tag it with kernel.cache_warmer.
So, if you want to know what cache warmup does in your app, just look for any services tagged with kernel.cache_warmer and see what they do!
There’s an easy command to find these tagged services:
app/console container:debug –show-private –tag kernel.cache_warmer
You need the –show-private option as the core warmers (explained below) aren’t public services — rather, they’re generated via compiler passes.
That sounds like hard work
Well, to save you having to trawl through your vendor code, I’ve done the hard work for you! The following list gives a brief overview of all the cache-warmers found in the Symfony 2.4 Standard Edition.
If you’ve got other vendors and want to know about their cache-warmers, well that really is left as an exercise for the reader!
AsseticBundle’s AssetManagerCacheWarmer — sets up references to assets in your Twig templates, ready for lazy-loading later
Symfony’s RouterCacheWarmer — parses your Symfony routing information (from your annotations and YAML files) and creates the appropriate PHP code to do URL matching and generation
Symfony’s TemplatePathsCacheWarmer — computes the association between template names and their paths on the disk. You can see them all listed in templates.php in your cache folder, where there’s an array of entries like the following:
‘DoctrineBundle:Collector:explain.html.twig’ => ‘/path/to/your/app/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig’,
TwigBundle’s TemplateCacheCacheWarmer — generates the Twig cache for all templates. You can see the generated files in the twig folder in your cache. (Although note that these are automatically regenerated on every page load if you’re in the dev environment).
Symfony Bridge’s Doctrine ProxyCacheWarmer — As the name suggests, this generates Doctrine Proxies. A Doctrine Proxy is just a wrapper that extends an entity class to provide Lazy Loading for it (more details). Unlike the other warmers in this list, the ProxyCacheWarmer isn’t optional.
So that’s it for warmups. However, you might notice some things in the cache for Symfony Standard Edition that aren’t listed above. They don’t appear if you clear the cache without warmup, so surely they’re generated by the warmers too?
Well, not directly. There are a few files which are generated by Compiler Passes, and so may be created if the service container is compiled as part of a cache warmup action. If you’d like to know more, read about appDevDebugProjectContainer.php (and its partner appProdProjectContainer.php) here, or classes.map here.
PS A note about optional warmers
You’ll have noticed the isOptional method for cache-warmers. As mentioned, this should return true if your app will work without the warmer having run, and false otherwise.
You can use this to control the behaviour of your cache-warming more accurately. You may already know that you can give cache:clear the option –no-warmup if you don’t want to run warmup at all, but there’s also –no-optional-warmers which only runs the required warmers. This option can also be passed to cache:warmup.
Hopefully cache warmup is now slightly less of a black box for you!