Repository: koordinator-sh/koordinator
Pull request #2449: scheduler: add ElasticQuota arg to control preemption of default quota (#2413)
Author: login: LennonChin
Labels: name: lgtm
name: approved
name: size/M
name: area/koord-scheduler
Created: 2025-05-19T10:38:31Z | Merged: 2025-05-21T02:17:52Z

### Ⅰ. Describe what this PR does

koord-scheduler

- The quota of pods that do not actively set through annotation will be recognized as the default quota. In some scenarios, many important pods are not scheduled by Koordinator, and their quotas are also recognized as default quota, this will cause the eviction operation to identify these important pods as victim pods, which is risky. We introduced a new argument `DisableDefaultQuotaPreemption` in ElasticQuota, when `DisableDefaultQuotaPreemption` is set to true, these pods can be avoided from being evicted.

<!--
- Summarize your change (**mandatory**)
- How does this PR work? Need a brief introduction for the changed logic (optional)
- Describe clearly one logical change and avoid lazy messages (optional)
- Describe any limitations of the current code (optional)
-->

### Ⅱ. Does this pull request fix one issue?

<!--If so, add "fixes #xxxx" below in the next line, for example, fixes #15. Otherwise, add "NONE" -->

### Ⅲ. Describe how to verify it

unit tests passed.

### Ⅳ. Special notes for reviews

### V. Checklist

- [x] I have written necessary docs and comments
- [ ] I have added necessary unit tests and integration tests
- [x] All checks passed in `make test`



Diff:
diff --git a/pkg/scheduler/apis/config/types.go b/pkg/scheduler/apis/config/types.go
index fdaa5a044c..e52eed5257 100644
--- a/pkg/scheduler/apis/config/types.go
+++ b/pkg/scheduler/apis/config/types.go
@@ -215,6 +215,9 @@ type ElasticQuotaArgs struct {
 	// EnableRuntimeQuota if false, use max instead of runtime for all checks.
 	EnableRuntimeQuota bool
 
+	// DisableDefaultQuotaPreemption if true, will not preempt pods in default quota.
+	DisableDefaultQuotaPreemption bool
+
 	// HookPlugins is expected to be configured with enabled hook plugins
 	HookPlugins []HookPluginConf
 }
diff --git a/pkg/scheduler/apis/config/v1/defaults.go b/pkg/scheduler/apis/config/v1/defaults.go
index 1bf20f9328..8ec28a4895 100644
--- a/pkg/scheduler/apis/config/v1/defaults.go
+++ b/pkg/scheduler/apis/config/v1/defaults.go
@@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(true)
 
 	defaultTimeout           = 600 * time.Second
 	defaultControllerWorkers = 1
