cacheJournal v SQLite funguje podivně

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

Nefunguje nám kešování s 9.3-stable. Občas (často) se cache nějakým způsobem zasekne a soubory s daty pak mají délku 0. V php_error.log je pak pro každý request několikrát následující warning:

[16-Feb-2010 14:55:35] PHP Warning: sqlite_exec(): cannot start a transaction within a transaction in /var/www/html/libraries/server/nette/0.9.3-stable/loader.php on line 1476

Čas od času se ještě objeví:

[16-Feb-2010 14:55:35] PHP Warning: sqlite_exec(): database disk image is malformed in /var/www/html/libraries/server/nette/0.9.3-stable/loader.php on line 1476

Smazání tempu tento problém občas vyřeší, občas ne. Objevuje se to jen na produkčním serveru, předpokládáme, že to bude mít souvislost s poměrně vysokou zátěží.

Nette 9.3-stable (min) / PHP5.3

Snažím se to právě nějak debuggovat a postnu update, pokud to ale už někdo řešil, budu vděčný za zprávu.

Wosonj
Člen | 36
+
0
-

Asi jsem našel chybu:

Nette\Caching\FileStorage::write(), r.216:

<?php
if (!sqlite_exec($db, "BEGIN; DELETE FROM cache WHERE file = '$dbFile'; $query COMMIT;")) {
	return;
}
?>

Podle dokumentace pokud tady dojde k chybě, tak může zůstat otevřená transakce a pak všechny další volání write() skončí výše uvedenou chybou. Měl by se vždy zavolat ROLLBACK.

Nemám s SQLite zkušenosti, jenom hádám. Pravděpodobně tam ale bude ještě nějaký jiný bug, který způsobí, že ten sqlite soubor se poškodí.

David Grudl
Nette Core | 8227
+
0
-

Že se poškodí SQLite soubor je průšvih, k tomu by nemělo dojít používáním jakkoliv chybných SQL konstrukcí.

Můžeš každopádně zkusit před return dát sqlite_exec($db, "ROLLBACK"), jestli to pomůže?

Wosonj
Člen | 36
+
0
-

David:

Jo, to jsem tam hned přidal a zatím to s tím běží.

Obávám se, že ta poškozená DB mohla být výsledkem mé činnosti, kdy první, co mne napadlo, bylo samozřejmě smazat temp, čímž jsem tu DB vlastně mazal SQLite pod rukama…

Jinak rychlý workaround, pokud by to postihlo ještě někoho, je dočasně cache vypnout:

do config.ini:

service.Nette-Caching-ICacheStorage = Nette\Caching\DummyStorage

Wosonj
Člen | 36
+
0
-

Ještě se vracím k této chybě – z našich zkušeností vyplývá, že k poškození SQLite databáze dochází při mazání tempu při deploymentu nové verze webu.

Pravděpodobně dojde k tomu, že nějaké vlákno s db pracuje, zatímco se databáze spolu s celým tempem smaže.

Aktuálně to řešíme tak, že součástí skriptu na nasazení nové verze je vypnutí a zapnutí httpd, aby k tomu nemohlo docházet. To je ale luxus, který si může dovolit jen někdo s vlastním serverem a není to rozhodně ideální stav (deploy jednoho webu způsobí výpadky všem ostatním a utnou se vlákna, kde může být rozdělaná důležitá práce).

Do budoucna by se mělo toto nějakým způsobem ošetřit na úrovni Nette – například chyby SQLite detekovat a pokud nějaká nastane, celý soubor preventivně zahodit.

Honza Kuchař
Člen | 1662
+
0
-

Nebo v deploy procesu nastavit storage na Dummy. 10 sekund počkat. Smazat cache. Nastavit zpět na FileStorage. Ale je fakt, že i po těch deseti sekundách s tou keší může pořád nějaké vlákno pracovat.

Editoval honzakuchar (14. 4. 2010 10:44)

David Grudl
Nette Core | 8227
+
0
-

Není nejlepší (i když ne nejjednodušší) jít nejčistší cestou a připojit se k souboru přes SQLite a zavolat DELETE * FROM cache?

Druhou možností, která se mi jeví jako atomická a snadná, je nejprve přejmenovat adresář v tempu a teprve poté jeho obsah mazat.

Michalek
Člen | 211
+
0
-

No, už týden to na ostrém serveru řeším taky. Každých pět minut mažu cronem celý temp (neptejte se proč, prostě proto :-) a občas se stane prostě nějaká chyba a soubor se poškodí. Celý web pak hází 500 a vůbec nic se pak nikam neloguje.

  1. Přejmenování a smazání tempu nepomáhá, pravděpodobně tam zůstane nějaké vlákno i tak a v nově vytvořeném souboru se to vytvoří blbě a je poškozený.
  2. DELETE FROM cache jsem zkoušel, ale stejně jako minule, zrovna se souborem asi pracuje přímo aplikace a smazání cache z jiného místa nemá účinek, protože se vzápětí obnoví starý obsah.

Odchytil jsem si poškozený cachejournal.sdb (můžu poskytnout) a po nakopírování na localhost dostávám také 500, takže jsem se pustil do nějakého zjišťování, kde to umře.

Za všechno „může“ FileStorage.php, kde ve funkci getDb() ještě sqlite_open funguje správně (nepozná, že je soubor nějak poškozený), sqlite_last_error($this->db) nehlásí chybu. Chyba nastane až po dalším řádku s @sqlite_exec a pak se hlásí chyba 1, respektive „SQL logic error or missing database“.

Prozatím jsem to vyřešil naprosto prasácky, prostě když je chyba, smažu soubor s databází. Při dalším načtení stránky se založí už dobře.

<?php
FileStorage.php
447	if(sqlite_last_error($this->db) == 1) {
448		sqlite_close($this->db);
449		unlink($this->dir . '/cachejournal.sdb');
450		return null;
451	}
?>

Za normálních okolností se to pravděpodobně nestane, takže asi není potřeba aby to nette nějak řešilo, spíš píšu kdyby na to někdy někdo narazil.

Editoval Michalek (15. 9. 2010 18:50)