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

mani 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 e32624f4 [YUNIKORN-3240] Ensure leaf queues doesn't violate parent 
queue's guaranteed settings (#1074)
e32624f4 is described below

commit e32624f4856aa876e40313c1b614d324811e0252
Author: mani <[email protected]>
AuthorDate: Mon Mar 30 16:24:27 2026 +0530

    [YUNIKORN-3240] Ensure leaf queues doesn't violate parent queue's 
guaranteed settings (#1074)
    
    Improved Preemptable resources calculation to consider parent's and/or 
ancestor preemptable resources based on guaranteed as well to ensure 
preemptable resources is set correctly upfront for the queue being worked upon. 
Since preemptable resource has been defined upfront based on the above method, 
checks to ensure usage doesn't go below guaranteed at any level being carried 
out at much later stage are avoided
    
    Closes: #1074
    
    Signed-off-by: mani <[email protected]>
---
 pkg/scheduler/objects/queue.go                |  19 ++++
 pkg/scheduler/objects/quota_preemptor.go      |  65 +++++++-----
 pkg/scheduler/objects/quota_preemptor_test.go | 143 +++++++++++++++-----------
 3 files changed, 142 insertions(+), 85 deletions(-)

diff --git a/pkg/scheduler/objects/queue.go b/pkg/scheduler/objects/queue.go
index 45ce26db..37792fb2 100644
--- a/pkg/scheduler/objects/queue.go
+++ b/pkg/scheduler/objects/queue.go
@@ -816,6 +816,25 @@ func (sq *Queue) GetActualGuaranteedResource() 
*resources.Resource {
        return resources.ComponentWiseMin(sq.guaranteedResource, 
parentGuaranteed)
 }
 
+// GetPreemptableResource returns the actual (including parent) preemptable 
resources for the queue.
+// Preemptable resource is nothing but the resources used above the guaranteed 
quota.
+func (sq *Queue) GetPreemptableResource() *resources.Resource {
+       if sq == nil {
+               return resources.NewResource()
+       }
+       parentPreemptable := sq.parent.GetPreemptableResource()
+       sq.RLock()
+       defer sq.RUnlock()
+
+       var preemptable *resources.Resource
+       if sq.guaranteedResource == nil {
+               preemptable = sq.allocatedResource.Clone()
+       } else {
+               preemptable = resources.SubOnlyExisting(sq.guaranteedResource, 
sq.allocatedResource.Clone())
+       }
+       return resources.ComponentWiseMin(preemptable, parentPreemptable)
+}
+
 func (sq *Queue) GetPreemptionDelay() time.Duration {
        sq.RLock()
        defer sq.RUnlock()
diff --git a/pkg/scheduler/objects/quota_preemptor.go 
b/pkg/scheduler/objects/quota_preemptor.go
index 108c2ce4..1eedf827 100644
--- a/pkg/scheduler/objects/quota_preemptor.go
+++ b/pkg/scheduler/objects/quota_preemptor.go
@@ -39,7 +39,7 @@ type QuotaPreemptionContext struct {
 }
 
 func NewQuotaPreemptor(queue *Queue) *QuotaPreemptionContext {
-       preemptor := &QuotaPreemptionContext{
+       return &QuotaPreemptionContext{
                queue:               queue,
                maxResource:         queue.cloneMaxResource(),
                guaranteedResource:  queue.GetGuaranteedResource(),
@@ -48,8 +48,6 @@ func NewQuotaPreemptor(queue *Queue) *QuotaPreemptionContext {
                preemptableResource: nil,
                allocations:         make([]*Allocation, 0),
        }
-
-       return preemptor
 }
 
 func (qpc *QuotaPreemptionContext) tryPreemption() {
@@ -60,7 +58,7 @@ func (qpc *QuotaPreemptionContext) tryPreemption() {
                qpc.tryPreemptionInternal()
                return
        }
-       leafQueues := make(map[*Queue]*resources.Resource)
+       leafQueues := make(map[*Queue]*QuotaPreemptionContext)
        getChildQueuesPreemptableResource(qpc.queue, qpc.preemptableResource, 
leafQueues)
 
        log.Log(log.SchedQuotaChangePreemption).Info("Triggering quota change 
preemption for parent queue",
@@ -69,10 +67,8 @@ func (qpc *QuotaPreemptionContext) tryPreemption() {
                zap.Int("no. of leaf queues with potential victims", 
len(leafQueues)),
        )
 
-       for leaf, leafPreemptableResource := range leafQueues {
-               leafQueueQCPC := NewQuotaPreemptor(leaf)
-               leafQueueQCPC.preemptableResource = leafPreemptableResource
-               leafQueueQCPC.tryPreemptionInternal()
+       for _, leafContext := range leafQueues {
+               leafContext.tryPreemptionInternal()
        }
 }
 
@@ -106,7 +102,7 @@ func (qpc *QuotaPreemptionContext) tryPreemptionInternal() {
 // In order to achieve a fair distribution of parent's preemptable resource 
among its children,
 // Higher (relatively) the usage is, higher the preemptable resource would be 
resulted in.
 // Usage above guaranteed (if set) is only considered to derive the 
preemptable resource.
-func getChildQueuesPreemptableResource(queue *Queue, parentPreemptableResource 
*resources.Resource, childQueues map[*Queue]*resources.Resource) {
+func getChildQueuesPreemptableResource(queue *Queue, parentPreemptableResource 
*resources.Resource, childQueues map[*Queue]*QuotaPreemptionContext) {
        children := queue.GetCopyOfChildren()
        if len(children) == 0 {
                return
@@ -154,12 +150,14 @@ func getChildQueuesPreemptableResource(queue *Queue, 
parentPreemptableResource *
                per := resources.GetSharesTypeWise(pRes, 
totalPreemptableResource)
                for k := range pRes.Resources {
                        if _, ok := parentPreemptableResource.Resources[k]; ok {
-                               value := math.RoundToEven(per[k] * 
float64(parentPreemptableResource.Resources[k]))
+                               value := int(math.Floor(per[k] * 
float64(parentPreemptableResource.Resources[k])))
                                childPreemptableResource.Resources[k] = 
resources.Quantity(value)
                        }
                }
                if c.IsLeafQueue() {
-                       childQueues[c] = childPreemptableResource
+                       leafContext := NewQuotaPreemptor(c)
+                       leafContext.preemptableResource = 
childPreemptableResource
+                       childQueues[c] = leafContext
                } else {
                        getChildQueuesPreemptableResource(c, 
childPreemptableResource, childQueues)
                }
@@ -169,19 +167,41 @@ func getChildQueuesPreemptableResource(queue *Queue, 
parentPreemptableResource *
 // setPreemptableResources Get the preemptable resources for the queue
 // Subtracting the usage from the max resource gives the preemptable resources.
 // It could contain both positive and negative values. Only negative values 
are preemptable.
+// In addition, derive the minimum preemptable resources over parents based on 
guaranteed resources recursively
+// and do the minimum of this with earlier preemptable resources to derive the 
net preemptable resources
 func (qpc *QuotaPreemptionContext) setPreemptableResources() {
        used := resources.SubOnlyExisting(qpc.allocatedResource, 
qpc.preemptingResource)
        if qpc.maxResource.IsEmpty() || used.IsEmpty() {
                return
        }
        actual := resources.SubOnlyExisting(qpc.maxResource, used)
-       preemptableResource := resources.NewResource()
+
+       netPreemptableResource := resources.NewResource()
        // Keep only the resource type which needs to be preempted
        for k, v := range actual.Resources {
                if v < 0 {
-                       preemptableResource.Resources[k] = 
resources.Quantity(math.Abs(float64(v)))
+                       netPreemptableResource.Resources[k] = 
resources.Quantity(math.Abs(float64(v)))
+               }
+       }
+
+       parentPreemptableResource := qpc.queue.GetPreemptableResource()
+
+       netParentPreemptableResource := resources.NewResource()
+       // Keep only the resource type which needs to be preempted
+       for k, v := range parentPreemptableResource.Resources {
+               if v < 0 {
+                       netParentPreemptableResource.Resources[k] = 
resources.Quantity(math.Abs(float64(v)))
                }
        }
+
+       // Min (current queue preemptable resources, Min(preemptable resources 
across parents based on guaranteed resources) )
+       preemptableResource := 
resources.ComponentWiseMinOnlyExisting(netPreemptableResource, 
netParentPreemptableResource)
+
+       log.Log(log.SchedQuotaChangePreemption).Debug("Preemptable resources 
calculated",
+               zap.String("queue", qpc.queue.GetQueuePath()),
+               zap.String("actual preemptable resources", 
netPreemptableResource.String()),
+               zap.String("preemptable resources based on guaranteed", 
netParentPreemptableResource.String()),
+               zap.String("net preemptable resources", 
preemptableResource.String()))
        if preemptableResource.IsEmpty() {
                return
        }
@@ -269,22 +289,13 @@ func (qpc *QuotaPreemptionContext) preemptVictims() {
                        continue
                }
 
-               // Keep collecting the victims until preemptable resource 
reaches and subtract the usage
-               if 
qpc.preemptableResource.StrictlyGreaterThanOnlyExisting(victimsTotalResource) {
+               // Keep collecting the victims until preemptable resource 
reaches
+               victimsTotalResource.AddTo(victimAlloc)
+               if 
qpc.preemptableResource.StrictlyGreaterThanOrEqualsOnlyExisting(victimsTotalResource)
 {
                        apps[application] = append(apps[application], victim)
-                       qpc.allocatedResource.SubFrom(victimAlloc)
-               }
-
-               // Has usage gone below the guaranteed resources?
-               // If yes, revert the recently added victim steps completely 
and try next victim.
-               if !qpc.guaranteedResource.IsEmpty() && 
qpc.guaranteedResource.StrictlyGreaterThanOnlyExisting(qpc.allocatedResource) {
-                       victims := apps[application]
-                       exceptRecentlyAddedVictims := victims[:len(victims)-1]
-                       apps[application] = exceptRecentlyAddedVictims
-                       qpc.allocatedResource.AddTo(victimAlloc)
-                       victimsTotalResource.SubFrom(victimAlloc)
                } else {
-                       victimsTotalResource.AddTo(victimAlloc)
+                       // reverse to try next victim
+                       victimsTotalResource.SubFrom(victimAlloc)
                }
        }
 
diff --git a/pkg/scheduler/objects/quota_preemptor_test.go 
b/pkg/scheduler/objects/quota_preemptor_test.go
index 04b923ec..d3dff3ed 100644
--- a/pkg/scheduler/objects/quota_preemptor_test.go
+++ b/pkg/scheduler/objects/quota_preemptor_test.go
@@ -30,33 +30,49 @@ import (
 )
 
 func TestQuotaChangeGetPreemptableResource(t *testing.T) {
+       parent, err := NewConfiguredQueue(configs.QueueConfig{
+               Name:   "parent",
+               Parent: true,
+       }, nil, false, nil)
+       assert.NilError(t, err)
+
        leaf, err := NewConfiguredQueue(configs.QueueConfig{
                Name: "leaf",
-       }, nil, false, nil)
+       }, parent, false, nil)
        assert.NilError(t, err)
 
        testCases := []struct {
-               name         string
-               queue        *Queue
-               maxResource  *resources.Resource
-               usedResource *resources.Resource
-               preemptable  *resources.Resource
+               name             string
+               queue            *Queue
+               parentGuaranteed *resources.Resource
+               maxResource      *resources.Resource
+               usedResource     *resources.Resource
+               preemptable      *resources.Resource
        }{
-               {"nil max and nil used", leaf, nil, nil, nil},
-               {"nil max", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
nil},
-               {"nil used", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
nil, nil},
-               {"used below max", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500}), 
nil},
-               {"used above max", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
-               {"used above max, below max, equals max in specific res type 
and also with extra res types", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000, 
"cpu": 10, "gpu": 10}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500, 
"cpu": 10, "gpu": 9}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
-               {"used res type but max undefined", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"cpu": 150}), nil},
+               {"nil max and nil used", leaf, nil, nil, nil, nil},
+               {"nil max", leaf, nil, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
nil},
+               {"nil used", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
nil, nil},
+               {"used below max", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500}), 
nil},
+               {"used above max", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
+               {"used above max, below max, equals max in specific res type 
and also with extra res types", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000, 
"cpu": 10, "gpu": 10}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500, 
"cpu": 10, "gpu": 9}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
+               {"used res type but max undefined", leaf, nil, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"cpu": 150}), nil},
+               {"parent guaranteed set, lower than leaf's preemptable 
resources", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1200}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 300})},
+               {"parent guaranteed set, lower than leaf's preemptable 
resources - extra res types ", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1200}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500, 
"cpu": 10, "gpu": 9}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 300})},
+               {"parent guaranteed set, higher than leaf's preemptable 
resources", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 900}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
+               {"parent guaranteed set, higher than leaf's preemptable 
resources - extra res types", leaf, 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 900}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500, 
"cpu": 10, "gpu": 9}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
        }
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
+                       tc.queue.parent.guaranteedResource = tc.parentGuaranteed
                        tc.queue.maxResource = tc.maxResource
-                       tc.queue.allocatedResource = tc.usedResource
+                       tc.queue.IncAllocatedResource(tc.usedResource)
                        preemptor := NewQuotaPreemptor(tc.queue)
                        preemptor.setPreemptableResources()
                        assert.Equal(t, 
resources.Equals(preemptor.preemptableResource, tc.preemptable), true)
+
+                       // reset
+                       err = tc.queue.DecAllocatedResource(tc.usedResource)
+                       assert.NilError(t, err)
                })
        }
 }
