Skip to content

Copie programată a bazei de producție, în afara mașinii

Vitalie requested to merge 14_database-backup into main

Ține de #14 (closed). Nu îl închide — se închide după ce o restaurare a fost făcută efectiv pe o copie reală de producție.

Problema

Tot ce a făcut vreodată un client stă într-un singur fișier SQLite, pe un singur volum Docker, pe o singură mașină: comenzile, produsele, fiecare text editat din panou, conturile de admin. Nimic nu îl copia nicăieri. Un disc care cedează, un volum scăpat la un redeploy, o pagină coruptă — și nu mai există la ce să ne întoarcem.

Cum se ia copia

VACUUM INTO, nu cp. Fișierul e scris în timp ce magazinul servește, iar o copiere pe octeți îl poate prinde la mijlocul unei scrieri: copia se deschide, iar coruperea iese la iveală exact în ziua în care cineva are nevoie de ea. VACUUM INTO ia același instantaneu de citire pe care l-ar lua o interogare și scrie din el o bază compactată, deci rezultatul e întotdeauna un fișier pe care SQLite îl deschide.

Rulează prin Prisma, nu prin sqlite3: imaginea de runtime nu are shell-ul sqlite (verificat în backend/Dockerfile — se instalează doar openssl), iar adăugarea lui doar ca să livrăm un backup ar fi încă un lucru de ținut în funcțiune.

Verificat local pe o bază reală: SQLite 3.46.0, copie în 31 ms, 3,98 MB, iar copia se deschide cu numărătorile intacte — 61 de produse, 1 comandă, 1 admin, 72 de articole.

Unde ajunge

În afara volumului de date, deliberat. O copie care stă lângă fișierul pe care îl protejează nu supraviețuiește defecțiunii pentru care există. Copia se scrie în /tmp din container, se trage cu docker compose cp în spațiul de lucru al jobului, și de acolo pleacă de pe mașină ca artefact.

Artefactul e singura stocare din afara mașinii pe care o are proiectul fără să ceară cuiva credențiale. Înseamnă însă că fișierul stă în GitLab și conține nume, telefoane și adrese de e-mail ale clienților, citibile de oricine are acces la proiect. E un compromis asumat față de a nu avea niciun backup, nu un răspuns definitiv — pasul următor e stocare de obiecte cu retenția ei proprie, și cere o decizie și o cheie.

Retenția

Două joburi, două programări, retenția fiind chiar expirarea artefactului:

  • backup:production:daily8 zile, ca șapte copii să fie mereu acolo chiar și în ziua în care una pică
  • backup:production:weekly5 săptămâni, adică patru supraviețuitoare plus cea care se înlocuiește

Setul săptămânal există pentru defecțiunea pe care n-o observă nimeni două săptămâni — un rând corupt, o migrare proastă — peste care o săptămână de copii zilnice a trecut deja.

Ambele rulează doar la programare: $CI_PIPELINE_SOURCE == "schedule", deci nimic nu pornește la un push sau la un merge.

Gărzile din script

Verificate, nu doar scrise: fără --out dă mesajul de utilizare; --out fără valoare la fel; o cale care conține ghilimea simplă e refuzată, pentru că ghilimeaua e exact ce ar închide literalul dat lui VACUUM INTO.

Scriptul citește numărătorile din copie, nu din original, și cade dacă diferă de sursă. Un backup pe care nu l-a deschis nimeni e o presupunere.

Verificat

Comandă Rezultat
npx tsc --noEmit --strict --types node prisma/backup-database.ts exit 0
backend/ npm run build exit 0
backend/ npm test 550 de teste, toate trec
python3 -c "yaml.safe_load(...)" YAML valid, ambele joburi înregistrate

Ce urmează, imediat după fuziune

  1. Se creează cele două programări în GitLab, cu variabila BACKUP_KIND.
  2. Se rulează programarea zilnică o dată, ca să existe o copie reală de producție.
  3. Se face restaurarea pe acea copie și se compară numărul de comenzi și de produse cu producția. Abia atunci #14 (closed) se închide.

Atenție: acest MR nu primește pipeline — atinge .gitlab-ci.yml, iar workflow.rules exclude explicit orice pipeline al cărui set de modificări îl include.

Rămâne pentru Vitalie

Dacă hostingul face deja instantanee de volum și cu ce frecvență — nu se poate afla din repo. Și decizia despre stocarea de obiecte, dacă artefactele GitLab nu sunt un loc acceptabil pentru date de clienți pe termen lung.

Merge request reports