Kešování objektu
- Tomik
- Nette Evangelist | 485
Zdravím!
Omlouvám se, zda již budu otvírat řešené téma, hledal jsem, ale
nenašel.
Můžu poprosit o podělení se o rady v oblasti kešování objektů? Konkrétně mi jde o Acl objekt.
Ale princip se určitě hodí i pro jiné objekty, které jsou např. generované z DB.
Napadlo mě sice pár nápadů, ovšem buď jsem při jejich realizaci udělal nějakou chybu, či ty nápady nevedou k cíly.
Díky!
- romansklenar
- Člen | 655
Mrkni na toto. Sice je to zralé na přepsání, ale než bych se k tomu dostal … (proč nemůžu zastavit čas aspoň na týden? :))
V bootstrapu pak je registrace služeb a jejich přiřazení k objektu User.
Environment::getServiceLocator()->addService(array('Acl', 'factory'), 'Nette\Security\IAuthorizator');
Environment::getServiceLocator()->addService('Users', 'Nette\Security\IAuthenticator');
$user = Environment::getUser();
$acl = Environment::getService('Nette\Security\IAuthorizator');
$user->setAuthorizationHandler($acl);
Celá továrnička funguje tak, že pokud není v keši objekt Acl, vytoří
se a jako klíč se použije název třídy + checksum z tabulky ve které je
uložená ACL (což je celé číslo). Pokud se něco v tabulce Acl změní,
změní se i vlastnost tabulky checksum → v keši nebude položka
s takovým klíčem → pak se zovu sestaví celý celý objekt Acl a nakešuje
se. Aby to celé fungovalo, musí mít tabulka nastaveno
CHECKSUM = 1
a ROW_FORMAT = FIXED
(pokud není,
provede se ALTER TABLE a nastaví to, viz. kód, zálohuj si ale před
tím DB).
That's it.
- Tomik
- Nette Evangelist | 485
Díky. Vypadá to pěkně, jen mám drobný problém s tím, že přes toto se to nedostane:
<?php
$table = dibi::fetch("SHOW TABLE STATUS FROM [:database:] WHERE [name] LIKE %s;", $aclTable);
$checksum = $table['Checksum'];
if (!$checksum) {
/*try {*/
if (dibi::query("ALTER TABLE [:cms:acl] CHECKSUM = 1 ROW_FORMAT = FIXED")) {
$table = dibi::fetch("SHOW TABLE STATUS FROM [:database:] WHERE [name] LIKE %s;", $aclTable);
$checksum = $table['Checksum'];
} else {
throw new RuntimeException("Can't find out 'Checksum' atribute on table '$aclTable' at database");
}
/*} catch (RuntimeException $e) { return $acl = new self; }*/
}
?>
Protože ať dělám co dělám, vždy mi databáze vrátí ve výsledku
políčko Checksum prázdné. Resp. dotaz
dibi::query("ALTER TABLE [:cms:acl] CHECKSUM = 1 ROW_FORMAT = FIXED")
vyhodí FALSE
a rovnou to vyhodí výjimku
throw new RuntimeException("Can't find out 'Checksum' atribute on table `$aclTable
at database");`.
Nevíš co s tím? Díky!
- romansklenar
- Člen | 655
Zkus dát pryč ten prefix: [:cms:acl]
→ [acl]
.
Já mám tabulky pojmenované oproti tomu obrázku prefixem
cms_
.
- romansklenar
- Člen | 655
A když provedeš dotaz ručně v db stane se něco?
Zkus nejdříve
SHOW TABLE STATUS FROM `nazev_db` WHERE `name` LIKE 'cms_acl';
a mrkni co je v Checksum
. Pokud to není kladné celé číslo
tak spusť (pro jistotu)
ALTER TABLE `cms_acl` CHECKSUM = 1 ROW_FORMAT = FIXED;
pak jdi do cms_acl
a proveď tam nějaký UPDATE
nebo INSERT
, tím by se měl checksum aktualizovat. Nakonec znovu
spusť první dotaz mrkni se jestli teď v checksumu je kladné
celé číslo.
- Jakub Šulák
- Člen | 222
Ještě bych zkontroloval, zda oba příkazy CHECKSUM a ROW_FORMAT podporuje DB, na které to provozuješ. Toto je asi psáno na mysql, nevím zda jiné DB mají ty příkazy stejně…
- romansklenar
- Člen | 655
Nn, viz kód: zkončil by jinou výjimkou:
NotImplementedException("Database drive $driver was not implemented yet into application", 500);
- Tomik
- Nette Evangelist | 485
romansklenar napsal(a):
A když provedeš dotaz ručně v db stane se něco?
Zkus nejdříve
SHOW TABLE STATUS FROM `nazev_db` WHERE `name` LIKE 'cms_acl';
a mrkni co je v
Checksum
. Pokud to není kladné celé číslo tak spusť (pro jistotu)ALTER TABLE `cms_acl` CHECKSUM = 1 ROW_FORMAT = FIXED;
pak jdi do
cms_acl
a proveď tam nějakýUPDATE
neboINSERT
, tím by se měl checksum aktualizovat. Nakonec znovu spusť první dotaz mrkni se jestli teď v checksumu je kladné celé číslo.
Ha! Tak tady bude chyba, na začátku mi to v Checksum ukazovalo NULL, po provedení popsaných operací, bohužel stále NULL. Docela by mě zajímalo čím to. :)
DB mám MySQL verze 5.0.51b-community-nt, tabulky jsou InnoDB a porovnávání utf8_general_ci. Sice to asi není důležité, ale bůhví, co na to bude mít vliv. :(
Editoval Tomik (17. 1. 2009 15:12)
- romansklenar
- Člen | 655
Přesně tak, viz dokumentace MySql.
EDIT:
Můžeš ale použít místo Sory, to vlastně tež nejde…Checksum
→
Update_time
.
Editoval romansklenar (17. 1. 2009 15:26)
- Tomik
- Nette Evangelist | 485
Já vůl! :) Díky, tohle mě fakt nenapadlo, přitom jsem o tom četl, asi tak rok zpátky. Začínám evidentně zapomínat.
Díky.
Vzhledem k tomu, že InnoDB nutně nepotřebuji, prostě zmigruju zpátky na
MyISAM, ale čistě teoreticky, předpokládám, že by to za určitých
podmínek šlo řešit pomocí záznamu Data_length
, ne? Vzhledem
k tomu, že mám v tabulce ACL ještě sloupec description
,
stačilo by přidat nakonec upravených záznamů mezeru (popř. pokud tam už
je ji odebrat). Tím by se změnila velikost dat a zaznamenal bych změnu. Ale
jednodušší pro mě bude prostě přemigrovat na MyISAM. :)
- phx
- Člen | 651
Nejsem si jist, ale InnoDB ma tu nevyhodu, ze kdyz tam jednou das X MB dat a pak je smazes tak soubor na HDD se jiz nezmensi. Otazka zni co to udela s Data_lenght.
Jinak moznosti by bylo dat tam sloupecek last_update kde pokazde vlozit NOW() a za checksum povazovat MAX(last_update).
- Tomik
- Nette Evangelist | 485
phx napsal(a):
Nejsem si jist, ale InnoDB ma tu nevyhodu, ze kdyz tam jednou das X MB dat a pak je smazes tak soubor na HDD se jiz nezmensi. Otazka zni co to udela s Data_lenght.
Jinak moznosti by bylo dat tam sloupecek last_update kde pokazde vlozit NOW() a za checksum povazovat MAX(last_update).
Díky. No, stejně asi tu DB zmigruju, je to jednodušší. :)