Skip to content

feat(spider-scheduler): Add scheduling state of a single resource group for the resource-group-round-robin scheduler core. - #462

Merged
LinZhihao-723 merged 7 commits into
y-scope:mainfrom
LinZhihao-723:scheduling-unit
Aug 30, 2026
Merged

feat(spider-scheduler): Add scheduling state of a single resource group for the resource-group-round-robin scheduler core.#462
LinZhihao-723 merged 7 commits into
y-scope:mainfrom
LinZhihao-723:scheduling-unit

Conversation

@LinZhihao-723

@LinZhihao-723 LinZhihao-723 commented Aug 27, 2026

Copy link
Copy Markdown
Member

Description

This is the third piece of the resource_group_round_robin scheduler core, following the job registry in #444 and the dispatch queue in #449. It lands RgSchedulingState, the per-resource-group state the core draws assignments from. The core that drives it follows in a later PR, so nothing is wired up and no behaviour changes.

The existing round_robin core is untouched.

What the module holds

RgSchedulingState owns one resource group's scheduling position: a queue of owed finalizations, an active job list rotated over by a round-robin arm, a pending job queue that feeds it, and two per-tick downgrade buffers. try_make_assignment is its whole decision surface — it publishes at most one assignment per call and reports why it could not through MakeAssignmentError.

Finalization tasks are dispatched ahead of regular ones. Regular tasks come from the inner round-robin over active jobs, which is the second level of the core's two-level rotation.

The decisions worth reviewing

A job spends at most one downgrade life per tick. A job that yields nothing loses a life, and is retired once it has none left. The two buffers are what keep that accounting honest: a job removed from the active list and a job passed over in the pending queue are both held aside until apply_downgrades runs at the end of the tick, so neither can be examined — and charged — twice. Returning them to the pending queue immediately would let a single tick spend the whole budget. Demoted jobs go back to the queue's head with their budget restored; passed-over pending jobs go to its tail with their budget untouched, which is what keeps retirement reachable at all.

A publication failure is unrecoverable by design. The task is taken before it is published rather than after. The dispatch queues are unbounded, so publish can only fail on a closed queue, and the core answers that by ending the tick and stopping the runtime — there is no later tick to preserve the task for. An earlier revision read the task, published, then removed it; that bought a re-admission that can never happen, at the cost of a three-step dance and a redundant arena lookup per assignment.

Admission is bounded by the tick's remaining free space, not by a channel bound. try_make_assignment refuses once the group's queue occupancy reaches free. This is the dynamic threshold scheme with its control parameter fixed at 1; the queues themselves stay unbounded, as #449 established.

pop_regular_task's termination rests on DOWNGRADE_LIVES == 1. With one life, a barren active job is always demoted on its first visit, so the loop can never revisit one and needs no visit counter. That precondition is a const assert in the function rather than a comment, and it names what a larger budget would require.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  • Ensure all workflows pass.
  • Add unit test cases to assert the basic behaviors.

Summary by CodeRabbit

  • New Features

    • Added resource-group round-robin scheduling to improve fairness and balance across jobs.
    • Prioritized finalization work while managing active and pending jobs more effectively.
    • Added safeguards for queue capacity, task availability, job downgrades, and queue closure conditions.
  • Bug Fixes

    • Improved job promotion, retirement tracking, and task dispatch ordering for more predictable scheduling behaviour.
  • Tests

    • Added comprehensive coverage for scheduling order, admission limits, task publication, queue handling, and job lifecycle transitions.

…p-round-robin scheduler core:

