Skip to content

Commit

Permalink
fix(QOSSort): refine queue sorting logic by adding inqueue timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
googs1025 committed Jan 13, 2025
1 parent f633dd2 commit b6e4c8a
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 20 deletions.
37 changes: 29 additions & 8 deletions pkg/qos/queue_sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,43 @@ func (pl *Sort) Name() string {

// Less is the function used by the activeQ heap algorithm to sort pods.
// It sorts pods based on their priorities. When the priorities are equal, it uses
// the Pod QoS classes to break the tie.
// the Pod QoS classes to break the tie. If both the priority and QoS class are equal,
// it uses PodQueueInfo.timestamp to determine the order.
func (*Sort) Less(pInfo1, pInfo2 *framework.QueuedPodInfo) bool {
p1 := corev1helpers.PodPriority(pInfo1.Pod)
p2 := corev1helpers.PodPriority(pInfo2.Pod)
return (p1 > p2) || (p1 == p2 && compQOS(pInfo1.Pod, pInfo2.Pod))

if p1 != p2 {
return p1 > p2
}
qosResult := compQOS(pInfo1.Pod, pInfo2.Pod)
if qosResult != 0 {
return qosResult > 0
}
return pInfo1.Timestamp.Before(pInfo2.Timestamp)
}

func compQOS(p1, p2 *v1.Pod) bool {
// compQOS compares the QoS classes of two Pods and returns:
//
// 1 if p1 has a higher precedence QoS class than p2,
// -1 if p2 has a higher precedence QoS class than p1,
// 0 if both have the same QoS class.
func compQOS(p1, p2 *v1.Pod) int {
p1QOS, p2QOS := v1qos.GetPodQOS(p1), v1qos.GetPodQOS(p2)
if p1QOS == v1.PodQOSGuaranteed {
return true

// Define the precedence order of QoS classes using a map
qosOrder := map[v1.PodQOSClass]int{
v1.PodQOSBestEffort: 1,
v1.PodQOSBurstable: 2,
v1.PodQOSGuaranteed: 3,
}
if p1QOS == v1.PodQOSBurstable {
return p2QOS != v1.PodQOSGuaranteed

if qosOrder[p1QOS] > qosOrder[p2QOS] {
return 1
} else if qosOrder[p1QOS] < qosOrder[p2QOS] {
return -1
}
return p2QOS == v1.PodQOSBestEffort
return 0
}

// New initializes a new plugin and returns it.
Expand Down
43 changes: 32 additions & 11 deletions pkg/qos/queue_sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package qos

import (
"testing"
"time"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
Expand All @@ -31,6 +32,8 @@ func createPodInfo(pod *v1.Pod) *framework.PodInfo {
}

func TestSortLess(t *testing.T) {
earlierTime := time.Now()
laterTime := earlierTime.Add(time.Second)
tests := []struct {
name string
pInfo1 *framework.QueuedPodInfo
Expand Down Expand Up @@ -58,14 +61,16 @@ func TestSortLess(t *testing.T) {
want: false,
},
{
name: "p1 and p2 are both BestEfforts",
name: "p1 and p2 are both BestEfforts, but p2 is added to schedulingQ earlier than p1",
pInfo1: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p1", 0, nil, nil)),
PodInfo: createPodInfo(makePod("p1", 0, nil, nil)),
Timestamp: laterTime,
},
pInfo2: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p2", 0, nil, nil)),
PodInfo: createPodInfo(makePod("p2", 0, nil, nil)),
Timestamp: earlierTime,
},
want: true,
want: false,
},
{
name: "p1 is BestEfforts, p2 is Guaranteed",
Expand All @@ -88,14 +93,16 @@ func TestSortLess(t *testing.T) {
want: false,
},
{
name: "both p1 and p2 are Burstable",
name: "both p1 and p2 are Burstable, but p2 is added to schedulingQ earlier than p1",
pInfo1: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p1", 0, getResList("100m", "100Mi"), getResList("200m", "200Mi"))),
PodInfo: createPodInfo(makePod("p1", 0, getResList("100m", "100Mi"), getResList("200m", "200Mi"))),
Timestamp: laterTime,
},
pInfo2: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p2", 0, getResList("100m", "100Mi"), getResList("200m", "200Mi"))),
PodInfo: createPodInfo(makePod("p2", 0, getResList("100m", "100Mi"), getResList("200m", "200Mi"))),
Timestamp: earlierTime,
},
want: true,
want: false,
},
{
name: "p1 is Guaranteed, p2 is Burstable",
Expand All @@ -108,12 +115,26 @@ func TestSortLess(t *testing.T) {
want: true,
},
{
name: "both p1 and p2 are Guaranteed",
name: "both p1 and p2 are Guaranteed, but p1 is added to schedulingQ earlier than p2",
pInfo1: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p1", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
PodInfo: createPodInfo(makePod("p1", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
Timestamp: earlierTime,
},
pInfo2: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p2", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
PodInfo: createPodInfo(makePod("p2", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
Timestamp: laterTime,
},
want: true,
},
{
name: "both p1 and p2 are Guaranteed, but p1 is added to schedulingQ earlier than p2",
pInfo1: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p1", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
Timestamp: earlierTime,
},
pInfo2: &framework.QueuedPodInfo{
PodInfo: createPodInfo(makePod("p2", 0, getResList("100m", "100Mi"), getResList("100m", "100Mi"))),
Timestamp: laterTime,
},
want: true,
},
Expand Down
10 changes: 9 additions & 1 deletion test/integration/qos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package integration
import (
"context"
"fmt"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -127,6 +128,12 @@ func TestQOSPlugin(t *testing.T) {
err = wait.PollUntilContextTimeout(testCtx.Ctx, time.Millisecond*200, wait.ForeverTestTimeout, false, func(ctx context.Context) (bool, error) {
pendingPods, _ := testCtx.Scheduler.SchedulingQueue.PendingPods()
if len(pendingPods) == len(pods) {
// Collect Pod names into a slice.
podNames := make([]string, len(pendingPods))
for i, podInfo := range pendingPods {
podNames[i] = podInfo.Name
}
t.Logf("All Pods are in the pending queue: %v", strings.Join(podNames, ", "))
return true, nil
}
return false, nil
Expand All @@ -137,9 +144,10 @@ func TestQOSPlugin(t *testing.T) {

// Expect Pods are popped in the QoS class order.
logger := klog.FromContext(testCtx.Ctx)
expectedOrder := []string{"guaranteed", "burstable", "bestefforts"}
for i := len(podNames) - 1; i >= 0; i-- {
podInfo, _ := testCtx.Scheduler.NextPod(logger)
if podInfo.Pod.Name != podNames[i] {
if podInfo.Pod.Name != expectedOrder[i] {
t.Errorf("Expect Pod %q, but got %q", podNames[i], podInfo.Pod.Name)
} else {
t.Logf("Pod %q is popped out as expected.", podInfo.Pod.Name)
Expand Down

0 comments on commit b6e4c8a

Please sign in to comment.