Skip to content

scheduler: call Initialize() and UnInitialize() in action lifecycle#5069

Open
aadhil2k4 wants to merge 2 commits intovolcano-sh:masterfrom
aadhil2k4:issue_5052
Open

scheduler: call Initialize() and UnInitialize() in action lifecycle#5069
aadhil2k4 wants to merge 2 commits intovolcano-sh:masterfrom
aadhil2k4:issue_5052

Conversation

@aadhil2k4
Copy link
Contributor

What type of PR is this?

bugfix

What this PR does / why we need it:

  • Currently, scheduler actions only invoke Execute() during a scheduling cycle.
  • Initialize() and UnInitialize() are defined in the Action interface but are called only in unit tests and never in production code.
  • This change ensures every scheduler follows its full lifecycle - Initialize(), Execute(), UnInitialize().

Which issue(s) this PR fixes:

Fixes #5052

Special notes for your reviewer:

NONE

Does this PR introduce a user-facing change?

NONE

Copilot AI review requested due to automatic review settings March 2, 2026 05:54
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical bug in the scheduler where action lifecycle methods, Initialize() and UnInitialize(), were not being called during normal operation. By integrating these calls into the runOnce() function, the change guarantees that all scheduler actions adhere to their intended full lifecycle, improving robustness and preventing potential resource leaks or uninitialized states.

Highlights

  • Scheduler Action Lifecycle: Ensured that scheduler actions now correctly invoke Initialize() before execution and UnInitialize() after execution, completing their full lifecycle.
  • Bug Fix: Addressed a bug where Initialize() and UnInitialize() methods of scheduler actions were only called in unit tests and not in production code.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • pkg/scheduler/scheduler.go
    • Modified the runOnce function to include calls to Initialize() and UnInitialize() for each scheduler action.
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@volcano-sh-bot volcano-sh-bot added the size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. label Mar 2, 2026
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly implements the full lifecycle for scheduler actions by ensuring Initialize() and UnInitialize() are called around Execute(). This is a good fix that aligns the implementation with the Action interface definition. I've added one suggestion to make the implementation slightly more concise and readable by grouping the lifecycle calls within a single anonymous function.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Ensures scheduler actions follow their full lifecycle during each scheduling cycle by invoking Initialize(), Execute(), and UnInitialize() in production (not only in unit tests), addressing #5052.

Changes:

  • Call Initialize() before executing each scheduler action.
  • Ensure UnInitialize() is invoked after each action execution via defer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@volcano-sh-bot volcano-sh-bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Mar 2, 2026
@volcano-sh-bot volcano-sh-bot added size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Mar 2, 2026
@volcano-sh-bot volcano-sh-bot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Mar 2, 2026
Copy link
Contributor

@hajnalmt hajnalmt left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!
Can you align with @devzizu in the original issue or here please? You have completely different lifecycle assumptions than him about the introduction of these functions, and I lean towards his way.

I want to mention that the implementation is not easy on this if we move on to the scheduler's lifetime path. Because you need to account for hot-reload as on config reload pc.actions gets replaced with fresh instances, so you need to prevent the old actions' workers to leak.

