Files
tidb/executor/oomtest/oom_test.go

243 lines
8.5 KiB
Go

// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Note: All the tests in this file will be executed sequentially.
package oomtest
import (
"os"
"strings"
"sync"
"testing"
"github.com/pingcap/failpoint"
"github.com/pingcap/log"
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/testkit/testsetup"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestMain(m *testing.M) {
testsetup.SetupForCommonTest()
registerHook()
opts := []goleak.Option{
goleak.IgnoreTopFunction("github.com/golang/glog.(*loggingT).flushDaemon"),
goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"),
goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"),
}
goleak.VerifyTestMain(m, opts...)
}
func TestMemTracker4UpdateExec(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t_MemTracker4UpdateExec (id int, a int, b int, index idx_a(`a`))")
log.SetLevel(zap.InfoLevel)
oom.SetTracker("")
tk.MustExec("insert into t_MemTracker4UpdateExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "schemaLeaseChecker is not set for this transaction", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 244
tk.MustExec("update t_MemTracker4UpdateExec set a = 4")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
}
func TestMemTracker4InsertAndReplaceExec(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t (id int, a int, b int, index idx_a(`a`))")
tk.MustExec("insert into t values (1,1,1), (2,2,2), (3,3,3)")
tk.MustExec("create table t_MemTracker4InsertAndReplaceExec (id int, a int, b int, index idx_a(`a`))")
log.SetLevel(zap.InfoLevel)
oom.SetTracker("")
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "schemaLeaseChecker is not set for this transaction", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
oom.SetTracker("")
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
oom.SetTracker("")
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec select * from t")
require.Equal(t, "", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec select * from t")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
oom.SetTracker("")
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec select * from t")
require.Equal(t, "", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec select * from t")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
tk.Session().GetSessionVars().DMLBatchSize = 1
tk.Session().GetSessionVars().BatchInsert = true
oom.SetTracker("")
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("insert into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
oom.SetTracker("")
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.MustExec("replace into t_MemTracker4InsertAndReplaceExec values (1,1,1), (2,2,2), (3,3,3)")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
tk.Session().GetSessionVars().MemQuotaQuery = -1
}
func TestMemTracker4DeleteExec(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_cost_model_version=1")
tk.MustExec("create table MemTracker4DeleteExec1 (id int, a int, b int, index idx_a(`a`))")
tk.MustExec("create table MemTracker4DeleteExec2 (id int, a int, b int, index idx_a(`a`))")
// delete from single table
log.SetLevel(zap.InfoLevel)
tk.MustExec("insert into MemTracker4DeleteExec1 values(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5)")
oom.SetTracker("")
tk.MustExec("delete from MemTracker4DeleteExec1")
require.Equal(t, "", oom.GetTracker())
tk.MustExec("insert into MemTracker4DeleteExec1 values (1,1,1), (2,2,2), (3,3,3)")
tk.Session().GetSessionVars().MemQuotaQuery = 1
tk.Session().GetSessionVars().MemTracker.SetBytesLimit(1)
tk.MustExec("delete from MemTracker4DeleteExec1")
require.Equal(t, "expensive_query during bootstrap phase", oom.GetTracker())
// delete from multiple table
tk.Session().GetSessionVars().MemQuotaQuery = 100000
tk.MustExec("insert into MemTracker4DeleteExec1 values(1,1,1)")
tk.MustExec("insert into MemTracker4DeleteExec2 values(1,1,1)")
oom.SetTracker("")
tk.MustExec("delete MemTracker4DeleteExec1, MemTracker4DeleteExec2 from MemTracker4DeleteExec1 join MemTracker4DeleteExec2 on MemTracker4DeleteExec1.a=MemTracker4DeleteExec2.a")
require.Equal(t, "", oom.GetTracker())
tk.MustExec("insert into MemTracker4DeleteExec1 values(1,1,1)")
tk.MustExec("insert into MemTracker4DeleteExec2 values(1,1,1)")
oom.SetTracker("")
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/disableFixedRowCountHint", "return"))
defer func() {
require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/disableFixedRowCountHint"))
}()
tk.Session().GetSessionVars().EnabledRateLimitAction = true
tk.Session().GetSessionVars().MemQuotaQuery = 10000
tk.MustExec("delete MemTracker4DeleteExec1, MemTracker4DeleteExec2 from MemTracker4DeleteExec1 join MemTracker4DeleteExec2 on MemTracker4DeleteExec1.a=MemTracker4DeleteExec2.a")
require.Equal(t, "memory exceeds quota, rateLimitAction delegate to fallback action", oom.GetTracker())
}
var oom *oomCapture
func registerHook() {
conf := &log.Config{Level: os.Getenv("log_level"), File: log.FileLogConfig{}}
_, r, _ := log.InitLogger(conf)
oom = &oomCapture{r.Core, "", sync.Mutex{}}
lg := zap.New(oom)
log.ReplaceGlobals(lg, r)
}
type oomCapture struct {
zapcore.Core
tracker string
mu sync.Mutex
}
func (h *oomCapture) SetTracker(tracker string) {
h.mu.Lock()
defer h.mu.Unlock()
h.tracker = tracker
}
func (h *oomCapture) GetTracker() string {
h.mu.Lock()
defer h.mu.Unlock()
return h.tracker
}
func (h *oomCapture) Write(entry zapcore.Entry, fields []zapcore.Field) error {
if entry.Message == "memory exceeds quota" {
err, _ := fields[0].Interface.(error)
str := err.Error()
begin := strings.Index(str, "8001]")
if begin == -1 {
panic("begin not found")
}
end := strings.Index(str, " holds")
if end == -1 {
panic("end not found")
}
h.tracker = str[begin+len("8001]") : end]
return nil
}
// They are just common background task and not related to the oom.
if entry.Message == "SetTiFlashGroupConfig" ||
entry.Message == "record table item load status failed due to not finding item" {
return nil
}
h.mu.Lock()
h.tracker = entry.Message
h.mu.Unlock()
return nil
}
func (h *oomCapture) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if h.Enabled(e.Level) {
return ce.AddCore(e, h)
}
return ce
}