Skip to content

add sharding controllers to support node partitioning among multiple scheduler - #4777

Merged
volcano-sh-bot merged 19 commits into
volcano-sh:masterfrom
ssfffss:sharding-controller
Dec 30, 2025
Merged

add sharding controllers to support node partitioning among multiple scheduler#4777
volcano-sh-bot merged 19 commits into
volcano-sh:masterfrom
ssfffss:sharding-controller

Conversation

@ssfffss

@ssfffss ssfffss commented Dec 2, 2025

Copy link
Copy Markdown
Contributor

What type of PR is this?

feature: this PR implements sharding controllers for dynamic node partitioning between schedulers

What this PR does / why we need it:

This PR introduces a new Sharding Controller to Volcano that dynamically partitions cluster nodes between different scheduler types (particularly for Agentic AI workloads). The controller:

  1. Automatically calculates node resource utilization from pod requests (no external metrics server needed)
  2. Assigns nodes to specialized schedulers based on configurable utilization thresholds and warmup preferences
  3. Uses event-driven architecture with informer caches to minimize API server load
  4. Provides hard filtering strategy for reliable node assignment with mutual exclusivity guarantees
  5. Supports configurable scheduler policies through CLI flags
  6. Includes comprehensive testing suite with performance validation for large clusters
    This enables Volcano to efficiently support both traditional batch workloads and emerging Agentic AI workloads on the same cluster, improving resource utilization and scheduling performance.

Which issue(s) this PR fixes:

Fixes #4722

Special notes for your reviewer:

This PR depends on the NodeShard CRD implementation in volcano-sh/apis

  1. All tests pass in CI pipeline including large-scale performance tests (100-node cluster sync in <200ms)
  2. Metrics calculation uses existing informer caches - no additional API server load
  3. Controller follows Volcano's existing controller pattern with proper queue management and error handling
  4. Default configuration includes two scheduler types (volcano-scheduler and agent-scheduler) but is fully configurable
  5. Documentation has been updated with design doc and user guide

Does this PR introduce a user-facing change?

Add new Sharding Controller that dynamically partitions cluster nodes between different scheduler types. This enables efficient scheduling of both traditional batch workloads and Agentic AI workloads on the same cluster.

@volcano-sh-bot volcano-sh-bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Dec 2, 2025
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @ssfffss, 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 introduces a significant feature to Volcano: a Sharding Controller designed to optimize resource utilization and scheduling performance in clusters running diverse workloads, particularly Agentic AI alongside traditional batch jobs. By dynamically partitioning nodes based on real-time resource utilization and configurable policies, it allows specialized schedulers to operate on dedicated node subsets. This approach mitigates scheduler contention, addresses resource fragmentation, and enhances the overall efficiency and scalability of the Volcano scheduling platform.

Highlights

  • Dynamic Node Partitioning: Introduces a new Sharding Controller to dynamically partition cluster nodes among different scheduler types, specifically supporting Agentic AI workloads alongside traditional batch workloads.
  • Resource Utilization Calculation: Automatically calculates node resource utilization based on pod requests, eliminating the need for an external metrics server.
  • Configurable Assignment Strategy: Assigns nodes to specialized schedulers using configurable utilization thresholds, warmup preferences, and hard filtering for mutual exclusivity.
  • API Server Load Minimization: Employs an event-driven architecture with informer caches to efficiently react to cluster state changes and minimize API server load.
  • Scalability & Performance: Designed for scalability, supporting large clusters (100-node sync in <200ms), with comprehensive testing including performance validation.
  • Dependency: Relies on the NodeShard CRD implementation from volcano-sh/apis.
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.

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

Copy link
Copy Markdown

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 introduces a new Sharding Controller to Volcano, which is a significant feature for enabling dynamic node partitioning between different schedulers. The implementation is comprehensive, covering the core controller logic, configuration handling, event processing, and an extensive suite of tests. The inclusion of a detailed design document is also highly appreciated. My review focuses on a critical dependency issue in go.mod, a high-severity bug in the event processing logic, and several medium-severity suggestions to improve code consistency, robustness, and documentation clarity.

