Věci, kterým se vyhnout, díl 1: INSERT IGNORE

Legacy code strikes back

Spoléhat se na nedokumentované funkce jazyka je poměrně riskantní. A když se to smíchá s INSERT IGNORE, jde to opravdu špatně.

Při jedné z aktualizací operačního systému na začátku roku 2016 byl Perl aktualizován z verze 5.14 na verzi 5.18. Bohužel se změnilo zpracování hashů, pořadí klíčů ve zcela specifickém scénáři už nebylo dodržováno a kód se rozbil. Máte pravdu, Perl pořadí nikdy nesliboval, ale náš kód spoléhal na některá specifika implementace.

Úryvek ze seznamu změn Perlu pro verzi 5.18:

Ve výchozím nastavení mohou nyní dvě různé hash proměnné se stejnými klíči a hodnotami poskytovat svůj obsah v jiném pořadí tam, kde byl dříve shodný.

Proč to byl problém? Kód připravoval seznam hashů pro vložení do databáze. Poté použil první položku k přípravě seznamu názvů sloupců, které budou předány následujícím dotazům INSERT. Každý odeslaný dotaz INSERT však používal jiné hashe pro přípravu seznamu hodnot k vložení. To vedlo k následujícímu:

INSERT IGNORE INTO foo (id, date) VALUES (1, ‚2006-10-11`), (‚2007-12-09‘, 2), …

Zásadně nebylo dodrženo pořadí názvů sloupců a příslušných hodnot. MySQL samozřejmě normálně hlásí chybu, když se někdo pokusí vložit datum do celočíselného sloupce. Ale vzpomínáte si na citaci z dokumentace MySQL, že? Tyto chyby se nevyvolávají, pokud se použije INSERT IGNORE a dojde k tiché konverzi dat:

Data konzistentně nekonzistentní

Skript po aktualizaci Perlu běžel v pořádku (no, alespoň jeho stderr byl tichý), ale po několika dnech jsme dostali hlášení o chybě, že něco není v pořádku s našimi interními nástroji, které používají tabulky poháněné Perlem. Soudě podle hlášení bylo něco dost podezřelé. A naše obavy potvrdil rychlý dotaz SELECT:

Nekonzistence dat, poháněná INSERT IGNORE

Tak co tu máme. Časové razítko a celočíselné hodnoty převedené na řetězec a vložené jako jméno uživatele, roky uložené v celočíselných sloupcích (je 2016 počet editací nebo rok poslední editace). Nekonzistence dat v nejlepší formě.

Nemusíte souhlasit, ale pro mě nejsou lepší žádná data než „data“ jako řádky výše.

Postižená tabulka váží přes 75 GiB a ukládá 460 mm řádků. Můžeme je přegenerovat, ale je to dlouhý proces. Skript běží ve chvíli, kdy čtete tento příběh. Tedy, běží už pět týdnů… To vše díky IGNORE v jediném dotazu.

Poučení

  • INSERT IGNORE je tichá bestie, která jen čeká na vaši chybu, aby z dat udělala nesmyslný soubor hodnot.
  • Nikdy neignorujte chyby. Dejte o nich hlasitě vědět. Selhávejte včas.
  • Spoléhat se na nedokumentované vlastnosti jazyka (Perl a pořadí hashovacích klíčů je jen jedním z mnoha příkladů) je riskantní hra.

Byli jste varováni. Nyní zkontrolujte svůj kód nebo ngrep svůj provoz na dotazy INSERT IGNORE.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.