diff --git a/build.sh b/build.sh index fb7fc1d..9c209e1 100644 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/bash # # -RELEASE_VERSION="0.9.8" +RELEASE_VERSION="0.9.9" buildpath=/tmp/gh-ost target=gh-ost diff --git a/doc/interactive-commands.md b/doc/interactive-commands.md index b6455bf..563804c 100644 --- a/doc/interactive-commands.md +++ b/doc/interactive-commands.md @@ -21,6 +21,7 @@ replication lag on to determine throttling - `max-load=`: modify the `max-load` config; applies on next running copy-iteration The `max-load` format must be: `some_status=[,some_status=...]`. For example: `Threads_running=50,threads_connected=1000`, and you would then write/echo `max-load=Threads_running=50,threads_connected=1000` to the socket. - `critical-load=`: change critical load setting (exceeding given thresholds causes panic and abort) +- `nice-ratio=`: change _nice_ ratio: 0 for aggressive, positive integer `n`: for any unit of time spent copying rows, spend `n` units of time sleeping. - `throttle-query`: change throttle query - `throttle-control-replicas`: change list of throttle-control replicas, these are replicas `gh-ost` will cehck - `throttle`: force migration suspend diff --git a/go/base/context.go b/go/base/context.go index 4d7ebb7..5d9b1ae 100644 --- a/go/base/context.go +++ b/go/base/context.go @@ -58,6 +58,7 @@ type MigrationContext struct { defaultNumRetries int64 ChunkSize int64 + NiceRatio int64 MaxLagMillisecondsThrottleThreshold int64 ReplictionLagQuery string ThrottleControlReplicaKeys *mysql.InstanceKeyMap diff --git a/go/cmd/gh-ost/main.go b/go/cmd/gh-ost/main.go index f898cca..572457f 100644 --- a/go/cmd/gh-ost/main.go +++ b/go/cmd/gh-ost/main.go @@ -71,6 +71,7 @@ func main() { flag.BoolVar(&migrationContext.SwitchToRowBinlogFormat, "switch-to-rbr", false, "let this tool automatically switch binary log format to 'ROW' on the replica, if needed. The format will NOT be switched back. I'm too scared to do that, and wish to protect you if you happen to execute another migration while this one is running") chunkSize := flag.Int64("chunk-size", 1000, "amount of rows to handle in each iteration (allowed range: 100-100,000)") defaultRetries := flag.Int64("default-retries", 60, "Default number of retries for various operations before panicking") + flag.Int64Var(&migrationContext.NiceRatio, "nice-ratio", 0, "force being 'nice', imply sleep time per chunk time. Example values: 0 is aggressive. 3: for every ms spend in a rowcopy chunk, spend 3ms sleeping immediately after") flag.Int64Var(&migrationContext.MaxLagMillisecondsThrottleThreshold, "max-lag-millis", 1500, "replication lag at which to throttle operation") flag.StringVar(&migrationContext.ReplictionLagQuery, "replication-lag-query", "", "Query that detects replication lag in seconds. Result can be a floating point (by default gh-ost issues SHOW SLAVE STATUS and reads Seconds_behind_master). If you're using pt-heartbeat, query would be something like: SELECT ROUND(UNIX_TIMESTAMP() - MAX(UNIX_TIMESTAMP(ts))) AS delay FROM my_schema.heartbeat") diff --git a/go/logic/migrator.go b/go/logic/migrator.go index bfd6849..17ff1c8 100644 --- a/go/logic/migrator.go +++ b/go/logic/migrator.go @@ -790,6 +790,7 @@ func (this *Migrator) onServerCommand(command string, writer *bufio.Writer) (err fmt.Fprintln(writer, `available commands: status # Print a status message chunk-size= # Set a new chunk-size +nice-ratio= # Set a new nice-ratio, integer (0 is agrressive) critical-load= # Set a new set of max-load thresholds max-load= # Set a new set of max-load thresholds throttle-query= # Set a new throttle-query @@ -813,6 +814,16 @@ help # This message this.printStatus(ForcePrintStatusAndHint, writer) } } + case "nice-ratio": + { + if niceRatio, err := strconv.Atoi(arg); err != nil { + fmt.Fprintf(writer, "%s\n", err.Error()) + return log.Errore(err) + } else { + atomic.StoreInt64(&this.migrationContext.NiceRatio, int64(niceRatio)) + this.printStatus(ForcePrintStatusAndHint, writer) + } + } case "max-load": { if err := this.migrationContext.ReadMaxLoad(arg); err != nil { @@ -963,11 +974,12 @@ func (this *Migrator) printMigrationStatusHint(writers ...io.Writer) { )) maxLoad := this.migrationContext.GetMaxLoad() criticalLoad := this.migrationContext.GetCriticalLoad() - fmt.Fprintln(w, fmt.Sprintf("# chunk-size: %+v; max lag: %+vms; max-load: %s; critical-load: %s", + fmt.Fprintln(w, fmt.Sprintf("# chunk-size: %+v; max lag: %+vms; max-load: %s; critical-load: %s; nice-ratio: %d", atomic.LoadInt64(&this.migrationContext.ChunkSize), atomic.LoadInt64(&this.migrationContext.MaxLagMillisecondsThrottleThreshold), maxLoad.String(), criticalLoad.String(), + atomic.LoadInt64(&this.migrationContext.NiceRatio), )) if this.migrationContext.ThrottleFlagFile != "" { fmt.Fprintln(w, fmt.Sprintf("# Throttle flag file: %+v", @@ -1264,10 +1276,16 @@ func (this *Migrator) executeWriteFuncs() error { select { case copyRowsFunc := <-this.copyRowsQueue: { + copyRowsStartTime := time.Now() // Retries are handled within the copyRowsFunc if err := copyRowsFunc(); err != nil { return log.Errore(err) } + if niceRatio := atomic.LoadInt64(&this.migrationContext.NiceRatio); niceRatio > 0 { + copyRowsDuration := time.Now().Sub(copyRowsStartTime) + sleepTime := copyRowsDuration * time.Duration(niceRatio) + time.Sleep(sleepTime) + } } default: {