Skip to content

Sweep orphaned Xata branches in the cleanup cron - #63

Merged
exekias merged 1 commit into
mainfrom
fix-orphan-branch-cleanup
Jun 11, 2026
Merged

exekias merged 1 commit into
mainfrom
fix-orphan-branch-cleanup

Conversation

@exekias

@exekias exekias commented Jun 11, 2026

Copy link
Copy Markdown
Member

Problem

Sandbox branches in the Xata project were lingering for many days. The nightly cleanup cron (/api/cron/cleanup-branches) only deleted branches still tracked by a userBranch row, but several paths orphan branches in Xata with no row:

  • dropBranchForLesson and the quota evictor run deleteBranch(...).catch(() => {}) and then delete the DB row regardless — a transient Xata API failure leaves the branch untracked forever.
  • The error/race paths in ensureBranchForLesson swallow delete failures the same way.
  • A crash or function timeout between createBranch and the DB insert leaves a branch with no row at all.

Once orphaned, no code ever looked at those branches again. A dry-run of scripts/xata-cleanup.ts against the sandbox project currently shows dozens of stale lp-* branches.

Fix

  • lib/branch-manager.ts: add sweepOrphanBranches, run by cleanupIdleBranches after the idle pass and template prune. It deletes any Xata branch that is not the parent (main / XATA_PARENT_BRANCH), not a tpl-* template, and not referenced by any userBranch row. A one-hour createdAt grace period avoids racing an in-flight create (the branch exists in Xata up to ~2 minutes before its row lands). Results are reported in a new orphans: { scanned, dropped } field of the cron response; failures land in the existing errors array.
  • scripts/xata-cleanup.ts: the manual script kept only main and would have deleted all templates; it now also keeps tpl-*, matching the keep policy (main + templates, remove the rest).

Testing

  • npx tsc --noEmit and eslint pass.
  • Dry-run of scripts/xata-cleanup.ts against the live sandbox project (read-only) confirms main and every tpl-* branch are tagged KEEP and only lp-* sandboxes are tagged DELETE.
  • The orphan sweep itself runs on the next nightly cron; orphans.dropped in the response will show the backlog being cleared.

🤖 Generated with Claude Code

The nightly cleanup only deleted branches still tracked by a userBranch
row. Branches orphaned in Xata — delete failures swallowed on the user
path after the row was removed, or a crash between createBranch and the
row insert — were never revisited and lingered indefinitely.

Add an orphan sweep to cleanupIdleBranches: delete any branch that is
not the parent (main), not a tpl-* template, and not referenced by a
userBranch row, with a one-hour createdAt grace period so an in-flight
create is never swept mid-provisioning.

Also make the manual scripts/xata-cleanup.ts keep tpl-* templates
instead of deleting everything except main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
learn-postgres Ready Ready Preview Jun 11, 2026 1:44pm

@exekias
exekias merged commit a167fe6 into main Jun 11, 2026
3 checks passed
@exekias
exekias deleted the fix-orphan-branch-cleanup branch June 11, 2026 14:13

This branch was successfully deployed

1 active deployment
Preview — 2e40e702 Deployed Jun 11, 2026 by vercel[bot]
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