Cache výstupu komponenty BEZ opakovaného volání render() když se čte z cache

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

Ahoj,
cachuju celou komponentu, např:

{cache 'ahojahoj', expire => '+1 hour'}
{control egcategories}
{/cache}

Metoda render v komponentě egcategories udělá jen:

$template = $this->template;
$template->setFile(__DIR__ . 'sablona.latte');
$template->tralala = $this->database->table('egkategorie')->where('active = 1')->fetchAll();

Jak to dostat do cache? Teda přesněji – jak donutit nette, aby v případě čtení z cache nespouštělo tu render metodu, která může x vteřin čekat nad zamknutou tabulkou, než vypíše data? Výstup té komponenty se sice do cache uloží a nette z toho pak i čte, nicméně stále spouští i tu render metodu, jako by cache neměl.
Očekával bych postup
načíst z db
uložit do cache
a na hodinu zobrazovat obsah z cache

Realita je ovšem:
Načíst z db
uložit do cache
a příště:
zase načíst z db
zobrazit z cache

Snad to popisuju dobře :D
Prostě potřebuju, aby se nespouštěl kód v komponentě v případě, že už nette načetlo předgenerovaný obsah z cache a taky ho to zobrazilo, tudíž není třeba spouštět komponentu, jejíž výsledek se už stejně nikde nepromítne (použijí se cache data bez ohledu na to, že se načetlo z db něco novějšího..)

díky

ps problém už byl několikrát zmíněn, např. zde: https://forum.nette.org/…ch-jak-na-to#…
Ovšem řešení v podobě předávání tisíce závislostí a předávání anonymních funkcí přes proměnnou do šablony mi přijde jako absurdní šílenost :)

Editoval neznamy_uzivatel (13. 8. 2016 17:25)

CZechBoY
Člen | 3608
+
0
-

Proč neuložíš výsledek z DB do cache a normálně nerenderuješ pokaždý? Máš tam složitej render nebo jinej důvod? :-)

neznamy_uzivatel
Člen | 115
+
0
-
  1. Chci, aby se to nezdrzovalo renderovanim.
  2. Chtel bych, aby to fungovalo jak ma. Chci to pouzit na vice mistech a prakticky lze cachovat pouze staticky obsah :D
  3. To co vraci Table (Selection?) cachovat pokud se neco nezmenilo primo nelze bohuzel.

To cachovani primo v sablonach by bylo krasne, super, excelentne genialni, ucinne a jednoduche… kdyby fungovalo..

CZechBoY
Člen | 3608
+
0
-

Můžeš si uložit výsledek dotazu jako pole třeba pomocí

$rows = $selection->fetchAssoc('[]->');

a řádek budeš mít pořád jako objekt(stdClass). Tohle potom můžeš cachovat.

Pokud chceš cachovat i render tak máš změřený, že to je fakt nějaký pomalý, nebo prostě chceš aby to nerenderovalo dvakrát?

neznamy_uzivatel
Člen | 115
+
0
-

Tohle vyzkouším, dík :) Pomůže mi to na jiných místech, kde mám šílené cykly, abych to celé přetáhl do pole a zacachoval :)

Jinak tenhle konkrétní případ je opravdu hodně pomalý, proto řeším, aby byl v cache celý ten html výstup. Jsou tam tisíce položek (desetitisíce dotazů napříč desítkami tabulek), php na tom stráví přes 2.5 sec.. (z toho mysql asi 250ms)

A mimo to… proste pocit, ze se neco natahlo z cache a presto se se zobrazenim ceka na funkci, ktera ty data znou nacte a zpracuje aniz by se ty nove data nekde projevily je… :D lehce neprijemny, kdyz se generovani stranky priblizuje bez cache ke trem vterinam a s cache v sablone asi na 2.95 :(

Editoval neznamy_uzivatel (13. 8. 2016 17:00)

Spectator
Člen | 48
+
0
-

Tak to netahej v komponentě, ale předávej jí výsledek už jako hotovou věc.

Spectator
Člen | 48
+
0
-

neznamy_uzivatel napsal(a):

Tohle vyzkouším, dík :) Pomůže mi to na jiných místech, kde mám šílené cykly, abych to celé přetáhl do pole a zacachoval :)

Jinak tenhle konkrétní případ je opravdu hodně pomalý, proto řeším, aby byl v cache celý ten html výstup. Jsou tam tisíce položek (desetitisíce dotazů napříč desítkami tabulek), php na tom stráví přes 2.5 sec.. (z toho mysql asi 250ms)

A mimo to… proste pocit, ze se neco natahlo z cache a presto se se zobrazenim ceka na funkci, ktera ty data znou nacte a zpracuje aniz by se ty nove data nekde projevily je… :D lehce neprijemny, kdyz se generovani stranky priblizuje bez cache ke trem vterinam a s cache v sablone asi na 2.95 :(

V tom případě je problém asi jinde :-) 3s je dost … MOC

