Cache: critacal section globalne
- hrach
- Člen | 1838
Ahoj,
toto se tu již řešilo, naprosto
bez výsledku.
- předpokládejme aplikaci, která používá cache jak na „frontendu“, tak na „backendu“.
- backendove generovani cache zabírá mnoho času
- frontendove generovani cache zabira malo casu
- v aktualni situaci staci, aby drive bylo spusteno 2. a cely krok 3. bude cekat zbytecne na jeho dokonceni.
Ukázkový kód:
<?php
/**
* Base class for all application presenters.
*
* @author John Doe
* @package MyApplication
*/
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
public function actionFrontendCache()
{
$dir = $this->context->params['tempDir']. '/cache';
$journal = new Nette\Caching\Storages\FileJournal($dir);
$storage = new Nette\Caching\Storages\FileStorage($dir, $journal);
$cache = new Nette\Caching\Cache($storage);
$value = $cache->load('quickKey');
if ($value === NULL) {
$value = $cache->save('quickKey', callback($this, 'getQuickCache'));
}
$this->template->cachedValue = $value;
}
public function actionBackendCache()
{
$dir = $this->context->params['tempDir']. '/cache';
$journal = new Nette\Caching\Storages\FileJournal($dir);
$storage = new Nette\Caching\Storages\FileStorage($dir, $journal);
$cache = new Nette\Caching\Cache($storage, 'backend');
$value = $cache->load('slowKey');
if ($value === NULL) {
$value = $cache->save('slowKey', callback($this, 'getSlowCache'));
}
$this->template->cachedValue = $value;
}
public function getQuickCache()
{
return 'Nette framework je nejlepší, ale s bugy! '. time();
}
public function getSlowCache()
{
sleep(15);
return 'Pomala cache '. time();
}
}
Vymazte si cache, spuste do jednoho panelu backendCache, do druhe otevrete frontendCache. Ackoliv se jedna o uplne rozdilne cache, framework se „zasekne“. Toto je vazeni #fail prvniho radu. Pro jasnou ilustraci nepouzivam zadne sluzby, ba dokonce ani stejne namespace.
Nerad bych se poustel do demonstracniho videa, jak moc velky fail to je. Řešením je upravit entry critical section, aby bral ohled nějaký klíč, který by byl v případě cache namespace + klic.
- Filip Procházka
- Moderator | 4668
V první řadě, nevidím opravdu důvod proč by jsi to nemohl napsat
jinak. Kdyby jsi těch pár řádků nevynechal, nemáš tyhle
problémy.
Pak tu máš také metodu Nette\Caching\Cache::call(), která vykonává velice podobnou funkci a dovolil bych si tvrdit, že i lépe. Nebo taky zajímavá funkce Nette\Caching\Cache::wrap(), které jsem si všiml až při psaní tohohle.
No a pak je tu faktor session. S tím jsi počítal? Víš, že Nette zavře session až při ukončení requestu a proto se navzájem požadavky blokují? (což se nevylučuje s generováním cache, které se může také blokovat a nejspíše i blokuje).
Je to problém, který je potřeba vzít na vědomí, ale nevidím to
jako kritický problém, pouze problém v použití. Osobně bych podporu
callbacků zrušil, nevidím v tom přínos. Stejně tam ten callback dáváš
pokaždé znovu.
//edit: Ty namespacy jsou dobrý nápad. Ostatně by mě zajímalo, proč to David odstraňoval. Nechceš poslat pull? Píšu rozespalý, příště budu lépe číst:)
Editoval HosipLan (13. 9. 2011 23:09)
- hrach
- Člen | 1838
Cache::call()
danou situaci resi, nicmene castecne. Nemuzu mu predat ani callback, ani clousure, tedy jsem nucen dany kod vyhradit do globalni funkce. Prasecinky?- To, ze danou chybu lze obejit jinym zpusobem neznamena, ze to neni chyba, ktera je zavazna a mela by byt fixnuta
- proč do toho pleteš session? Od kdy používáš session v backendových skriptech, např. v cronu? Nemá to nic společného. A i kdyby mělo, existuje něco jako Session::close()
- Filip Procházka
- Moderator | 4668
proč do toho pleteš session?
Protože ta ti to taky blokuje (ale jenom uživatele) :)
//edit:
Myslím si, že řešením by bylo vrátit se k původní implementaci
if ($cache->load($key) === NULL) {
CriticalSection::start([$namespace]);
$cache->save($key, $trida->slozitaOperace());
CriticalSection::end();
}
$data = $cache->load($key);
Editoval HosipLan (14. 9. 2011 8:07)
- Filip Procházka
- Moderator | 4668
Pravda, to by mohlo jít docela snadno. Callback je možné snadno převádět na textovou podobu.
- jansfabik
- Člen | 193
David to odstranil proto, že to bylo závislé na konstantě TEMP_DIR (pro každý lock se vytvářel soubor v cache). V případě, že by se tato konstanta za běhu aplikace změnila (v současné implementaci se v bootstrapu tato proměnná mění), může to způsobit problémy.
Současná verze používá soubor libs/Nette/lockfile.
Doplněno: I když teď mě napadá, že by se to dalo jakžtakž čistě vyřešit přídáním statické metody ve stylu CriticalSection::setTempDir($tempDir). To by se zavolalo někde v bootstrapu a nešlo by to pak už nijak změnit. A v temp složce by se pro každý klíč vytvořil soubor a ten by se lockoval.
Doplnění 2: A aby tam nebyl BC break, tak pokud zavolám enter() a není zvolena temp složka, tak se defaultně vybere složka s nette.
Editoval jansfabik (14. 9. 2011 18:22)
- hrach
- Člen | 1838
@jansfabik: Díky za osvětlení.
- sic moc nechápu, jak někoho vůbec může napadnout měnit konstantu TEMP_DIR, od toho je to konstanta. (Leda nejak magicky?)
- params se ted muze menit, ale reknete mi nejakej duvod, proc?
Je pravda, ze toto je drobnustka, ktera by se mela vyresit, nicmene kvuli tomuto odebirat tak zasadni funkcionalitu mi neprijde moudre.
- Filip Procházka
- Moderator | 4668
sic moc nechápu, jak někoho vůbec může napadnout měnit konstantu TEMP_DIR, od toho je to konstanta. (Leda nejak magicky?)
Kdyby to tedy zůstalo v cache, nevidím moc problém ukládat do složky,
se kterou pracuje cache, ještě lock soubor. Cestu zná, tak ji může třídě
CriticalSection
říct.
$section = new CriticalSection($this->namespace,$key);
$section->start();
$this->storage->save($key, $callback->invokeArgs($args));
$section->end();
params se ted muze menit, ale reknete mi nejakej duvod, proc?
Protože je pohodlnější k tomu přistupovat jako k public property, než přes get a set (ten samý problém jako u session, když zapisuješ do vnořeného pole?).
- hrach
- Člen | 1838
HosipLan napsal(a):
Protože je pohodlnější k tomu přistupovat jako k public property, než přes get a set (ten samý problém jako u session, když zapisuješ do vnořeného pole?).
Asi si me spatne pochopil. Je mi vcelku jedno, jestli je to konstanta, nebo promenna, atp. To je kdyztak jinam. Danou vetou jsem myslel, zda je vubec treba (a proc?) menit temp adresar behem behu. ← coz byl pry duvod.
- Filip Procházka
- Moderator | 4668
Já bych řekl, že důvod k tomu není. Jen se puristům nelíbilo, že Nette „zaneřáďuje“ globální namespace. Ale to jsme hodně mimo téma :)