// ensuring Initialize() is called before Execute() and UnInitialize()
// is deferred for guaranteed cleanup after execution.
func (pc *Scheduler) executeAction(action framework.Action, ssn *framework.Session) {
func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have this unnecessary anonymous function wrapper here? It doesn't do anything.

// is deferred for guaranteed cleanup after execution.
func (pc *Scheduler) executeAction(action framework.Action, ssn *framework.Session) {
func() {
action.Initialize()
Copy link
Contributor

Choose a reason for hiding this comment

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

We have a conflicting lifecycle assumptions here. The way devzizu imagined this at L827 is that Initialize() and UnInitialize() will be called once for the scheduler's lifetime (start/shutdown), not per-cycle.

I think I agree with him, since session is not an argument of this function the initial design thought should have been to call it only once. We have OpenSession and CloseSession to adhere for a session level lifecycle.

Copy link

@devzizu devzizu Mar 3, 2026

Choose a reason for hiding this comment

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

Indeed, the absence of session as a parameter to Initialize()/UnInitialize() is a good signal that they were designed for scheduler-lifetime scope, not per-cycle. Per-cycle setup already has its natural place, i.e. Execute(ssn) itself, or OnSessionOpen/OnSessionClose on the plugin side (which do receive the session).

As @hajnalmt is stating, calling them per-cycle would also conflict with the async worker design proposed in #5033, since workers are meant to run for the scheduler's lifetime.

@aadhil2k4 @hajnalmt, I suggest we move the logic to https://github.com/volcano-sh/volcano/blob/master/pkg/scheduler/scheduler.go#L91? There, we can iterate over the actions and call Initialize(), while UnInitialize() can be called by consuming stopCh?

Copy link
Member

Choose a reason for hiding this comment

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

I donot see any action implement Initialize now.

Copy link
Contributor

Choose a reason for hiding this comment

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

@hzxuzhonghu
This is needed for devzizu here currently we don't have a way for the actions to gracefully initilize and unitilize workers spawned by any action. We decided to do this separately because we need to support this I think.

Copy link
Member

Choose a reason for hiding this comment

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

ACK, i agree with only calling init/unInit once liftime rather than per cycle

@aadhil2k4
Copy link
Contributor Author

Hi!! I was occupied last week and couldnt check on this. I'll implement the changes and let you know.

@volcano-sh-bot volcano-sh-bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Mar 10, 2026
- Calls Initialize() once on scheduler startup and calls UnInitialize() once on scheduler shutdown via stopCh.
- Updated tests to match this new lifecycle change.

Signed-off-by: Aadhil Ahamed <aadhil2k4@gmail.com>
@volcano-sh-bot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign k82cn for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@volcano-sh-bot volcano-sh-bot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Mar 10, 2026
@aadhil2k4
Copy link
Contributor Author

@hajnalmt @devzizu I have moved the lifecycle handling from per-cycle execution to the scheduler lifetime as discussed.
Please confirm if this implementation looks correct? If so, I’ll proceed with implementing the hot-reload handling.

- Unintialize old actions and Initialize newly loaded actions when the scheduler configuration is reloaded.

Signed-off-by: Aadhil Ahamed <aadhil2k4@gmail.com>
@aadhil2k4 aadhil2k4 requested review from devzizu and hajnalmt March 14, 2026 13:52
}

// Initialize all configured actions once.
func (pc *Scheduler) initActions() {
Copy link

Choose a reason for hiding this comment

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

It looks like initActions() is defined but only called in the tests, and could be called in Run() as well right?

Additionally, I believe we should separate concerns between loadSchedulerConf and Run(), but let me know what you think here:

  • Keep loadSchedulerConf doing only what it does today (parse + swap).
  • Run() can call initActions() explicitly after the first loadSchedulerConf().
  • watchSchedulerConf (hot-reload path) explicitly calls UnInitialize() on old actions and Initialize() on new ones before swapping them into pc.actions under the mutex, so runOnce() never sees uninitialized actions.

Comment on lines +90 to +100
func (pc *Scheduler) initActions() {
pc.mutex.Lock()
actions := pc.actions
pc.mutex.Unlock()
for _, action := range actions {
action.Initialize()
}
}

// Uninitialize all configured actions.
func (pc *Scheduler) cleanupActions() {
Copy link
Member

Choose a reason for hiding this comment

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

move to test file if only needed there and no need to make it as method of Scheduler

var err error
if !pc.disableDefaultConf {
pc.once.Do(func() {
pc.actions, pc.plugins, pc.configurations, pc.metricsConf, err = UnmarshalSchedulerConf(DefaultSchedulerConf)
Copy link
Member

Choose a reason for hiding this comment

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

donot we need to call init for these actions too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Denotes a PR that changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

scheduler: initialize and uninitialize are not called in the codebase

6 participants