@@ -172,10 +188,10 @@ func TestQuotaChangeTryPreemption(t *testing.T) {
                {"no victims available", leaf, oldMax, newMax, nil, 
preemptable, []*Allocation{}, 0, 0},
                {"suitable victims available", leaf, oldMax, newMax, nil, 
preemptable, suitableVictims, 2, 1},
                {"skip over sized victims", leaf, oldMax, newMax, nil, 
preemptable, oversizedVictims, 2, 1},
-               {"guaranteed not set, victims total resource might go over the 
requirement a bit", leaf, oldMax, newMax, nil, preemptable, overflowVictims, 3, 
2},
-               {"guaranteed set but lower than max, victims total resource 
might go over the requirement a bit", leaf, oldMax, newMax, lowerGuaranteed, 
preemptable, overflowVictims, 3, 2},
-               {"best effort - guaranteed set and equals max, victims total 
resource might fall below the requirement a bit", leaf, oldMax, newMax, 
guaranteed, preemptable, shortfallVictims, 4, 2},
-               {"best effort - guaranteed set, max not set earlier but now, 
victims total resource might fall below the requirement a bit", leaf, nil, 
newMax, guaranteed, preemptable, shortfallVictims, 4, 2},
+               {"guaranteed not set", leaf, oldMax, newMax, nil, preemptable, 
overflowVictims, 3, 1},
+               {"guaranteed set but lower than max", leaf, oldMax, newMax, 
lowerGuaranteed, preemptable, overflowVictims, 3, 1},
+               {"best effort - guaranteed set and equals max", leaf, oldMax, 
newMax, guaranteed, preemptable, shortfallVictims, 4, 2},
+               {"best effort - guaranteed set, max not set earlier but now", 
leaf, nil, newMax, guaranteed, preemptable, shortfallVictims, 4, 2},
        }
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
@@ -283,7 +299,7 @@ func TestQuotaChangeTryPreemptionWithDifferentResTypes(t 
*testing.T) {
                },
                {"overflow victims available with extra resource types other 
than defined in guaranteed and vice versa", leaf, oldMax, newMax, 
lowerGuaranteedWithNewResTypes,
                        []test{
-                               {overflowVictims, 3, 2},
+                               {overflowVictims, 3, 1},
                        },
                },
        }
