Skip to content

Commit 5b4516f

Browse files
DOC Document changes to caching (#543)
1 parent 9c02f9c commit 5b4516f

File tree

6 files changed

+113
-36
lines changed

6 files changed

+113
-36
lines changed

en/00_Getting_Started/00_Server_Requirements.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,9 @@ See [silverstripe/vendor-plugin](https://github.com/silverstripe/vendor-plugin)
231231

232232
Silverstripe CMS relies on various [caches](/developer_guides/performance/caching/)
233233
to achieve performant responses. By default, those caches are stored in a temporary filesystem folder, and are not
234-
shared between multiple server instances. Alternative cache backends such as Redis can be
235-
[configured](/developer_guides/performance/caching/).
234+
shared between multiple server instances.
235+
236+
No in-memory cache is used by default. To improve performance, we recommend [configuring an in-memory cache](/developer_guides/performance/caching/#adapters) such as Redis or Memcached.
236237

237238
While cache objects can expire, when using filesystem caching the files are not actively pruned. For long-lived server
238239
instances, this can become a capacity issue over time - see

en/00_Getting_Started/03_Environment_Management.md

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ Silverstripe core environment variables are listed here, though you're free to d
126126
| `SS_DATABASE_MEMORY` | Used for [SQLite3](https://github.com/silverstripe/silverstripe-sqlite3) database connectors. |
127127
| `SS_TRUSTED_PROXY_IPS` | IP address or CIDR range to trust proxy headers from. If left blank no proxy headers are trusted. Can be set to 'none' (trust none) or '*' (trust all). See [Request hostname forgery](/developer_guides/security/secure_coding/#request-hostname-forgery) for more information. |
128128
| `SS_ALLOWED_HOSTS` | A comma deliminated list of hostnames the site is allowed to respond to. See [Request hostname forgery](/developer_guides/security/secure_coding/#request-hostname-forgery) for more information. |
129+
| `SS_IN_MEMORY_CACHE_FACTORY` | The [`InMemoryCacheFactory`](api:SilverStripe\Core\Cache\InMemoryCacheFactory) class to use for instantiating in-memory cache adapters. See [caching adapters](/developer_guides/performance/caching/#adapters) for more details. |
129130
| `SS_MANIFESTCACHE` | The manifest cache to use (defaults to file based caching). Must be a `Psr\Cache\CacheItemPoolInterface`, `Psr\SimpleCache\CacheInterface`, or `SilverStripe\Core\Cache\CacheFactory` class implementation. |
130131
| `SS_IGNORE_DOT_ENV` | If set, the `.env` file will be ignored. This is good for live to mitigate any performance implications of loading the `.env` file. |
131132
| `SS_BASE_URL` | The URL to use when it isn't determinable by other means (for example for CLI commands). Should either start with a protocol (e.g. `https://www.example.com`) or with a double forward slash (e.g. `//www.example.com`). |

en/02_Developer_Guides/05_Extending/05_Injector.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ use SilverStripe\Core\Injector\Factory;
466466

467467
class MyFactory implements Factory
468468
{
469-
public function create($service, array $params = [])
469+
public function create(string $service, array $params = []): object
470470
{
471471
return new MyServiceImplementation(...$params);
472472
}

en/02_Developer_Guides/08_Performance/01_Caching.md

+61-31
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ SilverStripe\Core\Injector\Injector:
5656
> Please read the [versioned cache segmentation](#versioned-cache-segmentation) section for more information.
5757

5858
Cache objects are instantiated through a [CacheFactory](api:SilverStripe\Core\Cache\CacheFactory),
59-
which determines which cache adapter is used (see "Adapters" below for details).
59+
which determines which cache adapter is used (see [adapters](#adapters) below for details).
6060
This factory allows us you to globally define an adapter for all cache instances.
6161

6262
```php
@@ -180,44 +180,74 @@ interface. Use this interface to trigger `clear()` on your caches.
180180

181181
## Adapters
182182

183-
Silverstripe CMS tries to identify the most performant cache available on your system
184-
through the [DefaultCacheFactory](api:SilverStripe\Core\Cache\DefaultCacheFactory) implementation:
183+
We use the `smyfony/cache` library which supports various [cache adapters](https://github.com/symfony/cache/tree/6.1/Adapter).
184+
185+
Silverstripe CMS tries to provide a sensible default cache implementation for your system
186+
through the [`DefaultCacheFactory`](api:SilverStripe\Core\Cache\DefaultCacheFactory) implementation.
185187

186188
- `PhpFilesAdapter` (PHP with [opcache](https://php.net/manual/en/book.opcache.php) enabled).
187189
This cache has relatively low [memory defaults](https://php.net/manual/en/opcache.configuration.php#ini.opcache.memory-consumption).
188190
We recommend increasing it for large applications, or enabling the
189-
[file_cache fallback](https://php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache)
190-
- `ApcuAdapter` (requires APC) with a `FilesystemAdapter` fallback (for larger cache volumes)
191-
- `FilesystemAdapter` if none of the above is available
191+
[`file_cache` fallback](https://php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache).
192+
You must have [`opcache.enable_cli`](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.enable-cli) set to `true`
193+
to use this cache adapter. This is so that your cache is shared between CLI and the webserver.
194+
- `FilesystemAdapter` if the above isn't available
192195

193-
The library supports various [cache adapters](https://github.com/symfony/cache/tree/6.1/Adapter)
194-
which can provide better performance, particularly in multi-server environments with shared caches like Memcached.
196+
### Adding an in-memory cache adapter
195197

196-
Since we're using dependency injection to create caches,
197-
you need to define a factory for a particular adapter,
198-
following the `SilverStripe\Core\Cache\CacheFactory` interface.
199-
Different adapters will require different constructor arguments.
200-
We've written factories for the most common cache scenarios:
201-
`FilesystemCacheFactory`, `MemcachedCacheFactory` and `ApcuCacheFactory`.
198+
The cache adapter needs to be available before configuration has been loaded, so we use an environment variable to determine which class will be used to instantiate the in-memory cache adapter. The class must be referenced using its Fully Qualified Class Name (including namespace), and must be an instance of [`InMemoryCacheFactory`](api:SilverStripe\Core\Cache\InMemoryCacheFactory).
202199

203-
Example: Configure core caches to use [memcached](https://www.danga.com/memcached/),
204-
which requires the [memcached PHP extension](https://php.net/memcached),
205-
and takes a `MemcachedClient` instance as a constructor argument.
200+
```bash
201+
SS_IN_MEMORY_CACHE_FACTORY="SilverStripe\Core\Cache\MemcachedCacheFactory"
202+
```
206203

207-
```yml
208-
---
209-
After: '#versionedcache'
210-
---
211-
SilverStripe\Core\Injector\Injector:
212-
MemcachedClient:
213-
class: 'Memcached'
214-
calls:
215-
- [ addServer, [ 'localhost', 11211 ] ]
216-
MemcachedCacheFactory:
217-
class: 'SilverStripe\Core\Cache\MemcachedCacheFactory'
218-
constructor:
219-
client: '%$MemcachedClient'
220-
SilverStripe\Core\Cache\CacheFactory: '%$MemcachedCacheFactory'
204+
Silverstripe CMS comes with three in-memory cache factories you can choose from, each with their own requirements. You can of course add your own, as well.
205+
206+
#### Memcached cache
207+
208+
[Memcached](https://www.danga.com/memcached/) is a "high-performance, distributed memory object caching system". To use this cache, you must install the [memcached PHP extension](https://php.net/memcached).
209+
210+
You can tell the cache adapter which Memcached server(s) to connect to by defining a DSN in the `SS_MEMCACHED_DSN` environment variable.
211+
212+
> [!TIP]
213+
> The format for the DSN is exactly as defined in [the Symfony documentation](https://symfony.com/doc/current/components/cache/adapters/memcached_adapter.html#configure-the-connection).
214+
215+
```bash
216+
SS_IN_MEMORY_CACHE_FACTORY="SilverStripe\Core\Cache\MemcachedCacheFactory"
217+
SS_MEMCACHED_DSN="memcached://localhost:11211"
218+
```
219+
220+
#### Redis cache
221+
222+
[Redis](https://redis.io/) is an "in-memory database for caching and streaming" with built-in replication and a lot of deployment options. To use this cache, you must install one of:
223+
224+
- [predis/predis](https://github.com/predis/predis/)
225+
- [cachewerk/relay](https://github.com/cachewerk/relay)
226+
- [the Redis PHP extension](https://github.com/phpredis/phpredis)
227+
228+
You can tell the cache adapter which Redis server(s) to connect to by defining a DSN in the `SS_REDIS_DSN` environment variable.
229+
230+
> [!TIP]
231+
> The format for the DSN is exactly as defined in [the Symfony documentation](https://symfony.com/doc/current/components/cache/adapters/redis_adapter.html#configure-the-connection).
232+
233+
```bash
234+
SS_IN_MEMORY_CACHE_FACTORY="SilverStripe\Core\Cache\RedisCacheFactory"
235+
SS_REDIS_DSN="redis://verysecurepassword@localhost:6379"
236+
```
237+
238+
#### APCu cache
239+
240+
APCu is "an in-memory key-value store for PHP". This runs locally on the same computer as your webserver, and is only available for the webserver itself. To use this cache, you must install the [APCu PHP extension](https://www.php.net/apcu).
241+
242+
> [!WARNING]
243+
> APCu cache cannot be shared with your CLI, which means flushing cache from the terminal will not flush the cache your webserver uses.
244+
>
245+
> The filesystem cache will still be used as a backup, which *is* shared with CLI, so flushing the cache with a web request will always flush the cache CLI uses.
246+
247+
We include this option because it requires very little effort to set up, so it may be appropriate for smaller projects. Just remember to always flush the cache with an HTTP request using `?flush=1`.
248+
249+
```bash
250+
SS_IN_MEMORY_CACHE_FACTORY="SilverStripe\Core\Cache\ApcuCacheFactory"
221251
```
222252

223253
## Versioned cache segmentation

en/02_Developer_Guides/16_Execution_Pipeline/02_Manifests.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ which need to be generated and expired during runtime.
2020

2121
## Storage
2222

23-
By default, manifests are serialised and cached via a cache generated by the [ManifestCacheFactory](api:SilverStripe\Core\Cache\ManifestCacheFactory).
23+
By default, manifests are serialised and cached via a cache generated by the [`ManifestCacheFactory`](api:SilverStripe\Core\Cache\ManifestCacheFactory).
2424
This can be customised via `SS_MANIFESTCACHE` environment variable to point to either another
25-
[CacheFactory](api:SilverStripe\Core\Cache\CacheFactory) or a class that implements the `CacheInterface`
25+
[`CacheFactory`](api:SilverStripe\Core\Cache\CacheFactory) or a class that implements the `CacheInterface`
2626
interface provided by [php-fig/cache](https://github.com/php-fig/cache).
2727

28+
See [caching](/developer_guides/performance/caching/) for more information about caching, including setting up in-memory cache adapters.
29+
2830
## Traversing the filesystem
2931

3032
Since manifests usually extract their information from files in the project root,

en/08_Changelogs/6.0.0.md

+43
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ title: 6.0.0 (unreleased)
88

99
- [Features and enhancements](#features-and-enhancements)
1010
- [Run `CanonicalURLMiddleware` in all environments by default](#url-middleware)
11+
- [Changes to default cache adapters](#caching)
1112
- [Changes to scaffolded form fields](#scaffolded-fields)
1213
- [Other new features](#other-new-features)
1314
- [Bug fixes](#bug-fixes)
1415
- [API changes](#api-changes)
1516
- [Most extension hook methods are now protected](#hooks-protected)
17+
- [Strict typing for `Factory` implementations](#factory-strict-typing)
1618
- [General changes](#api-general)
1719

1820
## Features and enhancements
@@ -32,6 +34,20 @@ CanonicalURLMiddleware::singleton()->setEnabledEnvs([
3234
]);
3335
```
3436

37+
### Changes to default cache adapters {#caching}
38+
39+
The [`DefaultCacheFactory`](api:SilverStripe\Core\Cache\DefaultCacheFactory) used to use APCu cache by default for your webserver - but we found this cache isn't actually shared with CLI. This means flushing cache from the CLI didn't actually flush the cache your webserver was using.
40+
41+
What's more, the `PHPFilesAdapter` used as a fallback wasn't enabled for CLI, which resulted in having a different filesystem cache for CLI than is used for the webserver.
42+
43+
We've made the following changes to resolve this problem:
44+
45+
- The `PHPFilesAdapter` will only be used if it's available for both the webserver *and* CLI. Otherwise, `FilesystemAdapter` will be used for both.
46+
- There is no default in-memory cache used by `DefaultCacheFactory`.
47+
- Cache factories have been implemented for Redis, Memcached, and APCu, with a new [`InMemoryCacheFactory`](api:SilverStripe\Core\Cache\InMemoryCacheFactory) interface available for your own implementations.
48+
49+
We strongly encourage you to configure an appropriate in-memory cache for your use-case. See [adding an in-memory cache adapter](/developer_guides/performance/caching/#adapters) for details.
50+
3551
### Changes to scaffolded form fields {#scaffolded-fields}
3652

3753
Some of the form fields have changed when scaffolding form fields for relations.
@@ -68,6 +84,33 @@ This release includes a number of bug fixes to improve a broad range of areas. C
6884

6985
Core implementations of most extension hooks such as `updateCMSFields()` now have protected visibility. Formally they had public visibility which meant they could be called directly which was not how they were intended to be used. Extension hook implementations are still able to be declared public in project code, though it is recommended that all extension hook methods are declared protected in project code to follow best practice.
7086

87+
### Strict typing for `Factory` implementations {#factory-strict-typing}
88+
89+
The [`Factory::create()`](api:SilverStripe\Core\Injector\Factory::create()) method now has strict typehinting. The first argument must be a string, and either `null` or an object must be returned.
90+
91+
One consequence of this is that you can no longer directly pass an instantiated anonymous class object into [`Injector::load()`](SilverStripe\Core\Injector\Injector::load()). Instead, you need to get the class name using [`get_class()`](https://www.php.net/manual/en/function.get-class.php) and pass that in as the class.
92+
93+
```php
94+
use App\ClassToReplace;
95+
use SilverStripe\Core\Injector\Injector;
96+
97+
// Use `get_class()` to get the class name for your anonymous class
98+
$replacementClass = get_class(new class () {
99+
private string $property;
100+
101+
public function __construct(string $value = null)
102+
{
103+
$this->property = $value;
104+
}
105+
});
106+
107+
Injector::inst()->load([
108+
ClassToReplace::class => [
109+
'class' => $replacementClass,
110+
],
111+
]);
112+
```
113+
71114
### General changes {#api-general}
72115

73116
- [`DataObject::write()`](api:SilverStripe\ORM\DataObject::write()) has a new boolean `$skipValidation` parameter. This can be useful for scenarios where you want to automatically create a new record with no data initially without restricting how developers can set up their validation rules.

0 commit comments

Comments
 (0)