vitess.io/vitess@v0.16.2/go/vt/throttler/throttler.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 provides a client-side, local throttler which is used to
    18  // throttle (and actively pace) writes during the resharding process.
    19  //
    20  // The throttler has two main goals:
    21  // a) allow resharding data into an existing keyspace by throttling at a fixed
    22  // rate
    23  // b) ensure that the MySQL replicas do not become overloaded
    24  //
    25  // To support b), the throttler constantly monitors the health of all replicas
    26  // and reduces the allowed rate if the replication lag is above a certain
    27  // threshold.
    28  package throttler
    29  
    30  import (
    31  	"fmt"
    32  	"math"
    33  	"sync"
    34  	"time"
    35  
    36  	"vitess.io/vitess/go/vt/discovery"
    37  	"vitess.io/vitess/go/vt/log"
    38  
    39  	throttlerdatapb "vitess.io/vitess/go/vt/proto/throttlerdata"
    40  )
    41  
    42  const (
    43  	// NotThrottled will be returned by Throttle() if the application is currently
    44  	// not throttled.
    45  	NotThrottled time.Duration = 0
    46  
    47  	// ZeroRateNoProgess can be used to set maxRate to 0. In this case, the
    48  	// throttler won't let any requests through until the rate is increased again.
    49  	ZeroRateNoProgess = 0
    50  
    51  	// MaxRateModuleDisabled can be set in NewThrottler() to disable throttling
    52  	// by a fixed rate.
    53  	MaxRateModuleDisabled = math.MaxInt64
    54  
    55  	// InvalidMaxRate is a constant which will fail in a NewThrottler() call.
    56  	// It should be used when returning maxRate in an error case.
    57  	InvalidMaxRate = -1
    58  
    59  	// ReplicationLagModuleDisabled can be set in NewThrottler() to disable
    60  	// throttling based on the MySQL replication lag.
    61  	ReplicationLagModuleDisabled = math.MaxInt64
    62  
    63  	// InvalidMaxReplicationLag is a constant which will fail in a NewThrottler()
    64  	// call. It should be used when returning maxReplicationlag in an error case.
    65  	InvalidMaxReplicationLag = -1
    66  )
    67  
    68  // Throttler provides a client-side, thread-aware throttler.
    69  // See the package doc for more information.
    70  //
    71  // Calls of Throttle() and ThreadFinished() take threadID as parameter which is
    72  // in the range [0, threadCount). (threadCount is set in NewThrottler().)
    73  // NOTE: Trottle() and ThreadFinished() assume that *per thread* calls to them
    74  //
    75  //	are serialized and must not happen concurrently.
    76  type Throttler struct {
    77  	// name describes the Throttler instance and is used e.g. in the webinterface.
    78  	name string
    79  	// unit describes the entity the throttler is limiting e.g. "queries" or
    80  	// "transactions". It is used for example in the webinterface.
    81  	unit string
    82  	// manager is where this throttler registers and unregisters itself
    83  	// at creation and deletion (Close) respectively.
    84  	manager *managerImpl
    85  
    86  	closed bool
    87  	// modules is the list of modules which can limit the current rate. The order
    88  	// of the list defines the priority of the module. (Lower rates trump.)
    89  	modules []Module
    90  	// maxRateModule is stored in its own field because we need it when we want
    91  	// to change its maxRate. It's also included in the "modules" list.
    92  	maxRateModule *MaxRateModule
    93  	// rateUpdateChan is used to notify the throttler that the current max rate
    94  	// must be recalculated and updated.
    95  	rateUpdateChan chan struct{}
    96  	// maxReplicationLagModule is stored in its own field because we need it to
    97  	// record the replication lag. It's also included in the "modules" list.
    98  	maxReplicationLagModule *MaxReplicationLagModule
    99  
   100  	// The group below is unguarded because the fields are immutable and each
   101  	// thread accesses an element at a different index.
   102  	threadThrottlers []*threadThrottler
   103  	threadFinished   []bool
   104  
   105  	mu sync.Mutex
   106  	// runningThreads tracks which threads have not finished yet.
   107  	runningThreads map[int]bool
   108  	// threadRunningsLastUpdate caches for updateMaxRate() how many threads were
   109  	// running at the previous run.
   110  	threadRunningsLastUpdate int
   111  
   112  	nowFunc func() time.Time
   113  
   114  	// actualRateHistory tracks for past seconds the total actual rate (based on
   115  	// the number of unthrottled Throttle() calls).
   116  	actualRateHistory *aggregatedIntervalHistory
   117  }
   118  
   119  // NewThrottler creates a new Throttler instance.
   120  // Use the constants MaxRateModuleDisabled or ReplicationLagModuleDisabled
   121  // if you want to disable parts of its functionality.
   122  // maxRate will be distributed across all threadCount threads and must be >=
   123  // threadCount. If it's lower, it will be automatically set to threadCount.
   124  // maxRate can also be set to 0 which will effectively pause the user and
   125  // constantly block until the rate has been increased again.
   126  // unit refers to the type of entity you want to throttle e.g. "queries" or
   127  // "transactions".
   128  // name describes the Throttler instance and will be used by the webinterface.
   129  func NewThrottler(name, unit string, threadCount int, maxRate, maxReplicationLag int64) (*Throttler, error) {
   130  	return newThrottler(GlobalManager, name, unit, threadCount, maxRate, maxReplicationLag, time.Now)
   131  }
   132  
   133  func newThrottler(manager *managerImpl, name, unit string, threadCount int, maxRate, maxReplicationLag int64, nowFunc func() time.Time) (*Throttler, error) {
   134  	// Verify input parameters.
   135  	if maxRate < 0 {
   136  		return nil, fmt.Errorf("maxRate must be >= 0: %v", maxRate)
   137  	}
   138  	if maxReplicationLag < 0 {
   139  		return nil, fmt.Errorf("maxReplicationLag must be >= 0: %v", maxReplicationLag)
   140  	}
   141  
   142  	// Enable the configured modules.
   143  	maxRateModule := NewMaxRateModule(maxRate)
   144  	actualRateHistory := newAggregatedIntervalHistory(1024, 1*time.Second, threadCount)
   145  	maxReplicationLagModule, err := NewMaxReplicationLagModule(NewMaxReplicationLagModuleConfig(maxReplicationLag), actualRateHistory, nowFunc)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	var modules []Module
   151  	modules = append(modules, maxRateModule)
   152  	modules = append(modules, maxReplicationLagModule)
   153  
   154  	// Start each module (which might start own Go routines).
   155  	rateUpdateChan := make(chan struct{}, 10)
   156  	for _, m := range modules {
   157  		m.Start(rateUpdateChan)
   158  	}
   159  
   160  	runningThreads := make(map[int]bool, threadCount)
   161  	threadThrottlers := make([]*threadThrottler, threadCount)
   162  	for i := 0; i < threadCount; i++ {
   163  		threadThrottlers[i] = newThreadThrottler(i, actualRateHistory)
   164  		runningThreads[i] = true
   165  	}
   166  	t := &Throttler{
   167  		name:                    name,
   168  		unit:                    unit,
   169  		manager:                 manager,
   170  		modules:                 modules,
   171  		maxRateModule:           maxRateModule,
   172  		maxReplicationLagModule: maxReplicationLagModule,
   173  		rateUpdateChan:          rateUpdateChan,
   174  		threadThrottlers:        threadThrottlers,
   175  		threadFinished:          make([]bool, threadCount),
   176  		runningThreads:          runningThreads,
   177  		nowFunc:                 nowFunc,
   178  		actualRateHistory:       actualRateHistory,
   179  	}
   180  
   181  	// Initialize maxRate.
   182  	t.updateMaxRate()
   183  
   184  	if err := t.manager.registerThrottler(name, t); err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	// Watch for rate updates from the modules and update the internal maxRate.
   189  	go func() {
   190  		for range rateUpdateChan {
   191  			t.updateMaxRate()
   192  		}
   193  	}()
   194  
   195  	return t, nil
   196  }
   197  
   198  // Throttle returns a backoff duration which specifies for how long "threadId"
   199  // should wait before it issues the next request.
   200  // If the duration is zero, the thread is not throttled.
   201  // If the duration is not zero, the thread must call Throttle() again after
   202  // the backoff duration elapsed.
   203  // The maximum value for the returned backoff is 1 second since the throttler
   204  // internally operates on a per-second basis.
   205  func (t *Throttler) Throttle(threadID int) time.Duration {
   206  	if t.closed {
   207  		panic(fmt.Sprintf("BUG: thread with ID: %v must not access closed Throttler", threadID))
   208  	}
   209  	if t.threadFinished[threadID] {
   210  		panic(fmt.Sprintf("BUG: thread with ID: %v already finished", threadID))
   211  	}
   212  	return t.threadThrottlers[threadID].throttle(t.nowFunc())
   213  }
   214  
   215  // ThreadFinished marks threadID as finished and redistributes the thread's
   216  // rate allotment across the other threads.
   217  // After ThreadFinished() is called, Throttle() must not be called anymore.
   218  func (t *Throttler) ThreadFinished(threadID int) {
   219  	if t.threadFinished[threadID] {
   220  		panic(fmt.Sprintf("BUG: thread with ID: %v already finished", threadID))
   221  	}
   222  
   223  	t.mu.Lock()
   224  	delete(t.runningThreads, threadID)
   225  	t.mu.Unlock()
   226  
   227  	t.threadFinished[threadID] = true
   228  	t.rateUpdateChan <- struct{}{}
   229  }
   230  
   231  // Close stops all modules and frees all resources.
   232  // When Close() returned, the Throttler object must not be used anymore.
   233  func (t *Throttler) Close() {
   234  	for _, m := range t.modules {
   235  		m.Stop()
   236  	}
   237  	close(t.rateUpdateChan)
   238  	t.closed = true
   239  	t.manager.unregisterThrottler(t.name)
   240  }
   241  
   242  // updateMaxRate recalculates the current max rate and updates all
   243  // threadThrottlers accordingly.
   244  // The rate changes when the number of thread changes or a module updated its
   245  // max rate.
   246  func (t *Throttler) updateMaxRate() {
   247  	// Set it to infinite initially.
   248  	maxRate := int64(math.MaxInt64)
   249  
   250  	// Find out the new max rate (minimum among all modules).
   251  	for _, m := range t.modules {
   252  		if moduleMaxRate := m.MaxRate(); moduleMaxRate < maxRate {
   253  			maxRate = moduleMaxRate
   254  		}
   255  	}
   256  
   257  	// Set the new max rate on each thread.
   258  	t.mu.Lock()
   259  	defer t.mu.Unlock()
   260  	threadsRunning := len(t.runningThreads)
   261  	if threadsRunning == 0 {
   262  		// Throttler is done. Set rates don't matter anymore.
   263  		return
   264  	}
   265  
   266  	if maxRate != ZeroRateNoProgess && maxRate < int64(threadsRunning) {
   267  		log.Warningf("Set maxRate is less than the number of threads (%v). To prevent threads from starving, maxRate was increased from: %v to: %v.", threadsRunning, maxRate, threadsRunning)
   268  		maxRate = int64(threadsRunning)
   269  	}
   270  	maxRatePerThread := maxRate / int64(threadsRunning)
   271  	// Distribute the remainder of the division across all threads.
   272  	remainder := maxRate % int64(threadsRunning)
   273  	remainderPerThread := make(map[int]int64, threadsRunning)
   274  	for id := 0; remainder > 0; {
   275  		remainderPerThread[id]++
   276  		remainder--
   277  		id++
   278  	}
   279  
   280  	for threadID := range t.runningThreads {
   281  		t.threadThrottlers[threadID].setMaxRate(maxRatePerThread + remainderPerThread[threadID])
   282  	}
   283  	t.threadRunningsLastUpdate = threadsRunning
   284  }
   285  
   286  // MaxRate returns the current rate of the MaxRateModule.
   287  func (t *Throttler) MaxRate() int64 {
   288  	return t.maxRateModule.MaxRate()
   289  }
   290  
   291  // SetMaxRate updates the rate of the MaxRateModule.
   292  func (t *Throttler) SetMaxRate(rate int64) {
   293  	t.maxRateModule.SetMaxRate(rate)
   294  }
   295  
   296  // RecordReplicationLag must be called by users to report the "ts" tablet health
   297  // data observed at "time".
   298  // Note: After Close() is called, this method must not be called anymore.
   299  func (t *Throttler) RecordReplicationLag(time time.Time, th *discovery.TabletHealth) {
   300  	t.maxReplicationLagModule.RecordReplicationLag(time, th)
   301  }
   302  
   303  // GetConfiguration returns the configuration of the MaxReplicationLag module.
   304  func (t *Throttler) GetConfiguration() *throttlerdatapb.Configuration {
   305  	return t.maxReplicationLagModule.getConfiguration()
   306  }
   307  
   308  // UpdateConfiguration updates the configuration of the MaxReplicationLag module.
   309  func (t *Throttler) UpdateConfiguration(configuration *throttlerdatapb.Configuration, copyZeroValues bool) error {
   310  	return t.maxReplicationLagModule.updateConfiguration(configuration, copyZeroValues)
   311  }
   312  
   313  // ResetConfiguration resets the configuration of the MaxReplicationLag module
   314  // to its initial settings.
   315  func (t *Throttler) ResetConfiguration() {
   316  	t.maxReplicationLagModule.resetConfiguration()
   317  }
   318  
   319  // log returns the most recent changes of the MaxReplicationLag module.
   320  func (t *Throttler) log() []result {
   321  	return t.maxReplicationLagModule.log()
   322  }