@@ -356,25 +372,25 @@ func TestQuotaChangeGetChildQueuesPreemptableResource(t 
*testing.T) {
        }{
                {"normal preemptable resources  - normal distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 100}),
                        
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 11}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 42}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 25})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 41}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 24})},
 
                {"twice the preemptable resources - twice the normal 
distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 200}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 45}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 83}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 50})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 44}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}),
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 83}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 49})},
 
                {"half the preemptable resources - half the normal 
distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 50}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 11}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 6}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 21}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 5}),
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 20}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12})},
        }
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
-                       childQueues := make(map[*Queue]*resources.Resource)
+                       childQueues := make(map[*Queue]*QuotaPreemptionContext)
                        getChildQueuesPreemptableResource(tc.parentQueue, 
tc.parentPreemptable, childQueues)
                        assert.Equal(t, len(childQueues), 4)
-                       assert.Equal(t, resources.Equals(childQueues[leaf111], 
tc.leaf111PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf12], 
tc.leaf12PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf211], 
tc.leaf211PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf22], 
tc.leaf22PRes), true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf111].preemptableResource, tc.leaf111PRes), 
true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf12].preemptableResource, tc.leaf12PRes), true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf211].preemptableResource, tc.leaf211PRes), 
true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf22].preemptableResource, tc.leaf22PRes), true)
                        if _, ok := childQueues[parent.GetChildQueue("leaf3")]; 
