Skip to content

fix: eliminate head-of-line blocking in RedisWorker.fetch_task() - #7975

Closed
dkliban wants to merge 1 commit into
pulp:mainfrom
dkliban:fix/7900-fetch-task-hol-blocking
Closed

fix: eliminate head-of-line blocking in RedisWorker.fetch_task()#7975
dkliban wants to merge 1 commit into
pulp:mainfrom
dkliban:fix/7900-fetch-task-hol-blocking

Conversation

@dkliban

@dkliban dkliban commented Aug 14, 2026

Copy link
Copy Markdown
Member

Summary

Fixes head-of-line blocking in RedisWorker.fetch_task() when thousands of
tasks need the same exclusive resource (#7900).

After acquire_locks reports a resource is blocked, tasks needing that resource
are excluded from subsequent DB queries using the GIN-indexed
reserved_resources_record overlap check. Both raw and shared: prefixed
variants are excluded so tasks needing either exclusive or shared access
to the blocked resource are skipped.

Changes

  • Track blocked resources in a set across fetch_task() iterations
  • Exclude tasks needing blocked resources from DB queries via __overlap
  • Add both raw and shared: prefixed names to the exclusion set
  • Filter task_lock sentinel from blocked resource tracking
  • Reset fetch_limit when new blocked resources are discovered
  • Preserve doubling fallback for edge cases (FIFO skips, claim races)

Testing

  • Functional test: 1000 blocked tasks + 1 free task, asserts fetch_task()
    finds the free task in <= 5 acquire_locks calls (old algorithm needs ~8)
  • Test uses real PostgreSQL records, real Redis locks, no mocks
  • Verified: test fails on original code, passes on fixed code

Benchmark results (from factory)

Scale Old (acquire/time) New (acquire/time)
100 5 / 15ms 2 / 8ms
2,000 9 / 242ms 2 / 7ms
10,000 11 / 1,063ms 2 / 16ms
18,000 12 / 2,170ms 2 / 21ms

Fixes #7900

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

When the task queue has thousands of tasks needing the same blocked
resource, workers could not reach tasks for free resources further
down the queue. The old algorithm doubled its query window and
restarted from position 0 each time, re-scanning the same blocked
tasks repeatedly.

Track resources that acquire_locks() reports as blocked and exclude
tasks needing those resources from subsequent DB queries using
PostgreSQL's array overlap operator against the existing GIN index
on reserved_resources_record. In the incident scenario (17,000+
tasks blocked on one repository), this reduces the search from ~11
doubling iterations fetching ~37,000 rows to 2 queries fetching ~40
rows.

Closes: pulp#7900

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

RedisWorker fetch_task() head-of-line blocking: workers starved when queue head is serialized on one resource (WORKER_TYPE=redis)

1 participant