Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changes/unreleased/fixed-20260728-212512.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Fixed
body: |-
Allow PodGroup minMember of 0 for workloads with no gang requirement
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ spec:
MinMember defines the minimal number of members/tasks to run the pod group;
if there's not enough resources to start all tasks, the scheduler
will not start anyone.
A value of 0 means no gang requirement: all pods are scheduled elastically (e.g. scale-to-zero workloads).
format: int32
type: integer
parallelism:
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/scheduling/v2alpha2/podgroup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type PodGroupSpec struct {
// MinMember defines the minimal number of members/tasks to run the pod group;
// if there's not enough resources to start all tasks, the scheduler
// will not start anyone.
// A value of 0 means no gang requirement: all pods are scheduled elastically (e.g. scale-to-zero workloads).
MinMember int32 `json:"minMember,omitempty" protobuf:"bytes,1,opt,name=minMember"`

// Queue defines the queue to allocate resource for PodGroup; if queue does not exist,
Expand Down
16 changes: 13 additions & 3 deletions pkg/podgrouper/podgrouper/plugins/knative/knative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package knative

import (
"strconv"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -113,6 +112,17 @@ func TestGetPodGroupMetadata(t *testing.T) {
}

func TestGetPodGroupMetadata_MinScale(t *testing.T) {
for minScale, expectedMinAvailable := range map[string]int32{
"3": 3,
"0": 0, // scale-to-zero: no gang requirement
} {
t.Run("min-scale="+minScale, func(t *testing.T) {
testGetPodGroupMetadataMinScale(t, minScale, expectedMinAvailable)
})
}
}

func testGetPodGroupMetadataMinScale(t *testing.T, minScale string, expectedMinAvailable int32) {
service := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Service",
Expand Down Expand Up @@ -156,7 +166,7 @@ func TestGetPodGroupMetadata_MinScale(t *testing.T) {
Namespace: "test_namespace",
UID: "2",
Annotations: map[string]string{
"autoscaling.knative.dev/min-scale": "3",
"autoscaling.knative.dev/min-scale": minScale,
},
},
Spec: knative.RevisionSpec{},
Expand Down Expand Up @@ -197,7 +207,7 @@ func TestGetPodGroupMetadata_MinScale(t *testing.T) {
assert.Equal(t, "pg-revision-2", metadata.Name)
assert.Equal(t, constants.InferencePriorityClass, metadata.PriorityClassName)
assert.Equal(t, "test_queue", metadata.Queue)
assert.Equal(t, "3", strconv.Itoa(int(metadata.MinAvailable)))
assert.Equal(t, expectedMinAvailable, metadata.MinAvailable)
}

func TestGetPodGroupMetadataBackwardsCompatibility(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/scheduler/api/podgroup_info/job_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ func (pgi *PodGroupInfo) setSubGroups(podGroup *enginev2alpha2.PodGroup) {
}
defaultSubGroup, found := pgi.SubGroups[DefaultSubGroup]
if !found {
pgi.SubGroups[DefaultSubGroup] = NewSubGroupInfo(DefaultSubGroup, max(podGroup.Spec.MinMember, 1))
pgi.SubGroups[DefaultSubGroup] = NewSubGroupInfo(DefaultSubGroup, max(podGroup.Spec.MinMember, 0))
} else {
defaultSubGroup.SetMinAvailable(max(podGroup.Spec.MinMember, 1))
defaultSubGroup.SetMinAvailable(max(podGroup.Spec.MinMember, 0))
}
}

Expand Down
30 changes: 30 additions & 0 deletions pkg/scheduler/api/podgroup_info/job_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,36 @@ func TestAddTaskInfo(t *testing.T) {
}
}

func TestSetPodGroupMinMember(t *testing.T) {
tests := []struct {
name string
minMember int32
expectedMinAvail int32
}{
{"zero minMember is honored", 0, 0},
{"positive minMember is honored", 3, 3},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
info := NewPodGroupInfo("group-1")
info.SetPodGroup(&v2alpha2.PodGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "group-1",
Namespace: "ns-1",
},
Spec: v2alpha2.PodGroupSpec{
Queue: "queue-1",
MinMember: test.minMember,
},
})

if got := info.SubGroups[DefaultSubGroup].GetMinAvailable(); got != test.expectedMinAvail {
t.Errorf("expected minAvailable %d, got %d", test.expectedMinAvail, got)
}
})
}
}

func TestDeleteTaskInfo(t *testing.T) {
// case1
case01_uid := common_info.PodGroupID("owner1")
Expand Down
26 changes: 26 additions & 0 deletions test/e2e/suites/integrations/third_party/knative/knative_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ var _ = Describe("Knative integration", Ordered, func() {
"Expected minmember of podgroup to be 1")
})

It("knative service with scale-to-zero - minscale 0", func(ctx context.Context) {
namespace := queue.GetConnectedNamespaceToQueue(testCtx.Queues[0])
serviceName := "knative-service-" + utils.GenerateRandomK8sName(10)

knativeService := getKnativeServiceObject(serviceName, namespace, testCtx.Queues[0].Name)

knativeService.Spec.ConfigurationSpec.Template.Annotations[minScaleAnnotation] = "0"

Expect(testCtx.ControllerClient.Create(ctx, knativeService)).To(Succeed())
defer func() {
Expect(testCtx.ControllerClient.Delete(ctx, knativeService)).To(Succeed())
}()

// Knative still creates the initial pod on deployment even with min-scale 0
pods := getServicePods(ctx, testCtx, serviceName, namespace, 1)

wait.ForPodsScheduled(ctx, testCtx.ControllerClient, namespace, pods)

var podGroups v2alpha2.PodGroupList
Expect(testCtx.ControllerClient.List(ctx, &podGroups, client.InNamespace(namespace))).To(Succeed())
Expect(len(podGroups.Items)).To(Equal(1),
"Expected one podgroup for the revision")
Expect(podGroups.Items[0].Spec.MinMember).To(Equal(int32(0)),
"Expected minmember of podgroup to be 0")
})

Context("Non-Gang scheduling", func() {
BeforeAll(func(ctx context.Context) {
setKnativeGangAndWait(ctx, testCtx, pointer.Bool(false))
Expand Down
Loading