Padání při přihlášení a odhlášení – regenerateId

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

Ahoj,

už si s tím nevím rady – při přihlašování a odhlašování na produkčních serverech mi to čas od času spadne do dumpu. Občas to vyhnije úplně a server ani neodpoví, občas to napíše je že nemá dost paměti, občas zvládne vygenerovat i dump, ale bohužel bez callstacku.

Chyba je tato:
Fatal error: Allowed memory size of 41943040 bytes exhausted (tried to allocate 279308 bytes) in /Nette/Http/Session.php on line 245

přičemž na zmiňovaném řádku je funkce regenerateId() volá session_start();
(Nette 2.0.5 a mám trochu podezření, jestli to nedělá od upgradu na tuto verzi…ale protože na localhostu vše funguje bez problémů, začal jsem si chyby všímat až s odstupem času na serverech).

Zkoušel jsem to i najiném serveru, kde je memory-limit asi 128M a tam to taky skončí chybou na paměť. Při tom za normálních okolností využívají scripty asi 8–12MB.

Nenapadá vás kde začít hledat problém?

V konfigu mám:

	nette:
		session:
			autoStart: true
			expiration: +2days

Samotný presenter pro přihlašování není nic složitého, autenticator taktéž.

class FrontendPresenter extends \BasePresenter {

    /** @persistent */
    public $backlink = '';

    public function startup() {
        parent::startup();
        //$this->session->start(); // required by $form->addProtection()
    }

    protected function createComponentSignInForm() {
        $form = new Form;
        $form->addText('username', 'Účet:')
                ->setRequired('Vyplňte prosím účet.');
        $form->addPassword('password', 'Heslo:')
                ->setRequired('Vyplňte prosím heslo.');
        $form->addSubmit('send', 'Přihlásit se');
	$form->onSuccess[] = callback($this, 'signInFormSubmitted');
        return $form;
    }

    public function signInFormSubmitted($form) {
        try {
            $values = $form->getValues();
            $user = $this->getUser();
            $user->login($values->username, $values->password);

            $this->application->restoreRequest($this->backlink);
            $this->redirect(':Akce:Homepage:Frontend:default');
        } catch (NS\AuthenticationException $e) {
            $form->addError($e->getMessage());
        }
    }

    public function actionOut() {
        $this->getUser()->logout();
        $this->flashMessage('Byl jste odhlášen.');
        $this->redirect('in');
    }
}
David Grudl
Nette Core | 8218
+
0
-

session start může vyvolat autoloading, pokud je v session uložený objekt. Že by třeba RobotLoader zařval na nedostatku paměti? Funkce token_get_all je docela žrout. Zkusil bych logovat do souboru, kdy je a s jakým souborem volána.

David Grudl
Nette Core | 8218
+
0
-

(BTW, strašně rád bych z robotloaderu funkci token_get_all vyhodil, ale není legrace regulárem parsovat jazyk, ve kterém lze $s = "{$var["class Abc"]}";)

Filip111
Člen | 244
+
0
-

Co přesně mám hledat?
Zkusil jsem zbastlit nějaký log, kde je čas a invoker funkce…asi.
Z toho ale vždy vypadne jen
20/09/2012 14:22:32 _rebuildCallback

Asi vymejšlim kraviny…jsem vůbec na správným místě?

Díky.

$soubor = fopen("log.txt", "a");
$trace = debug_backtrace();
$name = $trace[2]['function'];
$str = date("d/m/Y H:i:s") . ' ' . $name;
fwrite($soubor, "$str\n");

celá funkce funkce

private function scanScript($file)
	{
		$T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
		$T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
		$T_TRAIT = PHP_VERSION_ID < 50400 ? -1 : T_TRAIT;

		$expected = FALSE;
		$namespace = '';
		$level = $minLevel = 0;
		$time = filemtime($file);
		$s = file_get_contents($file);

		foreach ($this->list as $class => $pair) {
			if (is_array($pair) && $pair[0] === $file) {
				unset($this->list[$class]);
			}
		}

		if ($matches = Strings::match($s, '#//nette'.'loader=(\S*)#')) {
			foreach (explode(',', $matches[1]) as $name) {
				$this->addClass($name, $file, $time);
			}
			return;
		}

		$soubor = fopen("log.txt", "a");
		$trace = debug_backtrace();
		$name = $trace[2]['function'];
		$str = date("d/m/Y H:i:s") . ' ' . $name;
		fwrite($soubor, "$str\n");
		foreach (@token_get_all($s) as $token) { // intentionally @
			if (is_array($token)) {
				switch ($token[0]) {
				case T_COMMENT:
				case T_DOC_COMMENT:
				case T_WHITESPACE:
					continue 2;

				case $T_NS_SEPARATOR:
				case T_STRING:
					if ($expected) {
						$name .= $token[1];
					}
					continue 2;

				case $T_NAMESPACE:
				case T_CLASS:
				case T_INTERFACE:
				case $T_TRAIT:
					$expected = $token[0];
					$name = '';
					continue 2;
				case T_CURLY_OPEN:
				case T_DOLLAR_OPEN_CURLY_BRACES:
					$level++;
				}
			}

			if ($expected) {
				switch ($expected) {
				case T_CLASS:
				case T_INTERFACE:
				case $T_TRAIT:
					if ($level === $minLevel) {
						$this->addClass($namespace . $name, $file, $time);
					}
					break;

				case $T_NAMESPACE:
					$namespace = $name ? $name . '\\' : '';
					$minLevel = $token === '{' ? 1 : 0;
				}

				$expected = NULL;
			}

			if ($token === '{') {
				$level++;
			} elseif ($token === '}') {
				$level--;
			}
		}
		fclose($soubor);
	}
