-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: support global memory control for tidb #37794
Changes from 30 commits
80f544f
92f8385
a0e7727
9576c18
bee18e2
6f468c9
87673bf
a5a9053
ea05d04
b198b25
46a1b10
54ba480
80c6f9e
3a19a02
e818081
a730889
74c21fa
2840262
1600d1d
757463d
795c192
75d0191
9826ae0
667f838
d2001fb
3114a47
1e46db7
cf5c4f7
089c6ac
29bf319
8fea589
57c1ada
9da15d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6063,3 +6063,66 @@ func TestTableLockPrivilege(t *testing.T) { | |
tk2.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") | ||
tk.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") | ||
} | ||
|
||
func TestGlobalMemoryControl(t *testing.T) { | ||
store, dom := testkit.CreateMockStoreAndDomain(t) | ||
|
||
tk0 := testkit.NewTestKit(t, store) | ||
tk0.MustExec("set global tidb_mem_oom_action = 'cancel'") | ||
tk0.MustExec("set global tidb_server_memory_limit = 512 << 20") | ||
tk0.MustExec("set global tidb_server_memory_limit_sess_min_size = 128") | ||
|
||
tk1 := testkit.NewTestKit(t, store) | ||
tracker1 := tk1.Session().GetSessionVars().StmtCtx.MemTracker | ||
|
||
tk2 := testkit.NewTestKit(t, store) | ||
tracker2 := tk2.Session().GetSessionVars().StmtCtx.MemTracker | ||
|
||
tk3 := testkit.NewTestKit(t, store) | ||
tracker3 := tk3.Session().GetSessionVars().StmtCtx.MemTracker | ||
|
||
sm := &testkit.MockSessionManager{ | ||
PS: []*util.ProcessInfo{tk1.Session().ShowProcess(), tk2.Session().ShowProcess(), tk3.Session().ShowProcess()}, | ||
} | ||
dom.ServerMemoryLimitHandle().SetSessionManager(sm) | ||
go dom.ServerMemoryLimitHandle().Run() | ||
|
||
tracker1.Consume(100 << 20) // 100 MB | ||
tracker2.Consume(200 << 20) // 200 MB | ||
tracker3.Consume(300 << 20) // 300 MB | ||
|
||
test := make([]int, 128<<20) // Keep 1GB HeapInUse | ||
time.Sleep(500 * time.Millisecond) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the purpose of this Sleep? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check goroutine checks the memory usage every 100ms. The Sleep() make sure that Top1Tracker can be Canceled. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's better to add some comment for it. |
||
|
||
// Kill Top1 | ||
require.False(t, tracker1.NeedKill.Load()) | ||
require.False(t, tracker2.NeedKill.Load()) | ||
require.True(t, tracker3.NeedKill.Load()) | ||
require.Equal(t, memory.MemUsageTop1Tracker.Load(), tracker3) | ||
util.WithRecovery( // Next Consume() will panic and cancel the SQL | ||
func() { | ||
tracker3.Consume(1) | ||
}, func(r interface{}) { | ||
require.True(t, strings.Contains(r.(string), "Out Of Memory Quota!")) | ||
}) | ||
tracker2.Consume(300 << 20) // Sum 500MB, Not Panic, Waiting t3 cancel finish. | ||
time.Sleep(500 * time.Millisecond) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It takes time to cancel? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait for cancel |
||
require.False(t, tracker2.NeedKill.Load()) | ||
// Kill Finished | ||
tracker3.Consume(-(300 << 20)) | ||
// Simulated SQL is Canceled and the time is updated | ||
sm.PSMu.Lock() | ||
ps := *sm.PS[2] | ||
ps.Time = time.Now() | ||
sm.PS[2] = &ps | ||
sm.PSMu.Unlock() | ||
time.Sleep(500 * time.Millisecond) | ||
// Kill the Next SQL | ||
util.WithRecovery( // Next Consume() will panic and cancel the SQL | ||
func() { | ||
tracker2.Consume(1) | ||
}, func(r interface{}) { | ||
require.True(t, strings.Contains(r.(string), "Out Of Memory Quota!")) | ||
}) | ||
require.Equal(t, test[0], 0) // Keep 1GB HeapInUse | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -716,6 +716,54 @@ var defaultSysVars = []*SysVar{ | |
return nil | ||
}, | ||
}, | ||
{Scope: ScopeGlobal, Name: TiDBServerMemoryLimit, Value: strconv.FormatUint(DefTiDBServerMemoryLimit, 10), Type: TypeUnsigned, MinValue: 0, MaxValue: math.MaxUint64, | ||
GetGlobal: func(s *SessionVars) (string, error) { | ||
return memory.ServerMemoryLimit.String(), nil | ||
}, | ||
Validation: func(s *SessionVars, normalizedValue string, originalValue string, scope ScopeFlag) (string, error) { | ||
intVal, err := strconv.ParseUint(normalizedValue, 10, 64) | ||
if err != nil { | ||
return "", err | ||
} | ||
if intVal > 0 && intVal < (512<<20) { // 512 MB | ||
s.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(TiDBServerMemoryLimit, originalValue)) | ||
intVal = 512 << 20 | ||
} | ||
return strconv.FormatUint(intVal, 10), nil | ||
Comment on lines
+723
to
+732
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not set MinValue at line 719? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0 means disable this feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK... I see. This is kind of confusing |
||
}, | ||
SetGlobal: func(s *SessionVars, val string) error { | ||
intVal, err := strconv.ParseUint(val, 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
memory.ServerMemoryLimit.Store(intVal) | ||
return nil | ||
}, | ||
}, | ||
{Scope: ScopeGlobal, Name: TiDBServerMemoryLimitSessMinSize, Value: strconv.FormatUint(DefTiDBServerMemoryLimitSessMinSize, 10), Type: TypeUnsigned, MinValue: 0, MaxValue: math.MaxUint64, | ||
GetGlobal: func(s *SessionVars) (string, error) { | ||
return memory.ServerMemoryLimitSessMinSize.String(), nil | ||
}, | ||
Validation: func(s *SessionVars, normalizedValue string, originalValue string, scope ScopeFlag) (string, error) { | ||
intVal, err := strconv.ParseUint(normalizedValue, 10, 64) | ||
if err != nil { | ||
return "", err | ||
} | ||
if intVal > 0 && intVal < 128 { // 128 Bytes | ||
s.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(TiDBServerMemoryLimitSessMinSize, originalValue)) | ||
intVal = 128 | ||
} | ||
return strconv.FormatUint(intVal, 10), nil | ||
}, | ||
SetGlobal: func(s *SessionVars, val string) error { | ||
intVal, err := strconv.ParseUint(val, 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
memory.ServerMemoryLimitSessMinSize.Store(intVal) | ||
return nil | ||
}, | ||
}, | ||
{Scope: ScopeGlobal, Name: TiDBEnableColumnTracking, Value: BoolToOnOff(DefTiDBEnableColumnTracking), Type: TypeBool, GetGlobal: func(s *SessionVars) (string, error) { | ||
return BoolToOnOff(EnableColumnTracking.Load()), nil | ||
}, SetGlobal: func(s *SessionVars, val string) error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is virtual memory instead of real allocation before you visit the data.
OS will handle the logical -> physical memory address mapping and allocate the physical pages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only simulates the situation of
heapinuse
growth.In normally, TiDB will use the memory immediately after allocation.