SQLite a purtat un bug de corupție timp de 16 ani. Iată de ce al tău e probabil în regulă și ce să verifici oricum.

Săptămâna aceasta, Tailscale a publicat un postmortem pe care fiecare echipă care rulează SQLite ar trebui să-l citească. Timp de șase luni, începând din august anul trecut, planul lor de control tot corupa bazele de date. Nouăsprezece incidente separate, nicio cauză evidentă, nicio modificare recentă de cod care să le explice. Cauza s-a dovedit a fi o cursă de date care stătea în SQLite din versiunea 3.7.0, lansată în iulie 2010. S-a ascuns acolo timp de aproximativ șaisprezece ani.

Echipa SQLite îl numește bug-ul WAL-reset. Afectează doar bazele de date care rulează în modul WAL (write-ahead logging) atunci când două sau mai multe conexiuni au același fișier deschis pe fire sau procese separate și încearcă să scrie sau să facă checkpoint în același moment. Când momentul se potrivește, un checkpoint poate lăsa un marcaj în indexul WAL pretinzând că o parte din jurnal a fost deja copiată în fișierul principal al bazei de date, deși nu a fost. Un checkpoint ulterior omite atunci acele date, iar fișierul se corupe. A fost rezolvat în SQLite 3.51.3 pe 13 martie 2026, cu backport-uri la versiunile 3.44.6 și 3.50.7.

Iată partea care merită reținută. Dezvoltatorii SQLite nu au putut reproduce intenționat acest bug. A trebuit să modifice propriul cod-sursă pentru a declanșa un callback exact la momentul nepotrivit în timpul unui checkpoint. Telemetria proprie plasează rata de apariție în lumea reală la același nivel sau sub rata defecțiunilor SSD și a erorilor cauzate de raze cosmice. Deci, dacă rulezi SQLite într-o configurație normală, aproape sigur nu ai fost afectat și probabil că tot nu ești. Tailscale a fost lovit pentru că a preluat controlul manual al checkpoint-urilor și le-a rulat agresiv pentru backup-uri rapide. Au ieșit de pe calea bătătorită, iar o cursă de unu la un miliard a devenit un eveniment lunar.

Ce am face de fapt în privința asta

Fă upgrade, dar nu intra în panică. Ajunge la SQLite 3.51.3 sau mai nou, sau la unul dintre backport-uri, în ritmul tău. Problema este că „versiunea ta de SQLite” este rareori evidentă. SQLite este integrat aproape peste tot și nu îl instalezi direct. Vine inclus în runtime-ul limbajului tău, ORM-ul tău, sistemul de operare mobil și platforma edge.

În lucrul nostru nativ pentru iOS și Android, acesta este cel mai ușor de uitat. SQLite-ul de sistem este legat de versiunea OS pe care o rulează utilizatorii tăi, nu de versiunea față de care ai compilat, și nu poți alege când fac upgrade. Dacă aplicația ta face ceva neobișnuit cu conexiunile sau checkpoint-urile — de exemplu, un fir de sincronizare în fundal care scrie în timp ce interfața citește — include propriul build SQLite ca să controlezi versiunea în loc să moștenești ce livrează dispozitivul. Am făcut exact asta în aplicații offline-first din domeniul sănătății și IoT, unde un magazin local corupt înseamnă date pe care utilizatorul nu le va mai putea recupera niciodată.

Pe partea de backend și edge, verifică față de ce face legătura driverul tău. Din experiența noastră cu PHP și Docker, versiunea SQLite inclusă într-o imagine de bază poate rămâne cu luni în urma upstream-ului, iar „suntem pe cel mai recent framework” nu îți spune nimic despre biblioteca C de dedesubt. SQLite serverless și edge — adică D1, Turso, LiteFS și altele similare — merită o privire specială, pentru că acele platforme se bazează intens pe modul WAL și accesul concurent. Exact acesta este profilul de care are nevoie acest bug.

Lecția mai importantă este despre backup-uri

Detaliul care m-a marcat nu este condiția de cursă. Este faptul că Tailscale a observat doar pentru că un pipeline separat le-a citit backup-urile S3 și a rulat PRAGMA integrity_check împotriva lor. Bazele lor de date live arătau sănătoase. Corupția stătea deja în fișierele pe care le stocau pentru mai târziu.

În angajamentele noastre de securitate și pentesting, backup-urile sunt locul unde găsim cele mai înfricoșătoare lacune. Echipele le fac religios și nu le restaurează niciodată. Un backup pe care nu l-ai restaurat este o ipoteză, nu o plasă de siguranță. Dacă stochezi fișiere SQLite, rulează PRAGMA integrity_check pe o copie în mod regulat și restaurează efectiv într-un mediu de test din când în când. Corupția care apare abia în ziua în care ai nevoie de backup este cel mai prost mod de a învăța asta.

Încă un lucru din postmortem care e ușor de trecut cu vederea. După ce Tailscale a implementat rezolvarea, o schimbare de rotunjire din versiune a declanșat avertismente false de corupție pe bazele de date care foloseau indecși de expresii. Le-au prins pentru că au lansat mai întâi la câteva shard-uri canary, apoi la restul. Lansările în faze nu sunt birocrație. Sunt modul în care găsești al doilea bug pe care l-a introdus rezolvarea primului.

Ce poți face săptămâna aceasta

Află ce versiune de SQLite rulează cu adevărat sistemele tale de producție și aplicațiile tale lansate. Nu versiunea framework-ului, ci biblioteca SQLite efectivă. Apoi alege un backup și demonstrează că îl poți restaura corect. Asta e o muncă de câteva ore de după-amiază și, pentru majoritatea echipelor, valorează mai mult decât upgrade-ul în sine.

Dacă identificarea versiunii SQLite pe care o rulează cu adevărat aplicațiile și serviciile tale, și dacă backup-urile tale ar supraviețui unui restore, ți se pare familiară, hai să vorbim.

databasesexpert-analysissoftware-developmentsqlitetech-news