github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/requests_counter_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package xdsclient
    20  
    21  import (
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  )
    26  
    27  const testService = "test-service-name"
    28  
    29  type counterTest struct {
    30  	name              string
    31  	maxRequests       uint32
    32  	numRequests       uint32
    33  	expectedSuccesses uint32
    34  	expectedErrors    uint32
    35  }
    36  
    37  var tests = []counterTest{
    38  	{
    39  		name:              "does-not-exceed-max-requests",
    40  		maxRequests:       1024,
    41  		numRequests:       1024,
    42  		expectedSuccesses: 1024,
    43  		expectedErrors:    0,
    44  	},
    45  	{
    46  		name:              "exceeds-max-requests",
    47  		maxRequests:       32,
    48  		numRequests:       64,
    49  		expectedSuccesses: 32,
    50  		expectedErrors:    32,
    51  	},
    52  }
    53  
    54  func resetClusterRequestsCounter() {
    55  	src = &clusterRequestsCounter{
    56  		clusters: make(map[clusterNameAndServiceName]*ClusterRequestsCounter),
    57  	}
    58  }
    59  
    60  func testCounter(t *testing.T, test counterTest) {
    61  	requestsStarted := make(chan struct{})
    62  	requestsSent := sync.WaitGroup{}
    63  	requestsSent.Add(int(test.numRequests))
    64  	requestsDone := sync.WaitGroup{}
    65  	requestsDone.Add(int(test.numRequests))
    66  	var lastError atomic.Value
    67  	var successes, errors uint32
    68  	for i := 0; i < int(test.numRequests); i++ {
    69  		go func() {
    70  			counter := GetClusterRequestsCounter(test.name, testService)
    71  			defer requestsDone.Done()
    72  			err := counter.StartRequest(test.maxRequests)
    73  			if err == nil {
    74  				atomic.AddUint32(&successes, 1)
    75  			} else {
    76  				atomic.AddUint32(&errors, 1)
    77  				lastError.Store(err)
    78  			}
    79  			requestsSent.Done()
    80  			if err == nil {
    81  				<-requestsStarted
    82  				counter.EndRequest()
    83  			}
    84  		}()
    85  	}
    86  	requestsSent.Wait()
    87  	close(requestsStarted)
    88  	requestsDone.Wait()
    89  	loadedError := lastError.Load()
    90  	if test.expectedErrors > 0 && loadedError == nil {
    91  		t.Error("no error when error expected")
    92  	}
    93  	if test.expectedErrors == 0 && loadedError != nil {
    94  		t.Errorf("error starting request: %v", loadedError.(error))
    95  	}
    96  	// We allow the limits to be exceeded during races.
    97  	//
    98  	// But we should never over-limit, so this test fails if there are less
    99  	// successes than expected.
   100  	if successes < test.expectedSuccesses || errors > test.expectedErrors {
   101  		t.Errorf("unexpected number of (successes, errors), expected (%v, %v), encountered (%v, %v)", test.expectedSuccesses, test.expectedErrors, successes, errors)
   102  	}
   103  }
   104  
   105  func (s) TestRequestsCounter(t *testing.T) {
   106  	defer resetClusterRequestsCounter()
   107  	for _, test := range tests {
   108  		t.Run(test.name, func(t *testing.T) {
   109  			testCounter(t, test)
   110  		})
   111  	}
   112  }
   113  
   114  func (s) TestGetClusterRequestsCounter(t *testing.T) {
   115  	defer resetClusterRequestsCounter()
   116  	for _, test := range tests {
   117  		counterA := GetClusterRequestsCounter(test.name, testService)
   118  		counterB := GetClusterRequestsCounter(test.name, testService)
   119  		if counterA != counterB {
   120  			t.Errorf("counter %v %v != counter %v %v", counterA, *counterA, counterB, *counterB)
   121  		}
   122  	}
   123  }
   124  
   125  func startRequests(t *testing.T, n uint32, max uint32, counter *ClusterRequestsCounter) {
   126  	for i := uint32(0); i < n; i++ {
   127  		if err := counter.StartRequest(max); err != nil {
   128  			t.Fatalf("error starting initial request: %v", err)
   129  		}
   130  	}
   131  }
   132  
   133  func (s) TestSetMaxRequestsIncreased(t *testing.T) {
   134  	defer resetClusterRequestsCounter()
   135  	const clusterName string = "set-max-requests-increased"
   136  	var initialMax uint32 = 16
   137  
   138  	counter := GetClusterRequestsCounter(clusterName, testService)
   139  	startRequests(t, initialMax, initialMax, counter)
   140  	if err := counter.StartRequest(initialMax); err == nil {
   141  		t.Fatal("unexpected success on start request after max met")
   142  	}
   143  
   144  	newMax := initialMax + 1
   145  	if err := counter.StartRequest(newMax); err != nil {
   146  		t.Fatalf("unexpected error on start request after max increased: %v", err)
   147  	}
   148  }
   149  
   150  func (s) TestSetMaxRequestsDecreased(t *testing.T) {
   151  	defer resetClusterRequestsCounter()
   152  	const clusterName string = "set-max-requests-decreased"
   153  	var initialMax uint32 = 16
   154  
   155  	counter := GetClusterRequestsCounter(clusterName, testService)
   156  	startRequests(t, initialMax-1, initialMax, counter)
   157  
   158  	newMax := initialMax - 1
   159  	if err := counter.StartRequest(newMax); err == nil {
   160  		t.Fatalf("unexpected success on start request after max decreased: %v", err)
   161  	}
   162  }