Filip111
Člen | 244
+
0
-

S tím co mi poradil David jsem se daleko nedostal…sorry, nemam takovou znalost vnitřku frameworku.

Mám ale další poznatek – když začnu se systémem pracovat, je zpočátku přihlašování/odhlašování v pořádku. Až po cca 20-ti requestech (normální práce se systémem – přihlášení, pár zobrazení stránky, odhlášení a znova přihlášení) to začne padat.

Je to jak kdyby se do session něco stále načítalo a až v okamžiku přihlášení/odhlášení to systém neustojí. Zajímavé ale je, že to dělá jen na serverech (bez ohledu na development nebo production mode).
Na localhostu se to chová korektně i když snížím memory limit na 40MB, na serveru nestačí 70MB.

Aurielle
Člen | 1281
+
0
-

Nepoužíváš na serveru minifikované Nette?

Filip111
Člen | 244
+
0
-

ne, je to klasická vícesouborová verze

Filip111
Člen | 244
+
0
-

tak ještě další poznatky – na localhostu v development modu bez problémů pracuji s 16MB memory limitem (včetně generování cache robot loaderem).
pokud na localhostu přepnu do production modu začne to taky padat – což je sice supr, že dokážu konečně chybu cíleně zopakovat, ale i tak moc netuším, čeho se chytit
Nějaký nápad? Díky.

Ještě mě napadá – jde v production modu zapnout debug bur, abych viděl nějaký peak ve využití paměti? (Pokud nastavím memory limit na 256MB, tak to zatím funguje v pořádku)
Z dokumentace jsem pochopil, že debug bar jde ruku v ruce s development modem, takže to asi nejde.

Editoval Filip111 (21. 9. 2012 9:34)

Filip111
Člen | 244
+
0
-

Já vím, že si tu píšu sám se sebou :), ale zkrátka nemůžu jinak – dokud to nevyřeším, nehnu se dál.

Mám další poznatky – pomocí memory_get_peak jsem zjistil, že při každém requestu si vezme php o něco více paměti. (opět platí jen pro produkční mod, při development modu si bere pořád stejně paměti).

Bez ohledu na to, jak je nastaven memory_limit si bere stále víc a víc, až se dostane na hranici limitu a tam zůstane. Jenže login a lougout z nějakého důvodu dokáží tuto hranici přelézt nebo se o to alepoň pokusí a scripty končí chybou na nedostatek paměti.

Teď ta zajímavější část – problém způsobuje stálé narůstání sessiony, která má pak klidně desítky MB. Při každém requestu se do ní vypíše dump..asi aktuálního presenteru, včetně containeru, requestu a všeho dalšího.

Prohledal jsem scripty a zdá se, že dump nikde sám nevolám. Stejně tak nikde nevkládám do session nějaký nesmysly a už vůbec ne dump. (ale i kdybych to tam vkládal, projevilo by se to přece i při development modu).

Tak a teď babo raď.

22
Člen | 1478
+
0
-

A nemůžeš si to odkrokovat, co se tam děje?

Filip111
Člen | 244
+
0
-

Ach jo – jasně že dalo. Pořád se nemůžu naučit debugovat php, ačkoliv pro javu, delphi, abap beru debugovaní jako samozřejmost :(

K problému – do sessiony se ukládají data z RequestPanelu https://componette.org/search/?…
Pokud zakomentuji jeho registraci, zdá se že je vše bez problémů (ještě zkusím jak to poběží na serverech).

Použití request panelu mám podle návodu:

public function startup() {
   parent::startup();
   RequestsPanel::register();
 }
pekelnik
Člen | 462
+
0
-

Request panel ja zabugovana pi… ehm vec…

krome toho nette uz dnes umi zobrazovat 2 a vice debug baru i pres presmerovani…

Editoval pekelnik (22. 9. 2012 10:11)

jasir
Člen | 746
+
0
-

Jako sorry, není to zabugovaná pí.. :-), jestli o nějakém bugu víš, tak ho nahlaš. Mě slouží dobře.

Problém je v tom, že panel flushne svoje data v session až při vykreslení, takže v produkčním módu nikdy.
V produkčním módu se vůbec nemá registrovat, imho ostatně jako žadný panel.

Je pravda, že teď nedávno se Nette naučilo si pamatovat dumpy i přes requesty, čímž trochu ztrácí smysl.

pekelnik
Člen | 462
+
0
-

Nechtel jsem se nikoho dotknout :)

Jen si pamatuju ze jsem tento panel vyzkousel jako vsechny nove addony a delalo to takove veci ze jsem si zapamatoval ze to nemam pouzivat. Tot vse.

Filip111
Člen | 244
+
0
-

Trochu mě zaráží zmínka, že se v produkčním modu nemá žádný panel registrovat. To je poprvé co něco takového slyším.
Předpokládal jsem, že se vše řídí nastavením produkčního/development modu a panely mohou být zaregistrovány stále, jen se zkrátka při produkčním modu nenastartují.

Jaká je tedy obvyklá praxe?

jasir
Člen | 746
+
0
-

Ztrácíš zbytečně výkon registrací něčeho, co nepotřebuješ. I v jiných panelech můžou být akce, které se provedou už před vykreslením, a jejich registrace je tedy časově náročnější.
Obalit registraci podmínkou:

if ($debug) {
   ...registrace panelů + další čístě developer mode akce
}

je normální praxe. Dělá to tak i samotný framework

Editoval jasir (23. 9. 2012 11:48)