vitess.io/vitess@v0.16.2/go/vt/throttler/manager_test.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 throttler
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"sort"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"google.golang.org/protobuf/proto"
    28  
    29  	throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata"
    30  )
    31  
    32  // We base our test data on these defaults.
    33  var (
    34  	defaultTargetLag              = defaultMaxReplicationLagModuleConfig.TargetReplicationLagSec
    35  	defaultIgnoreNSlowestReplicas = defaultMaxReplicationLagModuleConfig.IgnoreNSlowestReplicas
    36  )
    37  
    38  type managerTestFixture struct {
    39  	m      *managerImpl
    40  	t1, t2 *Throttler
    41  }
    42  
    43  func (f *managerTestFixture) setUp() error {
    44  	f.m = newManager()
    45  	var err error
    46  	f.t1, err = newThrottler(f.m, "t1", "TPS", 1 /* threadCount */, MaxRateModuleDisabled, ReplicationLagModuleDisabled, time.Now)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	f.t2, err = newThrottler(f.m, "t2", "TPS", 1 /* threadCount */, MaxRateModuleDisabled, ReplicationLagModuleDisabled, time.Now)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	return nil
    55  }
    56  
    57  func (f *managerTestFixture) tearDown() {
    58  	f.t1.Close()
    59  	f.t2.Close()
    60  }
    61  
    62  func TestManager_Registration(t *testing.T) {
    63  	m := newManager()
    64  	t1, err := newThrottler(m, "t1", "TPS", 1 /* threadCount */, MaxRateModuleDisabled, ReplicationLagModuleDisabled, time.Now)
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	if err := m.registerThrottler("t1", t1); err == nil {
    69  		t.Fatalf("manager should not accept a duplicate registration of a throttler: %v", err)
    70  	}
    71  	t1.Close()
    72  
    73  	// Unregistering an unregistered throttler should log an error.
    74  	m.unregisterThrottler("t1")
    75  }
    76  
    77  func TestManager_SetMaxRate(t *testing.T) {
    78  	f := &managerTestFixture{}
    79  	if err := f.setUp(); err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	defer f.tearDown()
    83  
    84  	// Test SetMaxRate().
    85  	want := []string{"t1", "t2"}
    86  	if got := f.m.SetMaxRate(23); !reflect.DeepEqual(got, want) {
    87  		t.Errorf("manager did not set the rate on all throttlers. got = %v, want = %v", got, want)
    88  	}
    89  
    90  	// Test MaxRates().
    91  	wantRates := map[string]int64{
    92  		"t1": 23,
    93  		"t2": 23,
    94  	}
    95  	if gotRates := f.m.MaxRates(); !reflect.DeepEqual(gotRates, wantRates) {
    96  		t.Errorf("manager did not set the rate on all throttlers. got = %v, want = %v", gotRates, wantRates)
    97  	}
    98  }
    99  
   100  func TestManager_GetConfiguration(t *testing.T) {
   101  	f := &managerTestFixture{}
   102  	if err := f.setUp(); err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	defer f.tearDown()
   106  
   107  	// Test GetConfiguration() when all throttlers are returned.
   108  	want := map[string]*throttlerdatapb.Configuration{
   109  		"t1": defaultMaxReplicationLagModuleConfig.Clone().Configuration,
   110  		"t2": defaultMaxReplicationLagModuleConfig.Clone().Configuration,
   111  	}
   112  	got, err := f.m.GetConfiguration("" /* all */)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	if !reflect.DeepEqual(got, want) {
   117  		t.Errorf("manager did not return the correct initial config for all throttlers. got = %v, want = %v", got, want)
   118  	}
   119  
   120  	// Test GetConfiguration() when a specific throttler is requested.
   121  	wantT2 := map[string]*throttlerdatapb.Configuration{
   122  		"t2": defaultMaxReplicationLagModuleConfig.Clone().Configuration,
   123  	}
   124  	gotT2, err := f.m.GetConfiguration("t2")
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	if !reflect.DeepEqual(gotT2, wantT2) {
   129  		t.Errorf("manager did not return the correct initial config for throttler: %v got = %v, want = %v", "t2", gotT2, wantT2)
   130  	}
   131  
   132  	// Now change the config and then reset it back.
   133  	newConfig := &throttlerdatapb.Configuration{
   134  		TargetReplicationLagSec: defaultTargetLag + 1,
   135  		IgnoreNSlowestReplicas:  defaultIgnoreNSlowestReplicas + 1,
   136  	}
   137  	allNames, err := f.m.UpdateConfiguration("", newConfig, false /* copyZeroValues */)
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  	// Verify it was changed.
   142  	if err := checkConfig(f.m, []string{"t1", "t2"}, allNames, defaultTargetLag+1, defaultIgnoreNSlowestReplicas+1); err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	// Reset only "t2".
   146  	if names, err := f.m.ResetConfiguration("t2"); err != nil || !reflect.DeepEqual(names, []string{"t2"}) {
   147  		t.Fatalf("Reset failed or returned wrong throttler names: %v err: %v", names, err)
   148  	}
   149  	gotT2AfterReset, err := f.m.GetConfiguration("t2")
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	if !reflect.DeepEqual(gotT2AfterReset, wantT2) {
   154  		t.Errorf("manager did not return the correct initial config for throttler %v after reset: got = %v, want = %v", "t2", gotT2AfterReset, wantT2)
   155  	}
   156  	// Reset all throttlers.
   157  	if names, err := f.m.ResetConfiguration(""); err != nil || !reflect.DeepEqual(names, []string{"t1", "t2"}) {
   158  		t.Fatalf("Reset failed or returned wrong throttler names: %v err: %v", names, err)
   159  	}
   160  	gotAfterReset, err := f.m.GetConfiguration("")
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	if !reflect.DeepEqual(gotAfterReset, want) {
   165  		t.Errorf("manager did not return the correct initial config for all throttlers after reset. got = %v, want = %v", got, want)
   166  	}
   167  }
   168  
   169  func TestManager_UpdateConfiguration_Error(t *testing.T) {
   170  	f := &managerTestFixture{}
   171  	if err := f.setUp(); err != nil {
   172  		t.Fatal(err)
   173  	}
   174  	defer f.tearDown()
   175  
   176  	// Check that errors from Verify() are correctly propagated.
   177  	invalidConfig := &throttlerdatapb.Configuration{
   178  		// max < 2 is not allowed.
   179  		MaxReplicationLagSec: 1,
   180  	}
   181  	if _, err := f.m.UpdateConfiguration("t2", invalidConfig, false /* copyZeroValues */); err == nil {
   182  		t.Fatal("expected error but got nil")
   183  	} else {
   184  		want := "max_replication_lag_sec must be >= 2"
   185  		if !strings.Contains(err.Error(), want) {
   186  			t.Fatalf("received wrong error. got = %v, want contains = %v", err, want)
   187  		}
   188  	}
   189  }
   190  
   191  func TestManager_UpdateConfiguration_Partial(t *testing.T) {
   192  	f := &managerTestFixture{}
   193  	if err := f.setUp(); err != nil {
   194  		t.Fatal(err)
   195  	}
   196  	defer f.tearDown()
   197  
   198  	// Verify that a partial update only updates that one field.
   199  	wantIgnoreNSlowestReplicas := defaultIgnoreNSlowestReplicas + 1
   200  	partialConfig := &throttlerdatapb.Configuration{
   201  		IgnoreNSlowestReplicas: wantIgnoreNSlowestReplicas,
   202  	}
   203  	names, err := f.m.UpdateConfiguration("t2", partialConfig, false /* copyZeroValues */)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	if err := checkConfig(f.m, []string{"t2"}, names, defaultTargetLag, wantIgnoreNSlowestReplicas); err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	// Repeat test for all throttlers.
   211  	allNames, err := f.m.UpdateConfiguration("" /* all */, partialConfig, false /* copyZeroValues */)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  	if err := checkConfig(f.m, []string{"t1", "t2"}, allNames, defaultTargetLag, wantIgnoreNSlowestReplicas); err != nil {
   216  		t.Fatal(err)
   217  	}
   218  }
   219  
   220  func TestManager_UpdateConfiguration_ZeroValues(t *testing.T) {
   221  	f := &managerTestFixture{}
   222  	if err := f.setUp(); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	defer f.tearDown()
   226  
   227  	// Test the explicit copy of zero values.
   228  	zeroValueConfig := proto.Clone(defaultMaxReplicationLagModuleConfig.Configuration).(*throttlerdatapb.Configuration)
   229  	zeroValueConfig.IgnoreNSlowestReplicas = 0
   230  	names, err := f.m.UpdateConfiguration("t2", zeroValueConfig, true /* copyZeroValues */)
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	if err := checkConfig(f.m, []string{"t2"}, names, defaultTargetLag, 0); err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	// Repeat test for all throttlers.
   238  	allNames, err := f.m.UpdateConfiguration("" /* all */, zeroValueConfig, true /* copyZeroValues */)
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  	if err := checkConfig(f.m, []string{"t1", "t2"}, allNames, defaultTargetLag, 0); err != nil {
   243  		t.Fatal(err)
   244  	}
   245  }
   246  
   247  func checkConfig(m *managerImpl, throttlers []string, updatedThrottlers []string, targetLag int64, ignoreNSlowestReplicas int32) error {
   248  	// Sort list of throttler names because they came from a randomized Go map.
   249  	sort.Strings(updatedThrottlers)
   250  
   251  	if !reflect.DeepEqual(throttlers, updatedThrottlers) {
   252  		return fmt.Errorf("list of updated throttler names is wrong. got = %v, want = %v", throttlers, updatedThrottlers)
   253  	}
   254  
   255  	// Get configuration(s).
   256  	var throttlerName string
   257  	if len(throttlers) == 1 {
   258  		throttlerName = throttlers[0]
   259  	}
   260  	configs, err := m.GetConfiguration(throttlerName)
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	if throttlerName != "" {
   266  		if len(configs) != 1 {
   267  			return fmt.Errorf("GetConfiguration() should return only the configuration for throttler: %v but returned: %v", throttlerName, configs)
   268  		}
   269  	}
   270  
   271  	// Check each configuration.
   272  	for _, name := range throttlers {
   273  		if got, want := configs[name].IgnoreNSlowestReplicas, ignoreNSlowestReplicas; got != want {
   274  			return fmt.Errorf("%v: wrong throttler config for IgnoreNSlowestReplicas: got = %v, want = %v", name, got, want)
   275  		}
   276  		if got, want := configs[name].TargetReplicationLagSec, targetLag; got != want {
   277  			return fmt.Errorf("%v: wrong throttler config for TargetReplicationLagSec: got = %v, want = %v", name, got, want)
   278  		}
   279  	}
   280  
   281  	return nil
   282  }