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_variantswith missing or soft-deletedassetsparents - active
pwa_cacheswith missing or soft-deletedprojectsparents - active
access_logswith missing or soft-deletedprojectsorusersparents - active
access_logswithNULL projectIdorNULL userIdas warnings, because admin/system or public/anonymous access may be valid - active
production_presentation_accessgrants with missing, soft-deleted, orNULLprojectId/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
deletedAtcolumn is scanned for soft-deleted row count - the report includes oldest and newest
deletedAttimestamps 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.