Padání při přihlášení a odhlášení – regenerateId
- Filip111
- Člen | 244
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
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
(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
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
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.
- Filip111
- Člen | 244
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
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ď.
- Filip111
- Člen | 244
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();
}
- jasir
- Člen | 746
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.
- Filip111
- Člen | 244
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
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)