mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
Merge pull request #17674 from gmarek/fix-test
Auto commit by PR queue bot
This commit is contained in:
commit
fcdf32f336
@ -57,22 +57,39 @@ func pathWithPrefix(prefix, resource, namespace, name string) string {
|
|||||||
return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
|
return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that MaxInFlightLimit works, i.e.
|
||||||
|
// - "long" requests such as proxy or watch, identified by regexp are not accounted despite
|
||||||
|
// hanging for the long time,
|
||||||
|
// - "short" requests are correctly accounted, i.e. there can be only size of channel passed to the
|
||||||
|
// constructor in flight at any given moment,
|
||||||
|
// - subsequent "short" requests are rejected instantly with apropriate error,
|
||||||
|
// - subsequent "long" requests are handled normally,
|
||||||
|
// - we correctly recover after some "short" requests finish, i.e. we can process new ones.
|
||||||
func TestMaxInFlight(t *testing.T) {
|
func TestMaxInFlight(t *testing.T) {
|
||||||
const Iterations = 3
|
const AllowedInflightRequestsNo = 3
|
||||||
|
// Size of inflightRequestsChannel determines how many concurent inflight requests
|
||||||
|
// are allowed.
|
||||||
|
inflightRequestsChannel := make(chan bool, AllowedInflightRequestsNo)
|
||||||
|
// notAccountedPathsRegexp specifies paths requests to which we don't account into
|
||||||
|
// requests in flight.
|
||||||
|
notAccountedPathsRegexp := regexp.MustCompile(".*\\/watch")
|
||||||
|
|
||||||
|
// Calls is used to wait until all server calls are received. We are sending
|
||||||
|
// AllowedInflightRequestsNo of 'long' not-accounted requests and the same number of
|
||||||
|
// 'short' accounted ones.
|
||||||
|
calls := &sync.WaitGroup{}
|
||||||
|
calls.Add(AllowedInflightRequestsNo * 2)
|
||||||
|
// Block is used to keep requests in flight for as long as we need to. All requests will
|
||||||
|
// be unblocked at the same time.
|
||||||
block := sync.WaitGroup{}
|
block := sync.WaitGroup{}
|
||||||
block.Add(1)
|
block.Add(1)
|
||||||
oneAccountedFinished := sync.WaitGroup{}
|
|
||||||
oneAccountedFinished.Add(1)
|
|
||||||
var once sync.Once
|
|
||||||
sem := make(chan bool, Iterations)
|
|
||||||
|
|
||||||
re := regexp.MustCompile("[.*\\/watch][^\\/proxy.*]")
|
server := httptest.NewServer(
|
||||||
|
MaxInFlightLimit(
|
||||||
// Calls verifies that the server is actually blocked up before running the rest of the test
|
inflightRequestsChannel,
|
||||||
calls := &sync.WaitGroup{}
|
notAccountedPathsRegexp,
|
||||||
calls.Add(Iterations * 3)
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// A short, accounted request that does not wait for block WaitGroup.
|
||||||
server := httptest.NewServer(MaxInFlightLimit(sem, re, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if strings.Contains(r.URL.Path, "dontwait") {
|
if strings.Contains(r.URL.Path, "dontwait") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -80,48 +97,49 @@ func TestMaxInFlight(t *testing.T) {
|
|||||||
calls.Done()
|
calls.Done()
|
||||||
}
|
}
|
||||||
block.Wait()
|
block.Wait()
|
||||||
})))
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
// These should hang, but not affect accounting.
|
// These should hang, but not affect accounting.
|
||||||
for i := 0; i < Iterations; i++ {
|
for i := 0; i < AllowedInflightRequestsNo; i++ {
|
||||||
// These should hang waiting on block...
|
// These should hang waiting on block...
|
||||||
go func() {
|
go func() {
|
||||||
expectHTTP(server.URL+"/foo/bar/watch", http.StatusOK, t)
|
expectHTTP(server.URL+"/foo/bar/watch", http.StatusOK, t)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
// Check that sever is not saturated by not-accounted calls
|
||||||
// These should hang, but not affect accounting.
|
|
||||||
for i := 0; i < Iterations; i++ {
|
|
||||||
// These should hang waiting on block...
|
|
||||||
go func() {
|
|
||||||
expectHTTP(server.URL+"/proxy/foo/bar", http.StatusOK, t)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
expectHTTP(server.URL+"/dontwait", http.StatusOK, t)
|
expectHTTP(server.URL+"/dontwait", http.StatusOK, t)
|
||||||
|
oneAccountedFinished := sync.WaitGroup{}
|
||||||
|
oneAccountedFinished.Add(1)
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
for i := 0; i < Iterations; i++ {
|
// These should hang and be accounted, i.e. saturate the server
|
||||||
|
for i := 0; i < AllowedInflightRequestsNo; i++ {
|
||||||
// These should hang waiting on block...
|
// These should hang waiting on block...
|
||||||
go func() {
|
go func() {
|
||||||
expectHTTP(server.URL, http.StatusOK, t)
|
expectHTTP(server.URL, http.StatusOK, t)
|
||||||
once.Do(oneAccountedFinished.Done)
|
once.Do(oneAccountedFinished.Done)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
// We wait for all calls to be received by the server
|
||||||
calls.Wait()
|
calls.Wait()
|
||||||
|
// Disable calls notifications in the server
|
||||||
calls = nil
|
calls = nil
|
||||||
|
|
||||||
// Do this multiple times to show that it rate limit rejected requests don't block.
|
// Do this multiple times to show that it rate limit rejected requests don't block.
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
expectHTTP(server.URL, errors.StatusTooManyRequests, t)
|
expectHTTP(server.URL, errors.StatusTooManyRequests, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that non-accounted URLs still work
|
// Validate that non-accounted URLs still work
|
||||||
expectHTTP(server.URL+"/dontwait/watch", http.StatusOK, t)
|
expectHTTP(server.URL+"/dontwait/watch", http.StatusOK, t)
|
||||||
|
|
||||||
|
// Let all hanging requests finish
|
||||||
block.Done()
|
block.Done()
|
||||||
|
|
||||||
// Show that we recover from being blocked up.
|
// Show that we recover from being blocked up.
|
||||||
// However, we should until at least one of the requests really finishes.
|
// Too avoid flakyness we need to wait until at least one of the requests really finishes.
|
||||||
oneAccountedFinished.Wait()
|
oneAccountedFinished.Wait()
|
||||||
expectHTTP(server.URL, http.StatusOK, t)
|
expectHTTP(server.URL, http.StatusOK, t)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user