Nette Documentation Preview

syntax
Úložiště
********

<div class=perex>

Úložiště určuje, kde data z cache fyzicky leží: v souborech na disku, na serveru Memcached nebo v databázi SQLite. Vyměnit ho můžete, aniž byste sáhli na kód, který cache používá. Tato stránka vám pomůže:

- vybrat správné úložiště a zjistit, co které umí
- nastavit ho v aplikaci Nette, případně cache vypnout
- napsat si vlastní úložiště

</div>


Výběr úložiště
==============

Pokud si nejste jistí, nechte výchozí `FileStorage`. Stačí mu adresář, je bezpečné, i když najednou čte a zapisuje mnoho požadavků, a podporuje všechny druhy závislostí.

Když aplikace běží na více serverech, `FileStorage` dá každému z nich vlastní cache. `NewMemcachedStorage` ukládá data na server, který sdílejí všechny, jenže tagy a priority si drží v [journalu |#journal], což je soubor na každém serveru zvlášť, takže mazání podle tagu na jednom serveru se k ostatním nedostane.

Úložiště se liší tím, které [závislosti |expiration] umí respektovat:

|-------------------------------------------------------------------------------------------------
| Úložiště | Tagy, priority | Soubory, konstanty, callbacky | Jiné položky | Klouzavá expirace | Mazání jmenných prostorů | Ochrana proti stampede
|-------------------------------------------------------------------------------------------------
| [#FileStorage] | s [journalem |#journal] | ano | ano | ano | ano | ano
| [#NewMemcachedStorage] | s [journalem |#journal] | ano | ne | ano | ignoruje | ne
| [#SQLiteStorage] | jen tagy | ignoruje | ignoruje | ano | ignoruje | ne
| [#MemoryStorage] | ignoruje | ignoruje | ignoruje | ignoruje | ignoruje | ne
| [#DevNullStorage] | nic neukládá | | | | |

Kde tabulka uvádí ignoruje, úložiště závislost potichu zahodí, takže položka podle ní prostě nevyexpiruje. `MemoryStorage` ignoruje dokonce i expiraci. Jen `NewMemcachedStorage` vyhodí `Nette\NotSupportedException`, a to pro závislost na jiných položkách.

Ochrana proti stampede znamená, že když o chybějící položku požádá najednou mnoho požadavků, generátor z `load()` spustí jen první z nich a ostatní počkají na jeho výsledek. Bez ní hodnotu počítají všechny zároveň.


Konfigurace
===========

V aplikaci postavené na Nette poskytuje DI kontejner `FileStorage`, které ukládá data do podadresáře `cache` v adresáři pro [dočasné soubory |application:bootstrapping#Dočasné soubory], a k němu i journal. Dostanete ho pomocí [dependency injection |dependency-injection:passing-dependencies], když si řeknete o typ `Nette\Caching\IStorage`.

Pokud chcete jiné úložiště, nahraďte v konfiguraci službu `cache.storage`:

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

Kontejner obsahuje tyto dvě služby:

| Název | Typ | Popis
|----------------------------------------------------------
| `cache.storage` | [api:Nette\Caching\IStorage] | úložiště
| `cache.journal` | [api:Nette\Caching\Storages\IJournal] | [journal |#journal] pro tagy a priority


Vypnutí cache
-------------

Pokud chcete cache vypnout, třeba v testech, použijte [#DevNullStorage], které nic neukládá:

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

Na cache šablon Latte a DI kontejneru to nemá vliv, ty jsou samostatné a toto úložiště nepoužívají. Během vývoje je ostatně [není potřeba vypínat |nette:troubleshooting#Jak vypnout cache během vývoje].


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

`Nette\Caching\Storages\FileStorage` zapisuje každou položku do souboru v zadaném adresáři, který musí existovat. Tagy a priority potřebují [journal |#journal]:

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

Všechny operace jsou atomické. Nikdy nepřečtete soubor, který jiný požadavek ještě nedopsal, a nikdo vám ho nemůže smazat pod rukama, zatímco ho čtete. Úložiště je proto bezpečné i pod velkou zátěží.

Navíc chrání před [cache stampede |https://en.wikipedia.org/wiki/Cache_stampede]. Když o chybějící položku požádá najednou mnoho požadavků, třeba hned po nasazení, první z nich soubor zamkne a spustí generátor. Ostatní počkají na zámek a pak přečtou čerstvou hodnotu, místo aby ji počítaly znovu.

Vyexpirované soubory čas od času smaže konstruktor. Pokud je chcete smazat v okamžiku, který si zvolíte sami, zavolejte `$storage->clean([])`.


NewMemcachedStorage
===================

`Nette\Caching\Storages\NewMemcachedStorage` ukládá data na server [Memcached |https://memcached.org]. Konstruktor přijímá adresu serveru a port, výchozí je 11211. Tagy a priority potřebují [journal |#journal].

.[caution]
Vyžaduje PHP rozšíření `memcached`.

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

Memcached neumí vypsat své klíče, takže úložiště mazání podle jmenných prostorů ignoruje. Pro závislost na jiných položkách vyhodí výjimku. Smazání celé cache vyprázdní celý server Memcached včetně dat jiných aplikací.

Starší třída `MemcachedStorage` používá zastaralé rozšíření `memcache`. Místo ní použijte `NewMemcachedStorage`.


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

`Nette\Caching\Storages\SQLiteStorage` drží celou cache v jediném souboru databáze SQLite. Tagy si ukládá do vlastní tabulky, takže nepotřebuje journal.

.[caution]
Vyžaduje PHP rozšíření `pdo` a `pdo_sqlite`.

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

Úložiště podporuje expiraci, klouzavou expiraci a tagy. Priority, závislosti na souborech, konstantách, callbackech a jiných položkách i mazání podle jmenných prostorů potichu ignoruje.


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

`Nette\Caching\Storages\MemoryStorage` drží data v PHP poli, takže se s koncem požadavku ztratí. Hodí se pro testy a pro cachování v rámci jednoho požadavku.

Ignoruje všechny závislosti včetně expirace a z podmínek metody `clean()` respektuje jen `Cache::ALL`. Myslete na to, když jím v testech nahrazujete skutečné úložiště: položka, která by tam vyexpirovala, tady zůstane.


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

`Nette\Caching\Storages\DevNullStorage` nic neukládá, takže každé `load()` zavolá generátor. Použijte ho pro [vypnutí cache |#Vypnutí cache].


Journal
=======

`FileStorage` a `NewMemcachedStorage` neumí své položky vyhledávat podle tagu nebo priority, proto si vedou samostatný index, tzv. journal. Vestavěný `Nette\Caching\Storages\SQLiteJournal` ho ukládá do databáze SQLite:

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

.[caution]
Vyžaduje PHP rozšíření `pdo` a `pdo_sqlite`.

V aplikaci postavené na Nette se journal vytvoří za vás jako služba `cache.journal` v souboru `journal.s3db` a úložišti se předá automaticky. Nahradit ho může vaše vlastní implementace rozhraní `Nette\Caching\Storages\IJournal`:

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

Bez journalu tato úložiště fungují také, jen uložení položky s tagy nebo prioritou vyhodí výjimku.


Vlastní úložiště
================

Úložiště je třída, která implementuje rozhraní `Nette\Caching\IStorage` s pěti metodami:

- `read($key)` vrátí uloženou hodnotu, nebo `null`, když položka neexistuje nebo vyexpirovala
- `lock($key)` zamkne položku, než `Cache` spustí generátor; zámek uvolní `write()` nebo `remove()` a úložiště, které před cache stampede nechrání, nechá metodu prázdnou
- `write($key, $data, array $dependencies)` hodnotu uloží
- `remove($key)` položku smaže
- `clean(array $conditions)` smaže položky podle podmínek metody [clean() |expiration#Mazání položek]; prázdné pole znamená odstranit vyexpirované položky

Závislosti přicházejí jako pole s klíči `Cache::*`, které už `Cache` zjednodušila: expirace je počet sekund od teď, ze souborů a konstant se staly callbacky a jiné položky jsou dané svými klíči v úložišti. Při čtení položky ověřte její callbacky pomocí `Nette\Caching\Cache::checkCallbacks()`. Pro závislost nebo podmínku, kterou vaše úložiště neumí, vyhoďte `Nette\NotSupportedException`.

Volitelné rozhraní `Nette\Caching\IBulkReader` umožní úložišti číst více položek najednou.

Krátkým úplným příkladem je zdrojový kód [SQLiteStorage |https://github.com/nette/caching/blob/v2.5/src/Caching/Storages/SQLiteStorage.php].

{{maintitle: Úložiště Nette Caching: soubory, Memcached, SQLite}}
{{description: Vyberte, kam cache z Nette ukládá data: soubory, Memcached nebo SQLite. Porovnejte, co které úložiště umí, a nastavte ho ve své aplikaci.}}

Úložiště

Úložiště určuje, kde data z cache fyzicky leží: v souborech na disku, na serveru Memcached nebo v databázi SQLite. Vyměnit ho můžete, aniž byste sáhli na kód, který cache používá. Tato stránka vám pomůže:

  • vybrat správné úložiště a zjistit, co které umí
  • nastavit ho v aplikaci Nette, případně cache vypnout
  • napsat si vlastní úložiště

Výběr úložiště

Pokud si nejste jistí, nechte výchozí FileStorage. Stačí mu adresář, je bezpečné, i když najednou čte a zapisuje mnoho požadavků, a podporuje všechny druhy závislostí.

Když aplikace běží na více serverech, FileStorage dá každému z nich vlastní cache. NewMemcachedStorage ukládá data na server, který sdílejí všechny, jenže tagy a priority si drží v journalu, což je soubor na každém serveru zvlášť, takže mazání podle tagu na jednom serveru se k ostatním nedostane.

Úložiště se liší tím, které závislosti umí respektovat:

Úložiště Tagy, priority Soubory, konstanty, callbacky Jiné položky Klouzavá expirace Mazání jmenných prostorů Ochrana proti stampede
FileStorage s journalem ano ano ano ano ano
NewMemcachedStorage s journalem ano ne ano ignoruje ne
SQLiteStorage jen tagy ignoruje ignoruje ano ignoruje ne
MemoryStorage ignoruje ignoruje ignoruje ignoruje ignoruje ne
DevNullStorage nic neukládá          

Kde tabulka uvádí ignoruje, úložiště závislost potichu zahodí, takže položka podle ní prostě nevyexpiruje. MemoryStorage ignoruje dokonce i expiraci. Jen NewMemcachedStorage vyhodí Nette\NotSupportedException, a to pro závislost na jiných položkách.

Ochrana proti stampede znamená, že když o chybějící položku požádá najednou mnoho požadavků, generátor z load() spustí jen první z nich a ostatní počkají na jeho výsledek. Bez ní hodnotu počítají všechny zároveň.

Konfigurace

V aplikaci postavené na Nette poskytuje DI kontejner FileStorage, které ukládá data do podadresáře cache v adresáři pro dočasné soubory, a k němu i journal. Dostanete ho pomocí dependency injection, když si řeknete o typ Nette\Caching\IStorage.

Pokud chcete jiné úložiště, nahraďte v konfiguraci službu cache.storage:

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

Kontejner obsahuje tyto dvě služby:

Název Typ Popis
cache.storage Nette\Caching\IStorage úložiště
cache.journal Nette\Caching\Storages\IJournal journal pro tagy a priority

Vypnutí cache

Pokud chcete cache vypnout, třeba v testech, použijte DevNullStorage, které nic neukládá:

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

Na cache šablon Latte a DI kontejneru to nemá vliv, ty jsou samostatné a toto úložiště nepoužívají. Během vývoje je ostatně není potřeba vypínat.

FileStorage

Nette\Caching\Storages\FileStorage zapisuje každou položku do souboru v zadaném adresáři, který musí existovat. Tagy a priority potřebují journal:

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

Všechny operace jsou atomické. Nikdy nepřečtete soubor, který jiný požadavek ještě nedopsal, a nikdo vám ho nemůže smazat pod rukama, zatímco ho čtete. Úložiště je proto bezpečné i pod velkou zátěží.

Navíc chrání před cache stampede. Když o chybějící položku požádá najednou mnoho požadavků, třeba hned po nasazení, první z nich soubor zamkne a spustí generátor. Ostatní počkají na zámek a pak přečtou čerstvou hodnotu, místo aby ji počítaly znovu.

Vyexpirované soubory čas od času smaže konstruktor. Pokud je chcete smazat v okamžiku, který si zvolíte sami, zavolejte $storage->clean([]).

NewMemcachedStorage

Nette\Caching\Storages\NewMemcachedStorage ukládá data na server Memcached. Konstruktor přijímá adresu serveru a port, výchozí je 11211. Tagy a priority potřebují journal.

Vyžaduje PHP rozšíření memcached.

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

Memcached neumí vypsat své klíče, takže úložiště mazání podle jmenných prostorů ignoruje. Pro závislost na jiných položkách vyhodí výjimku. Smazání celé cache vyprázdní celý server Memcached včetně dat jiných aplikací.

Starší třída MemcachedStorage používá zastaralé rozšíření memcache. Místo ní použijte NewMemcachedStorage.

SQLiteStorage

Nette\Caching\Storages\SQLiteStorage drží celou cache v jediném souboru databáze SQLite. Tagy si ukládá do vlastní tabulky, takže nepotřebuje journal.

Vyžaduje PHP rozšíření pdo a pdo_sqlite.

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

Úložiště podporuje expiraci, klouzavou expiraci a tagy. Priority, závislosti na souborech, konstantách, callbackech a jiných položkách i mazání podle jmenných prostorů potichu ignoruje.

MemoryStorage

Nette\Caching\Storages\MemoryStorage drží data v PHP poli, takže se s koncem požadavku ztratí. Hodí se pro testy a pro cachování v rámci jednoho požadavku.

Ignoruje všechny závislosti včetně expirace a z podmínek metody clean() respektuje jen Cache::ALL. Myslete na to, když jím v testech nahrazujete skutečné úložiště: položka, která by tam vyexpirovala, tady zůstane.

DevNullStorage

Nette\Caching\Storages\DevNullStorage nic neukládá, takže každé load() zavolá generátor. Použijte ho pro vypnutí cache.

Journal

FileStorage a NewMemcachedStorage neumí své položky vyhledávat podle tagu nebo priority, proto si vedou samostatný index, tzv. journal. Vestavěný Nette\Caching\Storages\SQLiteJournal ho ukládá do databáze SQLite:

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

Vyžaduje PHP rozšíření pdo a pdo_sqlite.

V aplikaci postavené na Nette se journal vytvoří za vás jako služba cache.journal v souboru journal.s3db a úložišti se předá automaticky. Nahradit ho může vaše vlastní implementace rozhraní Nette\Caching\Storages\IJournal:

services:
	cache.journal: MyJournal

Bez journalu tato úložiště fungují také, jen uložení položky s tagy nebo prioritou vyhodí výjimku.

Vlastní úložiště

Úložiště je třída, která implementuje rozhraní Nette\Caching\IStorage s pěti metodami:

  • read($key) vrátí uloženou hodnotu, nebo null, když položka neexistuje nebo vyexpirovala
  • lock($key) zamkne položku, než Cache spustí generátor; zámek uvolní write() nebo remove() a úložiště, které před cache stampede nechrání, nechá metodu prázdnou
  • write($key, $data, array $dependencies) hodnotu uloží
  • remove($key) položku smaže
  • clean(array $conditions) smaže položky podle podmínek metody clean(); prázdné pole znamená odstranit vyexpirované položky

Závislosti přicházejí jako pole s klíči Cache::*, které už Cache zjednodušila: expirace je počet sekund od teď, ze souborů a konstant se staly callbacky a jiné položky jsou dané svými klíči v úložišti. Při čtení položky ověřte její callbacky pomocí Nette\Caching\Cache::checkCallbacks(). Pro závislost nebo podmínku, kterou vaše úložiště neumí, vyhoďte Nette\NotSupportedException.

Volitelné rozhraní Nette\Caching\IBulkReader umožní úložišti číst více položek najednou.

Krátkým úplným příkladem je zdrojový kód SQLiteStorage.