Comment thread go.mod Outdated
Comment thread pkg/controllers/sharding/pod_events.go Outdated
Comment thread docs/design/sharding_controller.md
Comment thread docs/design/sharding_controller.md Outdated
Comment thread pkg/controllers/sharding/config.go
Comment thread pkg/controllers/sharding/config.go Outdated
Comment thread pkg/controllers/sharding/event_handlers.go Outdated
Comment thread pkg/controllers/sharding/event_handlers.go Outdated
Comment thread pkg/controllers/sharding/node_events.go Outdated
Comment thread pkg/controllers/sharding/sharding_manager.go
@qi-min

qi-min commented Dec 2, 2025

Copy link
Copy Markdown
Contributor

/assign @JesseStutler

@ssfffss
ssfffss force-pushed the sharding-controller branch from 83a3732 to 1b6b5d8 Compare December 9, 2025 13:00
@ssfffss
ssfffss changed the base branch from agent-scheduler to master December 9, 2025 13:02
@ssfffss
ssfffss force-pushed the sharding-controller branch 2 times, most recently from b0c4f02 to f54d047 Compare December 10, 2025 08:28
Comment thread pkg/controllers/sharding/config.go Outdated
"agent-scheduler:agent:0.7:1.0:true:2:100",
},
}
controllerOptions.ParseConfig()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

config parse error is not handled

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will print log now;

Comment thread pkg/controllers/sharding/config.go
Comment thread pkg/controllers/sharding/event_handlers.go
Comment thread pkg/controllers/sharding/node_utilization.go
Comment thread pkg/controllers/sharding/node_utilization.go
Comment thread pkg/controllers/sharding/sharding_controller.go
Comment thread pkg/controllers/sharding/sharding_manager.go
Comment thread pkg/controllers/sharding/sharding_manager.go Outdated
Comment thread pkg/controllers/sharding/sharding_manager.go
Comment thread pkg/controllers/sharding/sharding_manager.go
Comment thread pkg/controllers/sharding/metrics_cache.go Outdated
Comment thread pkg/controllers/sharding/sharding_manager.go Outdated
Comment thread pkg/controllers/sharding/sharding_controller.go
Comment thread pkg/controllers/sharding/config.go
Comment thread pkg/controllers/sharding/event_handlers.go Outdated
Comment thread pkg/controllers/sharding/node_events.go Outdated
Comment thread pkg/controllers/sharding/node_events.go
Comment thread pkg/controllers/sharding/event_handlers.go Outdated
@YaoZengzeng

Copy link
Copy Markdown
Member

/lgtm

@volcano-sh-bot volcano-sh-bot added the lgtm Indicates that a PR is ready to be merged. label Dec 22, 2025
JesseStutler and others added 5 commits December 22, 2025 20:00
Signed-off-by: JesseStutler <chenzicong4@huawei.com>
…schedulers

Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
… and fix lint errors

Signed-off-by: ssfffss <senbof@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a comprehensive Sharding Controller for Volcano that enables dynamic node partitioning between different scheduler types (particularly for Agentic AI workloads). The controller calculates node resource utilization from pod requests and assigns nodes to specialized schedulers based on configurable thresholds.

Key Changes

  • New sharding controller with event-driven architecture using informer caches to minimize API server load
  • Hard filtering strategy for node assignment with mutual exclusivity guarantees between schedulers
  • Comprehensive test suite including unit tests, integration tests, and performance tests for large clusters
  • Configuration support through CLI flags for scheduler policies
  • Integration with existing Volcano controller manager

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
pkg/controllers/sharding/sharding_controller.go Main controller implementation with informers, event handlers, and sync logic
pkg/controllers/sharding/sharding_manager.go Core assignment algorithm using hard filtering and node prioritization
pkg/controllers/sharding/sharding_types.go Type definitions for metrics, assignments, and configuration
pkg/controllers/sharding/config.go Configuration parsing and CLI flag support
pkg/controllers/sharding/node_events.go Node event handling for additions, updates, and deletions
pkg/controllers/sharding/pod_events.go Pod event handling to trigger node metrics updates
pkg/controllers/sharding/event_handlers.go Common event processing logic
pkg/controllers/sharding/node_utilization.go Node metrics calculation from pod requests
pkg/controllers/sharding/metrics_cache.go Thread-safe metrics cache implementation
pkg/controllers/sharding/test_utils.go Test utilities and helper functions
pkg/controllers/sharding/*_test.go Comprehensive test suite covering functionality, updates, performance, and boundary conditions
cmd/controller-manager/main.go Registration of sharding controller
cmd/controller-manager/app/options/options.go Controller manager options updated to disable sharding by default
cmd/controller-manager/app/options/options_test.go Updated tests for controller selection logic
docs/design/sharding_controller.md Detailed design documentation with architecture diagrams

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

// Setup controller with appropriate configs for performance testing
opt := &TestControllerOption{
InitialObjects: objects,
// FIX: Increase max nodes for performance testing

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The typo "FIX: Increase max nodes for performance testing" appears to be a leftover comment from development. This should be removed or converted to a proper comment explaining why max nodes is set to 50.

Suggested change
// FIX: Increase max nodes for performance testing
// Use a higher MaxNodes value to better exercise sharding behavior under large-cluster load

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed;

duration := time.Since(startTime)
klog.V(4).Infof("Calculated shard assignments in %v for %d nodes", duration, len(nodes))

// FIX: Add performance metrics

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The comment "FIX: Add performance metrics" appears to be a development leftover. This should be removed since the performance metric check is already implemented on the next line.

Suggested change
// FIX: Add performance metrics

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

}

testCtrl := NewTestShardingController(t, opt)
defer close(testCtrl.StopCh)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The defer close(testCtrl.StopCh) pattern is repeated. Same issue - the stop channel should not be closed by this test function.

Suggested change
defer close(testCtrl.StopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

Comment on lines +130 to +164
// calculateShardAssignmentsBatched processes nodes in batches for large clusters
func (sm *ShardingManager) calculateShardAssignmentsBatched(
nodes []*corev1.Node,
currentShards []*shardv1alpha1.NodeShard,
) (map[string]*ShardAssignment, error) {
batchSize := defaultBatchSize
assignments := make(map[string]*ShardAssignment)

// Process nodes in batches
for i := 0; i < len(nodes); i += batchSize {
end := i + batchSize
if end > len(nodes) {
end = len(nodes)
}

batch := nodes[i:end]
batchAssignments, err := sm.CalculateShardAssignments(batch, currentShards)
if err != nil {
return nil, err
}

// Merge assignments
for scheduler, assignment := range batchAssignments {
if existing, exists := assignments[scheduler]; exists {
existing.NodesDesired = append(existing.NodesDesired, assignment.NodesDesired...)
} else {
assignments[scheduler] = assignment
}
}

// Small delay between batches to prevent resource starvation
time.Sleep(10 * time.Millisecond)
}

return assignments, nil

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

There's an issue with the recursive call here. The function calls itself with the same nodes array in batches, but the batching logic will re-batch those batches infinitely. The recursive call should process the batch without further batching, or this should use an iterative approach instead of recursion.

Suggested change
// calculateShardAssignmentsBatched processes nodes in batches for large clusters
func (sm *ShardingManager) calculateShardAssignmentsBatched(
nodes []*corev1.Node,
currentShards []*shardv1alpha1.NodeShard,
) (map[string]*ShardAssignment, error) {
batchSize := defaultBatchSize
assignments := make(map[string]*ShardAssignment)
// Process nodes in batches
for i := 0; i < len(nodes); i += batchSize {
end := i + batchSize
if end > len(nodes) {
end = len(nodes)
}
batch := nodes[i:end]
batchAssignments, err := sm.CalculateShardAssignments(batch, currentShards)
if err != nil {
return nil, err
}
// Merge assignments
for scheduler, assignment := range batchAssignments {
if existing, exists := assignments[scheduler]; exists {
existing.NodesDesired = append(existing.NodesDesired, assignment.NodesDesired...)
} else {
assignments[scheduler] = assignment
}
}
// Small delay between batches to prevent resource starvation
time.Sleep(10 * time.Millisecond)
}
return assignments, nil
// calculateShardAssignmentsBatched processes nodes in batches for large clusters.
// To avoid mutual recursion with CalculateShardAssignments, this helper delegates
// directly to the core assignment logic exposed by CalculateShardAssignments,
// rather than re-batching internally.
func (sm *ShardingManager) calculateShardAssignmentsBatched(
nodes []*corev1.Node,
currentShards []*shardv1alpha1.NodeShard,
) (map[string]*ShardAssignment, error) {
// Delegate to the main CalculateShardAssignments implementation to avoid
// recursive re-batching of already-batched node slices.
return sm.CalculateShardAssignments(nodes, currentShards)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is not an issue; as when the number of nodes is larger than the configured batch size, then all nodes will be processed in batches in the calculateShardAssignmentsBatched function; When it calls the CalculateShardAssignments function, it will process directly as the number of nodes is smaller than the batch size;

}

testCtrl := NewTestShardingController(t, opt)
defer close(testCtrl.StopCh)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The defer close(testCtrl.StopCh) is incorrect. This function does not create or own the stop channel - it's passed in via opt.StopCh. Closing a channel that may be shared or already closed can cause a panic. The cleanup should be handled by the test caller, not this function.

Suggested change
defer close(testCtrl.StopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

}

testCtrl := NewTestShardingController(t, opt)
defer close(testCtrl.StopCh)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The defer close(testCtrl.StopCh) pattern is repeated. Same issue as previous - the stop channel is not owned by this test function and closing it can cause a panic.

Suggested change
defer close(testCtrl.StopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

}

testCtrl := NewTestShardingController(t, opt)
defer close(testCtrl.StopCh)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The defer close(testCtrl.StopCh) pattern is repeated. Same issue - the stop channel should not be closed by this test function.

Suggested change
defer close(testCtrl.StopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

Comment on lines +181 to +185
// FIX 1: Start informer factories HERE
sc.kubeInformerFactory.Start(stopCh)
sc.vcInformerFactory.Start(stopCh)

// FIX 2: Add specific sync checks with detailed logging

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The comment "FIX 1: Start informer factories HERE" and "FIX 2: Add specific sync checks with detailed logging" appear to be development leftovers. These should be removed since the fixes are already implemented.

Suggested change
// FIX 1: Start informer factories HERE
sc.kubeInformerFactory.Start(stopCh)
sc.vcInformerFactory.Start(stopCh)
// FIX 2: Add specific sync checks with detailed logging
sc.kubeInformerFactory.Start(stopCh)
sc.vcInformerFactory.Start(stopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

}

testCtrl := NewTestShardingController(t, opt)
defer close(testCtrl.StopCh)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The defer close(testCtrl.StopCh) pattern is repeated across multiple test functions. This is incorrect - the stop channel is not owned by these test functions and closing a channel that may be shared or already closed can cause a panic. The cleanup should be handled by the test caller, not within these functions.

Suggested change
defer close(testCtrl.StopCh)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

Comment on lines +28 to +33
const (
NODE_SOURCE = "node-controller"
NODE_ADD_EVENT = "node-added"
NODE_UPDATE_EVENT = "node-updated"
NODE_DELETE_EVENT = "node-deleted"
)

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

Constants should follow Go naming conventions. The constants NODE_SOURCE, NODE_ADD_EVENT, etc., should use camelCase or MixedCaps instead of snake_case. For example: NodeSource, NodeAddEvent, NodeUpdateEvent, NodeDeleteEvent.

Copilot uses AI. Check for mistakes.
Comment thread docs/design/sharding_controller.md Outdated

## 3. Architecture Overview

```mermaid

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is AI generated rubbish, this is not readable


// Initialize initializes the controller
func (sc *ShardingController) Initialize(opt *framework.ControllerOption) error {
klog.V(6).Infof("Initializing ShardingController...")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
klog.V(6).Infof("Initializing ShardingController...")
klog.V(2).Infof("Initializing ShardingController...")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed;

func (sc *ShardingController) Initialize(opt *framework.ControllerOption) error {
klog.V(6).Infof("Initializing ShardingController...")
sc.ctx = context.Background()
sc.controllerOptions = *NewShardingControllerOptions()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: either change controllerOptions or NewShardingControllerOptions return value type.

sc.initNodeIndices()

// Initialize queues
sc.queue = workqueue.NewTypedRateLimitingQueueWithConfig(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what is the queue for? NodeShard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

nodeshard;

Comment thread pkg/controllers/sharding/sharding_controller.go
Comment on lines +43 to +46
sc.UpdateNodeMetrics(nodeName, metrics)

// Log significant changes
if prevMetrics := sc.GetNodeMetrics(nodeName); prevMetrics != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How could you get previous metrics as you have updated it?

@ssfffss ssfffss Dec 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

klog.Infof("Cache synchronization completed successfully")

// Initialize node metrics
sc.initializeNodeMetrics()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this same with refreshNodeMetrics, please keep one

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed;

}

// processNodeEventWithRetry processes a node event with retry logic
func (sc *ShardingController) processNodeEventWithRetry(nodeName, eventType, source string, maxRetries int) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

eventType and source are not even used, why do you make eventKey so complex

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this will be used for future threshold optimization including resource usage changes and updating frequency;

func (sc *ShardingController) processNodeEventWithRetry(nodeName, eventType, source string, maxRetries int) error {
var lastErr error

for i := 0; i < maxRetries; i++ {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With this loop, you make a node retry at most 3*3 times

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

remove the requeue logic;

Comment on lines +209 to +210
time.AfterFunc(200*time.Millisecond, func() {
sc.syncShards()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

any consideration about the delay?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we have two places triggering syncShards: 1. node update, 2. periordically

Vaguely remember we donot want to update node shards very frequently. So I think we should have only one routine running sync, if we want node change to trigger sync, we should aggregate node events rather than each node trigger once

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, you are right; Here we need to aggregate node events to trigger shard update but not for all nodes; this will be fixed in the next PR;

Comment on lines +385 to +398
func (sc *ShardingController) ensureNodeStatesUpdated() {
// trigger re-computation if the time interval since the last update exceeds timeout threshold
sc.metricsMutex.RLock()
lastUpdateTime := time.Time{}
for _, nodeMetrics := range sc.nodeMetricsCache {
if nodeMetrics.LastUpdated.After(lastUpdateTime) {
lastUpdateTime = nodeMetrics.LastUpdated
}
}
sc.metricsMutex.RUnlock()

if time.Since(lastUpdateTime) > updateTimeoutThreshold {
klog.V(4).Infof("Node states stale, triggering update")
sc.updateAllNodeStates()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I donot think we need update all again. iirc, the node metrcs are updated periordically and also by node event.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ensureNodeStatesUpdated is removed;

ShardSyncPeriod: 60 * time.Second,
EnableNodeEventTrigger: true,
SchedulerConfigsRaw: []string{
"volcano:volcano:0.0:0.6:false:2:100",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we use structured json instead of a simple string

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will fixed in the future with the configmap PR;

Signed-off-by: ssfffss <senbof@gmail.com>
Comment thread docs/design/sharding_controller.md
Comment thread pkg/controllers/sharding/node_utilization.go
Comment thread pkg/controllers/sharding/event_handlers.go
Comment thread docs/design/sharding_controller.md Outdated
Comment thread docs/design/sharding_controller.md Outdated
Comment thread pkg/controllers/sharding/sharding_controller.go
Comment thread pkg/controllers/sharding/sharding_manager.go
}

// round the utilization with 2 floating points
cpuUtilRounded := math.Round(resourceInfo.CPUUtilization*100) / 100

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We only think about CPU currently?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For the first version, we mainly verify the logic; It is tracked by issue #4879

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree with the Utilization-based Filtering, but should clarify in the doc that currently we only support CPU utilization and will support multi-dimensional resources in the future

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

Comment thread docs/design/sharding_controller.md Outdated
Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
Signed-off-by: ssfffss <senbof@gmail.com>
@JesseStutler

Copy link
Copy Markdown
Member

/lgtm
Wait for @hzxuzhonghu approving

@volcano-sh-bot volcano-sh-bot added the lgtm Indicates that a PR is ready to be merged. label Dec 27, 2025
if len(s.Controllers) > 1 {
return fmt.Errorf("wildcard '*' cannot be combined with other input")
if len(s.Controllers) > 1 && idx != len(s.Controllers)-1 {
return fmt.Errorf("wildcard '*' can only be placed at the final position when combined with other input")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am not sure why cannot put * at last? Previously i can be only put at first, now you changed to be any position except last

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is because when we check whether a controller is enabled or not through isControllerEnabled, its logic is when it finds "" mark, it returns true; it will ignore all the configurations after ''. Therefore, '*" can only be placed in the last position; This also aligns with our logic, we can put exceptions at first, and enable others; Easy to use in this way and logically clean;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

fs.Uint32Var(&s.WorkerThreadsForGC, "worker-threads-for-gc", defaultGCWorkers, "The number of threads for recycling jobs. The larger the number, the faster the job recycling, but requires more CPU load.")
fs.Uint32Var(&s.WorkerThreadsForQueue, "worker-threads-for-queue", defaultQueueWorkers, "The number of threads syncing queue operations. The larger the number, the faster the queue processing, but requires more CPU load.")
fs.StringSliceVar(&s.Controllers, "controllers", []string{defaultControllers}, fmt.Sprintf("Specify controller gates. Use '*' for all controllers, all knownController: %s ,and we can use "+
fs.StringSliceVar(&s.Controllers, "controllers", strings.Split(defaultControllers, ","), fmt.Sprintf("Specify controller gates. Use '*' for all controllers, all knownController: %s ,and we can use "+

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

suggest making *,-sharding-controller as the default

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed;

changePercent := float64(abs(len(event.NewNodes)-len(event.OldNodes))) / float64(len(event.OldNodes))
if changePercent > nodeCountChangeThreshold {
klog.Infof("Significant node change for %s: %.0f%% (%d -> %d nodes)",
event.SchedulerName, changePercent*100, len(event.OldNodes), len(event.NewNodes))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this function just log, seems very heavy to start a go routine for logging assignment change? Why not directly log it at the place where sender resides

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed;

go sc.assignmentChangeProcessor(stopCh)

// Start periodic metrics refresh
go wait.Until(sc.refreshNodeMetrics, nodeRefreshPeriod, stopCh)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IIUC, i think nodeRefreshPeriod should better <= shardSyncPeriod, but now shardSyncPeriod is far smaller, which means one syncShards after another maybe unnecessary because of no utils metrics changed during this time window

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed; but in the issues, we will remove it next time as we already use event to trigger node refresh, no need for periodic update;


// Implement NodeMetricsProvider interface
func (sc *ShardingController) GetNodeMetrics(nodeName string) *NodeMetrics {
return sc.nodeMetricsCache[nodeName]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: why donot protect with lock like GetAllNodeMetrics

Same question on UpdateNodeMetrics

@ssfffss ssfffss Dec 30, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

now the lock is the for whole cache, lock operations would be too frequent if for each node. Here needs careful optimizations on fine-granularity locks as referred in issue #4878

func (sm *ShardingManager) filterEligibleNodes(
config SchedulerConfig,
nodeResources map[string]*NodeResourceInfo,
nodeMap map[string]*corev1.Node,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: nodeResources should be kept only

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

kept now;

Signed-off-by: ssfffss <senbof@gmail.com>
@volcano-sh-bot volcano-sh-bot removed the lgtm Indicates that a PR is ready to be merged. label Dec 30, 2025

@hzxuzhonghu hzxuzhonghu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/approve

it has been disabled by default

@volcano-sh-bot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hzxuzhonghu

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

The pull request process is described 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 the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 30, 2025
@hzxuzhonghu

Copy link
Copy Markdown
Member

/lgtm

@volcano-sh-bot volcano-sh-bot added the lgtm Indicates that a PR is ready to be merged. label Dec 30, 2025
@volcano-sh-bot
volcano-sh-bot merged commit 3568eee into volcano-sh:master Dec 30, 2025
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Support fast scheduling for AI Agent Workloads

7 participants