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

78 lines
2.7 KiB
Markdown

# 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.