39948-vm/documentation/db-cleanup-audit.md
2026-07-03 16:11:24 +02:00

2.7 KiB

DB Cleanup Audit

This document describes the non-destructive database cleanup audit for finding orphan records, legacy schema leftovers, and soft-deleted row volume.

Manual Audit

The cleanup audit is intentionally run as one-time read-only SQL on the target database instead of a permanent application command. It should check the same categories listed below and must not delete or update rows.

If rows are found, first capture affected-row details and a backup/export, then apply cleanup through a reviewed migration or operations runbook.

What It Checks

Orphan and broken-reference checks:

  • active asset_variants with missing or soft-deleted assets parents
  • active pwa_caches with missing or soft-deleted projects parents
  • active access_logs with missing or soft-deleted projects or users parents
  • active access_logs with NULL projectId or NULL userId as warnings, because admin/system or public/anonymous access may be valid
  • active production_presentation_access grants with missing, soft-deleted, or NULL projectId/userId

Legacy schema checks:

  • old normalized constructor tables: page_elements, page_links, transitions
  • known removed columns: assets.is_deleted, assets.deleted_at_time, projects.is_deleted, projects.deleted_at_time, projects.phase, projects.entry_page_slug, projects.transition_settings

Soft-delete summary:

  • every table with a deletedAt column is scanned for soft-deleted row count
  • the report includes oldest and newest deletedAt timestamps per table

Retention Policy

Keep soft-deleted rows indefinitely by default.

Reason: the project uses Sequelize paranoid models, deletion volume is expected to be low, and restoring accidental deletes is more valuable than speculative storage cleanup.

Any physical deletion must be a separate migration or operations runbook with:

  • a fresh backup
  • explicit affected-row query
  • rollback or restore plan
  • table-specific retention threshold

Prevention

Asset deletion is handled in AssetsService. Because PostgreSQL foreign-key ON DELETE CASCADE does not fire for Sequelize paranoid soft deletes, AssetsService.remove() and AssetsService.deleteByIds() soft-delete active asset_variants rows in the same transaction as the parent assets soft delete. This prevents active variants from remaining attached to soft-deleted assets after commit.

Local Audit Result

Last local run: 2026-07-02.

Result:

  • failed orphan checks: 0
  • warning orphan checks: 0
  • legacy schema candidates: 0
  • tables with soft-deleted rows: 4

Soft-deleted rows were present in local assets, projects, tour_pages, and production_presentation_access. No deletion was performed.