ok {
                                t.Fatal("leaf 3 queue exists")
                        }
@@ -427,25 +443,25 @@ func 
TestQuotaChangeGetChildQueuesPreemptableResourceWithDifferentResTypes(t *te
        }{
                {"normal preemptable resources  - normal distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 100, 
"fourth": 100}),
                        
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 11}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 42}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 25})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 41}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 24})},
 
                {"twice the preemptable resources - twice the normal 
distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 200, 
"fourth": 100}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 45}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 83}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 50})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 44}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 22}),
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 83}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 49})},
 
                {"half the preemptable resources - half the normal 
distribution", parent, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 50, 
"fourth": 100}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 11}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 6}),
-                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 21}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12})},
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 5}),
+                       
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 20}), 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12})},
        }
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
-                       childQueues := make(map[*Queue]*resources.Resource)
+                       childQueues := make(map[*Queue]*QuotaPreemptionContext)
                        getChildQueuesPreemptableResource(tc.parentQueue, 
tc.parentPreemptable, childQueues)
                        assert.Equal(t, len(childQueues), 4)
-                       assert.Equal(t, resources.Equals(childQueues[leaf111], 
tc.leaf111PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf12], 
tc.leaf12PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf211], 
tc.leaf211PRes), true)
-                       assert.Equal(t, resources.Equals(childQueues[leaf22], 
tc.leaf22PRes), true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf111].preemptableResource, tc.leaf111PRes), 
true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf12].preemptableResource, tc.leaf12PRes), true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf211].preemptableResource, tc.leaf211PRes), 
true)
+                       assert.Equal(t, 
resources.Equals(childQueues[leaf22].preemptableResource, tc.leaf22PRes), true)
                        if _, ok := childQueues[parent.GetChildQueue("leaf3")]; 
