eduardweb.
Database & PrismaIntermediar#backend#database#postgres#sql#gdpr

Soft delete vs hard delete: Cum gândești schema ca să nu regreți la prima dispută GDPR

De Dan Ciobanu, 6 iun. 2026 · 1 vizualizări · 2 like-uri

Postat acum 3 zile
sql
-- 1. Index unic parțial în Postgres (rezolvă coliziunile la soft delete)
CREATE UNIQUE INDEX users_active_email_idx 
ON users (email) 
WHERE deleted_at IS NULL;

-- 2. Procedură simplă de anonimizare pentru GDPR (în loc de hard delete)
UPDATE users 
SET 
    email = 'deleted_' || id || '@yourdomain.internal',
    first_name = 'Anonim',
    last_name = 'Utilizator',
    phone = NULL,
    deleted_at = NOW()
WHERE id = 42 AND deleted_at IS NULL;

Salutare! Hai să vorbim despre o chestie pe care mulți o trecem la „las' că vedem noi mai încolo”, iar apoi ne lovește direct în producție: cum gestionăm ștergerile în baza de date fără să ne batem capul cu amenzi GDPR sau indexi sparți.

Cum m-am fript cu soft-delete-ul clasic

La un proiect trecut, o platformă SaaS de HR cu vreo 12.000 de utilizatori activi, am mers pe rețeta clasică: adăugăm deleted_at (nullable timestamp) pe toate tabelele importante. Sună excelent pe hârtie. Utilizatorul șterge un profil, noi doar îi punem timestamp-ul și gata, datele sunt „salvate” în caz că se răzgândește.

Problema a apărut când un utilizator „șters” a vrut să se înregistreze din nou cu aceeași adresă de email. Indexul unic pe coloana email a crăpat instantaneu, pentru că în baza de date adresa încă exista, chiar dacă avea deleted_at completat.

Dacă scoți indexul unic ca să permiți înregistrări multiple, riști să ai duplicate în rândul utilizatorilor activi din cauza unor race-condition-uri obscure. E un compromis mizerabil pe care nu vrei să-l faci.

Capcana GDPR: Soft delete-ul nu e ștergere

Aici e marea confuzie legală pe care o fac mulți developeri. Dacă un client îți cere „Dreptul de a fi uitat” (Right to be Forgotten) conform GDPR, iar tu doar îi pui deleted_at = NOW() în baza de date, ești în afara legii. Datele lui cu caracter personal (nume, prenume, telefon, email) sunt încă acolo, în serverul tău, gata de a fi expuse la un eventual SQL Injection.

Am avut un audit pe tema asta și am fost obligați să regândim tot fluxul în mai puțin de două săptămâni. N-a fost deloc distractiv.

Cum rezolvăm elegant problema?

Avem trei abordări mari, fiecare cu plusuri și minusuri:

  1. Indexul parțial (Cea mai simplă chestie pentru Postgres): Dacă folosești Postgres, poți crea un index unic care ignoră rândurile șterse. Este genial de simplu și rezolvă problema coliziunilor la înregistrare. Merge brici pentru entități non-critice (cum ar fi postări pe un blog sau tag-uri), dar nu rezolvă problema GDPR pentru datele personale.

  2. Anonimizarea (Anonymization): În loc să ștergi fizic rândul (ceea ce ar strica statisticile financiare sau rapoartele istorice), suprascrii datele sensibile cu valori dummy (ex: deleted_user_123@yourdomain.internal). Păstrezi ID-urile pentru foreign keys, dar scapi de PII (Personally Identifiable Information).

  3. Hard Delete cu arhivă anonimizată: Ștergi definitiv userul din tabela principală, dar înainte muți datele agregate (fără nume/email) într-un tabel istoric pentru BI (Business Intelligence).

Ce alegem?

Pentru proiectele mele actuale, folosesc o regulă simplă. Pentru date operaționale fără caracter personal (comentarii, log-uri interne, task-uri), merg pe soft-delete cu index parțial. Pentru date personale (profile de utilizatori, adrese de livrare, detalii de plată), merg pe anonimizare completă sau hard-delete direct.

Voi cum ați rezolvat coliziunile de indexi unici la soft-delete? Ați avut vreodată audit pe GDPR pe tema asta?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.