neznamy_uzivatel
Člen | 115
+
0
-

3s je dost málo na to množství dat… (jiné scripty mi běží i několik hodin – mám v db dohromady něco přes 20 mil. řádků) Proto je tohle všechno v backendu a výstupem má být HTML do cache, kde se nic hodinu nemá měnit.

Každopádně nechápu ten problém s cache. Jak se zdá nedělá to všude. Právě jsem to vyzkoušel na jiném serveru a vypadá to, že se ta render metoda spouští opravdu jen při generování cache.

///
Už je to docela zmatené tady :D
Prostě je to složité, generuje se HODNĚ dlouhý výstup z opravdu VELKÉ databáze a je potřeba použít efektivní cache. Vzholedem k tomu, že se to na jiném serveru chová jinak, tak užn fakt nevím. Mám na to jednoduchý testovací kód a zcela náhodně se mi na některých serverech ta render metoda zavolá a jinde, nebo jindy zase ne..

Editoval neznamy_uzivatel (13. 8. 2016 17:24)

CZechBoY
Člen | 3608
+
0
-

Ty vypisujes celou databazi? Vsech 20M radku?
Deset tisic dotazu pri nacteni jedny stranky?
Specifikuj spis kde mas bottleneck :-) zkus nejakej profiler, treba blackfire.

Spectator
Člen | 48
+
0
-

CZechBoY napsal(a):

Ty vypisujes celou databazi? Vsech 20M radku?
Deset tisic dotazu pri nacteni jedny stranky?
Specifikuj spis kde mas bottleneck :-) zkus nejakej profiler, treba blackfire.

Přesně tak, brzdí ti to něco zbytečného v kódu. 20 mil. řádků v dtb. není nic hrozného. Běžně se pracuje s tabulkami o stovkách mil. záznamů.

Martk
Člen | 661
+
0
-

Data se ti načítají i po keši, protože fetchAll pošle dotaz do mysql a nette/database to zpracuje, bez ohledu na to, zda-li to použiješ.

Můžeš udělat tohle:

class Lazy implements IteratorAggregate {

	private $callback;

	public function __construct(callable $callback) {
		$this->callback;
	}

	public function getIterator() {
		$callback = $this->callback;

		return new ArrayIterator($callback());
	}

}

Rozdíl už je potom jen v presenteru:

$template = $this->template;
$template->setFile(__DIR__ . 'sablona.latte');
$template->tralala = new Lazy(function () {
	$this->database->table('egkategorie')->where('active = 1')->fetchAll();
});

Klidně můžeš vynechat i tu anonymní funkci a předávat Selection a v Lazy volat fetchAll.

Editoval Antik (13. 8. 2016 18:08)

David Matějka
Moderator | 6445
+
+1
-

@Antik ale ten fetchAll ma umisteny v render metode komponenty a cachuje cele vykresleni komponenty, takze by se to provadet nemelo. problem bude nekde jinde..

neznamy_uzivatel
Člen | 115
+
0
-

CZechBoY: Nevypisuju celou db, ta komponenta je něco jako menu s kategoriema. Vypisuje se jen část dat v závislosti na dalších tabulkách. Kód uvádělý na začátku je jen ukázka, kterou jsem zkoušel mimo ten projekt, abych si byl jistý, že se to takhle chová i jinde. Jinak používám xdebug, blackfire neznám..

Spectator: 100 mil. záznamu v jedné tabulce v MySQL je už opravdu velice nepříjemná práce. V reálu na frontendu používám sphinx – v tak velké tabulce je naprosto nemyslitelné rozumě filtrovat/vyhledávat, hlavně když tam mám desítky možných filtrů a potřebuju facety.

Antik: Tak tomu vůbec nerozumím :)

Pokud správně chápu, má to fungovat tak jak jsem očekával – pokud tu komponentu hodím v šabloně do {cache}, tak by se dotaz v render té komponenty neměl provést. V tom případě je buď bug v nette, nebo ve mě. Takže jdu hledat co jsem pokazil :)
Nejhorší je, že to neudělá pokaždé. Dal jsem si logovat každé spuštění toho renderu a je to opravdu náhodné. z 10ti refreshů se někdy spustí jednou, někdy 3×, někdy 10x…

Editoval neznamy_uzivatel (13. 8. 2016 18:45)

Martk
Člen | 661
+
0
-

@DavidMatějka Přečetl jsem si to znova a mlátím se za to do hlavy. Omlouvám se za zmatek.

@neznamy_uzivatel To když někdy cache makro funguje, není způsobenou náhodou úpravou latte šablony s cache makrem? Tímto se cache invaliduje.

Editoval Antik (13. 8. 2016 19:39)