Optimalizácia práce s cache

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

Zdravím,
pracujem s trochu staršou verziou Nette: 2.0-dev (revision a8e74c1 released on 2010–10–14)

s cache sa hrám len veľmi krátko.
Riešim v tomto asi úplnú klasiku: cache-ovanie url tvorených z databázy.
Robím to zatiaľ takto (ilustrácia kódu):

<?php
class MyRouting{
	public static function getUrlById($id){
		$storage = new Nette\Caching\FileStorage(TEMP_DIR);
		$cache = new Cache($storage);

		$urls = $cache->getStorage()->read('urls');		//	je v cache zaznam  ??
		if(empty($urls[$id])){							//	ak nie, citame z DB
			$title = dibi::query("SELECT title FROM [pages] WHERE id=%i", $id)->fetchSingle();
			$title = Nette\String::webalize($title);
			$urls[$id] = $id.'-'.$title;
			$cache->save('urls', $urls);				//	ulozime ziskane url z databazy do cache
		}else{
			//	mame to z cache = je to ok
		}
		return $urls[$id];
	}
}
?>

Mám ale dojem, že to nerobím dobre. Ak to chápem správne, tak volaním $cache->getStorage()->read(‚..‘) reálne čítam zo súboru a $cache->save(‚..‘) zapisujem. Jedná sa o desiatky až stovky volaní pri jednom behu.

Chcel by som to vyriešiť tak, že niekde pri štarte si súbor so zapísanými (v minulosti vygenerovanými) url 1× prečítam do premennej, potom s tým pracujem ako s premennou (prípadne za behu niečo pridám) a ak sa niečo zmenilo, premennú znova zapíšem do súboru.

Ak toto riešiť s použitím Environment a cache? Z dokumentácie som sa dostal do tohto stavu, ale myslím, že to musí ísť nejak rozumnejšie.

Ďakujem

Editoval tomolas (6. 10. 2011 11:58)

tomolas
Člen | 66
+
0
-

Ďakujem veľmi pekne.
Je to síce krásna ukážka, ale ide do ešte väčšej abstkrakcie. Ja som skôr chcel pochopiť, ako čo najprimitívnejšie vyriešiť problém viacnásobného čítania súboru. Resp. kam mám dať $cache = new Cache, aby to bolo čo najrýchlejšie.
V tom príklade o niečom takom nie je reč.

Aj tak ďakujem za pomoc.

Filip Procházka
Moderator | 4668
+
0
-

To záleží na povaze dat. Přečti si celé vlákno.

Panda
Člen | 569
+
0
-

Osobně bych to vyřešil asi takto:
Pozor, kód je psaný pro aktuální verzi Nette, základní myšlenka je však stejná.

<?php

class MyRouting
{
	/** @var \Nette\DI\IContainer */
	private $container;

	/** @var \Nette\Caching\Cache */
	private $cache;

	/** @var array Lokální cache na URL. */
	private $urlList;

	public function __construct(\Nette\DI\IContainer $container)
	{
		$this->container = $container;
	}

	protected function getCache()
	{
		if ($this->cache === NULL) {
			$this->cache = new \Nette\Caching\Cache($this->container->cacheStorage);
		}
		return $this->cache;
	}

	public function getUrlById($id)
	{
		if ($this->urlList === NULL) {
			$cache = $this->getCache();
			if (isset($cache['urlList']) {
				$this->urlList = $cache['urlList'];
			} else {
				$this->urlList = array();
			}

			if (!isset($this->urlList[$id])) {
				$title = dibi::query('SELECT [title] FROM [pages] WHERE [id] = %i', $id)->fetchSingle();
				$this->urlList[$id] = $id . '-' . \Nette\Utils\Strings::webalize($title);
				$cache->save('urlList', $this->urlList);
			}
		}

		return $this->urlList[$id];
	}
}
?>

Cache se vytváří i čte skutečně jen jednou a to vždy v situaci, kdy je skutečně potřeba (lazy-loading). Zápis bude probíhat i vícekrát, ale předpokládám, že to zas takový problém nebude. Pokud bys i vícenásobné zápisy chtěl eliminovat, tak vždy při zjištění neexistence ID načti celou tabulku:

<?php
if (!isset($this->urlList[$id])) {
	$list = dibi::query('SELECT [id], [title] FROM [pages]');
	foreach ($list as $page) {
		$this->urlList[$id] = $page->id . '-' . \Nette\Utils\Strings::webalize($page->title);
	}
	$cache->save('urlList', $this->urlList);
}
?>
tomolas
Člen | 66
+
0
-

Ďakujem veľmi pekne!
Ja som to medzi časom vyriešil špinavo, ale rýchlo.
Aj tak vďaka za pekný príklad (aj pre ostatných).