@@ -190,6 +191,9 @@ func SetDefaults_ElasticQuotaArgs(obj *ElasticQuotaArgs) {
 	if obj.EnableRuntimeQuota == nil {
 		obj.EnableRuntimeQuota = defaultEnableRuntimeQuota
 	}
+	if obj.DisableDefaultQuotaPreemption == nil {
+		obj.DisableDefaultQuotaPreemption = defaultDisableDefaultQuotaPreemption
+	}
 }
 
 func SetDefaults_CoschedulingArgs(obj *CoschedulingArgs) {
diff --git a/pkg/scheduler/apis/config/v1/types.go b/pkg/scheduler/apis/config/v1/types.go
index fd3b0aacf5..818683d777 100644
--- a/pkg/scheduler/apis/config/v1/types.go
+++ b/pkg/scheduler/apis/config/v1/types.go
@@ -209,6 +209,9 @@ type ElasticQuotaArgs struct {
 	// EnableRuntimeQuota if false, use max instead of runtime for all checks.
 	EnableRuntimeQuota *bool `json:"enableRuntimeQuota,omitempty"`
 
+	// DisableDefaultQuotaPreemption if true, will not preempt pods in default quota.
+	DisableDefaultQuotaPreemption *bool `json:"disableDefaultQuotaPreemption,omitempty"`
+
 	// HookPlugins is expected to be configured with enabled hook plugins
 	HookPlugins []HookPluginConf `json:"hookPlugins,omitempty"`
 }
diff --git a/pkg/scheduler/apis/config/v1/zz_generated.conversion.go b/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
index 0655d8f4b7..276606efbb 100644
--- a/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
+++ b/pkg/scheduler/apis/config/v1/zz_generated.conversion.go
@@ -243,6 +243,9 @@ func autoConvert_v1_ElasticQuotaArgs_To_config_ElasticQuotaArgs(in *ElasticQuota
 	if err := metav1.Convert_Pointer_bool_To_bool(&in.EnableRuntimeQuota, &out.EnableRuntimeQuota, s); err != nil {
 		return err
 	}
+	if err := metav1.Convert_Pointer_bool_To_bool(&in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption, s); err != nil {
+		return err
+	}
 	out.HookPlugins = *(*[]config.HookPluginConf)(unsafe.Pointer(&in.HookPlugins))
 	return nil
 }
@@ -271,6 +274,9 @@ func autoConvert_config_ElasticQuotaArgs_To_v1_ElasticQuotaArgs(in *config.Elast
 	if err := metav1.Convert_bool_To_Pointer_bool(&in.EnableRuntimeQuota, &out.EnableRuntimeQuota, s); err != nil {
 		return err
 	}
+	if err := metav1.Convert_bool_To_Pointer_bool(&in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption, s); err != nil {
+		return err
+	}
 	out.HookPlugins = *(*[]HookPluginConf)(unsafe.Pointer(&in.HookPlugins))
 	return nil
 }
diff --git a/pkg/scheduler/apis/config/v1/zz_generated.deepcopy.go b/pkg/scheduler/apis/config/v1/zz_generated.deepcopy.go
index 14e7806a6c..a50d81b7a9 100644
--- a/pkg/scheduler/apis/config/v1/zz_generated.deepcopy.go
+++ b/pkg/scheduler/apis/config/v1/zz_generated.deepcopy.go
@@ -141,6 +141,11 @@ func (in *ElasticQuotaArgs) DeepCopyInto(out *ElasticQuotaArgs) {
 		*out = new(bool)
 		**out = **in
 	}
+	if in.DisableDefaultQuotaPreemption != nil {
+		in, out := &in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption
+		*out = new(bool)
+		**out = **in
+	}
 	if in.HookPlugins != nil {
 		in, out := &in.HookPlugins, &out.HookPlugins
 		*out = make([]HookPluginConf, len(*in))
diff --git a/pkg/scheduler/apis/config/v1beta3/defaults.go b/pkg/scheduler/apis/config/v1beta3/defaults.go
index 7d6073397e..045b7fbf1d 100644
--- a/pkg/scheduler/apis/config/v1beta3/defaults.go
+++ b/pkg/scheduler/apis/config/v1beta3/defaults.go
@@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(true)
 
 	defaultTimeout           = 600 * time.Second
 	defaultControllerWorkers = 1
@@ -190,6 +191,9 @@ func SetDefaults_ElasticQuotaArgs(obj *ElasticQuotaArgs) {
 	if obj.EnableRuntimeQuota == nil {
 		obj.EnableRuntimeQuota = defaultEnableRuntimeQuota
 	}
+	if obj.DisableDefaultQuotaPreemption == nil {
+		obj.DisableDefaultQuotaPreemption = defaultDisableDefaultQuotaPreemption
+	}
 }
 
 func SetDefaults_CoschedulingArgs(obj *CoschedulingArgs) {
diff --git a/pkg/scheduler/apis/config/v1beta3/types.go b/pkg/scheduler/apis/config/v1beta3/types.go
index 6eb0adbf87..ded16c1412 100644
--- a/pkg/scheduler/apis/config/v1beta3/types.go
+++ b/pkg/scheduler/apis/config/v1beta3/types.go
@@ -209,6 +209,9 @@ type ElasticQuotaArgs struct {
 	// EnableRuntimeQuota if false, use max instead of runtime for all checks.
 	EnableRuntimeQuota *bool `json:"enableRuntimeQuota,omitempty"`
 
+	// DisableDefaultQuotaPreemption if true, will not preempt pods in default quota.
+	DisableDefaultQuotaPreemption *bool `json:"disableDefaultQuotaPreemption,omitempty"`
+
 	// HookPlugins is expected to be configured with enabled hook plugins
 	HookPlugins []HookPluginConf `json:"hookPlugins,omitempty"`
 }
diff --git a/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go b/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
index 2ef6171f9d..130143bd46 100644
--- a/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
+++ b/pkg/scheduler/apis/config/v1beta3/zz_generated.conversion.go
@@ -243,6 +243,9 @@ func autoConvert_v1beta3_ElasticQuotaArgs_To_config_ElasticQuotaArgs(in *Elastic
 	if err := v1.Convert_Pointer_bool_To_bool(&in.EnableRuntimeQuota, &out.EnableRuntimeQuota, s); err != nil {
 		return err
 	}
+	if err := v1.Convert_Pointer_bool_To_bool(&in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption, s); err != nil {
+		return err
+	}
 	out.HookPlugins = *(*[]config.HookPluginConf)(unsafe.Pointer(&in.HookPlugins))
 	return nil
 }
@@ -271,6 +274,9 @@ func autoConvert_config_ElasticQuotaArgs_To_v1beta3_ElasticQuotaArgs(in *config.
 	if err := v1.Convert_bool_To_Pointer_bool(&in.EnableRuntimeQuota, &out.EnableRuntimeQuota, s); err != nil {
 		return err
 	}
+	if err := v1.Convert_bool_To_Pointer_bool(&in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption, s); err != nil {
+		return err
+	}
 	out.HookPlugins = *(*[]HookPluginConf)(unsafe.Pointer(&in.HookPlugins))
 	return nil
 }
diff --git a/pkg/scheduler/apis/config/v1beta3/zz_generated.deepcopy.go b/pkg/scheduler/apis/config/v1beta3/zz_generated.deepcopy.go
index 821488e2af..eb7769b920 100644
--- a/pkg/scheduler/apis/config/v1beta3/zz_generated.deepcopy.go
+++ b/pkg/scheduler/apis/config/v1beta3/zz_generated.deepcopy.go
@@ -141,6 +141,11 @@ func (in *ElasticQuotaArgs) DeepCopyInto(out *ElasticQuotaArgs) {
 		*out = new(bool)
 		**out = **in
 	}
+	if in.DisableDefaultQuotaPreemption != nil {
+		in, out := &in.DisableDefaultQuotaPreemption, &out.DisableDefaultQuotaPreemption
+		*out = new(bool)
+		**out = **in
+	}
 	if in.HookPlugins != nil {
 		in, out := &in.HookPlugins, &out.HookPlugins
 		*out = make([]HookPluginConf, len(*in))
diff --git a/pkg/scheduler/plugins/elasticquota/preempt.go b/pkg/scheduler/plugins/elasticquota/preempt.go
index 0c7be02f54..6cffbd0f32 100644
--- a/pkg/scheduler/plugins/elasticquota/preempt.go
+++ b/pkg/scheduler/plugins/elasticquota/preempt.go
@@ -290,5 +290,15 @@ func (g *Plugin) canPreempt(pod, victim *corev1.Pod) bool {
 	podQuotaName := g.getPodAssociateQuotaName(pod)
 	vicQuotaName := g.getPodAssociateQuotaName(victim)
 
+	// The quota of pods that do not actively set through annotation will be recognized as the default quota.
+	// In some scenarios, many important pods are not scheduled by Koordinator, and their quotas are
+	// also recognized as default quota, this will cause the eviction operation to identify these important
+	// pods as victim pods, which is risky. When DisableDefaultQuotaPreemption is set to true, these pods can
+	// be avoided from being evicted.
+	if g.pluginArgs.DisableDefaultQuotaPreemption &&
+		extension.DefaultQuotaName == vicQuotaName {
+		return false
+	}
+
 	return podPri > vicPri && podQuotaName == vicQuotaName
 }


Reviews:
user: login: saintube
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T02:35:35Z
user: login: LennonChin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T02:49:26Z
user: login: saintube
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T07:26:54Z
user: login: LennonChin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T09:58:38Z
user: login: saintube
body: /lgtm
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-20T11:08:09Z

Review comments:
diff_hunk: @@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(false)
path: pkg/scheduler/apis/config/v1/defaults.go
user: login: saintube
body: how about setting the default as `true`
created_at: 2025-05-20T02:34:20Z
author_association: MEMBER
diff_hunk: @@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(false)
path: pkg/scheduler/apis/config/v1/defaults.go
user: login: LennonChin
body: > how about setting the default as `true`

The default setting here is `false` to keep existing users in use so that there is no inconsistency with what is expected after the upgrade. Of course, it is also possible to set the default value to `true`.
created_at: 2025-05-20T02:49:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(false)
path: pkg/scheduler/apis/config/v1/defaults.go
user: login: saintube
body: The current behavior of preempting pods, when they did not set a valid quota, is dangerous instead of by design.
created_at: 2025-05-20T07:26:54Z
author_association: MEMBER
diff_hunk: @@ -69,9 +69,10 @@ var (
 
 	defaultQuotaGroupNamespace = "koordinator-system"
 
-	defaultMonitorAllQuotas       = pointer.Bool(false)
-	defaultEnableCheckParentQuota = pointer.Bool(false)
-	defaultEnableRuntimeQuota     = pointer.Bool(true)
+	defaultMonitorAllQuotas              = pointer.Bool(false)
+	defaultEnableCheckParentQuota        = pointer.Bool(false)
+	defaultEnableRuntimeQuota            = pointer.Bool(true)
+	defaultDisableDefaultQuotaPreemption = pointer.Bool(false)
path: pkg/scheduler/apis/config/v1/defaults.go
user: login: LennonChin
body: @saintube improved the default value to `true` and resolved conflicts, please review again.
created_at: 2025-05-20T09:58:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-19T10:46:57Z
body: ## [Codecov](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) Report
Attention: Patch coverage is `0%` with `9 lines` in your changes missing coverage. Please review.
> Project coverage is 65.94%. Comparing base [(`e5bee0d`)](https://app.codecov.io/gh/koordinator-sh/koordinator/commit/e5bee0dca4bef4ac8c5b61c3df6ded07b1ac7e58?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) to head [(`f4034d2`)](https://app.codecov.io/gh/koordinator-sh/koordinator/commit/f4034d25e095b6666da91e1fab0d026975d4a528?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh).
> Report is 54 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) | Patch % | Lines |
|---|---|---|
| [pkg/scheduler/plugins/elasticquota/preempt.go](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449?src=pr&el=tree&filepath=pkg%2Fscheduler%2Fplugins%2Felasticquota%2Fpreempt.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh#diff-cGtnL3NjaGVkdWxlci9wbHVnaW5zL2VsYXN0aWNxdW90YS9wcmVlbXB0Lmdv) | 0.00% | [9 Missing :warning: ](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #2449      +/-   ##
==========================================
- Coverage   65.94%   65.94%   -0.01%     
==========================================
  Files         472      472              
  Lines       55725    55734       +9     
==========================================
+ Hits        36749    36751       +2     
- Misses      16326    16334       +8     
+ Partials     2650     2649       -1     
```

| [Flag](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh) | `65.94% <0.00%> (-0.01%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/koordinator-sh/koordinator/pull/2449?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=koordinator-sh).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: koordinator-bot[bot]
created_at: 2025-05-21T02:17:48Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

Approval requirements bypassed by manually added approval.

This pull-request has been approved by: *<a href="https://github.com/koordinator-sh/koordinator/pull/2449#pullrequestreview-2853741382" title="LGTM">saintube</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=koordinator-sh%2Fkoordinator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- **[pkg/scheduler/OWNERS](https://github.com/koordinator-sh/koordinator/blob/main/pkg/scheduler/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["eahydra"]} -->
author_association: NONE