From c131c92b9f36f7fbd2b3625c3e1d0573932007b2 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 5 Sep 2023 20:21:40 +0200 Subject: [PATCH] scheduler: unit test case for concurrent event with other pod The problematic scenario was having one pod in flight, one event in the list, and then detecting a concurrent event for a second pod after the first pod is done. The new test case covers that. To make it work without assumptions about the implementation, the QueuedPodInfo returned by Pop must be the one passed to AddUnschedulableIfNotPresent after (potentially) populating UnschedulablePlugins. This is done via callback functions which bind to the same shared variable. --- .../internal/queue/scheduling_queue_test.go | 72 ++++++++++++++++--- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/pkg/scheduler/internal/queue/scheduling_queue_test.go b/pkg/scheduler/internal/queue/scheduling_queue_test.go index 2f8b064e92c..12b92bb7bdd 100644 --- a/pkg/scheduler/internal/queue/scheduling_queue_test.go +++ b/pkg/scheduler/internal/queue/scheduling_queue_test.go @@ -191,12 +191,25 @@ func Test_InFlightPods(t *testing.T) { pod := st.MakePod().Name("targetpod").UID("pod1").Obj() pod2 := st.MakePod().Name("targetpod2").UID("pod2").Obj() pod3 := st.MakePod().Name("targetpod3").UID("pod3").Obj() + var poppedPod, poppedPod2 *framework.QueuedPodInfo type action struct { // ONLY ONE of the following should be set. eventHappens *framework.ClusterEvent podPopped *v1.Pod podEnqueued *framework.QueuedPodInfo + callback func(t *testing.T, q *PriorityQueue) + } + + popPod := func(t *testing.T, q *PriorityQueue, pod *v1.Pod) *framework.QueuedPodInfo { + p, err := q.Pop() + if err != nil { + t.Fatalf("Pop failed: %v", err) + } + if p.Pod.UID != pod.UID { + t.Errorf("Unexpected popped pod: %v", p) + } + return p } tests := []struct { @@ -481,6 +494,54 @@ func Test_InFlightPods(t *testing.T) { }, }, }, + { + name: "pod is enqueued to activeQ because the failed plugin has a hint fn and it returns QueueImmediately for a concurrent event that was received while some other pod was in flight", + isSchedulingQueueHintEnabled: true, + initialPods: []*v1.Pod{pod, pod2}, + actions: []action{ + {callback: func(t *testing.T, q *PriorityQueue) { poppedPod = popPod(t, q, pod) }}, + {eventHappens: &NodeAdd}, + {callback: func(t *testing.T, q *PriorityQueue) { poppedPod2 = popPod(t, q, pod2) }}, + {eventHappens: &AssignedPodAdd}, + {callback: func(t *testing.T, q *PriorityQueue) { + logger, _ := ktesting.NewTestContext(t) + if err := q.AddUnschedulableIfNotPresent(logger, poppedPod, q.SchedulingCycle()); err != nil { + t.Errorf("Unexpected error from AddUnschedulableIfNotPresent: %v", err) + } + }}, + {callback: func(t *testing.T, q *PriorityQueue) { + logger, _ := ktesting.NewTestContext(t) + poppedPod2.UnschedulablePlugins = sets.New("fooPlugin1", "fooPlugin2", "fooPlugin3") + if err := q.AddUnschedulableIfNotPresent(logger, poppedPod2, q.SchedulingCycle()); err != nil { + t.Errorf("Unexpected error from AddUnschedulableIfNotPresent: %v", err) + } + }}, + }, + wantActiveQPodNames: []string{pod2.Name}, + wantInFlightPods: nil, + wantInFlightEvents: nil, + queueingHintMap: QueueingHintMapPerProfile{ + "": { + AssignedPodAdd: { + { + // it will be ignored because the hint fn returns QueueSkip that is weaker than queueHintReturnQueueImmediately from fooPlugin1. + PluginName: "fooPlugin3", + QueueingHintFn: queueHintReturnQueueSkip, + }, + { + // it will be ignored because the hint fn returns QueueAfterBackoff that is weaker than queueHintReturnQueueImmediately from fooPlugin1. + PluginName: "fooPlugin2", + QueueingHintFn: queueHintReturnQueueAfterBackoff, + }, + { + // The hint fn tells that this event makes a Pod scheudlable immediately. + PluginName: "fooPlugin1", + QueueingHintFn: queueHintReturnQueueImmediately, + }, + }, + }, + }, + }, } for _, test := range tests { @@ -502,18 +563,13 @@ func Test_InFlightPods(t *testing.T) { for _, action := range test.actions { switch { case action.podPopped != nil: - p, err := q.Pop() - if err != nil { - t.Fatalf("Pop failed: %v", err) - } - if p.Pod.UID != action.podPopped.UID { - t.Errorf("Unexpected popped pod: %v", p) - } - continue + popPod(t, q, action.podPopped) case action.eventHappens != nil: q.MoveAllToActiveOrBackoffQueue(logger, *action.eventHappens, nil, nil, nil) case action.podEnqueued != nil: q.AddUnschedulableIfNotPresent(logger, action.podEnqueued, q.SchedulingCycle()) + case action.callback != nil: + action.callback(t, q) } }