Skip to content

feat(db): delete app versions 90 days after org cancel - #2757

Merged
riderx merged 7 commits into
mainfrom
cursor/canceled-org-version-cleanup-9dba
Jul 27, 2026
Merged

feat(db): delete app versions 90 days after org cancel#2757
riderx merged 7 commits into
mainfrom
cursor/canceled-org-version-cleanup-9dba

Conversation

@riderx

@riderx riderx commented Jul 25, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Expired never-converted trials are now marked canceled (status=canceled, canceled_at=trial_at, is_good_plan=false), fixing the previous NULL status <> 'succeeded' miss.
  • New daily cron canceled_org_version_cleanup soft-deletes app_versions for orgs in canceled/deleted whose access ended more than 90 days ago (COALESCE(canceled_at, subscription_anchor_end, trial_at)).
  • Channel targets are unlinked first so channel-linked bundles are included; existing on_version_update + delete_old_deleted_versions finish R2/manifest cleanup and hard delete.
  • Added pgTAP coverage and service-role execute hardening check.

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)

  • Migration applies on Tinbase
  • Manual SQL assertions: trial → canceled; long-canceled versions soft-deleted; recent/paying/builtin kept; channels unlinked
  • CI supabase test db runs 64_test_canceled_org_version_cleanup.sql
  • Confirm cron row canceled_org_version_cleanup at 03:20 UTC
  • Confirm soft-delete queues on_version_update cleanup for affected versions

Generated with AI

Open in Web Open in Cursor 

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added automated cleanup for data associated with organizations canceled or deleted for more than 90 days.
    • Expired free trials are now marked as canceled, with related billing status updated.
    • Cleanup runs daily, removing eligible audit records and safely soft-deleting outdated app versions while preserving active data.
  • Bug Fixes

    • Channels linked to removed versions are automatically unlinked.
  • Tests

    • Added coverage for trial cancellation, data retention rules, cleanup behavior, and access restrictions.

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>
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4b85e462-bb53-46db-85f4-c9839e932ed6

📥 Commits

Reviewing files that changed from the base of the PR and between 1596bab and 4a9789d.

📒 Files selected for processing (3)
  • supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
  • supabase/tests/64_test_canceled_org_version_cleanup.sql
  • tests/security-definer-execute-hardening.test.ts
📝 Walkthrough

Walkthrough

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

Changes

Canceled organization cleanup

Layer / File(s) Summary
Cleanup functions and data handling
supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
Expires eligible free trials and batch-soft-deletes versions for organizations canceled or deleted for at least 90 days, unlinking affected channel references.
Audit purge and scheduled orchestration
supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
Adds runtime-bounded audit-log deletion, composes both cleanup phases in a wrapper, and configures the daily cron task.
Cleanup lifecycle validation
supabase/tests/64_test_canceled_org_version_cleanup.sql, tests/security-definer-execute-hardening.test.ts
Validates cleanup results, retention rules, cron configuration, service-role permissions, and blocked anonymous/authenticated execution.

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
Loading

Suggested labels: codex

Suggested reviewers: dalanir, wcaleniewolny

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: deleting app versions after org cancellation.
Description check ✅ Passed The description includes a solid Summary and Test Plan with relevant backend details; only the template's Checklist section is missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@codspeed-hq

codspeed-hq Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing cursor/canceled-org-version-cleanup-9dba (4a9789d) with main (30b5178)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 15:16 Inactive
@cursor
cursor Bot marked this pull request as ready for review July 25, 2026 15:25
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: medium. Not approving: Cursor Bugbot did not complete its review (check skipped, usage limit reached). Human review is needed for this data-retention migration; reviewers assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor
cursor Bot requested review from Dalanir and WcaleNieWolny July 25, 2026 15:26

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
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>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 15:37 Inactive
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

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>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 16:36 Inactive
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@coderabbitai coderabbitai Bot added the codex label Jul 25, 2026
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 16:42 Inactive
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: high. Not approving: Cursor Bugbot did not complete successfully (check skipped; usage limit reached). Human review is needed for this irreversible data-retention migration; reviewers already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between a0132cd and 1596bab.

📒 Files selected for processing (3)
  • supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
  • supabase/tests/64_test_canceled_org_version_cleanup.sql
  • tests/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)

Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/tests/64_test_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/tests/64_test_canceled_org_version_cleanup.sql
Comment thread tests/security-definer-execute-hardening.test.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql Outdated
Comment thread tests/security-definer-execute-hardening.test.ts
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>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 16:50 Inactive
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Comment thread supabase/migrations/20260725150503_canceled_org_version_cleanup.sql
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr July 25, 2026 16:56 Inactive
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@sonarqubecloud

Copy link
Copy Markdown

@riderx
riderx merged commit 98af34f into main Jul 27, 2026
55 of 75 checks passed
@riderx
riderx deleted the cursor/canceled-org-version-cleanup-9dba branch July 27, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants