Skip to content

Commit 23357d0

Browse files
author
Shlomi Noach
committed
WIP: decoupling general throttling from throttle logic
1 parent 75b2542 commit 23357d0

File tree

2 files changed

+128
-60
lines changed

2 files changed

+128
-60
lines changed

go/base/context.go

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ var (
4141
envVariableRegexp = regexp.MustCompile("[$][{](.*)[}]")
4242
)
4343

44+
type ThrottleCheckResult struct {
45+
ShouldThrottle bool
46+
Reason string
47+
}
48+
49+
func NewThrottleCheckResult(throttle bool, reason string) *ThrottleCheckResult {
50+
return &ThrottleCheckResult{
51+
ShouldThrottle: throttle,
52+
Reason: reason,
53+
}
54+
}
55+
4456
// MigrationContext has the general, global state of migration. It is used by
4557
// all components throughout the migration process.
4658
type MigrationContext struct {
@@ -96,33 +108,34 @@ type MigrationContext struct {
96108
InitiallyDropGhostTable bool
97109
CutOverType CutOver
98110

99-
Hostname string
100-
TableEngine string
101-
RowsEstimate int64
102-
RowsDeltaEstimate int64
103-
UsedRowsEstimateMethod RowsEstimateMethod
104-
HasSuperPrivilege bool
105-
OriginalBinlogFormat string
106-
OriginalBinlogRowImage string
107-
InspectorConnectionConfig *mysql.ConnectionConfig
108-
ApplierConnectionConfig *mysql.ConnectionConfig
109-
StartTime time.Time
110-
RowCopyStartTime time.Time
111-
RowCopyEndTime time.Time
112-
LockTablesStartTime time.Time
113-
RenameTablesStartTime time.Time
114-
RenameTablesEndTime time.Time
115-
pointOfInterestTime time.Time
116-
pointOfInterestTimeMutex *sync.Mutex
117-
CurrentLag int64
118-
controlReplicasLagResult mysql.ReplicationLagResult
119-
TotalRowsCopied int64
120-
TotalDMLEventsApplied int64
121-
isThrottled bool
122-
throttleReason string
123-
throttleMutex *sync.Mutex
124-
IsPostponingCutOver int64
125-
CountingRowsFlag int64
111+
Hostname string
112+
TableEngine string
113+
RowsEstimate int64
114+
RowsDeltaEstimate int64
115+
UsedRowsEstimateMethod RowsEstimateMethod
116+
HasSuperPrivilege bool
117+
OriginalBinlogFormat string
118+
OriginalBinlogRowImage string
119+
InspectorConnectionConfig *mysql.ConnectionConfig
120+
ApplierConnectionConfig *mysql.ConnectionConfig
121+
StartTime time.Time
122+
RowCopyStartTime time.Time
123+
RowCopyEndTime time.Time
124+
LockTablesStartTime time.Time
125+
RenameTablesStartTime time.Time
126+
RenameTablesEndTime time.Time
127+
pointOfInterestTime time.Time
128+
pointOfInterestTimeMutex *sync.Mutex
129+
CurrentLag int64
130+
controlReplicasLagResult mysql.ReplicationLagResult
131+
TotalRowsCopied int64
132+
TotalDMLEventsApplied int64
133+
isThrottled bool
134+
throttleReason string
135+
throttleGeneralCheckResult ThrottleCheckResult
136+
throttleMutex *sync.Mutex
137+
IsPostponingCutOver int64
138+
CountingRowsFlag int64
126139

127140
OriginalTableColumns *sql.ColumnList
128141
OriginalTableUniqueKeys [](*sql.UniqueKey)
@@ -355,6 +368,20 @@ func (this *MigrationContext) SetChunkSize(chunkSize int64) {
355368
atomic.StoreInt64(&this.ChunkSize, chunkSize)
356369
}
357370

371+
func (this *MigrationContext) SetThrottleGeneralCheckResult(checkResult *ThrottleCheckResult) *ThrottleCheckResult {
372+
this.throttleMutex.Lock()
373+
defer this.throttleMutex.Unlock()
374+
this.throttleGeneralCheckResult = *checkResult
375+
return checkResult
376+
}
377+
378+
func (this *MigrationContext) GetThrottleGeneralCheckResult() *ThrottleCheckResult {
379+
this.throttleMutex.Lock()
380+
defer this.throttleMutex.Unlock()
381+
result := this.throttleGeneralCheckResult
382+
return &result
383+
}
384+
358385
func (this *MigrationContext) SetThrottled(throttle bool, reason string) {
359386
this.throttleMutex.Lock()
360387
defer this.throttleMutex.Unlock()

go/logic/migrator.go

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ type Migrator struct {
5555
applier *Applier
5656
eventsStreamer *EventsStreamer
5757
server *Server
58+
throttler *Throttler
5859
hooksExecutor *HooksExecutor
5960
migrationContext *base.MigrationContext
6061

62+
firstThrottlingCollected chan bool
6163
tablesInPlace chan bool
6264
rowCopyComplete chan bool
6365
allEventsUpToLockProcessed chan bool
@@ -81,6 +83,7 @@ func NewMigrator() *Migrator {
8183
migrationContext: base.GetMigrationContext(),
8284
parser: sql.NewParser(),
8385
tablesInPlace: make(chan bool),
86+
firstThrottlingCollected: make(chan bool, 1),
8487
rowCopyComplete: make(chan bool),
8588
allEventsUpToLockProcessed: make(chan bool),
8689
panicAbort: make(chan error),
@@ -119,8 +122,44 @@ func (this *Migrator) initiateHooksExecutor() (err error) {
119122
}
120123

121124
// shouldThrottle performs checks to see whether we should currently be throttling.
122-
// It also checks for critical-load and panic aborts.
125+
// It merely observes the metrics collected by other components, it does not issue
126+
// its own metric collection.
123127
func (this *Migrator) shouldThrottle() (result bool, reason string) {
128+
generalCheckResult := this.migrationContext.GetThrottleGeneralCheckResult()
129+
if generalCheckResult.ShouldThrottle {
130+
return generalCheckResult.ShouldThrottle, generalCheckResult.Reason
131+
}
132+
// Replication lag throttle
133+
maxLagMillisecondsThrottleThreshold := atomic.LoadInt64(&this.migrationContext.MaxLagMillisecondsThrottleThreshold)
134+
lag := atomic.LoadInt64(&this.migrationContext.CurrentLag)
135+
if time.Duration(lag) > time.Duration(maxLagMillisecondsThrottleThreshold)*time.Millisecond {
136+
return true, fmt.Sprintf("lag=%fs", time.Duration(lag).Seconds())
137+
}
138+
checkThrottleControlReplicas := true
139+
if (this.migrationContext.TestOnReplica || this.migrationContext.MigrateOnReplica) && (atomic.LoadInt64(&this.allEventsUpToLockProcessedInjectedFlag) > 0) {
140+
checkThrottleControlReplicas = false
141+
}
142+
if checkThrottleControlReplicas {
143+
lagResult := this.migrationContext.GetControlReplicasLagResult()
144+
if lagResult.Err != nil {
145+
return true, fmt.Sprintf("%+v %+v", lagResult.Key, lagResult.Err)
146+
}
147+
if lagResult.Lag > time.Duration(maxLagMillisecondsThrottleThreshold)*time.Millisecond {
148+
return true, fmt.Sprintf("%+v replica-lag=%fs", lagResult.Key, lagResult.Lag.Seconds())
149+
}
150+
}
151+
// Got here? No metrics indicates we need throttling.
152+
return false, ""
153+
}
154+
155+
// readGeneralThrottleMetrics reads the once-per-sec metrics, and stores them onto this.migrationContext
156+
func (this *Migrator) readGeneralThrottleMetrics() error {
157+
158+
setThrottle := func(throttle bool, reason string) error {
159+
this.migrationContext.SetThrottleGeneralCheckResult(base.NewThrottleCheckResult(throttle, reason))
160+
return nil
161+
}
162+
124163
// Regardless of throttle, we take opportunity to check for panic-abort
125164
if this.migrationContext.PanicFlagFile != "" {
126165
if base.FileExists(this.migrationContext.PanicFlagFile) {
@@ -131,7 +170,7 @@ func (this *Migrator) shouldThrottle() (result bool, reason string) {
131170
for variableName, threshold := range criticalLoad {
132171
value, err := this.applier.ShowStatusVariable(variableName)
133172
if err != nil {
134-
return true, fmt.Sprintf("%s %s", variableName, err)
173+
return setThrottle(true, fmt.Sprintf("%s %s", variableName, err))
135174
}
136175
if value >= threshold {
137176
this.panicAbort <- fmt.Errorf("critical-load met: %s=%d, >=%d", variableName, value, threshold)
@@ -142,62 +181,60 @@ func (this *Migrator) shouldThrottle() (result bool, reason string) {
142181

143182
// User-based throttle
144183
if atomic.LoadInt64(&this.migrationContext.ThrottleCommandedByUser) > 0 {
145-
return true, "commanded by user"
184+
return setThrottle(true, "commanded by user")
146185
}
147186
if this.migrationContext.ThrottleFlagFile != "" {
148187
if base.FileExists(this.migrationContext.ThrottleFlagFile) {
149188
// Throttle file defined and exists!
150-
return true, "flag-file"
189+
return setThrottle(true, "flag-file")
151190
}
152191
}
153192
if this.migrationContext.ThrottleAdditionalFlagFile != "" {
154193
if base.FileExists(this.migrationContext.ThrottleAdditionalFlagFile) {
155194
// 2nd Throttle file defined and exists!
156-
return true, "flag-file"
157-
}
158-
}
159-
// Replication lag throttle
160-
maxLagMillisecondsThrottleThreshold := atomic.LoadInt64(&this.migrationContext.MaxLagMillisecondsThrottleThreshold)
161-
lag := atomic.LoadInt64(&this.migrationContext.CurrentLag)
162-
if time.Duration(lag) > time.Duration(maxLagMillisecondsThrottleThreshold)*time.Millisecond {
163-
return true, fmt.Sprintf("lag=%fs", time.Duration(lag).Seconds())
164-
}
165-
checkThrottleControlReplicas := true
166-
if (this.migrationContext.TestOnReplica || this.migrationContext.MigrateOnReplica) && (atomic.LoadInt64(&this.allEventsUpToLockProcessedInjectedFlag) > 0) {
167-
checkThrottleControlReplicas = false
168-
}
169-
if checkThrottleControlReplicas {
170-
lagResult := this.migrationContext.GetControlReplicasLagResult()
171-
if lagResult.Err != nil {
172-
return true, fmt.Sprintf("%+v %+v", lagResult.Key, lagResult.Err)
173-
}
174-
if lagResult.Lag > time.Duration(maxLagMillisecondsThrottleThreshold)*time.Millisecond {
175-
return true, fmt.Sprintf("%+v replica-lag=%fs", lagResult.Key, lagResult.Lag.Seconds())
195+
return setThrottle(true, "flag-file")
176196
}
177197
}
178198

179199
maxLoad := this.migrationContext.GetMaxLoad()
180200
for variableName, threshold := range maxLoad {
181201
value, err := this.applier.ShowStatusVariable(variableName)
182202
if err != nil {
183-
return true, fmt.Sprintf("%s %s", variableName, err)
203+
return setThrottle(true, fmt.Sprintf("%s %s", variableName, err))
184204
}
185205
if value >= threshold {
186-
return true, fmt.Sprintf("max-load %s=%d >= %d", variableName, value, threshold)
206+
return setThrottle(true, fmt.Sprintf("max-load %s=%d >= %d", variableName, value, threshold))
187207
}
188208
}
189209
if this.migrationContext.GetThrottleQuery() != "" {
190210
if res, _ := this.applier.ExecuteThrottleQuery(); res > 0 {
191-
return true, "throttle-query"
211+
return setThrottle(true, "throttle-query")
192212
}
193213
}
194214

195-
return false, ""
215+
return setThrottle(false, "")
216+
}
217+
218+
// initiateThrottlerMetrics initiates the various processes that collect measurements
219+
// that may affect throttling. There are several components, all running independently,
220+
// that collect such metrics.
221+
func (this *Migrator) initiateThrottlerMetrics() {
222+
go this.initiateHeartbeatReader()
223+
go this.initiateControlReplicasReader()
224+
225+
go func() {
226+
throttlerMetricsTick := time.Tick(1 * time.Second)
227+
this.readGeneralThrottleMetrics()
228+
this.firstThrottlingCollected <- true
229+
for range throttlerMetricsTick {
230+
this.readGeneralThrottleMetrics()
231+
}
232+
}()
196233
}
197234

198235
// initiateThrottler initiates the throttle ticker and sets the basic behavior of throttling.
199236
func (this *Migrator) initiateThrottler() error {
200-
throttlerTick := time.Tick(1 * time.Second)
237+
throttlerTick := time.Tick(100 * time.Millisecond)
201238

202239
throttlerFunction := func() {
203240
alreadyThrottling, currentReason := this.migrationContext.IsThrottled()
@@ -453,16 +490,15 @@ func (this *Migrator) Migrate() (err error) {
453490
if err := this.countTableRows(); err != nil {
454491
return err
455492
}
456-
457493
if err := this.addDMLEventsListener(); err != nil {
458494
return err
459495
}
460-
go this.initiateHeartbeatReader()
461-
go this.initiateControlReplicasReader()
462-
463496
if err := this.applier.ReadMigrationRangeValues(); err != nil {
464497
return err
465498
}
499+
go this.initiateThrottlerMetrics()
500+
log.Infof("Waiting for first throttle metrics to be collected")
501+
<-this.firstThrottlingCollected
466502
go this.initiateThrottler()
467503
if err := this.hooksExecutor.onBeforeRowCopy(); err != nil {
468504
return err
@@ -1206,6 +1242,11 @@ func (this *Migrator) addDMLEventsListener() error {
12061242
return err
12071243
}
12081244

1245+
func (this *Migrator) initiateThrottler() error {
1246+
this.throttler = NewThrottler(this.panicAbort)
1247+
return nil
1248+
}
1249+
12091250
func (this *Migrator) initiateApplier() error {
12101251
this.applier = NewApplier()
12111252
if err := this.applier.InitDBConnections(); err != nil {

0 commit comments

Comments
 (0)