Skip to content

Commit ad3bb94

Browse files
timvaillancourtdm-2
authored andcommitted
Add context/timeout to HTTP throttle check (github#1131)
* Add context/timeout to HTTP throttle check * Dont run `.GetThrottleHTTPInterval()` on every loop * Update help message * Var rename * 2022 * Add timeout flag * Add unix/tcp server commands, use ParseInt() for string->int64 * Var rename * Re-check http timeout on every loop iteration * Remove stale comment * Make throttle interval idempotent * var rename * Usage grammar * Make http timeout idempotent too * Parse time.Duration once * Move timeout to NewThrottler * Help update * Set User-Agent header * Re-add newline Co-authored-by: dm-2 <[email protected]>
1 parent 5acc5cc commit ad3bb94

File tree

5 files changed

+34
-8
lines changed

5 files changed

+34
-8
lines changed

.golangci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ linters:
99
enable:
1010
- gosimple
1111
- govet
12-
- unused
12+
- noctx
1313
- rowserrcheck
1414
- sqlclosecheck
15+
- unused
16+

go/base/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,9 @@ type MigrationContext struct {
185185
CurrentLag int64
186186
currentProgress uint64
187187
etaNanoseonds int64
188+
ThrottleHTTPIntervalMillis int64
188189
ThrottleHTTPStatusCode int64
190+
ThrottleHTTPTimeoutMillis int64
189191
controlReplicasLagResult mysql.ReplicationLagResult
190192
TotalRowsCopied int64
191193
TotalDMLEventsApplied int64

go/cmd/gh-ost/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2016 GitHub Inc.
2+
Copyright 2022 GitHub Inc.
33
See https://github.com/github/gh-ost/blob/master/LICENSE
44
*/
55

@@ -111,6 +111,8 @@ func main() {
111111
throttleControlReplicas := flag.String("throttle-control-replicas", "", "List of replicas on which to check for lag; comma delimited. Example: myhost1.com:3306,myhost2.com,myhost3.com:3307")
112112
throttleQuery := flag.String("throttle-query", "", "when given, issued (every second) to check if operation should throttle. Expecting to return zero for no-throttle, >0 for throttle. Query is issued on the migrated server. Make sure this query is lightweight")
113113
throttleHTTP := flag.String("throttle-http", "", "when given, gh-ost checks given URL via HEAD request; any response code other than 200 (OK) causes throttling; make sure it has low latency response")
114+
flag.Int64Var(&migrationContext.ThrottleHTTPIntervalMillis, "throttle-http-interval-millis", 100, "Number of milliseconds to wait before triggering another HTTP throttle check")
115+
flag.Int64Var(&migrationContext.ThrottleHTTPTimeoutMillis, "throttle-http-timeout-millis", 1000, "Number of milliseconds to use as an HTTP throttle check timeout")
114116
ignoreHTTPErrors := flag.Bool("ignore-http-errors", false, "ignore HTTP connection errors during throttle check")
115117
heartbeatIntervalMillis := flag.Int64("heartbeat-interval-millis", 100, "how frequently would gh-ost inject a heartbeat value")
116118
flag.StringVar(&migrationContext.ThrottleFlagFile, "throttle-flag-file", "", "operation pauses when this file exists; hint: use a file that is specific to the table being altered")
@@ -299,7 +301,7 @@ func main() {
299301
log.Infof("starting gh-ost %+v", AppVersion)
300302
acceptSignals(migrationContext)
301303

302-
migrator := logic.NewMigrator(migrationContext)
304+
migrator := logic.NewMigrator(migrationContext, AppVersion)
303305
err := migrator.Migrate()
304306
if err != nil {
305307
migrator.ExecOnFailureHook()

go/logic/migrator.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const (
6262

6363
// Migrator is the main schema migration flow manager.
6464
type Migrator struct {
65+
appVersion string
6566
parser *sql.AlterTableParser
6667
inspector *Inspector
6768
applier *Applier
@@ -87,8 +88,9 @@ type Migrator struct {
8788
finishedMigrating int64
8889
}
8990

90-
func NewMigrator(context *base.MigrationContext) *Migrator {
91+
func NewMigrator(context *base.MigrationContext, appVersion string) *Migrator {
9192
migrator := &Migrator{
93+
appVersion: appVersion,
9294
migrationContext: context,
9395
parser: sql.NewAlterTableParser(),
9496
ghostTableMigrated: make(chan bool),
@@ -1074,7 +1076,7 @@ func (this *Migrator) addDMLEventsListener() error {
10741076

10751077
// initiateThrottler kicks in the throttling collection and the throttling checks.
10761078
func (this *Migrator) initiateThrottler() error {
1077-
this.throttler = NewThrottler(this.migrationContext, this.applier, this.inspector)
1079+
this.throttler = NewThrottler(this.migrationContext, this.applier, this.inspector, this.appVersion)
10781080

10791081
go this.throttler.initiateThrottlerCollection(this.firstThrottlingCollected)
10801082
this.migrationContext.Log.Infof("Waiting for first throttle metrics to be collected")

go/logic/throttler.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package logic
77

88
import (
9+
"context"
910
"fmt"
1011
"net/http"
1112
"strings"
@@ -42,16 +43,22 @@ const frenoMagicHint = "freno"
4243
// Throttler collects metrics related to throttling and makes informed decision
4344
// whether throttling should take place.
4445
type Throttler struct {
46+
appVersion string
4547
migrationContext *base.MigrationContext
4648
applier *Applier
49+
httpClient *http.Client
50+
httpClientTimeout time.Duration
4751
inspector *Inspector
4852
finishedMigrating int64
4953
}
5054

51-
func NewThrottler(migrationContext *base.MigrationContext, applier *Applier, inspector *Inspector) *Throttler {
55+
func NewThrottler(migrationContext *base.MigrationContext, applier *Applier, inspector *Inspector, appVersion string) *Throttler {
5256
return &Throttler{
57+
appVersion: appVersion,
5358
migrationContext: migrationContext,
5459
applier: applier,
60+
httpClient: &http.Client{},
61+
httpClientTimeout: time.Duration(migrationContext.ThrottleHTTPTimeoutMillis) * time.Millisecond,
5562
inspector: inspector,
5663
finishedMigrating: 0,
5764
}
@@ -287,7 +294,17 @@ func (this *Throttler) collectThrottleHTTPStatus(firstThrottlingCollected chan<-
287294
if url == "" {
288295
return true, nil
289296
}
290-
resp, err := http.Head(url)
297+
298+
ctx, cancel := context.WithTimeout(context.Background(), this.httpClientTimeout)
299+
defer cancel()
300+
301+
req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
302+
if err != nil {
303+
return false, err
304+
}
305+
req.Header.Set("User-Agent", fmt.Sprintf("gh-ost/%s", this.appVersion))
306+
307+
resp, err := this.httpClient.Do(req)
291308
if err != nil {
292309
return false, err
293310
}
@@ -305,7 +322,8 @@ func (this *Throttler) collectThrottleHTTPStatus(firstThrottlingCollected chan<-
305322

306323
firstThrottlingCollected <- true
307324

308-
ticker := time.NewTicker(100 * time.Millisecond)
325+
collectInterval := time.Duration(this.migrationContext.ThrottleHTTPIntervalMillis) * time.Millisecond
326+
ticker := time.NewTicker(collectInterval)
309327
defer ticker.Stop()
310328
for range ticker.C {
311329
if atomic.LoadInt64(&this.finishedMigrating) > 0 {

0 commit comments

Comments
 (0)