Important

You are browsing the documentation for version 3.1 of OroCommerce, OroCRM and OroPlatform, which is no longer maintained. Read version 5.1 (the latest LTS version) of the Oro documentation to get up-to-date information.

See our Release Process documentation for more information on the currently supported and upcoming releases.

Cache in Oro Application

The OroCacheBundle is responsible for operations with various kinds of caches.

Abstract Cache Services

There are two abstract services you can use as a parent for your cache services:

  • oro.file_cache.abstract - this cache should be used for caching data private for each node in a web farm

  • oro.cache.abstract - this cache should be used for caching data that need to be shared between nodes in a web farm

The following example shows how these services can be used:

1services:
2    acme.test.cache:
3        public: false
4        parent: oro.cache.abstract
5        calls:
6            - [ setNamespace, [ 'acme_test' ] ]

Also each of these abstract services can be re-declared in the application configuration file, for example:

1services:
2    oro.cache.abstract:
3        abstract: true
4        class:                Oro\Bundle\CacheBundle\Provider\PhpFileCache
5        arguments:            [%kernel.cache_dir%/oro_data]

The oro.cache.abstract.without_memory_cache service is always declared automatically based on oro.cache.abstract service.

Warm Up Config Cache

The purpose is to update only cache that will be needed by the application without updating the cache of those resources, that have not been changed. This gives a big performance over the approach when the all cache is updated. Cache warming occurs in debug mode whenever you updated the resource files.

The following example shows how this services can be used:

 1```yaml
 2# To register your config dumper:
 3oro.config.dumper:
 4    class: Oro\Example\Dumper\CumulativeConfigMetadataDumper
 5    public: false
 6
 7# To register your config warmer with oro.config_cache_warmer.provider tag:
 8oro.configuration.provider.test:
 9    class: Oro\Example\Dumper\ConfigurationProvider
10    tags:
11        - { name: oro.config_cache_warmer.provider, dumper: 'oro.config.dumper' }
12
13```
14
15```php
16<?php
17
18namespace Oro\Example\Dumper;
19
20use Symfony\Component\DependencyInjection\ContainerBuilder;
21
22use Oro\Component\Config\Dumper\ConfigMetadataDumperInterface;
23use Oro\Bundle\CacheBundle\Provider\ConfigCacheWarmerInterface;
24
25class CumulativeConfigMetadataDumper implements ConfigMetadataDumperInterface
26{
27
28    /**
29     * Write meta file with resources related to specific config type
30     *
31     * @param ContainerBuilder $container container with resources to dump
32     */
33    public function dump(ContainerBuilder $container)
34    {
35    }
36
37    /**
38     * Check are config resources fresh?
39     *
40     * @return bool true if data in cache is present and up to date, false otherwise
41     */
42    public function isFresh()
43    {
44        return true;
45    }
46}
47
48class ConfigurationProvider implements ConfigCacheWarmerInterface
49{
50    /**
51    * @param ContainerBuilder $containerBuilder
52    */
53    public function warmUpResourceCache(ContainerBuilder $containerBuilder)
54    {
55        // some logic
56        $resource = new CumulativeResource();
57        $containerBuilder->addResource($resource);
58    }
59}
60```

Caching Policy

Memory Based Cache

One of the most important things when dealing with caches is proper cache invalidation. When using memory based cache, we need to make sure that we do not keep old values in the memory. Consider this example:

 1<?php
 2
 3class LocalizationManager
 4{
 5    /** @var \Doctrine\Common\Cache\ArrayCache */
 6    private $cacheProvider;
 7
 8    public function getLocalization($id)
 9    {
10        $localization = $this->cacheProvider->fetch($id);
11
12        // ... all other operations, fetch from DB if cache is empty
13        // ... save in cache data from DB
14
15        return $localization;
16    }
17
18}

Since $cacheProvider in our example is an implementation of memory ArrayCache, we will keep the data there until the process ends. With HTTP request this would work perfectly well, but when our LocalizationManager is used in some long-running cli processes, we have to manually clear memory cache after every change with Localizations. Missing cache clearing for any of these cases leads to outdated data in LocalizationManager.

Persistent or Shared Cache

Let us have a look at our example once again. Since LocalizationManager is used in the CLI and we do not have the shared memory, we would not be able to invalidate the cache between different processes. We probably would go for some more persistent (shared) way of caching, for example, FilesystemCache. Now, we are able to share cache between processes, but this approach causes performance degradation. In general, the memory cache is much faster than the persistent one.

Cache Chaining

The solution to the issue mentioned above is to keep a healthy balance between the fast and shared cache. It is implemented in the ChainCache class.

 1<?php
 2
 3namespace Oro\Bundle\CacheBundle\Provider;
 4
 5use Doctrine\Common\Cache\ArrayCache;
 6use Doctrine\Common\Cache\ChainCache;
 7
 8class MemoryCacheChain extends ChainCache
 9{
10    /**
11     * {@inheritdoc}
12     */
13    public function __construct($cacheProviders = [])
14    {
15        if (PHP_SAPI !== 'cli') {
16            array_unshift($cacheProviders, new ArrayCache());
17        }
18
19        parent::__construct($cacheProviders);
20    }
21}

This class checks whether a request comes from the CLI. If not, the memory ArrayCache is added to the top of the cache providers which are being used for caching. With these priorities set, all HTTP requests gain performance when dealing with caches in memory and the CLI processes have no issues with the outdated data as they use the persistent cache.

Default Cache Implementation

There are two abstract services you can use as a parent for your cache services. Default implementations are following: - for CLI requests: MemoryCacheChain with only Oro\Bundle\CacheBundle\Provider\FilesystemCache as a cache provider - for other requests: MemoryCacheChain with ArrayCache on the top of FilesystemCache

For services based on oro.cache.abstract.without_memory_cache the MemoryCacheChain is not used.

Caching of Symfony Validation Rules

By default, rules for Symfony Validation Component are cached using oro.cache.abstract service, but you can change this to make validation caching suit some custom requirements. To do this, you need to redefine the oro_cache.provider.validation service.

Caching Data based on Complex Objects

Cache Hit Ratio is an important measure of cache efficiency. Choosing the right Cache Key might be a complex task, especially when the cache key should depend on the data stored in a Complex Object. Cache key generation strategy can vary in different cases and rely on different fields of the same object. To configure cache metadata for different scopes, use Resources/config/oro/cache_metadata.yml files that can be located in any bundle.

Here is an example of such configuration:

1 Oro\Bundle\OrderBundle\Entity\OrderAddress:
2     attributes:
3         id:
4             groups: ['shipping_context']
5         country:
6             groups: ['shipping_context', 'promotion']

Data from this configuration is used by the oro.cache.generator.object_cache_key service to provide cache keys for the given object and scope.