Safeguard
Developer Tools•

Enforce data retention with TTLs, partitions and deletion jobs

Three ways to delete personal data on schedule: database TTLs, time partitions you drop, and batch deletion jobs, with trade offs and verification steps.

Safeguard Security Team
3 min read

A retention schedule only counts if code enforces it. Use native TTLs for simple expiry, time partitions you drop for large append-only tables, and batch deletion jobs for rules that depend on state. Whichever you pick, monitor it and test that old data really disappears.

For how to decide the periods, see building a retention schedule. This post covers the mechanics.

Why enforcement fails

Storage limitation under Article 5(1)(e) says data must be kept in identifiable form no longer than necessary. Teams write the policy and then forget the jobs. Common causes: a cron job that failed silently, a new table added without a rule, or a delete that skipped replicas and indexes.

Option 1: native TTL

Some stores expire records automatically from a timestamp field or a configured lifetime. Caches, session stores and some document databases support this.

  • Good for: sessions, tokens, rate limit counters, temporary exports.
  • Watch for: expiry may be lazy or run in background, so data can linger for a while. Know the guarantee your store gives.

Option 2: drop time partitions

Partition large event, log or audit tables by day or month. When a partition passes its retention period, drop the whole partition. Dropping is fast and does not bloat the table the way millions of row deletes do.

  • Good for: logs, events, telemetry.
  • Watch for: it only works when retention depends on time alone. A rule such as "keep while the account is active" does not fit.

Option 3: batch deletion jobs

A scheduled job selects rows meeting the rule and deletes or anonymises them in small batches.

  • Good for: state based rules, such as closed accounts after a grace period.
  • Watch for: long transactions and locks. Delete in batches with limits, and make the job idempotent so a rerun is safe.

Cover the copies

Deletion in the primary store is not enough. List where else the data goes, from your data map, and extend the rule:

  • Read replicas and warehouse tables.
  • Search indexes and caches.
  • Backups, which follow their own expiry.
  • Vendor systems, via deletion APIs or contract terms. Under Article 28(3)(g), a processor must delete or return data at the controller's choice when services end.

Verify and monitor

  1. Emit a metric each run: rows examined, rows deleted, errors, oldest remaining record age.
  2. Alert when the oldest age exceeds the schedule plus a tolerance.
  3. Run a quarterly query per table that counts records older than policy. The answer should be zero.
  4. Test in staging with seeded old data.

Keep the rule next to the table definition so new tables get one. A review check that asks "what is the retention for this table?" catches most gaps.

Quick check

Is soft delete the same as retention enforcement?

No. Flagged rows still hold the data. Enforcement means the data is removed or irreversibly anonymised.

How do I prove deletion happened?

Keep job run logs and the periodic count of out of policy records. Store them without personal content.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.