Skip to content

fix(deployment): find orphaned deployments directly so the sweep can close them in minutes - #3980

Open
baktun14 wants to merge 2 commits into
mainfrom
fix/deployment-close-orphans-within-minutes
Open

baktun14 wants to merge 2 commits into
mainfrom
fix/deployment-close-orphans-within-minutes

Conversation

@baktun14

@baktun14 baktun14 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Why

Fixes CON-752.

An open, lease-less deployment holds the $0.50 minimum escrow deposit until the cleanup sweep closes it. Bids expire ~5 minutes after a deployment is created, so one with no lease after 10 minutes can never become active — yet the sweep ran only at 10:30 and 22:30 UTC. A trial user stranded by an orphan waited up to ~12 hours to get their credits back; on a $1.00 trial that is the entire balance gone for half a day.

The sweep could not simply be scheduled more often. It paged through every managed wallet and asked the chain DB whether that one owner had orphans, so a run cost a query per wallet and grew with the wallet table rather than with the handful of real orphans.

What

The sweep is inverted. findStaleDeployments now takes a batch of owners instead of one, so a single query screens 5,000 wallets and returns only the orphans worth closing — roughly a couple of dozen queries per run instead of tens of thousands. The predicate is unchanged; it moved from Sequelize GROUP BY/HAVING to raw SQL with unnest($1::text[]), the shape findClosureStates already uses. All seven existing repository cases were ported verbatim as a parity suite.

Scoping by owner rather than by a time window is deliberate. deployment is chain-wide, and an open lease-less deployment is the normal end state of every abandoned self-custody deployment on Akash — no lease means no spend, so the escrow never drains and the row stays open forever. A chain-wide query with a row cap would hand back those rows first and never close any of them, so the owner set is the only bound that keeps every returned row actionable.

Closing is untouched — still one transaction per owner, with the same already-closed drop/re-broadcast, fee-grant refill retry and unsettleable handling.

Two guards the 10-minute cadence made worth adding:

  • The sweep refuses to run when the indexer trails the chain far enough that a leased deployment would read as lease-less. The staleness cutoff is derived from the last indexed height, so a badly lagging indexer could otherwise close a live deployment — a hazard that already existed twice a day and would now get 72× the exposure.
  • One owner's orphans are split across transactions (20 closes each), so a wallet holding many of them cannot build a close the chain rejects for gas on every run forever.

Schedule moves to 4,14,24,34,44,54 * * * * with concurrencyPolicy: Forbid and activeDeadlineSeconds: 540, so runs cannot overlap. Minutes are offset off every other sweep, mint-act's */10 included. Staging sandbox moves off its weekly schedule to the same one.

--dry-run was added to the command (house convention for every other sweep), which reports what would close and broadcasts nothing.

Verification

  • npm run test:unit — 3694 passed (236 files)
  • npm run test:integration — 666 passed; the 4 markAsActive/markAsUsed throttle failures are pre-existing on native PG and reproduce identically at origin/main with these changes stashed
  • npm run test:functional — 546 passed
  • npx tsc --noEmit and npm run lint -- --quiet clean

Rollout note

Worth one run with --dry-run on the tightened schedule first, to see the real candidate counts in the DEPLOYMENT_CLEAN_UP_SWEEP_END log before it is trusted to broadcast.

Summary by CodeRabbit

  • New Features

    • Added a dry-run option for stale deployment cleanup, allowing operators to preview actions without making changes.
    • Cleanup now supports processing multiple managed wallets and reports operation results and errors.
    • Added safeguards for indexer delays, deployment state changes, and large cleanup batches.
  • Bug Fixes

    • Improved stale deployment detection to prevent duplicate results and avoid closing deployments with active or recently closed leases.
  • Chores

    • Stale deployment cleanup now runs every 10 minutes in production and staging, with concurrent runs prevented and execution time limited.

…close them in minutes

An open, lease-less deployment holds the minimum escrow deposit until the cleanup sweep
closes it. Bids expire about five minutes after a deployment is created, so one with no
lease after ten minutes can never become active — but the sweep only ran at 10:30 and
22:30, so a trial user could wait half a day to get their credits back.

The sweep could not simply run more often. It paged through every managed wallet and
asked the chain database whether that one owner had orphans, so a run cost a query per
wallet and grew with the wallet table rather than with the handful of real orphans.

It now asks about a batch of owners at a time: one query screens five thousand wallets
and comes back with only the orphans worth closing, which is a couple of dozen queries
per run instead of tens of thousands. Closing is unchanged — still one transaction per
owner, with the same already-closed drop, fee-grant refill and unsettleable handling.

Two things the ten-minute cadence made worth guarding. The sweep now refuses to run when
the indexer trails the chain far enough that a leased deployment would read as lease-less,
because the staleness cutoff is derived from the last indexed height. And one owner's
orphans are split across transactions, so a wallet holding many of them cannot build a
close the chain rejects for gas on every run forever.

The command also takes --dry-run, which reports what it would close and broadcasts
nothing, so the new query can be watched in production before it is trusted to act.
@baktun14
baktun14 requested a review from a team as a code owner September 18, 2026 21:22
@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Warning

Review paused — included plan limit reached

Keep your review moving with free on-demand reviews.

  • Run this review for free

On-demand reviews are free for the next 2 days.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Promotion and pricing details

On-demand reviews are free for the next 2 days. After that, they cost $0.25 per reviewed file.

Review limit details

