You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@yunikorn.apache.org by ma...@apache.org on 2026/10/15 10:32:13 UTC

(yunikorn-core) branch master updated: [YUNIKORN-3506] Fix child queue preemptable resource calculation in quota preemption (#1189)

This is an automated email from the ASF dual-hosted git repository.

manirajv06 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f53db83 [YUNIKORN-3506] Fix child queue preemptable resource calculation in quota preemption (#1189)
7f53db83 is described below

commit 7f53db83f710dd5833fbf518acf700d404feae92
Author: hedger9487 <he...@gmail.com>
AuthorDate: Thu Oct 15 16:02:02 2026 +0530

    [YUNIKORN-3506] Fix child queue preemptable resource calculation in quota preemption (#1189)
    
    Closes: #1189
    
    Signed-off-by: mani <ma...@gmail.com>
---
 pkg/scheduler/objects/quota_preemptor.go      | 23 +++-----
 pkg/scheduler/objects/quota_preemptor_test.go | 77 +++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 15 deletions(-)

diff --git a/pkg/scheduler/objects/quota_preemptor.go b/pkg/scheduler/objects/quota_preemptor.go
index da050b16..37a66bc4 100644
--- a/pkg/scheduler/objects/quota_preemptor.go
+++ b/pkg/scheduler/objects/quota_preemptor.go
@@ -165,25 +165,18 @@ func getChildQueuesPreemptableResource(queue *Queue, parentPreemptableResource *
 	// Total preemptable resource (sum of all children's preemptable resources) would be calculated along the way.
 	for _, child := range children {
 		allocated := child.GetAllocatedResource()
-		guaranteed := child.GetGuaranteedResource()
-		// Skip child if there is no usage or usage below or equals guaranteed
-		if allocated.IsEmpty() || guaranteed.StrictlyGreaterThanOrEqualsOnlyExisting(allocated) {
+		if allocated.IsEmpty() {
 			continue
 		}
-		var usedResource *resources.Resource
-		if !guaranteed.IsEmpty() {
-			usedResource = resources.SubOnlyExisting(guaranteed, allocated)
-		} else {
-			usedResource = allocated
-		}
-		preemptableResource := resources.NewResource()
-		for k, v := range usedResource.Resources {
-			if v < 0 {
-				preemptableResource.Resources[k] = v * -1
-			} else {
-				preemptableResource.Resources[k] = v
+		preemptableResource := resources.SubOnlyExisting(allocated, child.GetGuaranteedResource())
+		for resourceType, v := range preemptableResource.Resources {
+			if _, ok := parentPreemptableResource.Resources[resourceType]; !ok || v <= 0 {
+				delete(preemptableResource.Resources, resourceType)
 			}
 		}
+		if preemptableResource.IsEmpty() {
+			continue
+		}
 		childrenPreemptableResource[child] = preemptableResource
 		totalPreemptableResource.AddTo(preemptableResource)
 	}
diff --git a/pkg/scheduler/objects/quota_preemptor_test.go b/pkg/scheduler/objects/quota_preemptor_test.go
index 03f5db0d..d4b3bba1 100644
--- a/pkg/scheduler/objects/quota_preemptor_test.go
+++ b/pkg/scheduler/objects/quota_preemptor_test.go
@@ -512,6 +512,83 @@ func TestQuotaChangeGetChildQueuesPreemptableResourceWithDifferentResTypes(t *te
 	}
 }
 
+func TestGetChildQueuesPreemptableResource_UnderGuaranteedAndUnguaranteedTypes(t *testing.T) {
+	testCases := []struct {
+		name              string
+		child1Guaranteed  map[string]string
+		child1Allocated   map[string]resources.Quantity
+		child2Guaranteed  map[string]string
+		child2Allocated   map[string]resources.Quantity
+		parentPreemptable map[string]resources.Quantity
+		expectedChild1    map[string]resources.Quantity
+		expectedChild2    map[string]resources.Quantity
+	}{
+		{
+			name:              "under-guaranteed resource is not treated as preemptable",
+			child1Guaranteed:  map[string]string{"cpu": "10", "memory": "100"},
+			child1Allocated:   map[string]resources.Quantity{"cpu": 40, "memory": 20},
+			child2Guaranteed:  map[string]string{"cpu": "10", "memory": "10"},
+			child2Allocated:   map[string]resources.Quantity{"cpu": 40, "memory": 50},
+			parentPreemptable: map[string]resources.Quantity{"cpu": 20, "memory": 20},
+			expectedChild1:    map[string]resources.Quantity{"cpu": 10},
+			expectedChild2:    map[string]resources.Quantity{"cpu": 10, "memory": 20},
+		},
+		{
+			name:              "unguaranteed resource type is preemptable when guaranteed is set",
+			child1Guaranteed:  map[string]string{"cpu": "10"},
+			child1Allocated:   map[string]resources.Quantity{"cpu": 40, "gpu": 30},
+			child2Guaranteed:  map[string]string{"cpu": "10"},
+			child2Allocated:   map[string]resources.Quantity{"cpu": 40, "gpu": 30},
+			parentPreemptable: map[string]resources.Quantity{"cpu": 20, "gpu": 20},
+			expectedChild1:    map[string]resources.Quantity{"cpu": 10, "gpu": 10},
+			expectedChild2:    map[string]resources.Quantity{"cpu": 10, "gpu": 10},
+		},
+		{
+			name:              "child meeting guaranteed is not skipped when using unguaranteed resource",
+			child1Guaranteed:  map[string]string{"cpu": "10"},
+			child1Allocated:   map[string]resources.Quantity{"cpu": 10, "gpu": 30},
+			child2Guaranteed:  map[string]string{"cpu": "10"},
+			child2Allocated:   map[string]resources.Quantity{"cpu": 40, "gpu": 30},
+			parentPreemptable: map[string]resources.Quantity{"cpu": 20, "gpu": 20},
+			expectedChild1:    map[string]resources.Quantity{"gpu": 10},
+			expectedChild2:    map[string]resources.Quantity{"cpu": 20, "gpu": 10},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			parent, err := NewConfiguredQueue(configs.QueueConfig{Name: "parent", Parent: true}, nil, false, nil)
+			assert.NilError(t, err)
+
+			child1, err := NewConfiguredQueue(configs.QueueConfig{
+				Name:      "child1",
+				Resources: configs.Resources{Guaranteed: tc.child1Guaranteed},
+			}, parent, false, nil)
+			assert.NilError(t, err)
+			child1.allocatedResource = resources.NewResourceFromMap(tc.child1Allocated)
+
+			child2, err := NewConfiguredQueue(configs.QueueConfig{
+				Name:      "child2",
+				Resources: configs.Resources{Guaranteed: tc.child2Guaranteed},
+			}, parent, false, nil)
+			assert.NilError(t, err)
+			child2.allocatedResource = resources.NewResourceFromMap(tc.child2Allocated)
+
+			parentPreemptable := resources.NewResourceFromMap(tc.parentPreemptable)
+			childQueues := make(map[*Queue]*QuotaPreemptionContext)
+			getChildQueuesPreemptableResource(parent, parentPreemptable, childQueues)
+
+			expected1 := resources.NewResourceFromMap(tc.expectedChild1)
+			expected2 := resources.NewResourceFromMap(tc.expectedChild2)
+			assert.Equal(t, len(childQueues), 2)
+			assert.Assert(t, resources.Equals(childQueues[child1].preemptableResource, expected1),
+				"child1 expected %v, got %v", expected1, childQueues[child1].preemptableResource)
+			assert.Assert(t, resources.Equals(childQueues[child2].preemptableResource, expected2),
+				"child2 expected %v, got %v", expected2, childQueues[child2].preemptableResource)
+		})
+	}
+}
+
 func TestQuotaChangeTryPreemptionForParentQueue(t *testing.T) {
 	events.Init()
 	eventSystem := events.GetEventSystem().(*events.EventSystemImpl) //nolint:errcheck


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@yunikorn.apache.org
For additional commands, e-mail: issues-help@yunikorn.apache.org