Lucruri de evitat, Episodul 1: INSERT IGNORE
Codul legendar lovește din nou
Să te bazezi pe caracteristici de limbaj nedocumentate este destul de riscant. Iar atunci când este amestecat cu INSERT IGNORE lucrurile merg foarte prost.
În timpul uneia dintre actualizările sistemului de operare de la începutul anului 2016, Perl a fost actualizat de la 5.14 la 5.18. Din păcate, manipularea hash-urilor s-a schimbat, ordinea cheilor într-un scenariu destul de specific nu a mai fost menținută, iar codul s-a stricat. Aveți dreptate, Perl nu a promis niciodată ordinea, dar codul nostru se baza pe anumite particularități de implementare.
Un extras din jurnalul de modificări Perl pentru v5.18:
Din mod implicit, două variabile hash distincte cu chei și valori identice pot furniza acum conținutul lor într-o ordine diferită, în cazul în care acesta era anterior identic.
De ce a fost o problemă? Codul pregătea o listă de hash-uri care urmau să fie inserate în baza de date. Apoi folosea primul element pentru a pregăti lista de nume de coloane care să fie transmise interogărilor INSERT care vor urma. Cu toate acestea, fiecare interogare INSERT trimisă folosea hashes diferite pentru a pregăti lista de valori de inserat. Acest lucru a dus la următoarele:
INSERT IGNORE INTO foo (id, date) VALUES (1, ‘2006-10-11`), (‘2007-12-09’, 2), …
Basic, ordinea numelor coloanelor și a valorilor respective nu era păstrată. Desigur, MySQL ridică în mod normal o eroare atunci când se încearcă să se insereze o dată într-o coloană de numere întregi. Dar vă amintiți citatul din documentația MySQL, nu-i așa? Aceste erori nu sunt ridicate atunci când se folosește INSERT IGNORE și are loc o conversie silențioasă a datelor:
Data consistent inconsistent
Scriptul a funcționat bine după actualizarea Perl (ei bine, cel puțin stderr-ul său era silențios), dar după câteva zile am primit un raport de eroare că ceva nu este în regulă cu instrumentele noastre interne care folosesc tabelele alimentate de Perl. Judecând după raport, ceva era destul de dubios. Iar îngrijorările noastre au fost dovedite de o interogare rapidă SELECT:
Deci ce avem aici. Timestamp și valori întregi convertite în șir de caractere și inserate ca nume de utilizator, ani stocați în coloane de numere întregi (se numără edițiile din 2016 sau un an de la ultima editare). Inconsecvența datelor în cel mai bun caz.
Nu trebuie să fiți de acord, dar pentru mine nicio dată nu este mai bună decât „date” precum rândurile de mai sus.
Tabloul afectat cântărește peste 75 GiB și stochează 460 mm de rânduri. Le putem regenera, dar este un proces lung. Scriptul este în curs de execuție în timp ce citiți această poveste. Ei bine, rulează deja de cinci săptămâni… Totul datorită lui IGNORE într-o singură interogare.
Lecții învățate
- INSERT IGNORE este o bestie tăcută care abia așteaptă ca greșeala ta să transforme datele într-un set de valori fără sens.
- Niciodată să nu ignori erorile. Faceți-le gălăgioase. Eșuați devreme.
- Să vă bazați pe caracteristici de limbaj nedocumentate (Perl și ordinea cheilor hash este doar unul dintre numeroasele exemple) este un joc riscant.
Ați fost avertizați. Acum verificați-vă codul sau ngrep traficul pentru interogări INSERT IGNORE.
.