Or wait 24 minutes for your next included review.

Check out review usage here.

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: akash-network/console/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 68a1b052-3242-4ef6-8f95-45bd42d5d6ba

📥 Commits

Reviewing files that changed from the base of the PR and between 5726956 and 7b89003.

📒 Files selected for processing (4)
  • apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts
  • apps/api/src/deployment/repositories/deployment/deployment.repository.ts
  • apps/api/src/deployment/services/stale-managed-deployments-cleaner/stale-managed-deployments-cleaner.service.spec.ts
  • apps/api/src/deployment/services/stale-managed-deployments-cleaner/stale-managed-deployments-cleaner.service.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: akash-network/console/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: a2239032-fc66-4af7-b69e-8c4d8265e254

📥 Commits

Reviewing files that changed from the base of the PR and between d0e36d0 and 5726956.

📒 Files selected for processing (12)
  • .helm/console-api-prod-mainnet-values.yaml
  • .helm/console-api-staging-sandbox-values.yaml
  • apps/api/src/app/console.ts
  • apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts
  • apps/api/src/deployment/controllers/deployment/top-up-deployments.controller.spec.ts
  • apps/api/src/deployment/controllers/deployment/top-up-deployments.controller.ts
  • apps/api/src/deployment/repositories/deployment/deployment.repository.integration.ts
  • apps/api/src/deployment/repositories/deployment/deployment.repository.ts
  • apps/api/src/deployment/services/stale-managed-deployments-cleaner/stale-managed-deployments-cleaner.service.spec.ts
  • apps/api/src/deployment/services/stale-managed-deployments-cleaner/stale-managed-deployments-cleaner.service.ts
  • apps/api/src/deployment/types/state-deployments.ts
  • apps/api/test/seeders/stale-deployment.seeder.ts

Included review availability: Your plan provides up to 5 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Stale deployment cleanup

Layer / File(s) Summary
Repository contracts and stale deployment queries
apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts, apps/api/src/deployment/repositories/deployment/..., apps/api/src/deployment/types/state-deployments.ts, apps/api/test/seeders/...
Wallet iteration now uses keyset batches. Stale-deployment queries accept multiple owners, return string deployment IDs, and apply lease and cutoff filters.
Batched cleanup orchestration
apps/api/src/deployment/services/stale-managed-deployments-cleaner/...
The cleaner validates indexer lag, processes wallet batches, groups work by owner, supports dry runs, limits close batches to 20 deployments, and returns Ok or Err results.
Command and controller result propagation
apps/api/src/app/console.ts, apps/api/src/deployment/controllers/deployment/...
The command accepts --dry-run. The controller returns the cleanup service result. Tests cover the expanded options.
Scheduled cleanup configuration
.helm/console-api-prod-mainnet-values.yaml, .helm/console-api-staging-sandbox-values.yaml
Production and staging cleanup jobs run every 10 minutes, forbid concurrent runs, and use a 540-second active deadline.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Suggested reviewers: stalniy, ygrishajev

Merge Risk: ⚪ Minimal · up to 57269

No actionable defect remains; cleanup failures are surfaced correctly and the safety predicates are preserved.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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

@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.15663% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.47%. Comparing base (3a2d2f8) to head (7b89003).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...repositories/user-wallet/user-wallet.repository.ts 0.00% 6 Missing and 2 partials ⚠️
apps/api/src/app/console.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3980      +/-   ##
==========================================
- Coverage   83.91%   83.47%   -0.44%     
==========================================
  Files        1307     1207     -100     
  Lines       36242    33678    -2564     
  Branches     8725     8210     -515     
==========================================
- Hits        30411    28112    -2299     
+ Misses       5152     4904     -248     
+ Partials      679      662      -17     
Flag Coverage Δ *Carryforward flag
api 92.96% <89.15%> (-0.03%) ⬇️
deploy-web 75.10% <ø> (ø) Carriedforward from 5726956
log-collector ?
notifications 94.35% <ø> (ø) Carriedforward from 5726956
provider-console 81.68% <ø> (ø) Carriedforward from 5726956
provider-inventory ?
provider-proxy 89.05% <ø> (ø) Carriedforward from 5726956
tx-signer ?

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...ollers/deployment/top-up-deployments.controller.ts 100.00% <100.00%> (ø)
...t/repositories/deployment/deployment.repository.ts 92.06% <100.00%> (+0.12%) ⬆️
...eaner/stale-managed-deployments-cleaner.service.ts 100.00% <100.00%> (ø)
apps/api/src/app/console.ts 0.00% <0.00%> (ø)
...repositories/user-wallet/user-wallet.repository.ts 78.82% <0.00%> (-8.19%) ⬇️

... and 100 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@claude claude 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.

Findings marked 🟡 are optional suggestions and need no follow-up push.

… from spreading

Three things the review caught in the inverted sweep.

Screening a batch of owners ran unguarded, so a transient chain-database error on one
batch threw out of the sweep loop and skipped every remaining batch for that run. Before
the inversion each owner's query ran inside the error handler, so one failure was logged
and the rest carried on. The batch query is now handled the same way.

Chunking an owner's closes across transactions also reset the already-closed drop cap on
every chunk, so an owner with a hundred orphans could drop and re-broadcast fifteen times
in a run instead of three. The budget is now spent across all of an owner's transactions.

The new JSDoc blocks ran to several lines, which the repo's comment rule does not allow.

@claude claude 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.

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant