Skip to content

Commit

Permalink
narrow down the scope of EnqueueExtensions to subscribe less cluster …
Browse files Browse the repository at this point in the history
…events
  • Loading branch information
sanposhiho committed Oct 27, 2023
1 parent fd5c406 commit c7842d9
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 169 deletions.
13 changes: 11 additions & 2 deletions pkg/scheduler/framework/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,17 @@ type QueueSortPlugin interface {
}

// EnqueueExtensions is an optional interface that plugins can implement to efficiently
// move unschedulable Pods in internal scheduling queues. Plugins
// that fail pod scheduling (e.g., Filter plugins) are expected to implement this interface.
// move unschedulable Pods in internal scheduling queues.
// In the scheduler, Pods can be unschedulable by PreEnqueue, PreFilter, Filter, Reserve, and Permit plugins,
// and Pods rejected by these plugins are requeued based on this extension point.
// Failures from other extension points are regarded as temporal errors (e.g., network failure),
// and the scheduler requeue Pods without this extension point - always requeue Pods to activeQ after backoff.
// This is because such temporal errors cannot be resolved by specific cluster events,
// and we have no choise but keep retrying scheduling until the failure is resolved.
//
// Plugins that make pod unschedulable (PreEnqueue, PreFilter, Filter, Reserve, and Permit plugins) should implement this interface,
// otherwise the default implementation will be used, which is less efficient in requeueing Pods rejected by the plugin.
// And, if plugins other than above extension points support this interface, they are just ignored.
type EnqueueExtensions interface {
Plugin
// EventsToRegister returns a series of possible events that may cause a Pod
Expand Down
15 changes: 15 additions & 0 deletions pkg/scheduler/framework/runtime/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,22 @@ func (f *frameworkImpl) expandMultiPointPlugins(logger klog.Logger, profile *con
return nil
}

func shouldHaveEnqueueExtensions(p framework.Plugin) bool {
switch p.(type) {
// Only PreEnqueue, PreFilter, Filter, Reserve, and Permit plugins can (should) have EnqueueExtensions.
// See the comment of EnqueueExtensions for more detailed reason here.
case framework.PreEnqueuePlugin, framework.PreFilterPlugin, framework.FilterPlugin, framework.ReservePlugin, framework.PermitPlugin:
return true
}
return false
}

func (f *frameworkImpl) fillEnqueueExtensions(p framework.Plugin) {
if !shouldHaveEnqueueExtensions(p) {
// Ignore EnqueueExtensions from plugin which isn't PreEnqueue, PreFilter, Filter, Reserve, and Permit.
return
}

ext, ok := p.(framework.EnqueueExtensions)
if !ok {
// If interface EnqueueExtensions is not implemented, register the default enqueue extensions
Expand Down
210 changes: 43 additions & 167 deletions pkg/scheduler/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,12 +656,12 @@ func indexByPodAnnotationNodeName(obj interface{}) ([]string, error) {
}

const (
fakeNoop = "fakeNoop"
fakeNode = "fakeNode"
fakePod = "fakePod"
fakeNoopRuntime = "fakeNoopRuntime"
queueSort = "no-op-queue-sort-plugin"
fakeBind = "bind-plugin"
filterWithoutEnqueueExtensions = "filterWithoutEnqueueExtensions"
fakeNode = "fakeNode"
fakePod = "fakePod"
emptyEventsToRegister = "emptyEventsToRegister"
queueSort = "no-op-queue-sort-plugin"
fakeBind = "bind-plugin"
)

func Test_buildQueueingHintMap(t *testing.T) {
Expand All @@ -672,151 +672,61 @@ func Test_buildQueueingHintMap(t *testing.T) {
featuregateDisabled bool
}{
{
name: "no-op plugin",
plugins: []framework.Plugin{&fakeNoopPlugin{}},
name: "filter without EnqueueExtensions plugin",
plugins: []framework.Plugin{&filterWithoutEnqueueExtensionsPlugin{}},
want: map[framework.ClusterEvent][]*internalqueue.QueueingHintFunction{
{Resource: framework.Pod, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.Node, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSINode, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIDriver, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIStorageCapacity, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolume, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.StorageClass, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolumeClaim, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PodSchedulingContext, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: fakeNoop, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
{PluginName: filterWithoutEnqueueExtensions, QueueingHintFn: defaultQueueingHintFn},
},
},
},
{
name: "node and pod plugin",
plugins: []framework.Plugin{&fakeNodePlugin{}, &fakePodPlugin{}},
want: map[framework.ClusterEvent][]*internalqueue.QueueingHintFunction{
{Resource: framework.Pod, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.Pod, ActionType: framework.Add}: {
{PluginName: fakePod, QueueingHintFn: fakePodPluginQueueingFn},
},
{Resource: framework.Node, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.Node, ActionType: framework.Add}: {
{PluginName: fakeNode, QueueingHintFn: fakeNodePluginQueueingFn},
},
{Resource: framework.CSINode, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIDriver, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIStorageCapacity, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolume, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.StorageClass, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolumeClaim, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PodSchedulingContext, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
},
},
{
name: "node and pod plugin (featuregate is disabled)",
plugins: []framework.Plugin{&fakeNodePlugin{}, &fakePodPlugin{}},
featuregateDisabled: true,
want: map[framework.ClusterEvent][]*internalqueue.QueueingHintFunction{
{Resource: framework.Pod, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.Pod, ActionType: framework.Add}: {
{PluginName: fakePod, QueueingHintFn: defaultQueueingHintFn}, // default queueing hint due to disabled feature gate.
},
{Resource: framework.Node, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.Node, ActionType: framework.Add}: {
{PluginName: fakeNode, QueueingHintFn: defaultQueueingHintFn}, // default queueing hint due to disabled feature gate.
},
{Resource: framework.CSINode, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIDriver, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.CSIStorageCapacity, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolume, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.StorageClass, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PersistentVolumeClaim, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
{Resource: framework.PodSchedulingContext, ActionType: framework.All}: {
{PluginName: fakeBind, QueueingHintFn: defaultQueueingHintFn},
{PluginName: queueSort, QueueingHintFn: defaultQueueingHintFn},
},
},
},
}
Expand Down Expand Up @@ -889,10 +799,10 @@ func Test_UnionedGVKs(t *testing.T) {
want map[framework.GVK]framework.ActionType
}{
{
name: "no-op plugin",
name: "filter without EnqueueExtensions plugin",
plugins: schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
{Name: fakeNoop},
{Name: filterWithoutEnqueueExtensions},
{Name: queueSort},
{Name: fakeBind},
},
Expand Down Expand Up @@ -921,15 +831,7 @@ func Test_UnionedGVKs(t *testing.T) {
Disabled: []schedulerapi.Plugin{{Name: "*"}}, // disable default plugins
},
want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.All,
framework.Node: framework.All,
framework.CSINode: framework.All,
framework.CSIDriver: framework.All,
framework.CSIStorageCapacity: framework.All,
framework.PersistentVolume: framework.All,
framework.PersistentVolumeClaim: framework.All,
framework.StorageClass: framework.All,
framework.PodSchedulingContext: framework.All,
framework.Node: framework.Add,
},
},
{
Expand All @@ -943,15 +845,7 @@ func Test_UnionedGVKs(t *testing.T) {
Disabled: []schedulerapi.Plugin{{Name: "*"}}, // disable default plugins
},
want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.All,
framework.Node: framework.All,
framework.CSINode: framework.All,
framework.CSIDriver: framework.All,
framework.CSIStorageCapacity: framework.All,
framework.PersistentVolume: framework.All,
framework.PersistentVolumeClaim: framework.All,
framework.StorageClass: framework.All,
framework.PodSchedulingContext: framework.All,
framework.Pod: framework.Add,
},
},
{
Expand All @@ -966,52 +860,34 @@ func Test_UnionedGVKs(t *testing.T) {
Disabled: []schedulerapi.Plugin{{Name: "*"}}, // disable default plugins
},
want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.All,
framework.Node: framework.All,
framework.CSINode: framework.All,
framework.CSIDriver: framework.All,
framework.CSIStorageCapacity: framework.All,
framework.PersistentVolume: framework.All,
framework.PersistentVolumeClaim: framework.All,
framework.StorageClass: framework.All,
framework.PodSchedulingContext: framework.All,
framework.Pod: framework.Add,
framework.Node: framework.Add,
},
},
{
name: "no-op runtime plugin",
name: "empty EventsToRegister plugin",
plugins: schedulerapi.PluginSet{
Enabled: []schedulerapi.Plugin{
{Name: fakeNoopRuntime},
{Name: emptyEventsToRegister},
{Name: queueSort},
{Name: fakeBind},
},
Disabled: []schedulerapi.Plugin{{Name: "*"}}, // disable default plugins
},
want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.All,
framework.Node: framework.All,
framework.CSINode: framework.All,
framework.CSIDriver: framework.All,
framework.CSIStorageCapacity: framework.All,
framework.PersistentVolume: framework.All,
framework.PersistentVolumeClaim: framework.All,
framework.StorageClass: framework.All,
framework.PodSchedulingContext: framework.All,
},
want: map[framework.GVK]framework.ActionType{},
},
{
name: "plugins with default profile",
plugins: schedulerapi.PluginSet{Enabled: defaults.PluginsV1.MultiPoint.Enabled},
want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.All,
framework.Node: framework.All,
framework.CSINode: framework.All,
framework.CSIDriver: framework.All,
framework.CSIStorageCapacity: framework.All,
framework.PersistentVolume: framework.All,
framework.PersistentVolumeClaim: framework.All,
framework.StorageClass: framework.All,
framework.PodSchedulingContext: framework.All,
framework.CSINode: framework.All - framework.Delete,
framework.CSIDriver: framework.All - framework.Delete,
framework.CSIStorageCapacity: framework.All - framework.Delete,
framework.PersistentVolume: framework.All - framework.Delete,
framework.PersistentVolumeClaim: framework.All - framework.Delete,
framework.StorageClass: framework.All - framework.Delete,
},
},
}
Expand All @@ -1023,7 +899,7 @@ func Test_UnionedGVKs(t *testing.T) {
registry := plugins.NewInTreeRegistry()

cfgPls := &schedulerapi.Plugins{MultiPoint: tt.plugins}
plugins := []framework.Plugin{&fakeNodePlugin{}, &fakePodPlugin{}, &fakeNoopPlugin{}, &fakeNoopRuntimePlugin{}, &fakeQueueSortPlugin{}, &fakebindPlugin{}}
plugins := []framework.Plugin{&fakeNodePlugin{}, &fakePodPlugin{}, &filterWithoutEnqueueExtensionsPlugin{}, &emptyEventsToRegisterPlugin{}, &fakeQueueSortPlugin{}, &fakebindPlugin{}}
for _, pl := range plugins {
tmpPl := pl
if err := registry.Register(pl.Name(), func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
Expand Down Expand Up @@ -1084,12 +960,12 @@ func (t *fakebindPlugin) Bind(ctx context.Context, state *framework.CycleState,
return nil
}

// fakeNoopPlugin doesn't implement interface framework.EnqueueExtensions.
type fakeNoopPlugin struct{}
// filterWithoutEnqueueExtensionsPlugin implements Filter, but doesn't implement EnqueueExtensions.
type filterWithoutEnqueueExtensionsPlugin struct{}

func (*fakeNoopPlugin) Name() string { return fakeNoop }
func (*filterWithoutEnqueueExtensionsPlugin) Name() string { return filterWithoutEnqueueExtensions }

func (*fakeNoopPlugin) Filter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ *framework.NodeInfo) *framework.Status {
func (*filterWithoutEnqueueExtensionsPlugin) Filter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ *framework.NodeInfo) *framework.Status {
return nil
}

Expand Down Expand Up @@ -1133,15 +1009,15 @@ func (pl *fakePodPlugin) EventsToRegister() []framework.ClusterEventWithHint {
}
}

// fakeNoopRuntimePlugin implement interface framework.EnqueueExtensions, but returns nil
// at runtime. This can simulate a plugin registered at scheduler setup, but does nothing
// emptyEventsToRegisterPlugin implement interface framework.EnqueueExtensions, but returns nil from EventsToRegister.
// This can simulate a plugin registered at scheduler setup, but does nothing
// due to some disabled feature gate.
type fakeNoopRuntimePlugin struct{}
type emptyEventsToRegisterPlugin struct{}

func (*fakeNoopRuntimePlugin) Name() string { return fakeNoopRuntime }
func (*emptyEventsToRegisterPlugin) Name() string { return emptyEventsToRegister }

func (*fakeNoopRuntimePlugin) Filter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ *framework.NodeInfo) *framework.Status {
func (*emptyEventsToRegisterPlugin) Filter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ *framework.NodeInfo) *framework.Status {
return nil
}

func (*fakeNoopRuntimePlugin) EventsToRegister() []framework.ClusterEventWithHint { return nil }
func (*emptyEventsToRegisterPlugin) EventsToRegister() []framework.ClusterEventWithHint { return nil }

0 comments on commit c7842d9

Please sign in to comment.