vitess.io/vitess@v0.16.2/go/vt/throttler/throttlerclienttest/throttlerclient_testsuite.go (about)

     1  /*
     2  Copyright 2019 The Vitess 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 throttlerclienttest contains the testsuite against which each
    18  // RPC implementation of the throttlerclient interface must be tested.
    19  package throttlerclienttest
    20  
    21  // NOTE: This file is not test-only code because it is referenced by
    22  // tests in other packages and therefore it has to be regularly
    23  // visible.
    24  
    25  // NOTE: This code is in its own package such that its dependencies
    26  // (e.g.  zookeeper) won't be drawn into production binaries as well.
    27  
    28  import (
    29  	"reflect"
    30  	"strings"
    31  	"testing"
    32  
    33  	"context"
    34  
    35  	"google.golang.org/protobuf/proto"
    36  
    37  	"vitess.io/vitess/go/vt/throttler"
    38  	"vitess.io/vitess/go/vt/throttler/throttlerclient"
    39  
    40  	throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata"
    41  )
    42  
    43  // TestSuite runs the test suite on the given throttlerclient and throttlerserver.
    44  func TestSuite(t *testing.T, c throttlerclient.Client) {
    45  	tf := &testFixture{}
    46  	if err := tf.setUp(); err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	defer tf.tearDown()
    50  
    51  	tf.maxRates(t, c)
    52  
    53  	tf.setMaxRate(t, c)
    54  
    55  	tf.configuration(t, c)
    56  }
    57  
    58  // TestSuitePanics tests the panic handling of each RPC method. Unlike TestSuite
    59  // it does not use the real throttler.managerImpl. Instead, it uses FakeManager
    60  // which allows us to panic on each RPC.
    61  func TestSuitePanics(t *testing.T, c throttlerclient.Client) {
    62  	maxRatesPanics(t, c)
    63  
    64  	setMaxRatePanics(t, c)
    65  
    66  	getConfigurationPanics(t, c)
    67  
    68  	updateConfigurationPanics(t, c)
    69  
    70  	resetConfigurationPanics(t, c)
    71  }
    72  
    73  var throttlerNames = []string{"t1", "t2"}
    74  
    75  type testFixture struct {
    76  	throttlers []*throttler.Throttler
    77  }
    78  
    79  func (tf *testFixture) setUp() error {
    80  	for _, name := range throttlerNames {
    81  		t, err := throttler.NewThrottler(name, "TPS", 1 /* threadCount */, 1, throttler.ReplicationLagModuleDisabled)
    82  		if err != nil {
    83  			return err
    84  		}
    85  		tf.throttlers = append(tf.throttlers, t)
    86  	}
    87  	return nil
    88  }
    89  
    90  func (tf *testFixture) tearDown() {
    91  	for _, t := range tf.throttlers {
    92  		t.Close()
    93  	}
    94  }
    95  
    96  func (tf *testFixture) maxRates(t *testing.T, client throttlerclient.Client) {
    97  	_, err := client.SetMaxRate(context.Background(), 23)
    98  	if err != nil {
    99  		t.Fatalf("Cannot execute remote command: %v", err)
   100  	}
   101  
   102  	got, err := client.MaxRates(context.Background())
   103  	if err != nil {
   104  		t.Fatalf("Cannot execute remote command: %v", err)
   105  	}
   106  	want := map[string]int64{
   107  		"t1": 23,
   108  		"t2": 23,
   109  	}
   110  	if !reflect.DeepEqual(got, want) {
   111  		t.Fatalf("rate was not updated on all registered throttlers. got = %v, want = %v", got, throttlerNames)
   112  	}
   113  }
   114  
   115  func (tf *testFixture) setMaxRate(t *testing.T, client throttlerclient.Client) {
   116  	got, err := client.SetMaxRate(context.Background(), 23)
   117  	if err != nil {
   118  		t.Fatalf("Cannot execute remote command: %v", err)
   119  	}
   120  
   121  	if !reflect.DeepEqual(got, throttlerNames) {
   122  		t.Fatalf("rate was not updated on all registered throttlers. got = %v, want = %v", got, throttlerNames)
   123  	}
   124  }
   125  
   126  func (tf *testFixture) configuration(t *testing.T, client throttlerclient.Client) {
   127  	initialConfigs, err := client.GetConfiguration(context.Background(), "" /* all */)
   128  	if err != nil {
   129  		t.Fatalf("Cannot execute remote command: %v", err)
   130  	}
   131  
   132  	// Test UpdateConfiguration.
   133  	config := &throttlerdatapb.Configuration{
   134  		TargetReplicationLagSec:        1,
   135  		MaxReplicationLagSec:           2,
   136  		InitialRate:                    3,
   137  		MaxIncrease:                    0.4,
   138  		EmergencyDecrease:              0.5,
   139  		MinDurationBetweenIncreasesSec: 6,
   140  		MaxDurationBetweenIncreasesSec: 7,
   141  		MinDurationBetweenDecreasesSec: 8,
   142  		SpreadBacklogAcrossSec:         9,
   143  		IgnoreNSlowestReplicas:         10,
   144  		IgnoreNSlowestRdonlys:          11,
   145  		AgeBadRateAfterSec:             12,
   146  		BadRateIncrease:                0.13,
   147  		MaxRateApproachThreshold:       0.9,
   148  	}
   149  	names, err := client.UpdateConfiguration(context.Background(), "t2", config /* false */, true /* copyZeroValues */)
   150  	if err != nil {
   151  		t.Fatalf("Cannot execute remote command: %v", err)
   152  	}
   153  	if got, want := names, []string{"t2"}; !reflect.DeepEqual(got, want) {
   154  		t.Fatalf("returned names of updated throttlers is wrong. got = %v, want = %v", got, want)
   155  	}
   156  
   157  	// Test GetConfiguration.
   158  	configs, err := client.GetConfiguration(context.Background(), "t2")
   159  	if err != nil {
   160  		t.Fatalf("Cannot execute remote command: %v", err)
   161  	}
   162  	if len(configs) != 1 || configs["t2"] == nil {
   163  		t.Fatalf("wrong named configuration returned. got = %v, want configuration for t2", configs)
   164  	}
   165  	if got, want := configs["t2"], config; !proto.Equal(got, want) {
   166  		t.Fatalf("did not read updated config. got = %v, want = %v", got, want)
   167  	}
   168  
   169  	// Reset should return the initial configs.
   170  	namesForReset, err := client.ResetConfiguration(context.Background(), "" /* all */)
   171  	if err != nil {
   172  		t.Fatalf("Cannot execute remote command: %v", err)
   173  	}
   174  	if got, want := namesForReset, throttlerNames; !reflect.DeepEqual(got, want) {
   175  		t.Fatalf("returned names of reset throttlers is wrong. got = %v, want = %v", got, want)
   176  	}
   177  
   178  	// Verify that it was correctly set.
   179  	configsAfterReset, err := client.GetConfiguration(context.Background(), "" /* all */)
   180  	if err != nil {
   181  		t.Fatalf("Cannot execute remote command: %v", err)
   182  	}
   183  	if got, want := configsAfterReset, initialConfigs; !reflect.DeepEqual(got, want) {
   184  		t.Fatalf("wrong configurations after reset. got = %v, want = %v", got, want)
   185  	}
   186  }
   187  
   188  // FakeManager implements the throttler.Manager interface and panics on all
   189  // methods defined in the interface.
   190  type FakeManager struct {
   191  }
   192  
   193  const panicMsg = "RPC server implementation should handle this"
   194  
   195  // MaxRates implements the throttler.Manager interface. It always panics.
   196  func (fm *FakeManager) MaxRates() map[string]int64 {
   197  	panic(panicMsg)
   198  }
   199  
   200  // SetMaxRate implements the throttler.Manager interface. It always panics.
   201  func (fm *FakeManager) SetMaxRate(int64) []string {
   202  	panic(panicMsg)
   203  }
   204  
   205  // GetConfiguration implements the throttler.Manager interface. It always panics.
   206  func (fm *FakeManager) GetConfiguration(throttlerName string) (map[string]*throttlerdatapb.Configuration, error) {
   207  	panic(panicMsg)
   208  }
   209  
   210  // UpdateConfiguration implements the throttler.Manager interface. It always panics.
   211  func (fm *FakeManager) UpdateConfiguration(throttlerName string, configuration *throttlerdatapb.Configuration, copyZeroValues bool) ([]string, error) {
   212  	panic(panicMsg)
   213  }
   214  
   215  // ResetConfiguration implements the throttler.Manager interface. It always panics.
   216  func (fm *FakeManager) ResetConfiguration(throttlerName string) ([]string, error) {
   217  	panic(panicMsg)
   218  }
   219  
   220  // Test methods which test for each RPC that panics are caught.
   221  
   222  func maxRatesPanics(t *testing.T, client throttlerclient.Client) {
   223  	_, err := client.MaxRates(context.Background())
   224  	if !errorFromPanicHandler(err) {
   225  		t.Fatalf("MaxRates RPC implementation does not catch panics properly: %v", err)
   226  	}
   227  }
   228  
   229  func setMaxRatePanics(t *testing.T, client throttlerclient.Client) {
   230  	_, err := client.SetMaxRate(context.Background(), 23)
   231  	if !errorFromPanicHandler(err) {
   232  		t.Fatalf("SetMaxRate RPC implementation does not catch panics properly: %v", err)
   233  	}
   234  }
   235  
   236  func getConfigurationPanics(t *testing.T, client throttlerclient.Client) {
   237  	_, err := client.GetConfiguration(context.Background(), "")
   238  	if !errorFromPanicHandler(err) {
   239  		t.Fatalf("GetConfiguration RPC implementation does not catch panics properly: %v", err)
   240  	}
   241  }
   242  
   243  func updateConfigurationPanics(t *testing.T, client throttlerclient.Client) {
   244  	_, err := client.UpdateConfiguration(context.Background(), "", nil, false)
   245  	if !errorFromPanicHandler(err) {
   246  		t.Fatalf("UpdateConfiguration RPC implementation does not catch panics properly: %v", err)
   247  	}
   248  }
   249  
   250  func resetConfigurationPanics(t *testing.T, client throttlerclient.Client) {
   251  	_, err := client.ResetConfiguration(context.Background(), "")
   252  	if !errorFromPanicHandler(err) {
   253  		t.Fatalf("ResetConfiguration RPC implementation does not catch panics properly: %v", err)
   254  	}
   255  }
   256  
   257  func errorFromPanicHandler(err error) bool {
   258  	if err == nil || !strings.Contains(err.Error(), panicMsg) {
   259  		return false
   260  	}
   261  	return true
   262  }