k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/exempt_borrowing_test.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package flowcontrol
    18  
    19  import (
    20  	"testing"
    21  	"time"
    22  
    23  	fcboot "k8s.io/apiserver/pkg/apis/flowcontrol/bootstrap"
    24  	fqs "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/queueset"
    25  	testeventclock "k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/testing/eventclock"
    26  	"k8s.io/apiserver/pkg/util/flowcontrol/metrics"
    27  	"k8s.io/client-go/informers"
    28  	clientsetfake "k8s.io/client-go/kubernetes/fake"
    29  
    30  	flowcontrol "k8s.io/api/flowcontrol/v1"
    31  )
    32  
    33  func TestUpdateBorrowing(t *testing.T) {
    34  	startTime := time.Now()
    35  	clk, _ := testeventclock.NewFake(startTime, 0, nil)
    36  	plcExempt := fcboot.MandatoryPriorityLevelConfigurationExempt
    37  	plcHigh := fcboot.SuggestedPriorityLevelConfigurationWorkloadHigh
    38  	plcMid := fcboot.SuggestedPriorityLevelConfigurationWorkloadLow
    39  	plcLow := fcboot.MandatoryPriorityLevelConfigurationCatchAll
    40  	plcs := []*flowcontrol.PriorityLevelConfiguration{plcHigh, plcExempt, plcMid, plcLow}
    41  	fses := []*flowcontrol.FlowSchema{}
    42  	k8sClient := clientsetfake.NewSimpleClientset(plcLow, plcExempt, plcHigh, plcMid)
    43  	informerFactory := informers.NewSharedInformerFactory(k8sClient, 0)
    44  	flowcontrolClient := k8sClient.FlowcontrolV1()
    45  	serverCL := int(*plcHigh.Spec.Limited.NominalConcurrencyShares+
    46  		*plcMid.Spec.Limited.NominalConcurrencyShares+
    47  		*plcLow.Spec.Limited.NominalConcurrencyShares) * 6
    48  	config := TestableConfig{
    49  		Name:                   "test",
    50  		Clock:                  clk,
    51  		AsFieldManager:         "testfm",
    52  		FoundToDangling:        func(found bool) bool { return !found },
    53  		InformerFactory:        informerFactory,
    54  		FlowcontrolClient:      flowcontrolClient,
    55  		ServerConcurrencyLimit: serverCL,
    56  		ReqsGaugeVec:           metrics.PriorityLevelConcurrencyGaugeVec,
    57  		ExecSeatsGaugeVec:      metrics.PriorityLevelExecutionSeatsGaugeVec,
    58  		QueueSetFactory:        fqs.NewQueueSetFactory(clk),
    59  	}
    60  	ctlr := newTestableController(config)
    61  	_ = ctlr.lockAndDigestConfigObjects(plcs, fses)
    62  	if ctlr.nominalCLSum != serverCL {
    63  		t.Fatalf("Unexpected rounding: nominalCLSum=%d", ctlr.nominalCLSum)
    64  	}
    65  	stateExempt := ctlr.priorityLevelStates[plcExempt.Name]
    66  	stateHigh := ctlr.priorityLevelStates[plcHigh.Name]
    67  	stateMid := ctlr.priorityLevelStates[plcMid.Name]
    68  	stateLow := ctlr.priorityLevelStates[plcLow.Name]
    69  
    70  	// Scenario 1: everybody wants more than ServerConcurrencyLimit.
    71  	// Test the case of exempt borrowing so much that less than minCL
    72  	// is available to each non-exempt.
    73  	stateExempt.seatDemandIntegrator.Set(float64(serverCL + 100))
    74  	stateHigh.seatDemandIntegrator.Set(float64(serverCL + 100))
    75  	stateMid.seatDemandIntegrator.Set(float64(serverCL + 100))
    76  	stateLow.seatDemandIntegrator.Set(float64(serverCL + 100))
    77  	clk.SetTime(startTime.Add(borrowingAdjustmentPeriod))
    78  	ctlr.updateBorrowing()
    79  	if expected, actual := serverCL+100, stateExempt.currentCL; expected != actual {
    80  		t.Errorf("Scenario 1: expected %d, got %d for exempt", expected, actual)
    81  	} else {
    82  		t.Logf("Scenario 1: expected and got %d for exempt", expected)
    83  	}
    84  	if expected, actual := stateHigh.minCL, stateHigh.currentCL; expected != actual {
    85  		t.Errorf("Scenario 1: expected %d, got %d for hi", expected, actual)
    86  	} else {
    87  		t.Logf("Scenario 1: expected and got %d for hi", expected)
    88  	}
    89  	if expected, actual := stateMid.minCL, stateMid.currentCL; expected != actual {
    90  		t.Errorf("Scenario 1: expected %d, got %d for mid", expected, actual)
    91  	} else {
    92  		t.Logf("Scenario 1: expected and got %d for mid", expected)
    93  	}
    94  	if expected, actual := stateLow.minCL, stateLow.currentCL; expected != actual {
    95  		t.Errorf("Scenario 1: expected %d, got %d for lo", expected, actual)
    96  	} else {
    97  		t.Logf("Scenario 1: expected and got %d for lo", expected)
    98  	}
    99  
   100  	// Scenario 2: non-exempt want more than serverCL but get halfway between minCL and minCurrentCL.
   101  	expectedHigh := (stateHigh.nominalCL + stateHigh.minCL) / 2
   102  	expectedMid := (stateMid.nominalCL + stateMid.minCL) / 2
   103  	expectedLow := (stateLow.nominalCL + stateLow.minCL) / 2
   104  	expectedExempt := serverCL - (expectedHigh + expectedMid + expectedLow)
   105  	stateExempt.seatDemandIntegrator.Set(float64(expectedExempt))
   106  	clk.SetTime(startTime.Add(2 * borrowingAdjustmentPeriod))
   107  	ctlr.updateBorrowing()
   108  	clk.SetTime(startTime.Add(3 * borrowingAdjustmentPeriod))
   109  	ctlr.updateBorrowing()
   110  	if expected, actual := expectedExempt, stateExempt.currentCL; expected != actual {
   111  		t.Errorf("Scenario 2: expected %d, got %d for exempt", expected, actual)
   112  	} else {
   113  		t.Logf("Scenario 2: expected and got %d for exempt", expected)
   114  	}
   115  	if expected, actual := expectedHigh, stateHigh.currentCL; expected != actual {
   116  		t.Errorf("Scenario 2: expected %d, got %d for hi", expected, actual)
   117  	} else {
   118  		t.Logf("Scenario 2: expected and got %d for hi", expected)
   119  	}
   120  	if expected, actual := expectedMid, stateMid.currentCL; expected != actual {
   121  		t.Errorf("Scenario 2: expected %d, got %d for mid", expected, actual)
   122  	} else {
   123  		t.Logf("Scenario 2: expected and got %d for mid", expected)
   124  	}
   125  	if expected, actual := expectedLow, stateLow.currentCL; expected != actual {
   126  		t.Errorf("Scenario 2: expected %d, got %d for lo", expected, actual)
   127  	} else {
   128  		t.Logf("Scenario 2: expected and got %d for lo", expected)
   129  	}
   130  
   131  	// Scenario 3: only mid is willing to lend, and exempt borrows all of that.
   132  	// Test the case of regular borrowing.
   133  	expectedHigh = stateHigh.nominalCL
   134  	expectedMid = stateMid.minCL
   135  	expectedLow = stateLow.nominalCL
   136  	expectedExempt = serverCL - (expectedHigh + expectedMid + expectedLow)
   137  	stateExempt.seatDemandIntegrator.Set(float64(expectedExempt))
   138  	stateMid.seatDemandIntegrator.Set(float64(1))
   139  	clk.SetTime(startTime.Add(4 * borrowingAdjustmentPeriod))
   140  	ctlr.updateBorrowing()
   141  	clk.SetTime(startTime.Add(5 * borrowingAdjustmentPeriod))
   142  	ctlr.updateBorrowing()
   143  	if expected, actual := expectedExempt, stateExempt.currentCL; expected != actual {
   144  		t.Errorf("Scenario 3: expected %d, got %d for exempt", expected, actual)
   145  	} else {
   146  		t.Logf("Scenario 3: expected and got %d for exempt", expected)
   147  	}
   148  	if expected, actual := expectedHigh, stateHigh.currentCL; expected != actual {
   149  		t.Errorf("Scenario 3: expected %d, got %d for hi", expected, actual)
   150  	} else {
   151  		t.Logf("Scenario 3: expected and got %d for hi", expected)
   152  	}
   153  	if expected, actual := expectedMid, stateMid.currentCL; expected != actual {
   154  		t.Errorf("Scenario 3: expected %d, got %d for mid", expected, actual)
   155  	} else {
   156  		t.Logf("Scenario 3: expected and got %d for mid", expected)
   157  	}
   158  	if expected, actual := expectedLow, stateLow.currentCL; expected != actual {
   159  		t.Errorf("Scenario 3: expected %d, got %d for lo", expected, actual)
   160  	} else {
   161  		t.Logf("Scenario 3: expected and got %d for lo", expected)
   162  	}
   163  
   164  }