Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Outstanding uploads are not safely coordinated with queue resizing, worker refreshes, or backpressure, risking queue completion-count corruption, undelivered results, and unbounded resource growth.
3 open issues
What changed in this PR
This PR reduces queued-job latency by allowing serverless workers to take the next job while a completed job’s result uploads.
Changes:
- Adds an optional result-ready callback to the built-in job handler.
- Releases execution slots before uploads finish, while stopping intake on refresh requests and preserving custom-handler behavior.
- Adds HTTP-based tests for upload overlap, burst timing, refresh intake, and custom handlers.
| File | Description |
|---|---|
tests/test_serverless/test_result_overlap.py |
Adds slow-upload tests for overlap and handler behavior. |
runpod/serverless/modules/rp_scale.py |
Tracks uploading jobs separately and frees their execution slots early. |
runpod/serverless/modules/rp_job.py |
Invokes the optional callback immediately before sending results. |
Review effort: Balanced
| # Fetch as many jobs as the concurrency allows; a task that is only | ||
| # sending its result no longer counts against it | ||
| while ( | ||
| len(tasks) - len(self._sending_results) < self.current_concurrency |
| asking for the next job. Taking the next job while the upload is in | ||
| flight hides it for every job queued behind another. | ||
| """ | ||
| self._stop_intake_if_refreshing(job, job_result) |
| """ | ||
| self._stop_intake_if_refreshing(job, job_result) | ||
| self._sending_results.add(job["id"]) | ||
| self.job_progress.remove(job) |
deanq
requested review from
KAJdev,
colin99d,
jasonwang-runpod and
justinwlin
November 14, 2026 03:47
This branch has not been deployed
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.

What
A queue worker now takes the next job while the previous job's result uploads, instead of holding the slot until the
job-donePOST returns.Why
The result POST to
job-donetakes ~300 ms on every job (GPU and CPU), yet the worker held the job's slot until that response arrived beforeget_jobscould start the nextjob-take. Spaced-out requests pay nothing, but every job queued behind another paid the full upload time again: a 20-job burst on a single-slot worker took 14.4 s (~0.72 s/job, ~0.3 s of it waiting on the upload).Changes
rp_job.handle_jobtakes an optionalon_result_ready(result)callback, called just beforesend_result.JobScaler.handle_jobpasses_release_slotas that callback (only for the built-in handler; a customjobs_handlerkeeps the old behaviour)._release_slotruns the refresh/stopPod check first, so a worker about to restart still never takes another job (preserves the existing refresh guarantee), then marks the job as sending its result and wakesget_jobs.run_jobsno longer counts result-sending tasks againstcurrent_concurrency.Measured (4 rounds, 20-job burst on one warm worker)
Test plan
tests/test_serverless/test_result_overlap.py: overlap, no-stacking, refresh-still-stops-intake, custom-handler-keeps-slot. Runs the realJobScalerover HTTP against a fake ai-api whosejob-donetakes 0.3 s.make quality-check: 3461 passed, 7 skipped.Risks
job_in_progress=0while an upload is in flight; the platform handed out jobs normally in every live run.max_concurrency > 1, occupancy now falls at upload time; not separately benchmarked (bench uses one slot).