ok {
                                t.Fatal("leaf 3 queue exists")
                        }
@@ -461,7 +477,7 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
                NodeID:     "node",
                Attributes: nil,
                SchedulableResource: &si.Resource{
-                       Resources: map[string]*si.Quantity{"first": {Value: 
200}},
+                       Resources: map[string]*si.Quantity{"first": {Value: 
500}},
                },
        })
 
@@ -473,9 +489,15 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
        parent1, err := NewConfiguredQueue(parentConfig1, nil, false, nil)
        assert.NilError(t, err)
 
+       parentConfig2 := configs.QueueConfig{Name: "parent2", Parent: true}
+       parent2, err := NewConfiguredQueue(parentConfig2, nil, false, nil)
+       assert.NilError(t, err)
+
        leaf111G, leaf12G, leaf211G, leaf22G, leaf4G := createQueueSetups(t, 
parent, configs.Resources{Guaranteed: map[string]string{"first": "10"}}, 
configs.Resources{})
        leaf111, leaf12, leaf211, leaf22, leaf4 := createQueueSetups(t, 
parent1, configs.Resources{}, configs.Resources{})
 
+       leaf111WithParentG, leaf12WithParentG, leaf211WithParentG, 
leaf22WithParentG, leaf4WithParentG := createQueueSetups(t, parent2, 
configs.Resources{}, configs.Resources{})
+
        suitableVictims := make([]*Allocation, 0)
        suitableVictims = append(suitableVictims, createVictim(t, "ask1", node, 
5, resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10})))
        suitableVictims = append(suitableVictims, createVictim(t, "ask2", node, 
4, resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10})))
@@ -505,7 +527,6 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
        suitableVictims3 = append(suitableVictims3, createVictim(t, "ask11", 
node, 5, resources.NewResourceFromMap(map[string]resources.Quantity{"first": 
10})))
        suitableVictims3 = append(suitableVictims3, createVictim(t, "ask12", 
node, 4, resources.NewResourceFromMap(map[string]resources.Quantity{"first": 
10})))
        suitableVictims3 = append(suitableVictims3, createVictim(t, "ask13", 
node, 4, resources.NewResourceFromMap(map[string]resources.Quantity{"first": 
10})))
-
        leafGVictims[leaf22G] = suitableVictims3
        leafVictims[leaf22] = suitableVictims3
 
