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 }