Database refetch failed; row does not exist
- kolsi
- Člen | 131
Ahoj,
chtěl bych se zeptat, jestli někdo už nezažil tento problém či jak ho řešit. Přijde mi, že neděláme nic speciálního, ale čas od času se objeví chyba „Nette\InvalidStateException: Database refetch failed; row does not exist!“.
Nedokážu říct přesně za jakých podmínek se tak stane, ale vypadá to, že je větší pravděpodobnost výskytu, když se vymaže cache. Nejčastěji se to objevuje u tohoto kódu:
Presenter:
public function actionDefault($id) {
...
$comments_model = array();
foreach ($this->commentRepository->findByProjectId($id)->order('timestamp DESC') as $comment) {
$comm = ArrayHash::from($comment);
$comm->attachments = $comment->related('project_comment_attachment', 'comment_id');
$comments_model[] = $comm;
}
...
Šablona pak prochází komentáře a každého vypisuje jeho přílohy:
<li n:foreach="$comment->attachments as $attachment" class="{$attachment->type}">
...
</li>
a nejčasteji to padne právě na tom „{$attachment->type}“
V databázi jsou dvě tabulky: project_comment a project_comment_attachment, kde ta druhá obsahuje cizí klíč „comment_id“ do té první. Sloupec „type“ je pak varchar(45) v tabulce project_comment_attachment. Používáme NetteDatabase.
Editoval kolsi (15. 3. 2016 9:37)
- Oli
- Člen | 1215
Taky mě to na jednom projektu dělá. Taky netuším proč. Ale nestojí mě za to to zjišťovat (náklady x užitek). Ale mám pocit, že to mělo něco společného s NDBT cachí. Jednou za čas to prostě jednomu člověku spadne, další requesty už chodí v pořádku. Začal jsem používat Doctrine :-) (Ne kvůli tomuhle problemu)
- TomasHalasz
- Bronze Partner | 79
Ahoj, potýkám se se stejnou chybou… V šabloně plním proměnnou hodnotou z tabulky takto:
{var $value = $oneData[$key]}
a tady to spadne. Stejně jak psal Kolsi, nejpravděpodobněji to spadne hned po tom co vymažu soubory z cache, ale až tak při druhém načtení stránky. A dělá to jen na produkčním serveru s Ubuntu. Na lokále s Windows 10 se to ještě nestalo…
Tracy mám uloženou kdyby to pomohlo…
- davidindra
- Člen | 2
Taky mi to teď dělá, je to pro mě dost velkej problém (stává se mi to podstatně častěji než občas). Nic nestandardního nedělám, netuším, kde je problém.
- TomasHalasz
- Bronze Partner | 79
Mě to dělá celkem pravidelně. Když na produkčním serveru vymažu cache, otevřu si dvě anonymní okna a v každém se přihlásím do své aplikace pod jiným uživatelem (každý má vidět jiná data) tak se ta chyba stane. Když nastane tak stačí refresh stránky a jede to dál…
Tracy jsem nahrál zde: „:http://faktury.cz/…te_tracy.htm“
Vůbec si s tím nevím rady.. a dělá to jen na produkčním serveru…
- Unlink
- Člen | 298
Skúsim sa na to pozrieť, ale mám otázočku
každý má vidět jiná data
Tým myslíš rovnaké stĺpce ale iné riadky keďže chyba čo si poslal je
z datagridu
A druhá otázka, ako vyzerá primárny kľúč tej tabuľky, z ktorej
selectuješ + selectuješ aj niečo specialne tj. máš špecifikované vlastné
->select()
?
- TomasHalasz
- Bronze Partner | 79
Ano mají vidět stejné sloupce, ale jiné řádky.
Primární klíč je ID záznamu. Select je pak pro každého uživatele
s podmínkou cl_company_id = idCompany kde idCompany je ID firmy, ke které má
uživatel povolen přístup.
Nic speciálního neselectuju, až v šabloně vybírám pole, které chci
zobrazit.
- TomasHalasz
- Bronze Partner | 79
CZechBoY napsal(a):
Ja si do modelu predam uzivatele a z nej vyberu treba roli nebo neco z identity.
Nevim jak moc je to good practise… Jestli je treba lepsi predavat roli/id firmy parametrem..
To ano takto to dělám (výběr id firmy z identity), ale problém nastává až potom při selectu nad tabulkou, kde použiju filtr právě na id firmy.
- TomasHalasz
- Bronze Partner | 79
CZechBoY napsal(a):
A kdyby sis dal ty attachmenty nekde do vedlejsiho pole? Asociovany treba podle id ty nadrazeny tabulky..
Moc nerozumím jak to myslíš. Ale povedlo se mi trošku více problém izolovat. Totiž kromě filtru podle firmy tam mám ještě filtr na uživatel dané firmy, kterým je povolen přístup. Ti jsou zapsaní v další tabulce cl_partners_event_users.
Hlavní tabulka s daty je definována takto:
CREATE TABLE cl_partners_event
(
id
int(11) NOT NULL,
cl_partners_book_id
int(11) DEFAULT NULL,
cl_company_id
int(11) NOT NULL,
cl_users_id
int(11) DEFAULT NULL,
work_label
varchar(255) COLLATE utf32_czech_ci NOT NULL,
description
longtext COLLATE utf32_czech_ci NOT NULL);
ALTER TABLE cl_partners_event
ADD PRIMARY KEY (id
),
ADD KEY cl_company_id
(cl_company_id
),
ADD KEY cl_users_id
(cl_users_id
),
ADD KEY cl_partners_book_id
(cl_partners_book_id
);
ALTER TABLE cl_partners_event
MODIFY id
int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE cl_partners_event
ADD CONSTRAINT cl_partners_event_ibfk_1
FOREIGN KEY
(cl_company_id
) REFERENCES cl_company
(id
),
ADD CONSTRAINT cl_partners_event_ibfk_2
FOREIGN KEY
(cl_users_id
) REFERENCES cl_users
(id
),
ADD CONSTRAINT cl_partners_event_ibfk_3
FOREIGN KEY
(cl_partners_book_id
) REFERENCES cl_partners_book
(id
);
Tabulka s oprávněními pro jednotlivé uživatele je zde:
CREATE TABLE IF NOT EXISTS cl_partners_event_users
(
id
int(11) NOT NULL AUTO_INCREMENT,
cl_partners_event_id
int(11) NOT NULL,
cl_users_id
int(11) NOT NULL,
cl_company_id
int(11) NOT NULL,
PRIMARY KEY (id
),
KEY cl_partners_event_id
(cl_partners_event_id
),
KEY cl_users_id
(cl_users_id
),
KEY cl_company_id
(cl_company_id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci
AUTO_INCREMENT=87 ;
Samotný dotaz v modelu se pak skládá takto:
<?php
$this->tableName = 'cl_partners_event';
$this->userAccesTableName = 'cl_partners_event_users';
$company_id = 1 //id firmy z identity uzivatele;
$user_id = 2 //id uzivatele z identity;
return $this->database->table($this->tableName)->where($this->tableName.'.cl_company_id = ? AND ('.$this->tableName.'.cl_users_id = ?'.' OR :'.$this->userAccesTableName.'.cl_users_id = ?)',$company_id,$user_id,$user_id);
?>
Dotaz má fungovat tak, že vybere všechny záznamy, které patří dané firmě a uživateli, nebo patří dané firmě a uživatel je definován v přístupové tabulce cl_partners_event_users.
Tak jak to mám to funguje jak potřebuju, kromě těch pádů po vymazání cache…
- Unlink
- Člen | 298
No, tá výnimka sa vyhadzuje v prípade, keď je nutný refetch dát
(kvôli chýbajúcemu stĺpcu) a po znovunačítaní sa nepodarí načítať
všetky pôvodné riadky.
https://github.com/…4cc47133f151
To je dôvod, prečo sa to nedá nasimulovať pri vývoji, kde nieje moc reálne, aby ti daný riadok zmizol. Takže podľa môjho názoru je výnimka legitímna, lebo sa nič s tým spraviť nedá. Každopádne, k tejto situácii by dochádzať nemalo, jedine ako som spomínal, že jeden používateľ vidí iné stĺpce ako druhý, vtedy to nastať môže.
Ono sa to samozrejme dá obísť. Pokiaľ špecifikuješ stĺpce pomocou
->select()
manuálne, tak nette sa nepokúša využívať túto
chytrú cache.
Mohol by si prosím ťa ešte ukázať, ako ďalej s danou selection
pracuješ?
Mne sa podarilo nájsť trošku okrajový use-case ktorý nefunguje tak ako by
mal ale to súvisí skôr s týmto
https://forum.nette.org/…mazani-cache
i keď po tom mojom commite to môže namiesto Undefined offset vyhadzovať
výnimku.
- TomasHalasz
- Bronze Partner | 79
Unlink napsal(a):
No, tá výnimka sa vyhadzuje v prípade, keď je nutný refetch dát (kvôli chýbajúcemu stĺpcu) a po znovunačítaní sa nepodarí načítať všetky pôvodné riadky.
https://github.com/…4cc47133f151To je dôvod, prečo sa to nedá nasimulovať pri vývoji, kde nieje moc reálne, aby ti daný riadok zmizol. Takže podľa môjho názoru je výnimka legitímna, lebo sa nič s tým spraviť nedá. Každopádne, k tejto situácii by dochádzať nemalo, jedine ako som spomínal, že jeden používateľ vidí iné stĺpce ako druhý, vtedy to nastať môže.
Teď nevím jestli myslíš sloupce nebo řádky :-)
Pokud jde o řádky tak způsob jak je získávám jsem popsal výše. A je
pravda, že v jiných částech programu, kde nepoužívám způsob
přidělení záznamů uživatelům pomocí další tabulky se ta chyba zatím
nevyskytla.
Pokud by šlo o sloupce tak tam mám ještě udělanou takovou věc, že si každý uživatel může nastavit jaké sloupce chce vidět… ale chyba o kterou jde říká, že "nemůže najít řádek ":http://faktury.cz/…te_tracy.htm
- Unlink
- Člen | 298
TomasHalasz napsal(a):
Pokud by šlo o sloupce tak tam mám ještě udělanou takovou věc, že si každý uživatel může nastavit jaké sloupce chce vidět…
No, a toto bude jadro toho problému, pokiaľ sa to deje len v po vymazaní cache a potom už nie, tak je je kvôli tomu, že medzi tým ako si prvý krát načítal záznamy a potom ako sa načítali po druhý krát (keďže mu chýbal nejaký stĺpec) nejaký záznam zmizol.
Malo by sa to dať obísť takto
return $this->database->table($this->tableName)->select($this->tableName.'.*')->where($this->tableName.'.cl_company_id = ? AND ('.$this->tableName.'.cl_users_id = ?'.' OR :'.$this->userAccesTableName.'.cl_users_id = ?)',$company_id,$user_id,$user_id);
(keď tam pridáš ->select($this->tableName.'.*')
tak sa
nebude cachovať to použivanie stĺpcov a problém by mal zmiznúť)
Editoval Unlink (15. 5. 2016 14:34)
- TomasHalasz
- Bronze Partner | 79
Unlink napsal(a):
No, a toto bude jadro toho problému, pokiaľ sa to deje len v po vymazaní cache a potom už nie, tak je je kvôli tomu, že medzi tým ako si prvý krát načítal záznamy a potom ako sa načítali po druhý krát nejaký záznam zmizol.
Aha, to co píšeš se může stát. Po vymazání cache první uživatel
zobrazí stránku se záznamy s ID řekněme 1,2,3,4,5,6 a se sloupci A,B,C,D.
Stránka se mu otevře v pořádku. Pak druhý uživatel otevře stránku, ale
vidí jen záznamy 2,3,4,5 (protože k jiným nemá práva) a sloupce B,C,D
(protože si to takto nastavil).
Takže v takovémto případě nette chce udělat refresh dat, protože je
ještě nemá v cache. Je to tak? A chce dělat refresh pro záznam s ID
1 i když je tento záznam podmínkou vyloučen? Dá se říct proč to
tak je?
Jinak zkusím tu úpravu co jsi psal a dám vědět.
- TomasHalasz
- Bronze Partner | 79
Unlink napsal(a):
(keď tam pridáš
->select($this->tableName.'.*')
tak sa nebude cachovať to použivanie stĺpcov a problém by mal zmiznúť)
Super! Zatím to vypadá, že to pomohlo. Po vymazání cache žádný problém :-) Ještě budu testovat.
- Unlink
- Člen | 298
Nie, najskôr sa prihlási prvý používateľ, nette, keďže nemá cache
tak načíta všetky stĺpce tabuľky, ale používateľ si zobrazí len A,B,C
čo si nette zapamätá. Príde druhý používateľ, nette si zapamätalo že
minule sa použili stĺpce A,B,C tak načíta z databázy len nie. No druhý
používateľ požaduje povedzme A,B,D. Stĺpec D nieje k dispozícii tak nette
spraví to, že zoberie si IDčka všetkých záznamov ktoré načítalo a
pokúsi sa načítať všetky stĺpce pre dané IDčka
(SELECT * FROM table WHERE id IN(...)
) no a teraz sa môže stať,
že medzi tým ako prvý krát načítalo záznamy, a teraz keď ich číta
druhý krát, tak nejaký záznam zmizol čo sa na zaťaženom serveri
stať môže.
- TomasHalasz
- Bronze Partner | 79
Unlink napsal(a):
Nie, najskôr sa prihlási prvý používateľ, nette, keďže nemá cache tak načíta všetky stĺpce tabuľky, ale používateľ si zobrazí len A,B,C čo si nette zapamätá. Príde druhý používateľ, nette si zapamätalo že minule sa použili stĺpce A,B,C tak načíta z databázy len nie. No druhý používateľ požaduje povedzme A,B,D. Stĺpec D nieje k dispozícii tak nette spraví to, že zoberie si IDčka všetkých záznamov ktoré načítalo a pokúsi sa načítať všetky stĺpce pre dané IDčka (
SELECT * FROM table WHERE id IN(...)
) no a teraz sa môže stať, že medzi tým ako prvý krát načítalo záznamy, a teraz keď ich číta druhý krát, tak nejaký záznam zmizol čo sa na zaťaženom serveri stať môže.
No jenže u mě jsem na tom produkčním serveru úplně sám… takže nemůže jít o vymazání záznamu nebo změnu práv k záznamu takříkajíc „pod rukama“. Udělal jsem jen otevření stránky u jednoho uživatele a pak otevření u druhého.
A ještě jsem se chtěl zeptat, když to funguje tak, že nette nemá sloupec D k dispozici tak bere všechny ID, které načetlo předtím a podle nich hledá záznamy. Proč v tu chvíli nepracuje podle aktuálně požadovaného ID?
- Unlink
- Člen | 298
TomasHalasz napsal(a):
A ještě jsem se chtěl zeptat, když to funguje tak, že nette nemá sloupec D k dispozici tak bere všechny ID, které načetlo předtím a podle nich hledá záznamy. Proč v tu chvíli nepracuje podle aktuálně požadovaného ID?
Je to pre to, aby „obnovilo“ celú selection a nepokladalo dotazy pre
každý riadok zvlášť.
A načítanie len konkrétneho riadku by nepomohlo, pretože táto výnimka
nastáva práve vtedy, keď sa nepodarilo nájsť aktuálny riadok (inak to
vypisuje undefined offset).
Skús mi napísať na email, a môžme to nejako zdebugovať, pretože ak to vieš nasimulovať tak to je super, lebo táto chyba zvyčajne nastáva náhodne, za situácii aké som popisoval o pár postov vyššie, takže u teba bude problém zrejme inde.
- Pechy.cz
- Bronze Partner | 21
Taky se připojuji. Pro mě už je to docela velký problém. Vždycky jsem
řekl „V pohodě! Aktualizuj stránku“..
Jenže poslední dobou nepomůže ani aktualizace, nebo resp. pomůže, ale až
tak třeba 10. aktualizace. V tu chvíli se zase vše tváří nevinně.
Klienti už se začali ozývat, snažím se najít nějaké východisko, nebo
hack.
Co jsem si to zkoušel procházet, tak to dělá nahodile v podstatě kdekoliv
v systému. Na některých místech sice častěji, ale to je tím, že to jsou
více používané a vytěžované místa. (minutový cron a
nejnavštěvovanější stránka)
Jestli jsem to pochopil správně, tak se chyba objeví, když tahám data z řádku A, dojde k refetch a stačí že chybí řádek B, který byl v Selection taky? Protože řádek A, který používám je tam vždy..
- Unlink
- Člen | 298
Môžeš vyskúšať tento fix?
https://github.com/…ase/pull/137
Ak to robilo náhodne tak by to mohlo pomôcť
Editoval Unlink (2. 6. 2016 17:02)
- Pechy.cz
- Bronze Partner | 21
Unlink napsal(a):
Môžeš vyskúšať tento fix?
https://github.com/…ase/pull/137
Ak to robilo náhodne tak by to mohlo pomôcť
Večer to hodím na produkční a uvidíme ;-)
Každopádně i pokud to nepůjde, tak ti patří tisíce díků.
A pokud to půjde, tak o tisíce víc ;-)
Editoval Pechy.cz (3. 6. 2016 11:53)
- filsedla
- Člen | 101
Pořád se mi tahle chyba občas náhodně vyskytuje. Last muted error je „Warning: fopen(xxx/app/../temp/cache/_Nette.Database.xxx): failed to open stream: No such file or directory“.
edit: Dostávám „Database refetch failed; row with signature ‚xxx‘ does not exist“, je to trochu jiná chyba.
Editoval filsedla (18. 5. 2017 10:21)
- podolinek
- Člen | 7
Tato chyba se mi dneska podařila vyextrahovat do separe skeletonu. Ve zkratce to u mě je v tom, že volám metodu related nad resultsetem, pak zavolám count nad získanými related výsledkama a pak procházím foreachem. Bez zavolání count vše funguje jak má. Více v issue na githubu, kde je i uvedený skeleton s danou chybou.
- filsedla
- Člen | 101
Super! Taky se mi jednou už povedlo najít konzistentní proceduru, jak to přivodit obsahující načtení stránky 2, 3, 7, 8 seznamu objednávek na produkčním serveru a udělání 10 dřepů. Pak jsem na to zapomněl. Tohle je lepší.
Myslím, že jsem to taky zkoušel xdebugovat, ale vůbec jsem tomu nerozuměl.
- oldrich.valek
- Člen | 21
Mně se toto stává, když ve foreach nad řádky z jedné tabulky tahám pomocí related všechny závislé řádky z tabulky druhé (s použitím nějaké hodnoty z té druhé tabulky). Děje se to při následující sekvenci.
- Vymazat cache.
- Načíst stránku pouze s řádky v první tabulce, které zrovna nemají žádné závislé řádky v tabulce druhé.
- Následně načíst stránku, kde již řádky z první tabulky závislosti v druhé tabulce mají (odehrává se to změnou omezení řádků z první tabulky, např. jiný jazyk/kategorie apod.).
Po chybě již následující requesty probíhají bez problémů. Mám stable Nette 2.4 (Nette database v2.4.4, reference: 9b16e3ea2d9c21579962a17d81e17e4f71f59d6b). Mám pocit, že jsem tu někde kdysi četl, že se ta chyba snad ani nedá vyřešit, nebo jen vypnutím cache. Dosud, když se tato chyba objevila, tak jsem jen v daném místě zavolal po ->related(…) ještě ->select(‚*‘), což problém obchází.
Má konfigurace db spojení:
database:
default:
options: [PDO::MYSQL_ATTR_COMPRESS = true]
debugger: true
explain: true
conventions: discovered
autowired: true
Editoval oldrich.valek (10. 12. 2017 20:48)
- kolsi
- Člen | 131
Chyba se stále objevuje a bude to tak, jak píše oldrich.valek.
- Vymažu cache
- otevřu stránku s komentáři (foreach), kde žádný nemá přílohu (funguje)
- otevřu jinou stránku s komentáři, kde je příloha ⇒ bum „Database refetch failed; row with signature ‚44‘ does not exist!“
Stane se to vždy, když provedu tento postup. Po krátkém debugování jsem přišel na to, že je problém v tomto (snad to popíšu pochopitelně :-)):
- vykresluje se první komentář/příloha, a ActiveRow::get zavolá ActiveRow::accessColumn
- ActiveRow::accessColumn zavolá Selection::accessColumn
- Selection::accessColumn zavolá postupně emptyResultSet (kde vyprázdní $this->rows) a execute (provede SQL a vytáhne nová data z DB do $this->rows)
- nyní se začne vykreslovat další komentář/příloha, znovu se postupně zavolá ActiveRow::get → ActiveRow::accessColumn → Selection::accessColumn
- Selection::accessColumn zavolá emptyResultSet , které vyprázdní $this->rows
- Selection::accessColumn zavolá execute, které ale nic nedělá (viz podmínka !isset($this->refCacheCurrent[‚data‘], která nyní není „false“, protože byla nastavena u předchozího komentáře, v GroupedSelection::execute)
- podmínka v ActiveRow: „if (!isset($this->table[$this->getSignature()]))“ způsobí chybu, protože $this->rows (ke kterému se přistupuje přes $this->table) je prázdné (viz bod 5)
- Pechy.cz
- Bronze Partner | 21
My jsme se jej už téměř zbavili. Nemám tady kompletní soupis jako kolsi, ale stávalo se nám to hlavně v cronech.
Příklad – odesílání emailů
- 99% běhu cronu není co posílat, žádné emaily neexistuji, ($items jsou prázdný)
- pak se přidá email k odeslání, takže se vstoupí do foreach, kdy ale email není ještě autorizovaný, takže na první podmínce ->authorized je vráceno false a tím běh končí.
- jenomže v tu chvíli si database uložila do cache, že se přistupuje jen k sloupečku authorized.
- pak uživatel schválí odeslání emailu, a v dalším běhu cronu si již nette pamatuje, že si má tahat jen authorized
- na jakámkoliv dalším vytažení jiných dat, než je authorized to padne
- dočasným řešením bylo přidat select(‚*‘) (nebo samozřejmě vypsat sloupečky co budou potřeba) do všech metod, který tohle způsobovaly
- s tímhle to teď funguje bez problému, ale zbytečně se toho tahá moc
- TomasHalasz
- Bronze Partner | 79
Teď v poslední aktualizace nette/dbase 3.0.2 to je vyřešeno viz. zde: https://github.com/…e/issues/230#…
- MKI-Miro
- Člen | 278
Mne táto chyba začala vyskakovať
Database refetch failed; row with signature ‚1576‘ does not exist!
keď doťahuje $product->manufacturer->name
<SHOP>
67: <?php
68: $iterations = 0;
69: foreach ($products as $product) {
70: ?>
71: <SHOPITEM>
72: <ITEM_ID><?php echo LR\Filters::escapeHtmlText($product->ean) /* line 8 */ ?></ITEM_ID>
73: <PRODUCTNAME><?php echo LR\Filters::escapeHtmlText($product->manufacturer->name) /* line 10 */ ?> - Puzzle <?php
74: echo LR\Filters::escapeHtmlText($product->name_cz) /* line 10 */ ?></PRODUCTNAME>
75: <PRODUCT><?php echo LR\Filters::escapeHtmlText($product->manufacturer->name) /* line 11 */ ?> - Puzzle <?php
76: echo LR\Filters::escapeHtmlText($product->name_cz) /* line 11 */ ?></PRODUCT>
77: <DESCRIPTION>
ako to opraviť?
Editoval MKI-Miro (28. 10. 2020 23:31)