2013-07-18 07:09:21 +02:00
|
|
|
# Caching
|
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
## Overview
|
2013-07-18 07:09:21 +02:00
|
|
|
|
|
|
|
The framework uses caches to store infrequently changing values.
|
2017-02-23 20:39:57 +01:00
|
|
|
By default, the storage mechanism chooses the most performant adapter available
|
|
|
|
(PHP7 opcache, APC, or filesystem). Other cache backends can be configured.
|
2013-07-18 07:09:21 +02:00
|
|
|
|
|
|
|
The most common caches are manifests of various resources:
|
|
|
|
|
2017-07-03 03:22:12 +02:00
|
|
|
* PHP class locations ([ClassManifest](api:SilverStripe\Core\Manifest\ClassManifest))
|
|
|
|
* Configuration settings from YAML files ([ConfigManifest](api:ConfigManifest))
|
|
|
|
* Language files ([i18n](api:SilverStripe\i18n\i18n))
|
2013-07-18 07:09:21 +02:00
|
|
|
|
|
|
|
Flushing the various manifests is performed through a GET
|
|
|
|
parameter (`flush=1`). Since this action requires more server resources than normal requests,
|
|
|
|
executing the action is limited to the following cases when performed via a web request:
|
|
|
|
|
2016-01-14 11:59:53 +01:00
|
|
|
* The [environment](/getting_started/environment_management) is in "dev mode"
|
2013-07-18 07:09:21 +02:00
|
|
|
* A user is logged in with ADMIN permissions
|
|
|
|
* An error occurs during startup
|
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
## Configuration
|
2013-07-18 07:09:21 +02:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
We are using the [PSR-16](http://www.php-fig.org/psr/psr-16/) standard ("SimpleCache")
|
|
|
|
for caching, through the [symfony/cache](https://symfony.com/doc/current/components/cache.html) library.
|
|
|
|
Note that this library describes usage of [PSR-6](http://www.php-fig.org/psr/psr-6/) by default,
|
|
|
|
but also exposes caches following the PSR-16 interface.
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
Cache objects are configured via YAML
|
|
|
|
and SilverStripe's [dependency injection](/developer-guides/extending/injector) system.
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```yml
|
2017-10-27 04:38:27 +02:00
|
|
|
SilverStripe\Core\Injector\Injector:
|
|
|
|
Psr\SimpleCache\CacheInterface.myCache:
|
|
|
|
factory: SilverStripe\Core\Cache\CacheFactory
|
|
|
|
constructor:
|
|
|
|
namespace: "myCache"
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
Cache objects are instantiated through a [CacheFactory](SilverStripe\Core\Cache\CacheFactory),
|
|
|
|
which determines which cache adapter is used (see "Adapters" below for details).
|
|
|
|
This factory allows us you to globally define an adapter for all cache instances.
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2018-03-18 07:06:30 +01:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\Core\Injector\Injector;
|
2017-10-26 02:22:02 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
Caches are namespaced, which might allow granular clearing of a particular cache without affecting others.
|
|
|
|
In our example, the namespace is "myCache", expressed in the service name as
|
|
|
|
`Psr\SimpleCache\CacheInterface.myCache`. We recommend the `::class` short-hand to compose the full service name.
|
|
|
|
|
|
|
|
Clearing caches by namespace is dependant on the used adapter: While the `FilesystemCache` adapter clears only the namespaced cache,
|
|
|
|
a `MemcachedCache` adapter will clear all caches regardless of namespace, since the underlying memcached
|
|
|
|
service doesn't support this. See "Invalidation" for alternative strategies.
|
2013-11-21 14:13:51 +01:00
|
|
|
|
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
## Usage
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
Cache objects follow the [PSR-16](http://www.php-fig.org/psr/psr-16/) class interface.
|
2013-11-21 14:13:51 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
|
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
|
|
|
|
|
|
|
|
|
|
|
// create a new item by trying to get it from the cache
|
|
|
|
$myValue = $cache->get('myCacheKey');
|
|
|
|
|
|
|
|
// set a value and save it via the adapter
|
|
|
|
$cache->set('myCacheKey', 1234);
|
|
|
|
|
|
|
|
// retrieve the cache item
|
|
|
|
if (!$cache->has('myCacheKey')) {
|
|
|
|
// ... item does not exists in the cache
|
|
|
|
}
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-10-27 04:38:27 +02:00
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
## Invalidation
|
|
|
|
|
|
|
|
Caches can be invalidated in different ways. The easiest is to actively clear the
|
|
|
|
entire cache. If the adapter supports namespaced cache clearing,
|
|
|
|
this will only affect a subset of cache keys ("myCache" in this example):
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
|
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
|
|
|
|
|
|
|
// remove all items in this (namespaced) cache
|
|
|
|
$cache->clear();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
|
|
|
|
2017-02-23 20:39:57 +01:00
|
|
|
You can also delete a single item based on it's cache key:
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
|
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
2017-10-26 02:22:02 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// remove the cache item
|
|
|
|
$cache->delete('myCacheKey');
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-02-23 20:39:57 +01:00
|
|
|
|
|
|
|
Individual cache items can define a lifetime, after which the cached value is marked as expired:
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
2017-10-26 02:22:02 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
|
|
|
|
|
|
|
// remove the cache item
|
|
|
|
$cache->set('myCacheKey', 'myValue', 300); // cache for 300 seconds
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-02-23 20:39:57 +01:00
|
|
|
|
|
|
|
If a lifetime isn't defined on the `set()` call, it'll use the adapter default.
|
2013-11-21 14:13:51 +01:00
|
|
|
In order to increase the chance of your cache actually being hit,
|
2017-02-23 20:39:57 +01:00
|
|
|
it often pays to increase the lifetime of caches.
|
|
|
|
You can also set your lifetime to `0`, which means they won't expire.
|
|
|
|
Since many adapters don't have a way to actively remove expired caches,
|
|
|
|
you need to be careful with resources here (e.g. filesystem space).
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```yml
|
2017-10-27 04:38:27 +02:00
|
|
|
SilverStripe\Core\Injector\Injector:
|
|
|
|
Psr\SimpleCache\CacheInterface.cacheblock:
|
|
|
|
constructor:
|
|
|
|
defaultLifetime: 3600
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-02-23 20:39:57 +01:00
|
|
|
|
|
|
|
In most cases, invalidation and expiry should be handled by your cache key.
|
|
|
|
For example, including the `LastEdited` value when caching `DataObject` results
|
|
|
|
will automatically create a new cache key when the object has been changed.
|
|
|
|
The following example caches a member's group names, and automatically
|
|
|
|
creates a new cache key when any group is edited. Depending on the used adapter,
|
|
|
|
old cache keys will be garbage collected as the cache fills up.
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
|
|
|
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
|
|
|
|
|
|
|
// Automatically changes when any group is edited
|
|
|
|
$cacheKey = implode(['groupNames', $member->ID, Group::get()->max('LastEdited')]);
|
|
|
|
$cache->set($cacheKey, $member->Groups()->column('Title'));
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-02-23 20:39:57 +01:00
|
|
|
|
|
|
|
If `?flush=1` is requested in the URL, this will trigger a call to `flush()` on
|
|
|
|
any classes that implement the [Flushable](/developer_guides/execution_pipeline/flushable/)
|
|
|
|
interface. Use this interface to trigger `clear()` on your caches.
|
|
|
|
|
|
|
|
## Adapters
|
|
|
|
|
|
|
|
SilverStripe tries to identify the most performant cache available on your system
|
|
|
|
through the [DefaultCacheFactory](api:SilverStripe\Core\Cache\DefaultCacheFactory) implementation:
|
|
|
|
|
|
|
|
* - `PhpFilesCache` (PHP 5.6 or PHP 7 with [opcache](http://php.net/manual/en/book.opcache.php) enabled).
|
|
|
|
This cache has relatively low [memory defaults](http://php.net/manual/en/opcache.configuration.php#ini.opcache.memory-consumption).
|
|
|
|
We recommend increasing it for large applications, or enabling the
|
|
|
|
[file_cache fallback](http://php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache)
|
|
|
|
* - `ApcuCache` (requires APC) with a `FilesystemCache` fallback (for larger cache volumes)
|
|
|
|
* - `FilesystemCache` if none of the above is available
|
|
|
|
|
|
|
|
The library supports various [cache adapters](https://github.com/symfony/cache/tree/master/Simple)
|
|
|
|
which can provide better performance, particularly in multi-server environments with shared caches like Memcached.
|
|
|
|
|
|
|
|
Since we're using dependency injection to create caches,
|
|
|
|
you need to define a factory for a particular adapter,
|
|
|
|
following the `SilverStripe\Core\Cache\CacheFactory` interface.
|
|
|
|
Different adapters will require different constructor arguments.
|
|
|
|
We've written factories for the most common cache scenarios:
|
|
|
|
`FilesystemCacheFactory`, `MemcachedCacheFactory` and `ApcuCacheFactory`.
|
|
|
|
|
|
|
|
Example: Configure core caches to use [memcached](http://www.danga.com/memcached/),
|
|
|
|
which requires the [memcached PHP extension](http://php.net/memcached),
|
|
|
|
and takes a `MemcachedClient` instance as a constructor argument.
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
|
|
|
|
```yml
|
2017-10-27 04:38:27 +02:00
|
|
|
---
|
|
|
|
After:
|
|
|
|
- '#corecache'
|
|
|
|
---
|
|
|
|
SilverStripe\Core\Injector\Injector:
|
|
|
|
MemcachedClient:
|
|
|
|
class: 'Memcached'
|
|
|
|
calls:
|
|
|
|
- [ addServer, [ 'localhost', 11211 ] ]
|
2018-03-16 17:30:48 +01:00
|
|
|
MemcachedCacheFactory:
|
2017-10-27 04:38:27 +02:00
|
|
|
class: 'SilverStripe\Core\Cache\MemcachedCacheFactory'
|
|
|
|
constructor:
|
2018-03-16 17:30:48 +01:00
|
|
|
client: '%$MemcachedClient'
|
|
|
|
SilverStripe\Core\Cache\CacheFactory: '%$MemcachedCacheFactory'
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2017-02-23 20:39:57 +01:00
|
|
|
|
|
|
|
## Additional Caches
|
|
|
|
|
|
|
|
Unfortunately not all caches are configurable via cache adapters.
|
|
|
|
|
|
|
|
* [SSViewer](api:SilverStripe\View\SSViewer) writes compiled templates as PHP files to the filesystem
|
|
|
|
(in order to achieve opcode caching on `include()` calls)
|
|
|
|
* [ConfigManifest](api:SilverStripe\Core\Manifest\ConfigManifest) is hardcoded to use `FilesystemCache`
|
|
|
|
* [ClassManifest](api:SilverStripe\Core\Manifest\ClassManifest) and [ThemeManifest](api:SilverStripe\View\ThemeManifest)
|
|
|
|
are using a custom `ManifestCache`
|
|
|
|
* [i18n](api:SilverStripe\i18n\i18n) uses `Symfony\Component\Config\ConfigCacheFactoryInterface` (filesystem-based)
|