@@ -513,20 +534,23 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
        leafGVictims[leaf4G] = []*Allocation{v}
        leafVictims[leaf4] = []*Allocation{v}
 
-       expectedGVictims := make(map[*Queue]int)
-       expectedGVictims[leaf111G] = 2
-       expectedGVictims[leaf12G] = 1
-       expectedGVictims[leaf211G] = 5
-       expectedGVictims[leaf22G] = 3
-
-       expectedVictims := make(map[*Queue]int)
-       expectedVictims[leaf111] = 3
-       expectedVictims[leaf12] = 2
-       expectedVictims[leaf211] = 5
-       expectedVictims[leaf22] = 3
+       leafVictimsWithParentG := make(map[*Queue][]*Allocation)
+       leafVictimsWithParentG[leaf111WithParentG] = 
[]*Allocation{createVictim(t, "ask15", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 13})),
+               createVictim(t, "ask16", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12}))}
+       leafVictimsWithParentG[leaf12WithParentG] = 
[]*Allocation{createVictim(t, "ask17", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 13})),
+               createVictim(t, "ask18", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12}))}
+       leafVictimsWithParentG[leaf211WithParentG] = 
[]*Allocation{createVictim(t, "ask19", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 13})),
+               createVictim(t, "ask20", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12}))}
+       leafVictimsWithParentG[leaf22WithParentG] = 
[]*Allocation{createVictim(t, "ask21", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 13})),
+               createVictim(t, "ask22", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12}))}
+       leafVictimsWithParentG[leaf4WithParentG] = 
[]*Allocation{createVictim(t, "ask23", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 13})),
+               createVictim(t, "ask24", node, 4, 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 12}))}
 
        oldMax := 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 130})
        newMax := 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 10})
+       oldMax1 := 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 150})
+       newMax1 := 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 20})
+       newMax2 := 
resources.NewResourceFromMap(map[string]resources.Quantity{"first": 5})
 
        testCases := []struct {
                name            string
@@ -534,10 +558,12 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
                oldMax          *resources.Resource
                newMax          *resources.Resource
                victims         map[*Queue][]*Allocation
-               expectedVictims map[*Queue]int
+               expectedVictims int
        }{
-               {"Guaranteed set on one side of queue hierarchy - suitable 
victims available", parent, oldMax, newMax, leafGVictims, expectedGVictims},
-               {"Guaranteed set not set on any queue - suitable victims 
available", parent1, oldMax, newMax, leafVictims, expectedVictims},
+               {"Guaranteed set on one side of queue hierarchy - suitable 
victims available", parent, oldMax, newMax, leafGVictims, 11},
+               {"Guaranteed set not set on any queue - suitable victims 
available", parent1, oldMax, newMax, leafVictims, 9},
+               {"Guaranteed set only on parent queue but not on any child 
queues underneath - suitable victims available", parent2, oldMax1, newMax1, 
leafVictimsWithParentG, 5},
+               {"Guaranteed set only on parent queue but not on any child 
queues underneath - suitable victims available", parent2, oldMax1, newMax2, 
leafVictimsWithParentG, 5},
        }
        for _, tc := range testCases {
                t.Run(tc.name, func(t *testing.T) {
@@ -546,17 +572,18 @@ func TestQuotaChangeTryPreemptionForParentQueue(t 
*testing.T) {
                                assignAllocationsToQueue(v, q)
                        }
                        tc.queue.maxResource = tc.newMax
+                       tc.queue.guaranteedResource = tc.newMax
                        preemptor := NewQuotaPreemptor(tc.queue)
                        preemptor.tryPreemption()
-                       for q, asks := range tc.victims {
-                               var victimsCount int
+                       victimsCount := 0
+                       for _, asks := range tc.victims {
                                for _, a := range asks {
                                        if a.IsPreempted() {
                                                victimsCount++
                                        }
                                }
-                               assert.Equal(t, victimsCount, 
tc.expectedVictims[q])
                        }
+                       assert.Equal(t, victimsCount, tc.expectedVictims)
                        for _, v := range tc.victims {
                                removeAllocationAsks(node, v)
                        }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to