Skip to content

Commit 9197eed

Browse files
author
Shlomi Noach
authored
Merge pull request #73 from github/throttle-critical
Throttling & critical load
2 parents b31ba4e + 23cb8ea commit 9197eed

File tree

14 files changed

+381
-339
lines changed

14 files changed

+381
-339
lines changed

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22
#
33
#
4-
RELEASE_VERSION="0.9.4"
4+
RELEASE_VERSION="0.9.5"
55

66
buildpath=/tmp/gh-ost
77
target=gh-ost

go/base/context.go

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package base
77

88
import (
99
"fmt"
10-
"strconv"
1110
"strings"
1211
"sync"
1312
"sync/atomic"
@@ -65,9 +64,10 @@ type MigrationContext struct {
6564
ThrottleControlReplicaKeys *mysql.InstanceKeyMap
6665
ThrottleFlagFile string
6766
ThrottleAdditionalFlagFile string
67+
ThrottleQuery string
6868
ThrottleCommandedByUser int64
69-
maxLoad map[string]int64
70-
maxLoadMutex *sync.Mutex
69+
maxLoad LoadMap
70+
criticalLoad LoadMap
7171
PostponeCutOverFlagFile string
7272
SwapTablesTimeoutSeconds int64
7373
PanicFlagFile string
@@ -148,8 +148,8 @@ func newMigrationContext() *MigrationContext {
148148
ApplierConnectionConfig: mysql.NewConnectionConfig(),
149149
MaxLagMillisecondsThrottleThreshold: 1000,
150150
SwapTablesTimeoutSeconds: 3,
151-
maxLoad: make(map[string]int64),
152-
maxLoadMutex: &sync.Mutex{},
151+
maxLoad: NewLoadMap(),
152+
criticalLoad: NewLoadMap(),
153153
throttleMutex: &sync.Mutex{},
154154
ThrottleControlReplicaKeys: mysql.NewInstanceKeyMap(),
155155
configMutex: &sync.Mutex{},
@@ -278,46 +278,64 @@ func (this *MigrationContext) IsThrottled() (bool, string) {
278278
return this.isThrottled, this.throttleReason
279279
}
280280

281-
func (this *MigrationContext) GetMaxLoad() map[string]int64 {
282-
this.maxLoadMutex.Lock()
283-
defer this.maxLoadMutex.Unlock()
281+
func (this *MigrationContext) GetThrottleQuery() string {
282+
var query string
284283

285-
tmpMaxLoadMap := make(map[string]int64)
286-
for k, v := range this.maxLoad {
287-
tmpMaxLoadMap[k] = v
288-
}
289-
return tmpMaxLoadMap
284+
this.throttleMutex.Lock()
285+
defer this.throttleMutex.Unlock()
286+
287+
query = this.ThrottleQuery
288+
return query
289+
}
290+
291+
func (this *MigrationContext) SetThrottleQuery(newQuery string) {
292+
this.throttleMutex.Lock()
293+
defer this.throttleMutex.Unlock()
294+
295+
this.ThrottleQuery = newQuery
296+
}
297+
298+
func (this *MigrationContext) GetMaxLoad() LoadMap {
299+
this.throttleMutex.Lock()
300+
defer this.throttleMutex.Unlock()
301+
302+
return this.maxLoad.Duplicate()
303+
}
304+
305+
func (this *MigrationContext) GetCriticalLoad() LoadMap {
306+
this.throttleMutex.Lock()
307+
defer this.throttleMutex.Unlock()
308+
309+
return this.criticalLoad.Duplicate()
290310
}
291311

292312
// ReadMaxLoad parses the `--max-load` flag, which is in multiple key-value format,
293313
// such as: 'Threads_running=100,Threads_connected=500'
294314
// It only applies changes in case there's no parsing error.
295315
func (this *MigrationContext) ReadMaxLoad(maxLoadList string) error {
296-
if maxLoadList == "" {
297-
return nil
316+
loadMap, err := ParseLoadMap(maxLoadList)
317+
if err != nil {
318+
return err
298319
}
299-
this.maxLoadMutex.Lock()
300-
defer this.maxLoadMutex.Unlock()
320+
this.throttleMutex.Lock()
321+
defer this.throttleMutex.Unlock()
301322

302-
tmpMaxLoadMap := make(map[string]int64)
323+
this.maxLoad = loadMap
324+
return nil
325+
}
303326

304-
maxLoadConditions := strings.Split(maxLoadList, ",")
305-
for _, maxLoadCondition := range maxLoadConditions {
306-
maxLoadTokens := strings.Split(maxLoadCondition, "=")
307-
if len(maxLoadTokens) != 2 {
308-
return fmt.Errorf("Error parsing max-load condition: %s", maxLoadCondition)
309-
}
310-
if maxLoadTokens[0] == "" {
311-
return fmt.Errorf("Error parsing status variable in max-load condition: %s", maxLoadCondition)
312-
}
313-
if n, err := strconv.ParseInt(maxLoadTokens[1], 10, 0); err != nil {
314-
return fmt.Errorf("Error parsing numeric value in max-load condition: %s", maxLoadCondition)
315-
} else {
316-
tmpMaxLoadMap[maxLoadTokens[0]] = n
317-
}
327+
// ReadMaxLoad parses the `--max-load` flag, which is in multiple key-value format,
328+
// such as: 'Threads_running=100,Threads_connected=500'
329+
// It only applies changes in case there's no parsing error.
330+
func (this *MigrationContext) ReadCriticalLoad(criticalLoadList string) error {
331+
loadMap, err := ParseLoadMap(criticalLoadList)
332+
if err != nil {
333+
return err
318334
}
335+
this.throttleMutex.Lock()
336+
defer this.throttleMutex.Unlock()
319337

320-
this.maxLoad = tmpMaxLoadMap
338+
this.criticalLoad = loadMap
321339
return nil
322340
}
323341

go/base/load_map.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright 2016 GitHub Inc.
3+
See https://github.com/github/gh-ost/blob/master/LICENSE
4+
*/
5+
6+
package base
7+
8+
import (
9+
"fmt"
10+
"sort"
11+
"strconv"
12+
"strings"
13+
)
14+
15+
// LoadMap is a mapping of status variable & threshold
16+
// e.g. [Threads_connected: 100, Threads_running: 50]
17+
type LoadMap map[string]int64
18+
19+
func NewLoadMap() LoadMap {
20+
result := make(map[string]int64)
21+
return result
22+
}
23+
24+
// NewLoadMap parses a `--*-load` flag (e.g. `--max-load`), which is in multiple
25+
// key-value format, such as:
26+
// 'Threads_running=100,Threads_connected=500'
27+
func ParseLoadMap(loadList string) (LoadMap, error) {
28+
result := NewLoadMap()
29+
if loadList == "" {
30+
return result, nil
31+
}
32+
33+
loadConditions := strings.Split(loadList, ",")
34+
for _, loadCondition := range loadConditions {
35+
loadTokens := strings.Split(loadCondition, "=")
36+
if len(loadTokens) != 2 {
37+
return result, fmt.Errorf("Error parsing load condition: %s", loadCondition)
38+
}
39+
if loadTokens[0] == "" {
40+
return result, fmt.Errorf("Error parsing status variable in load condition: %s", loadCondition)
41+
}
42+
if n, err := strconv.ParseInt(loadTokens[1], 10, 0); err != nil {
43+
return result, fmt.Errorf("Error parsing numeric value in load condition: %s", loadCondition)
44+
} else {
45+
result[loadTokens[0]] = n
46+
}
47+
}
48+
49+
return result, nil
50+
}
51+
52+
// Duplicate creates a clone of this map
53+
func (this *LoadMap) Duplicate() LoadMap {
54+
dup := make(map[string]int64)
55+
for k, v := range *this {
56+
dup[k] = v
57+
}
58+
return dup
59+
}
60+
61+
// String() returns a string representation of this map
62+
func (this *LoadMap) String() string {
63+
tokens := []string{}
64+
for key, val := range *this {
65+
token := fmt.Sprintf("%s=%d", key, val)
66+
tokens = append(tokens, token)
67+
}
68+
sort.Strings(tokens)
69+
return strings.Join(tokens, ",")
70+
}

go/base/load_map_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2016 GitHub Inc.
3+
See https://github.com/github/gh-ost/blob/master/LICENSE
4+
*/
5+
6+
package base
7+
8+
import (
9+
"testing"
10+
11+
"github.com/outbrain/golib/log"
12+
test "github.com/outbrain/golib/tests"
13+
)
14+
15+
func init() {
16+
log.SetLevel(log.ERROR)
17+
}
18+
19+
func TestParseLoadMap(t *testing.T) {
20+
{
21+
loadList := ""
22+
m, err := ParseLoadMap(loadList)
23+
test.S(t).ExpectNil(err)
24+
test.S(t).ExpectEquals(len(m), 0)
25+
}
26+
{
27+
loadList := "threads_running=20,threads_connected=10"
28+
m, err := ParseLoadMap(loadList)
29+
test.S(t).ExpectNil(err)
30+
test.S(t).ExpectEquals(len(m), 2)
31+
test.S(t).ExpectEquals(m["threads_running"], int64(20))
32+
test.S(t).ExpectEquals(m["threads_connected"], int64(10))
33+
}
34+
{
35+
loadList := "threads_running=20=30,threads_connected=10"
36+
_, err := ParseLoadMap(loadList)
37+
test.S(t).ExpectNotNil(err)
38+
}
39+
{
40+
loadList := "threads_running=20,threads_connected"
41+
_, err := ParseLoadMap(loadList)
42+
test.S(t).ExpectNotNil(err)
43+
}
44+
}
45+
46+
func TestString(t *testing.T) {
47+
{
48+
m, _ := ParseLoadMap("")
49+
s := m.String()
50+
test.S(t).ExpectEquals(s, "")
51+
}
52+
{
53+
loadList := "threads_running=20,threads_connected=10"
54+
m, _ := ParseLoadMap(loadList)
55+
s := m.String()
56+
test.S(t).ExpectEquals(s, "threads_connected=10,threads_running=20")
57+
}
58+
}

go/base/utils.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ func FileExists(fileName string) bool {
3333
return false
3434
}
3535

36+
// StringContainsAll returns true if `s` contains all non empty given `substrings`
37+
// The function returns `false` if no non-empty arguments are given.
3638
func StringContainsAll(s string, substrings ...string) bool {
3739
nonEmptyStringsFound := false
3840
for _, substring := range substrings {
39-
if s == "" {
41+
if substring == "" {
4042
continue
4143
}
4244
if strings.Contains(s, substring) {

go/base/utils_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2016 GitHub Inc.
3+
See https://github.com/github/gh-ost/blob/master/LICENSE
4+
*/
5+
6+
package base
7+
8+
import (
9+
"testing"
10+
11+
"github.com/outbrain/golib/log"
12+
test "github.com/outbrain/golib/tests"
13+
)
14+
15+
func init() {
16+
log.SetLevel(log.ERROR)
17+
}
18+
19+
func TestStringContainsAll(t *testing.T) {
20+
s := `insert,delete,update`
21+
22+
test.S(t).ExpectFalse(StringContainsAll(s))
23+
test.S(t).ExpectFalse(StringContainsAll(s, ""))
24+
test.S(t).ExpectFalse(StringContainsAll(s, "drop"))
25+
test.S(t).ExpectTrue(StringContainsAll(s, "insert"))
26+
test.S(t).ExpectFalse(StringContainsAll(s, "insert", "drop"))
27+
test.S(t).ExpectTrue(StringContainsAll(s, "insert", ""))
28+
test.S(t).ExpectTrue(StringContainsAll(s, "insert", "update", "delete"))
29+
}

0 commit comments

Comments
 (0)