Skip to content

feat: let a backfill class cap how many batches run in parallel - #31

Open
guilleov wants to merge 4 commits into
mainfrom
feat/max-parallel-batches
Open

feat: let a backfill class cap how many batches run in parallel#31
guilleov wants to merge 4 commits into
mainfrom
feat/max-parallel-batches

Conversation

@guilleov

@guilleov guilleov commented Sep 3, 2026

Copy link
Copy Markdown

Why

DataDrip enqueues every batch of a run as soon as the dripper creates it. For a backfill whose batches serialize on one shared resource (the Iris CH-native backfill takes a global advisory lock so only one batch touches ClickHouse at a time), a run with hundreds of batches means hundreds of children holding within_24_hours worker threads while they block on the lock.

What

  • DataDrip::Backfill.max_parallel_batches (class method, default nil = unlimited, current behaviour).
  • BackfillRunBatch#enqueue only enqueues while fewer than that many siblings are enqueued/running; the rest stay pending.
  • DripperChild hands the slot to the oldest pending batch after it completes or fails; when the run was stopped it releases the pending batches as stopped so nothing sits in pending forever.
  • "Retry failed batches" goes through the same enqueue, so it respects the limit.
  • README section + CHANGELOG entry.

Tests

bundle exec rspec: 299 examples, 0 failures. New examples cover the enqueue gate (limit vs. no limit), slot hand-over on success and on failure, and the stopped-run release. rubocop: clean.

First consumer: Iris::ChBackfillEntity in the monorepo (factorialco/factorial#112674), which will set 1.

A run enqueues every batch the moment the dripper creates it, so a run with
hundreds of batches occupies as many worker threads even when the batches
serialize on one shared resource and simply wait. A backfill class can now
declare self.max_parallel_batches: only that many children are enqueued or
running at once, the rest stay pending, and the finishing child (completed or
failed) enqueues the oldest pending one. A stopped run releases its pending
batches as stopped. Default stays unlimited; the retry action respects the
limit through the same enqueue path.
Deleting a pending run raised FrozenError because after_commit :enqueue also
fired on the destroy commit and called enqueued! on the frozen record. A run
due now is pushed with perform_later instead of set(wait_until:), which the
inline adapter rejects; future runs are still scheduled.
Keeps main's commit-before-enqueue ordering in BackfillRun#enqueue and adds
the branch's no-scheduling path for runs due now.
@guilleov
guilleov marked this pull request as ready for review September 4, 2026 09:14
… job at whole seconds

The compiled stylesheet picked up main's classes only after the merge, and
the scheduling spec compared a sub-microsecond Ruby time with the persisted
value, which passes or fails depending on the Ruby build.
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.

2 participants