Skip to content

Add deleted_at timestamp for version deletion tracking#1442

Merged
riderx merged 5 commits into
mainfrom
riderx/investigate-updated-at
Jan 16, 2026
Merged

Add deleted_at timestamp for version deletion tracking#1442
riderx merged 5 commits into
mainfrom
riderx/investigate-updated-at

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Jan 15, 2026

Summary

Add a dedicated deleted_at timestamp column to track when app versions are soft-deleted. This replaces the unreliable approach of using updated_at, which is touched by many unrelated operations. The retention and cleanup functions now explicitly set/check deleted_at.

Test plan

  • All 831 existing database tests pass, including 15 retention tests
  • New tests verify deleted_at is set when retention marks versions as deleted
  • Tests confirm builtin and unknown versions are never hard-deleted, even after 1 year

Checklist

  • My code follows the code style of this project and passes linting
  • My change requires a change to the documentation
  • My change has adequate E2E test coverage
  • I have tested my code manually via database tests

Summary by CodeRabbit

  • New Features

    • Deletions now record a timestamp for tracking.
  • Improvements

    • Soft-delete mechanism standardized to use deletion timestamps.
    • Automatic cleanup routine removes old soft-deleted versions after retention period.
    • Built-in and unknown versions are protected from permanent deletion.
  • Tests

    • Expanded test suite covering deletion timestamps, retention behavior, and protection rules.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 15, 2026

Warning

Rate limit exceeded

@riderx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c4535de and e8c16ee.

📒 Files selected for processing (5)
  • supabase/functions/_backend/triggers/on_version_update.ts
  • supabase/functions/_backend/utils/postgres_schema.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20260114214731_add_deleted_at_column.sql
  • supabase/tests/16_test_retention.sql

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a deleted_at timestamp for soft-deletes: schema and types updated, deletion code now records timestamps, triggers and retention SQL functions adjusted to use deleted_at and hard-delete old soft-deleted versions, and tests expanded to cover the new behavior.

Changes

Cohort / File(s) Summary
Schema & Types
supabase/functions/_backend/utils/postgres_schema.ts, supabase/functions/_backend/utils/supabase.types.ts
Added deleted_at timestamp column to app_versions and updated Row/Insert/Update types for apikeys, app_versions, and get_versions_with_no_metadata to include `deleted_at: string
Deletion Logic
supabase/functions/_backend/public/bundle/delete.ts
Update calls now set deleted_at: new Date().toISOString() alongside setting deleted = true for single-version and bulk deletions
Trigger Logic
supabase/functions/_backend/triggers/on_version_update.ts
Trigger condition changed to detect changes to record.deleted_at (timestamp) instead of boolean record.deleted to invoke delete handling
Migrations & Retention
supabase/migrations/20260114214731_add_deleted_at_column.sql
Adds deleted_at to public.app_versions, migrates existing deleted rows, indexes deleted_at, adds update_app_versions_retention() to mark soft-deletes and delete_old_deleted_versions() to hard-delete >1 year old soft-deleted versions (excludes builtin/unknown)
Tests
supabase/tests/16_test_retention.sql
Expanded test plan from 12→15; added assertions that deleted_at is set on soft-delete and that builtin/unknown versions are never hard-deleted

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Backend
  participant Postgres
  participant Trigger
  participant RetentionJob

  Client->>Backend: request delete version(s)
  Backend->>Postgres: UPDATE app_versions set deleted=true, deleted_at=NOW()
  Postgres->>Trigger: row UPDATED (deleted_at changed)
  Trigger->>Backend: invoke deleteIt (cleanup/notification)
  Note over RetentionJob,Postgres: Periodic job
  RetentionJob->>Postgres: CALL delete_old_deleted_versions()
  Postgres->>Postgres: DELETE rows with deleted_at < now() - interval '1 year' (excl builtin/unknown)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I stamp the time when versions part,
A tiny tick upon each heart.
Soft shadows kept, a year to wait,
Then tidy hops to clear the slate. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a deleted_at timestamp for version deletion tracking across multiple files.
Description check ✅ Passed The description includes all critical sections with substantive content: summary explains the purpose and approach, test plan details verification steps and test results, and checklist is mostly completed with relevant items.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 155c559311

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +8 to +11
-- Step 2: Migrate existing deleted versions (set deleted_at = created_at for old data)
UPDATE public.app_versions
SET deleted_at = created_at
WHERE deleted = true AND deleted_at IS NULL;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid backfilling deleted_at with created_at

Backfilling deleted_at to created_at means any version created more than a year ago but deleted recently will look like it was deleted years ago; with the new delete_old_deleted_versions logic that uses deleted_at, those rows can be hard-deleted on the next cron run, violating the intended 1‑year grace period. This is especially likely for long‑lived apps where users delete old versions now. Consider backfilling from the previous deletion timestamp (updated_at from the prior retention logic) or NOW() to avoid premature hard deletes.

Useful? React with 👍 / 👎.

Comment on lines +294 to 296
// check if version was soft-deleted (deleted_at was set)
if (record.deleted_at && record.deleted_at !== oldRecord.deleted_at)
return deleteIt(c, record)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ensure manual deletions still trigger cleanup

The trigger now only calls deleteIt when deleted_at changes, but the existing delete endpoints (e.g., public/bundle/delete.ts) only set deleted: true without setting deleted_at. Those updates will skip cleanup and the row will never qualify for delete_old_deleted_versions (which also requires deleted_at), leaving storage and DB rows stranded. Either set deleted_at in those update paths or add a DB trigger to populate it when deleted flips true.

Useful? React with 👍 / 👎.

riderx and others added 2 commits January 15, 2026 18:59
Replace unreliable updated_at tracking with dedicated deleted_at column.
When update_app_versions_retention() marks versions as deleted, it now sets
deleted_at = NOW() for proper deletion timestamp tracking. The
delete_old_deleted_versions() function uses deleted_at (not updated_at) for
the 1-year retention check, and protects builtin/unknown versions from
hard-deletion.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1. Use updated_at instead of created_at for backfill to avoid premature
   hard-deletion of recently-deleted old versions
2. Set deleted_at in bundle/delete.ts to ensure manual deletions trigger
   S3 cleanup and qualify for hard-deletion after 1 year

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@riderx riderx force-pushed the riderx/investigate-updated-at branch from c4535de to d9b6f9b Compare January 15, 2026 18:59
riderx and others added 3 commits January 15, 2026 23:19
Ensures the encryption enforcement function also sets deleted_at when
soft-deleting non-compliant bundles, so S3 cleanup triggers properly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of manually setting deleted_at in every code path, use a
PostgreSQL BEFORE UPDATE trigger that automatically sets deleted_at
when deleted changes from false to true.

This is more robust and ensures deleted_at is always set correctly,
regardless of how the deletion happens (retention, manual, encryption
enforcement, etc).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit ddf2228 into main Jan 16, 2026
11 checks passed
@riderx riderx deleted the riderx/investigate-updated-at branch January 16, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant