Nette Documentation Preview

syntax
Storages
********

<div class=perex>

The storage decides where cached data physically lives: in files on disk, on a Memcached server or in an SQLite database. You can switch it without touching the code that uses the cache. This page helps you:

- choose the right storage and see what each one supports
- configure it in a Nette application, or turn the cache off
- use a Nette storage as a PSR-16 cache
- write a storage of your own

</div>


Choosing a Storage
==================

If you are not sure, keep the default `FileStorage`. It needs nothing but a directory, it is safe when many requests read and write at once, and it supports every kind of dependency.

When your application runs on several servers, `FileStorage` gives each of them its own cache. `MemcachedStorage` stores the data on a server shared by all of them, but it keeps tags and priorities in a [#journal], a file local to each server, so deleting by tag on one server does not reach the others.

The storages differ in which [dependencies |expiration] they can honor:

|-------------------------------------------------------------------------------------------------
| Storage | Tags, priorities | Files, constants, callbacks | Other items | Sliding | Cleaning namespaces | Stampede protection
|-------------------------------------------------------------------------------------------------
| [#FileStorage] | with [#journal] | yes | yes | yes | yes | yes
| [#MemcachedStorage] | with [#journal] | yes | no | yes | ignored | no
| [#SQLiteStorage] | tags only | ignored | ignored | yes | ignored | no
| [#MemoryStorage] | ignored | ignored | ignored | ignored | ignored | no
| [#DevNullStorage] | stores nothing | | | | |

Where the table says ignored, the storage silently drops the dependency, so the item just does not expire that way. `MemoryStorage` ignores even the expiration. Only `MemcachedStorage` throws `Nette\NotSupportedException`, for the dependency on other items.

Stampede protection means that when many requests ask for a missing item at once, only the first one runs the generator of `load()` and the others wait for its result. Without it, all of them compute the value at the same time.


Configuration
=============

In a Nette application, the DI container provides a `FileStorage` that stores data in the `cache` subdirectory of the directory for [temporary files |application:bootstrapping#Temporary Files], together with a journal. You get it via [dependency injection |dependency-injection:passing-dependencies] by asking for the type `Nette\Caching\Storage`.

To use another storage, replace the service `cache.storage` in the configuration:

```neon
services:
	cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')
```

The container holds these two services:

| Name | Type | Description
|----------------------------------------------------------
| `cache.storage` | [api:Nette\Caching\Storage] | the storage
| `cache.journal` | [api:Nette\Caching\Storages\Journal] | the [#journal] for tags and priorities


Turning Off the Cache
---------------------

To turn the cache off, for example in tests, use [#DevNullStorage], which stores nothing:

```neon
services:
	cache.storage: Nette\Caching\Storages\DevNullStorage
```

This does not affect the caches of Latte templates and of the DI container, which are separate and do not use this storage. They [do not need to be turned off |nette:troubleshooting#How to Disable Cache During Development] during development either.


FileStorage
===========

`Nette\Caching\Storages\FileStorage` writes each item into a file in the given directory, which must exist. Tags and priorities need a [#journal]:

```php
$journal = new Nette\Caching\Storages\SQLiteJournal('/path/to/temp/journal.s3db');
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp', $journal);
```

All operations are atomic. You can never read a file another request has not finished writing yet, and nobody can delete it while you are reading it. That makes the storage safe even under heavy load.

It also protects against the [cache stampede |https://en.wikipedia.org/wiki/Cache_stampede]. When many requests ask for a missing item at once, for example right after a deployment, the first one locks the file and runs the generator. The others wait for the lock and then read the fresh value instead of computing it again.

Expired files are removed now and then by the constructor. To remove them at a moment of your choosing, call `$storage->clean([])`.


MemcachedStorage
================

`Nette\Caching\Storages\MemcachedStorage` stores data on a [Memcached |https://memcached.org] server. The constructor takes the host and the port, 11211 by default. Tags and priorities need a [#journal].

.[caution]
Requires the `memcached` PHP extension.

```neon
services:
	cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')
```

Memcached cannot list its keys, so the storage ignores cleaning by namespaces. For the dependency on other items, it throws an exception. Clearing the whole cache empties the entire Memcached server, including the data of other applications.


SQLiteStorage
=============

`Nette\Caching\Storages\SQLiteStorage` keeps the whole cache in a single SQLite database file. It stores tags in its own table, so it needs no journal.

.[caution]
Requires the `pdo` and `pdo_sqlite` PHP extensions.

```neon
services:
	cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')
```

The storage supports expiration, sliding expiration and tags. Priorities, dependencies on files, constants, callbacks and other items, and cleaning by namespaces are silently ignored.


MemoryStorage
=============

`Nette\Caching\Storages\MemoryStorage` keeps data in a PHP array, so it is lost when the request ends. It is handy in tests and for caching within a single request.

It ignores all dependencies, expiration included, and of the conditions of `clean()` it honors only `Cache::All`. Keep that in mind when you use it in tests instead of a real storage: an item that would expire there stays here.


DevNullStorage
==============

`Nette\Caching\Storages\DevNullStorage` stores nothing, so every `load()` calls the generator. Use it to [turn the cache off |#Turning Off the Cache].


Journal
=======

`FileStorage` and `MemcachedStorage` cannot search their items by tag or priority, so they keep a separate index, the so-called journal. The built-in `Nette\Caching\Storages\SQLiteJournal` stores it in an SQLite database:

```php
$journal = new Nette\Caching\Storages\SQLiteJournal('/path/to/temp/journal.s3db');
```

.[caution]
Requires the `pdo` and `pdo_sqlite` PHP extensions.

In a Nette application, the journal is created for you as the service `cache.journal` in the file `journal.s3db` and passed to the storage automatically. Your own implementation of the interface `Nette\Caching\Storages\Journal` can replace it:

```neon
services:
	cache.journal: MyJournal
```

Without a journal, these storages work too, but storing an item with tags or a priority throws an exception.


Using a Storage as a PSR-16 Cache .{data-version:3.3.1}
=======================================================

Some libraries expect a [PSR-16 |https://www.php-fig.org/psr/psr-16/] cache. The adapter `Nette\Bridges\Psr\PsrCacheAdapter` gives them one on top of any Nette storage:

```php
$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);

$psrCache->set('key', 'value', 3600); // stores the value for 1 hour
$value = $psrCache->get('key', 'default');
```

The adapter supports all methods of PSR-16, including `getMultiple()`, `setMultiple()` and `deleteMultiple()`.


Writing Your Own Storage
========================

A storage is a class implementing the interface `Nette\Caching\Storage` (before version 3.1 named `Nette\Caching\IStorage`) with five methods:

- `read(string $key)` returns the stored value, or `null` when the item does not exist or has expired
- `lock(string $key): void` locks the item before `Cache` runs a generator; the lock is released by `write()` or `remove()`, and a storage that does not protect against the cache stampede leaves the method empty
- `write(string $key, mixed $data, array $dependencies): void` stores the value
- `remove(string $key): void` deletes the item
- `clean(array $conditions): void` deletes items by the conditions of [clean() |expiration#Deleting Items]; an empty array means removing the expired items

The dependencies arrive as an array with `Cache::*` keys, already simplified by `Cache`: the expiration is a number of seconds from now, files and constants have become callbacks, and other items are given by their storage keys. When reading an item, check its callbacks with `Nette\Caching\Cache::checkCallbacks()`. Throw `Nette\NotSupportedException` for a dependency or condition your storage cannot honor.

Optional interfaces add more. `Nette\Caching\BulkReader` and, since version 3.3, `Nette\Caching\BulkWriter` let the storage read and write several items in one round trip.

A short complete example is the source code of [SQLiteStorage |https://github.com/nette/caching/blob/v3.4/src/Caching/Storages/SQLiteStorage.php].

{{maintitle: Nette Caching Storages: Files, Memcached, SQLite}}
{{description: Choose where the Nette cache stores data: files, Memcached or SQLite. Compare what each storage supports and configure it in your application.}}

Storages

The storage decides where cached data physically lives: in files on disk, on a Memcached server or in an SQLite database. You can switch it without touching the code that uses the cache. This page helps you:

  • choose the right storage and see what each one supports
  • configure it in a Nette application, or turn the cache off
  • use a Nette storage as a PSR-16 cache
  • write a storage of your own

Choosing a Storage

If you are not sure, keep the default FileStorage. It needs nothing but a directory, it is safe when many requests read and write at once, and it supports every kind of dependency.

When your application runs on several servers, FileStorage gives each of them its own cache. MemcachedStorage stores the data on a server shared by all of them, but it keeps tags and priorities in a journal, a file local to each server, so deleting by tag on one server does not reach the others.

The storages differ in which dependencies they can honor:

Storage Tags, priorities Files, constants, callbacks Other items Sliding Cleaning namespaces Stampede protection
FileStorage with journal yes yes yes yes yes
MemcachedStorage with journal yes no yes ignored no
SQLiteStorage tags only ignored ignored yes ignored no
MemoryStorage ignored ignored ignored ignored ignored no
DevNullStorage stores nothing          

Where the table says ignored, the storage silently drops the dependency, so the item just does not expire that way. MemoryStorage ignores even the expiration. Only MemcachedStorage throws Nette\NotSupportedException, for the dependency on other items.

Stampede protection means that when many requests ask for a missing item at once, only the first one runs the generator of load() and the others wait for its result. Without it, all of them compute the value at the same time.

Configuration

In a Nette application, the DI container provides a FileStorage that stores data in the cache subdirectory of the directory for temporary files, together with a journal. You get it via dependency injection by asking for the type Nette\Caching\Storage.

To use another storage, replace the service cache.storage in the configuration:

services:
	cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')

The container holds these two services:

Name Type Description
cache.storage Nette\Caching\Storage the storage
cache.journal Nette\Caching\Storages\Journal the journal for tags and priorities

Turning Off the Cache

To turn the cache off, for example in tests, use DevNullStorage, which stores nothing:

services:
	cache.storage: Nette\Caching\Storages\DevNullStorage

This does not affect the caches of Latte templates and of the DI container, which are separate and do not use this storage. They do not need to be turned off during development either.

FileStorage

Nette\Caching\Storages\FileStorage writes each item into a file in the given directory, which must exist. Tags and priorities need a journal:

$journal = new Nette\Caching\Storages\SQLiteJournal('/path/to/temp/journal.s3db');
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp', $journal);

All operations are atomic. You can never read a file another request has not finished writing yet, and nobody can delete it while you are reading it. That makes the storage safe even under heavy load.

It also protects against the cache stampede. When many requests ask for a missing item at once, for example right after a deployment, the first one locks the file and runs the generator. The others wait for the lock and then read the fresh value instead of computing it again.

Expired files are removed now and then by the constructor. To remove them at a moment of your choosing, call $storage->clean([]).

MemcachedStorage

Nette\Caching\Storages\MemcachedStorage stores data on a Memcached server. The constructor takes the host and the port, 11211 by default. Tags and priorities need a journal.

Requires the memcached PHP extension.

services:
	cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')

Memcached cannot list its keys, so the storage ignores cleaning by namespaces. For the dependency on other items, it throws an exception. Clearing the whole cache empties the entire Memcached server, including the data of other applications.

SQLiteStorage

Nette\Caching\Storages\SQLiteStorage keeps the whole cache in a single SQLite database file. It stores tags in its own table, so it needs no journal.

Requires the pdo and pdo_sqlite PHP extensions.

services:
	cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')

The storage supports expiration, sliding expiration and tags. Priorities, dependencies on files, constants, callbacks and other items, and cleaning by namespaces are silently ignored.

MemoryStorage

Nette\Caching\Storages\MemoryStorage keeps data in a PHP array, so it is lost when the request ends. It is handy in tests and for caching within a single request.

It ignores all dependencies, expiration included, and of the conditions of clean() it honors only Cache::All. Keep that in mind when you use it in tests instead of a real storage: an item that would expire there stays here.

DevNullStorage

Nette\Caching\Storages\DevNullStorage stores nothing, so every load() calls the generator. Use it to turn the cache off.

Journal

FileStorage and MemcachedStorage cannot search their items by tag or priority, so they keep a separate index, the so-called journal. The built-in Nette\Caching\Storages\SQLiteJournal stores it in an SQLite database:

$journal = new Nette\Caching\Storages\SQLiteJournal('/path/to/temp/journal.s3db');

Requires the pdo and pdo_sqlite PHP extensions.

In a Nette application, the journal is created for you as the service cache.journal in the file journal.s3db and passed to the storage automatically. Your own implementation of the interface Nette\Caching\Storages\Journal can replace it:

services:
	cache.journal: MyJournal

Without a journal, these storages work too, but storing an item with tags or a priority throws an exception.

Using a Storage as a PSR-16 Cache

Some libraries expect a PSR-16 cache. The adapter Nette\Bridges\Psr\PsrCacheAdapter gives them one on top of any Nette storage:

$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);

$psrCache->set('key', 'value', 3600); // stores the value for 1 hour
$value = $psrCache->get('key', 'default');

The adapter supports all methods of PSR-16, including getMultiple(), setMultiple() and deleteMultiple().

Writing Your Own Storage

A storage is a class implementing the interface Nette\Caching\Storage (before version 3.1 named Nette\Caching\IStorage) with five methods:

  • read(string $key) returns the stored value, or null when the item does not exist or has expired
  • lock(string $key): void locks the item before Cache runs a generator; the lock is released by write() or remove(), and a storage that does not protect against the cache stampede leaves the method empty
  • write(string $key, mixed $data, array $dependencies): void stores the value
  • remove(string $key): void deletes the item
  • clean(array $conditions): void deletes items by the conditions of clean(); an empty array means removing the expired items

The dependencies arrive as an array with Cache::* keys, already simplified by Cache: the expiration is a number of seconds from now, files and constants have become callbacks, and other items are given by their storage keys. When reading an item, check its callbacks with Nette\Caching\Cache::checkCallbacks(). Throw Nette\NotSupportedException for a dependency or condition your storage cannot honor.

Optional interfaces add more. Nette\Caching\BulkReader and, since version 3.3, Nette\Caching\BulkWriter let the storage read and write several items in one round trip.

A short complete example is the source code of SQLiteStorage.