feat(db): delete app versions 90 days after org cancel - #2757
Conversation
Mark expired never-converted trials as canceled, then daily soft-delete app versions for orgs whose paid/trial access ended more than 90 days ago. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds service-only database functions and a daily cron task to expire free trials, clean up data for long-canceled organizations, and validate permissions and lifecycle outcomes through transactional SQL and security tests. ChangesCanceled organization cleanup
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant Cron as Daily cron task
participant Cleanup as cleanup_long_canceled_org_data
participant Versions as app_versions and channels
participant Audit as audit_logs
Cron->>Cleanup: Invoke scheduled cleanup
Cleanup->>Versions: Unlink channels and soft-delete eligible versions
Cleanup->>Audit: Delete eligible audit-log rows in bounded batches
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_ff5b98c4-db90-4ecb-96a0-454f221d11b2) |
There was a problem hiding this comment.
Stale comment
Risk: high. Cursor Bugbot did not complete successfully (check skipped; usage limit reached), so I cannot approve without automated review. This migration adds irreversible app-version deletion for canceled orgs and needs human review.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Unlink only channel targets pointing at deletion candidates, keep a single eligible_orgs CTE, and preserve builtin/unknown channel targets. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f6d74a96-d614-4cac-99ff-152c2a8592a2) |
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete (check skipped, usage limit reached) and this migration adds irreversible app-version cleanup for canceled orgs, above the low-risk approval threshold. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete (check skipped; usage limit reached), and this migration adds irreversible app-version deletion for canceled orgs. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver
Extend daily canceled-org cleanup to delete that org's audit_logs after the same 90-day grace window, using ctid batches and a max runtime so large backlogs cannot timeout the cron tick. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_be2b568b-132b-49a6-bacb-e32082f0efc4) |
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached). This migration adds irreversible app-version cleanup for canceled orgs; human review is still needed and Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete (check skipped; usage limit reached), and this migration adds irreversible app-version cleanup for canceled orgs, above the low-risk approval threshold. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d8a1f23c-6ac8-47a9-b12c-7920bbdefe1f) |
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped, usage limit reached) and this migration adds irreversible app-version cleanup for canceled orgs, above the low-risk approval threshold. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/migrations/20260725150503_canceled_org_version_cleanup.sql`:
- Around line 59-66: The duplicated eligibility predicate must use one shared
cutoff definition. In
supabase/migrations/20260725150503_canceled_org_version_cleanup.sql:59-66, add a
service-only public.long_canceled_org_ids() helper returning setof uuid, use
GREATEST over the available end-of-access timestamps, and replace the first
eligible_orgs CTE with the helper; in
supabase/migrations/20260725150503_canceled_org_version_cleanup.sql:155-162,
remove the duplicated CTE and join against public.long_canceled_org_ids() so
both cleanup phases share the same eligibility logic.
- Around line 76-94: Constrain the UPDATE in the cleared CTE to channels
belonging to the eligible organizations or app_ids represented by candidates,
while preserving the existing version and rollout_version unlinking logic.
Ensure the added restriction uses the candidate rows’ ownership fields so
PostgreSQL does not scan unrelated public.channels rows on each job tick.
- Around line 148-170: Remove the session-level statement_timeout override in
the cleanup loop, preserving the loop’s 60-second runtime checks and existing
indexes. Ensure each batch retains an appropriate timeout safeguard, using the
existing lock_timeout mechanism if available to bound contention with live
writes.
- Around line 17-24: Update the UPDATE predicate in the canceled-org cleanup
migration to target only never-converted trial rows, excluding records already
in the canceled target state. Preserve the existing succeeded/deleted exclusions
while preventing old already-canceled rows from being rewritten or having
canceled_at backdated.
In `@supabase/tests/64_test_canceled_org_version_cleanup.sql`:
- Around line 313-320: Add assertions in the cleanup test for fixture version
970301, verifying it remains undeleted after cleanup because the trial expired
only one day ago; also assert the associated audit rows remain, and update the
expected plan value as requested. Place these checks alongside the existing
970101 assertion without changing the long-canceled cleanup coverage.
- Line 3: Update the SELECT plan declaration in the canceled organization
version cleanup test to expect 21 assertions instead of 22, leaving the existing
assertions and test setup unchanged.
In `@tests/security-definer-execute-hardening.test.ts`:
- Around line 31-33: Add public.process_free_trial_expired() to the allowlist in
the security-definer execution test, alongside the existing maintenance
functions, so the service_role-only migration grant is covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0adae5a9-d6d7-4ab3-afc7-ad6f45bed562
📒 Files selected for processing (3)
supabase/migrations/20260725150503_canceled_org_version_cleanup.sqlsupabase/tests/64_test_canceled_org_version_cleanup.sqltests/security-definer-execute-hardening.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Share long_canceled_org_ids with GREATEST end-of-access cutoff, skip rewriting already-canceled trials, scope channel unlinks by app_id, and bound audit delete batches with remaining-budget statement_timeout. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_46566099-9d8b-4d47-ba29-eb057dc5598f) |
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached), and this migration adds irreversible app-version cleanup for canceled orgs, above the low-risk approval threshold. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached). This migration adds irreversible app-version cleanup for canceled orgs; human review is still needed and Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_1d3e267b-f7b4-46a0-823e-9ec7611b6387) |
There was a problem hiding this comment.
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached), and this migration adds irreversible app-version cleanup for canceled orgs, above the medium-risk approval threshold. Human review is still needed; Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver
There was a problem hiding this comment.
Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached). This migration adds irreversible app-version cleanup for canceled orgs; human review is still needed and Dalanir and WcaleNieWolny are already assigned as reviewers.
Sent by Cursor Approval Agent: Pull Request Approver External
|





Summary (AI generated)
canceled(status=canceled,canceled_at=trial_at,is_good_plan=false), fixing the previousNULL status <> 'succeeded'miss.canceled_org_version_cleanupsoft-deletesapp_versionsfor orgs incanceled/deletedwhose access ended more than 90 days ago (COALESCE(canceled_at, subscription_anchor_end, trial_at)).on_version_update+delete_old_deleted_versionsfinish R2/manifest cleanup and hard delete.Motivation (AI generated)
Canceled customers (and trials that never convert) currently keep bundle data indefinitely. Retention should start after paid access ends (renewal/period end, or Stripe cancel after past-due retries), then wait 90 days before removing app versions.
Business Impact (AI generated)
Reduces long-term storage cost for churned orgs and expired trials while giving a 90-day grace window after access ends in case they resubscribe.
Test Plan (AI generated)
supabase test dbruns64_test_canceled_org_version_cleanup.sqlcanceled_org_version_cleanupat 03:20 UTCon_version_updatecleanup for affected versionsGenerated with AI
Summary by CodeRabbit
New Features
Bug Fixes
Tests