Summary
The vault module runs recurring queue work in BeginBlocker/EndBlocker (keeper/abci.go:18-40), but most processors do not enforce a fixed per-block work budget. Block execution time therefore scales with the number of queued entries rather than a bounded per-block limit, so a large backlog of due vault timeouts, verification entries, or pending swap-outs can stretch block time and delay normal vault processing.
Details
1. Three processors walk their entire due set each block (no cap). Each processed vault can trigger reconciliation work (interest transfer, AUM fee collection, NAV publication), so cost is O(queue) per block:
handleVaultInterestTimeouts (keeper/reconcile.go) — PayoutTimeoutQueue.WalkDue over all due entries.
handleVaultFeeTimeouts (keeper/reconcile.go) — FeeTimeoutQueue.WalkDue over all due entries.
handleReconciledVaults (keeper/reconcile.go) — PayoutVerificationSet.Walk(ctx, nil, …) over the whole set.
2. The swap-out batch limit bounds processed jobs, not visited entries. In processPendingSwapOuts (keeper/payout.go:21-46), paused-vault entries return before the counter check, so they are skipped without counting against MaxSwapOutBatchSize:
vault, ok := k.tryGetVault(ctx, vaultAddr)
if ok && vault.Paused {
return false, nil // skipped, but walk continues and this is not counted
}
if processed == batchSize {
return true, nil
}
processed++
A queue front-loaded with paused entries is walked in full every block even though at most batchSize jobs are processed.
3. Paused entries can camp at the front of the swap-out queue. Expedited swap-outs are reinserted at timestamp zero (queue/pending_swap_out.go:129-146). Combined with (2), paused expedited entries can sit permanently at the front, and the iterator re-visits them every block before reaching processable jobs, starving active withdrawals behind them.
Proposed change
- Apply an explicit per-block visit limit to
handleVaultInterestTimeouts, handleVaultFeeTimeouts, and handleReconciledVaults; process at most a configured number of entries per block and leave the remainder queued for subsequent blocks.
- In
processPendingSwapOuts, count visited entries (including skipped paused ones) against the iteration budget, not only successfully processed jobs.
- Avoid paused entries permanently occupying the front of the swap-out queue, e.g. by separately indexing, refunding, or canceling pending swap-outs for paused vaults so they are not re-walked every block.
Relates to #124 (governance-parameterize the swap-out batch size) and supersedes the temporary-cap note in #75.
Tasks
Summary
The vault module runs recurring queue work in
BeginBlocker/EndBlocker(keeper/abci.go:18-40), but most processors do not enforce a fixed per-block work budget. Block execution time therefore scales with the number of queued entries rather than a bounded per-block limit, so a large backlog of due vault timeouts, verification entries, or pending swap-outs can stretch block time and delay normal vault processing.Details
1. Three processors walk their entire due set each block (no cap). Each processed vault can trigger reconciliation work (interest transfer, AUM fee collection, NAV publication), so cost is O(queue) per block:
handleVaultInterestTimeouts(keeper/reconcile.go) —PayoutTimeoutQueue.WalkDueover all due entries.handleVaultFeeTimeouts(keeper/reconcile.go) —FeeTimeoutQueue.WalkDueover all due entries.handleReconciledVaults(keeper/reconcile.go) —PayoutVerificationSet.Walk(ctx, nil, …)over the whole set.2. The swap-out batch limit bounds processed jobs, not visited entries. In
processPendingSwapOuts(keeper/payout.go:21-46), paused-vault entries return before the counter check, so they are skipped without counting againstMaxSwapOutBatchSize:A queue front-loaded with paused entries is walked in full every block even though at most
batchSizejobs are processed.3. Paused entries can camp at the front of the swap-out queue. Expedited swap-outs are reinserted at timestamp zero (
queue/pending_swap_out.go:129-146). Combined with (2), paused expedited entries can sit permanently at the front, and the iterator re-visits them every block before reaching processable jobs, starving active withdrawals behind them.Proposed change
handleVaultInterestTimeouts,handleVaultFeeTimeouts, andhandleReconciledVaults; process at most a configured number of entries per block and leave the remainder queued for subsequent blocks.processPendingSwapOuts, count visited entries (including skipped paused ones) against the iteration budget, not only successfully processed jobs.Relates to #124 (governance-parameterize the swap-out batch size) and supersedes the temporary-cap note in #75.
Tasks
handleVaultInterestTimeouts,handleVaultFeeTimeouts, andhandleReconciledVaults, leaving the remainder queued.processPendingSwapOutsto count visited (including paused) entries against the budget.