feat: deferred distribution scheduling with idempotent dispatch - #897
Merged
thlpkee20-wq merged 2 commits intoAug 31, 2026
Merged
Conversation
Add a deferred distribution queue so operators can schedule a specific distribution run for a future settlement window and have the scheduler honor it idempotently. - Add scheduled_distributions table (migration 021) with (offering_id, period_id) UNIQUE idempotency key and a partial index on due (run_at, status) rows plus stale processing reclaim. - Add ScheduledDistributionRepository: create, due/lease query, atomic claim, complete/fail/cancel, lookups (100% coverage). - Extend DistributionScheduler with processScheduledDistributions(now?) that claims due (and stale-processing) rows and dispatches them through DistributionEngine with the stored snapshot boundary. Completed rows are skipped across restarts; stale processing rows are reclaimed after a lease; duplicate enqueue is rejected at the table level. - Add admin-only endpoints: POST /distributions/schedule (enqueue, 409 on duplicate), GET /distributions/schedule (list), and DELETE /distributions/schedule/:id (cancel pending runs). - Document security/failure semantics and validation in docs/deferred-distribution-scheduling.md; track the new repository in jest coverage. Closes RevoraOrg#871
|
@BALLUCK004 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #871
Summary
Adds deferred distribution scheduling: operators can queue a specific distribution run at a future settlement window and the
DistributionSchedulerhonors it idempotently.What was built
src/db/migrations/021_create_scheduled_distributions.sql– newscheduled_distributionstable with(offering_id, period_id, run_at, status)and aUNIQUE (offering_id, period_id)idempotency key, plus a partial index for due rows and a partial index for per-offering listing.ScheduledDistributionRepository(src/db/repositories/scheduledDistributionRepository.ts) –create(409 on duplicate),findDueScheduledDistributions(now, leaseMs, limit)(due + stale-processing reclaim), atomicclaimScheduledDistribution,markCompleted/markFailed/markCancelled,findById/findByOffering/findAll.DistributionScheduler.processScheduledDistributions(now?)(src/services/distributionScheduler.ts) – picks due rows, claims atomically, dispatches throughDistributionEngine.distribute(offering_id, period, amount)with the stored snapshot boundary (period_start/period_end, falling back torun_at). Failure is sanitized toDistribution failed: <CLASS>and persisted on the row; per-row failure never aborts the tick. Configured via optionalscheduledDistributionRepo/staleLeaseMsoptions (source-compatible; safe no-op when not configured).src/routes/distributions.ts, admin-only):POST /distributions/schedule– enqueue (201; 400 validation; 403 non-admin; 404 unknown offering; 409 duplicate)GET /distributions/schedule– list (optionaloffering_idfilter)DELETE /distributions/schedule/:id– cancel a pending run (200; 404 if not cancellable)docs/deferred-distribution-scheduling.md– data model, scheduler semantics, idempotency-across-restart rationale, endpoint contracts, security/failure modes.Acceptance criteria mapping
distributionScheduler.ts+distributionEngine.ts– scheduler drives the engine with the queued snapshot boundary and amount; engine unchanged (existing idempotencyfindRunByParamsshort-circuit is what makes restart re-runs safe).adminrole (403 otherwise);error_messageonly stores sanitized failure class, never raw DB/provider errors.UPDATE ... WHERE status='scheduled'), 15-min lease reclaim for crashed schedulers, in-process skip of already-claimed rows, per-row isolation;completedrows are skipped on backfill after restart.run_at, cancelled/completed not re-picked, missing repo no-op, mark-failed throw, custom lease.Validation
src/db/repositories/scheduledDistributionRepository.test.ts– 17 tests, 100% statements/branches/functions/lines.src/services/distributionScheduler.test.ts– 8 newprocessScheduledDistributionstests pass.src/routes/__tests__/distributions.test.ts– 26 tests pass (13 new).jest.config.jscollectCoverageFrom.tsc --noEmitreports no errors in the changed files.Note: the repo has pre-existing failures unrelated to this change, reproduced on
masterwithout this PR: 60 failing tests insrc/services/distributionScheduler.test.ts(un-importedvalidateCronSyntax/STELLAR_MAINTENANCE_WINDOWS) and 6 insrc/services/__tests__/distributionScheduler.test.ts(private-member access), plus a repo-wideno-explicit-anylint backlog. Targeted suites for this change are green.Migration: apply with
npm run migrate(filename-unique, tracked inschema_version).