* Add `RgSchedulingUnit`, which owns one resource group's job lists, its pending finalizations, and the write side of its dispatch queue, and publishes at most one assignment per turn through `try_make_assignment`.
* Add `FinalizeKind` and `MakeAssignmentError` alongside the unit, following the convention that types specific to a scheduling algorithm live inside that algorithm's module.
* Export `DOWNGRADE_LIVES` from the job registry to the rest of the module, so that the unit's tests can name the downgrade budget they exhaust.
* Restore the dispatch queue registry's `close_dispatch_queue`, `close_broadcast_queue`, and `num_outstanding_hints` as `#[cfg(test)]` accessors. `DispatchQueueRegistryInner` is private to its own module, so the unit's tests cannot otherwise reach the channels a rejected publication is asserted against.
* Narrow the job registry's dead-code expectation to non-test builds. The unit's tests are the registry's first consumer, so the expectation is no longer fulfilled in the test build and the plain form fails the lint gate.
* Trim the module docstring to its high-level description.
* Drop the variant docstrings of `FinalizeKind` and shorten its own to the state it names.
* Shorten `MakeAssignmentError::DispatchQueueClosed`'s docstring to the two queues it can name and the fact that it is fatal.
* Move `FinalizeKind` below `MakeAssignmentError`.
…gState`:

* Rename the type and move its module from `scheduling_unit.rs` to `scheduling_state.rs`.
* Rename the test fixture, its helper, and its field to follow the type.
* Update every docstring and comment that named the type, in the job registry and the dispatch queue as well as in the renamed module.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 91bf5497-cf1f-4b9d-8224-bd9c3c60afbb

📥 Commits

Reviewing files that changed from the base of the PR and between 4d02784 and 982c1d7.

📒 Files selected for processing (1)
  • components/spider-scheduler/src/core_impl/resource_group_round_robin/scheduling_state.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/spider-scheduler/src/core_impl/resource_group_round_robin/scheduling_state.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

Adds RgSchedulingState for resource-group round-robin scheduling. It handles finalization priority, task admission, job rotation, downgrades, retirement, assignment publication, and queue-closure errors. It also adds module wiring, test queue controls, and comprehensive tests.

Changes

Resource-group round-robin scheduling

Layer / File(s) Summary
Scheduling-state contracts and module wiring
components/spider-scheduler/src/core_impl/resource_group_round_robin/{mod.rs,job_registry.rs,dispatch_queue.rs,scheduling_state.rs}
Adds scheduling-state types and dependencies. Moves DOWNGRADE_LIVES to shared module scope. Updates module dead-code expectations and scheduling-state terminology.
State lifecycle and assignment selection
components/spider-scheduler/src/core_impl/resource_group_round_robin/scheduling_state.rs
Adds task placement, finalization priority, dispatch admission thresholds, round-robin selection, job promotion, downgrade handling, retirement tracking, and assignment publication.
Scheduling behaviour validation
components/spider-scheduler/src/core_impl/resource_group_round_robin/{scheduling_state.rs,dispatch_queue.rs}, components/spider-scheduler/src/core_impl/resource_group_round_robin/job_registry.rs
Adds fixtures, queue controls, and tests for scheduling order, admission boundaries, job lifecycles, downgrade behaviour, task arrival, and publication failures.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 982c1

This change adds isolated scheduling-state functionality without wiring it into runtime behavior, so no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant RgSchedulingState
  participant JobRegistry
  participant id_issuer
  participant RgDispatchQueueWriter
  RgSchedulingState->>JobRegistry: select active or pending job
  RgSchedulingState->>id_issuer: issue JobId and TaskId
  RgSchedulingState->>RgDispatchQueueWriter: publish assignment
  RgDispatchQueueWriter-->>RgSchedulingState: report queue-closure error
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the addition of per-resource-group scheduling state for the resource-group round-robin scheduler core. It matches the main change.
Docstring Coverage ✅ Passed Docstring coverage is 97.92% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 48 functions across 4 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.97.1)

Clippy execution failed


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@LinZhihao-723
LinZhihao-723 marked this pull request as ready for review August 27, 2026 20:33
@LinZhihao-723
LinZhihao-723 requested review from a team and sitaowang1998 as code owners August 27, 2026 20:33

/// Errors returned by assignment decision-making.
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub(super) enum MakeAssignmentError {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a better naming? Maybe AssignmentError or AssignmentDecisionError?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming is to match try_make_assignment method, which is the only exposed API that returns this error.

  • AssignmentError is more confusing as it sounds like an error of the assignment.
  • AssignmentDecisionError is a bit broad while we want to tighten the scope that this error is only used for try_make_assignment.

Let me improve the docstring for better clarification.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated docstring.

}
}

/// Tops the active job list up to capacity from the pending job queue.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Tops the active job list up to capacity from the pending job queue.
/// Fills the active job list up to capacity from the pending job queue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, "top up" is a better term to match the behavior of this method. Rewrite it as "Tops up the active job list to capacity..."

) -> Option<(JobId, TaskIndex)> {
const _: () = assert!(
1 == DOWNGRADE_LIVES,
"An barren active job with no tasks is demoted on its first visit, so this loop never \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"An barren active job with no tasks is demoted on its first visit, so this loop never \
"A barren active job with no tasks is demoted on its first visit, so this loop never \

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. I replaced "barren" with "exhausted," but I thought the agent switched it back. Will do another round of check to fix it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

already visited."
);

loop {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We need a comment on this loop. Explain briefly what this loop does.
  2. It might be my problem, but I don't see any return of None if no active or pending job yields a task. In fact, I don't see any return in this case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. tbh I don't think the implementation of this function is hard to understand if we refer to the implementation doc. I rewrote the loop manually on top of Claude's implementation to make it straightforward:

    • Line 275 to 279: the active job list is already empty. We either promote a job from the pending queue or the loop stops.
    • Line 281 to 283: Adjust the arm.
    • Line 284 to 288: Get the job entry. If the job entry is already deleted by the registry, it should be discarded and replaced by a pending job. This is a well-documented behavior according to the design doc.
    • Line 289 to 293: A new task assignment is made.
    • Line 295 to 301: The job has no more tasks to schedule. Downgrade it. This is also a documented behavior according to the design doc.

    Each section is short and self-explanatory, assuming readers have context of how the scheduling policy works. Documenting the policy details is not the responsibility of an inline comment.

  2. In line 276, we apply the try operation to pop_promotable_job, which returns None if there's no job that can be promoted. None is propagated from this call.

@LinZhihao-723
LinZhihao-723 merged commit f053fc5 into y-scope:main Aug 30, 2026
24 checks passed
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