Cache: critacal section globalne

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
hrach
Člen | 1834
+
0
-

Ahoj,
toto se tu již řešilo, naprosto bez výsledku.

  1. předpokládejme aplikaci, která používá cache jak na „frontendu“, tak na „backendu“.
  2. backendove generovani cache zabírá mnoho času
  3. frontendove generovani cache zabira malo casu
  4. 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
+
0
-

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 | 1834
+
0
-
  • 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()
hrach
Člen | 1834
+
0
-

HosipLan napsal(a):
Ty namespacy jsou dobrý nápad. Ostatně by mě zajímalo, proč to David odstraňoval. Nechceš poslat pull?

K pullu se uchylim, az bude rozhodnuto (politicky). Proc to David odstranil nevim.

Filip Procházka
Moderator | 4668
+
0
-

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)

hrach
Člen | 1834
+
0
-

@HosipLan: tvůj návrh je stejně nedokonalý, toto mi zablokuje generování cache „jen“ na backendu/frontendu.

Prostě by tam mělo být CriticalSection::start("$namespace-$key");

Filip Procházka
Moderator | 4668
+
0
-

Pravda, to by mohlo jít docela snadno. Callback je možné snadno převádět na textovou podobu.

jansfabik
Člen | 193
+
0
-

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 | 1834
+
0
-

@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
+
0
-

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 | 1834
+
0
-

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
+
0
-

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 :)

David Grudl
Nette Core | 8082
+
0
-

Vyřešeno v Nette 2.0