Az elkerülendő dolgok, 1. rész: INSERT IGNORE

A hagyatéki kód visszavág

A dokumentálatlan nyelvi jellemzőkre támaszkodni meglehetősen kockázatos. És amikor ez INSERT IGNORE-val keveredik, a dolgok nagyon rosszra fordulnak.

A 2016 elején az egyik operációs rendszer frissítés során a Perl 5.14-ről 5.18-ra frissült. Sajnos a hash-ok kezelése megváltozott, a kulcsok sorrendje egy egészen speciális forgatókönyvben már nem volt karbantartva, és a kód elromlott. Igazad van, a Perl sosem ígérte meg a sorrendet, de a kódunk támaszkodott néhány végrehajtási sajátosságra.

Kivonat a v5.18-as Perl changelogból:

Előre két különböző hash-változó azonos kulcsokkal és értékekkel mostantól eltérő sorrendben adhatja meg a tartalmukat ott, ahol korábban azonos volt.

Mi volt a probléma? A kód az adatbázisba beszúrandó hash-ek listáját készítette elő. Ezután az első elemet a következő INSERT lekérdezéseknek átadandó oszlopnevek listájának előkészítésére használta. Azonban minden egyes elküldött INSERT lekérdezés különböző hash-okat használt a beszúrandó értékek listájának elkészítéséhez. Ez a következőkhöz vezetett:

INSERT IGNORE INTO foo (id, dátum) VALUES (1, ‘2006-10-11`), (‘2007-12-09’, 2), …

Az oszlopnevek és a hozzájuk tartozó értékek sorrendjét nem tartotta be. Természetesen a MySQL általában hibát jelez, ha egész szám oszlopba dátumot próbálunk beszúrni. De ugye emlékszel az idézetre a MySQL dokumentációjából? Ezek a hibák nem jelennek meg, ha az INSERT IGNORE-t használjuk, és csendes adatkonverzió történik:

Az adatok következetesen inkonzisztensek

A szkript a Perl frissítés után rendben futott (legalábbis az stderr-je csendes volt), de néhány nap múlva kaptunk egy hibajelentést, hogy valami baj van a Perl-alapú táblákat használó belső eszközeinkkel. A jelentésből ítélve valami eléggé gyanús volt. Aggodalmunkat pedig egy gyors SELECT lekérdezés igazolta:

Adatok inkonzisztenciája, powered by INSERT IGNORE

Szóval mi van itt. Időbélyeg és egész szám értékek sztringgé konvertálva és felhasználói névként beillesztve, egész szám oszlopokban tárolt évek (a 2016-os szerkesztések száma vagy az utolsó szerkesztés éve). Adatkonzisztencia a javából.

Nem kell egyetértened, de számomra a fenti sorokhoz hasonló “adatoknál” nincs jobb adat.

Az érintett táblázat több mint 75 GiB súlyú és 460 mm sorokat tárol. Regenerálni tudjuk őket, de ez egy hosszú folyamat. A szkript fut, miközben ezt a történetet olvasod. Nos, már öt hete fut… Mindez az IGNORE-nak köszönhetően egyetlen lekérdezésben.

Tanulságok

  • INSERT IGNORE egy csendes fenevad, amely csak a hibádra vár, hogy az adatokat értelmetlen értékhalmazzá változtassa.
  • Soha ne hagyd figyelmen kívül a hibákat. Tegye őket hangossá. Fail early.
  • A dokumentálatlan nyelvi tulajdonságokra támaszkodni (a Perl és a hash kulcsok sorrendje csak egy a számos példa közül) kockázatos játék.

Figyelmeztetlek. Most ellenőrizd a kódodat, vagy ngrepeld a forgalmadat INSERT IGNORE lekérdezésekre.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.