Chybné cachování Nette/Database způsobí zbytečné dotazy do databáze

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

Zdravím.

Všiml jsem si, že na jedné stránce s komplikovanějším výpisem dat (výpis evidence vozů), kde využívám Nette\Database, se mi pokládá na databázi neuvěřitelné množství dotazů (kolem tisíce dotazů při výpisu 100 vozů). Začal jsem tedy pátrat a separovat problém. Ze stavu, kdy jsem měl 20 tabulek a různé průchody přes ref() a related() mezi nimi, jsem se dostal až do situace, kdy mám tabulky tři (car, operator a number; car má více dopravců, dopravce má více numberů) a v presenteru následující kód:

$cars = $this->db->table('car')->limit(100);

foreach ($cars as $car) {
	foreach ($car->related('operator') as $operator) {
		foreach ($operator->related('number') as $number) {
			$number->number;
		}
	}
}

Čekal bych, že se položí tři dotazy (nepočítám-li první načtení po vymazání cache). Položí se jich ale pět. A to konkrétně tyto:

SELECT `id` FROM `car` LIMIT 100

SELECT `id`, `idvozu` FROM `operator` WHERE (`operator`.`idvozu` IN (...))

SELECT `id`, `idvuzdopravce` FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))
SELECT * FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))
SELECT `id`, `idvuzdopravce`, `number` FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))

Tyto dotazy se položí vždy, nepočítám-li první načtení po vymazání cache, kdy se tahají informace o struktuře databáze.

V tabulkách car a number je asi 20 tisíc záznamů, v operator 40 tisíc. Když z libovolné z těchto tabulek vymažu většinu záznamů, začnou se obratem pokládat jen 3 dotazy do databáze (tzn. problém nastává jenom při velkém množství záznamů). Tabulky operator a number jsem osekal o přebytečné sloupce a nechal tam jen cizí a primární klíče. Když se pokusím osekat takto i tabulku cars, problém se opět vyřeší a začnou se pokládat jen 3 dotazy. Problém je vyřešen, také když odstraním z kódu $number->number; a místo toho tam dám třeba jen $number->id; (= když potřebuji jen kolonky primárního nebo cizího klíče, problém je vyřešen).

Docela zajímavé, že? :-D

Zkoušel jsem Nette krokovat s xdebugem, ale moc moudrý jsem z toho nebyl :-) Možná je problém někde u poznamenávání do $this->previousAccessedColumns a následnému ukládání do cache, ale nijak extra do toho nevidím.

Jen pro úplnost: Nette 2.0.10, PHP 5.4.6–1ubuntu1.1.

hrach
Člen | 1838
+
0
-

Hmhm, tak fakt nevim co s tim. Nemuzes udelat minimalni dump, pri kterem je to spatne?

Eda
Backer | 220
+
0
-

Máš to na mejlu (školním).

hrach
Člen | 1838
+
0
-

no, chlapce, dals mi zabrat. https://github.com/…base-caching
jeste k tomu teda ale musim napsat test… :)

  • doba ramcoveho pochopeni problemu: 10min
  • doba kompletniho pochopeni problemu: 1,5h
  • doba na vyreseni: 30min
  • doba na test: odhad 30min
Eda
Backer | 220
+
0
-

Krutý! Funguje. Že bude za den od nahlášení hotová oprava, to jsem fakt nečekal. Díky a palec nahoru :-)

hrach
Člen | 1838
+
0
-

Hmhm, tak pri psani testu jsem objevil jeste chyby, takze stalo na tom makam, abych mel funkcni test a hlavne to spravne vsechno fungovalo :D

hrach
Člen | 1838
+
0
-

Už jsemto celý upravila a ofixoval. Nyní si myslím, že konečně cache (a zápis a čtení z ní) funguje korektně :))

enumag
Člen | 2118
+
0
-

@hrach: Dobrá práce! ;-)