Skip to content

Commit

Permalink
Merge branch 'forbidBaselineEvolution' of https://github.com/Reminisc…
Browse files Browse the repository at this point in the history
…ent/tidb into forbidBaselineEvolution
  • Loading branch information
Reminiscent committed Aug 5, 2021
2 parents af6a7e3 + 68a0b01 commit 9c6f681
Show file tree
Hide file tree
Showing 37 changed files with 1,571 additions and 655 deletions.
173 changes: 150 additions & 23 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

. "github.com/pingcap/check"
"github.com/pingcap/failpoint"
"github.com/pingcap/parser"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
Expand Down Expand Up @@ -61,6 +62,7 @@ func TestT(t *testing.T) {
}

var _ = Suite(&testSuite{})
var _ = SerialSuites(&testSerialSuite{})

type testSuite struct {
cluster testutils.Cluster
Expand Down Expand Up @@ -154,6 +156,44 @@ func (s *testSuite) cleanBindingEnv(tk *testkit.TestKit) {
s.domain.BindHandle().Clear()
}

type testSerialSuite struct {
cluster testutils.Cluster
store kv.Storage
domain *domain.Domain
}

func (s *testSerialSuite) SetUpSuite(c *C) {
flag.Lookup("mockTikv")
useMockTikv := *mockTikv
if useMockTikv {
store, err := mockstore.NewMockStore(
mockstore.WithClusterInspector(func(c testutils.Cluster) {
mockstore.BootstrapWithSingleStore(c)
s.cluster = c
}),
)
c.Assert(err, IsNil)
s.store = store
session.SetSchemaLease(0)
session.DisableStats4Test()
}
bindinfo.Lease = 0
d, err := session.BootstrapSession(s.store)
c.Assert(err, IsNil)
d.SetStatsUpdating(true)
s.domain = d
}

func (s *testSerialSuite) TearDownSuite(c *C) {
s.domain.Close()
s.store.Close()
}

func (s *testSerialSuite) cleanBindingEnv(tk *testkit.TestKit) {
tk.MustExec("delete from mysql.bind_info where source != 'builtin'")
s.domain.BindHandle().Clear()
}

func normalizeWithDefaultDB(c *C, sql, db string) (string, string) {
testParser := parser.New()
stmt, err := testParser.ParseOneStmt(sql, "", "")
Expand Down Expand Up @@ -1066,6 +1106,53 @@ func (s *testSuite) TestBaselineDBLowerCase(c *C) {
))
}

func (s *testSuite) TestShowGlobalBindings(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
stmtsummary.StmtSummaryByDigestMap.Clear()
tk.MustExec("drop database if exists SPM")
tk.MustExec("create database SPM")
tk.MustExec("use SPM")
tk.MustExec("create table t(a int, b int, key(a))")
tk.MustExec("create table t0(a int, b int, key(a))")
c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue)
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 0)
// Simulate existing bindings in the mysql.bind_info.
tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t`', 'select * from `spm` . `t` USE INDEX (`a`)', 'SPM', 'using', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" +
bindinfo.Manual + "')")
tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t0`', 'select * from `spm` . `t0` USE INDEX (`a`)', 'SPM', 'using', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" +
bindinfo.Manual + "')")
tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t`', 'select /*+ use_index(`t` `a`)*/ * from `spm` . `t`', 'SPM', 'using', '2000-01-03 09:00:00', '2000-01-03 09:00:00', '', '','" +
bindinfo.Manual + "')")
tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t0`', 'select /*+ use_index(`t0` `a`)*/ * from `spm` . `t0`', 'SPM', 'using', '2000-01-04 09:00:00', '2000-01-04 09:00:00', '', '','" +
bindinfo.Manual + "')")
tk.MustExec("admin reload bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 4)
c.Assert(rows[0][0], Equals, "select * from `spm` . `t0`")
c.Assert(rows[0][5], Equals, "2000-01-04 09:00:00.000")
c.Assert(rows[1][0], Equals, "select * from `spm` . `t0`")
c.Assert(rows[1][5], Equals, "2000-01-02 09:00:00.000")
c.Assert(rows[2][0], Equals, "select * from `spm` . `t`")
c.Assert(rows[2][5], Equals, "2000-01-03 09:00:00.000")
c.Assert(rows[3][0], Equals, "select * from `spm` . `t`")
c.Assert(rows[3][5], Equals, "2000-01-01 09:00:00.000")

rows = tk.MustQuery("show session bindings").Rows()
c.Assert(len(rows), Equals, 0)
tk.MustExec("create session binding for select a from t using select a from t")
tk.MustExec("create session binding for select a from t0 using select a from t0")
tk.MustExec("create session binding for select b from t using select b from t")
tk.MustExec("create session binding for select b from t0 using select b from t0")
rows = tk.MustQuery("show session bindings").Rows()
c.Assert(len(rows), Equals, 4)
c.Assert(rows[0][0], Equals, "select `b` from `spm` . `t0`")
c.Assert(rows[1][0], Equals, "select `b` from `spm` . `t`")
c.Assert(rows[2][0], Equals, "select `a` from `spm` . `t0`")
c.Assert(rows[3][0], Equals, "select `a` from `spm` . `t`")
}

func (s *testSuite) TestCaptureBaselinesDefaultDB(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
Expand Down Expand Up @@ -1260,13 +1347,13 @@ func (s *testSuite) TestAddEvolveTasks(c *C) {
tk.MustExec("admin flush bindings")
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
c.Assert(rows[1][3], Equals, "pending verify")
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
c.Assert(rows[0][3], Equals, "pending verify")
tk.MustExec("admin evolve bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
status := rows[1][3].(string)
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
status := rows[0][3].(string)
c.Assert(status == "using" || status == "rejected", IsTrue)
}

Expand All @@ -1284,21 +1371,12 @@ func (s *testSuite) TestRuntimeHintsInEvolveTasks(c *C) {
tk.MustExec("set @@tidb_evolve_plan_baselines=1")
tk.MustExec("create table t(a int, b int, c int, index idx_a(a), index idx_b(b), index idx_c(c))")

// these runtime hints which don't be contained by the original binding should be ignored
tk.MustExec("create global binding for select * from t where a >= 1 and b >= 1 and c = 0 using select * from t use index(idx_a) where a >= 1 and b >= 1 and c = 0")
tk.MustQuery("select /*+ MAX_EXECUTION_TIME(5000) */* from t where a >= 4 and b >= 1 and c = 0")
tk.MustQuery("select /*+ MAX_EXECUTION_TIME(5000) */ * from t where a >= 4 and b >= 1 and c = 0")
tk.MustExec("admin flush bindings")
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0") // MAX_EXECUTION_TIME is ignored

s.cleanBindingEnv(tk)
tk.MustExec("create global binding for select * from t where a >= 1 and b >= 1 and c = 0 using select /*+ MAX_EXECUTION_TIME(5000) */* from t use index(idx_a) where a >= 1 and b >= 1 and c = 0")
tk.MustQuery("select /*+ MAX_EXECUTION_TIME(5000) */* from t where a >= 4 and b >= 1 and c = 0")
tk.MustExec("admin flush bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), max_execution_time(5000)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`), max_execution_time(5000)*/ * FROM `test`.`t` WHERE `a` >= 4 AND `b` >= 1 AND `c` = 0")
}

func (s *testSuite) TestBindingCache(c *C) {
Expand Down Expand Up @@ -1770,8 +1848,8 @@ func (s *testSuite) TestReCreateBindAfterEvolvePlan(c *C) {
tk.MustExec("admin flush bindings")
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 0 AND `b` >= 0")
c.Assert(rows[1][3], Equals, "pending verify")
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` >= 0 AND `b` >= 0")
c.Assert(rows[0][3], Equals, "pending verify")

tk.MustExec("create global binding for select * from t where a >= 1 and b >= 1 using select * from t use index(idx_b) where a >= 1 and b >= 1")
rows = tk.MustQuery("show global bindings").Rows()
Expand Down Expand Up @@ -2106,15 +2184,15 @@ func (s *testSuite) TestIssue20417(c *C) {
tk.MustExec("admin flush bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), use_index(`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
c.Assert(rows[1][3], Equals, "pending verify")
c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
c.Assert(rows[0][3], Equals, "pending verify")
tk.MustExec("admin evolve bindings")
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 2)
c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[1][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`), use_index(`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
status := rows[1][3].(string)
c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `c` = ?")
c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541")
status := rows[0][3].(string)
c.Assert(status == "using" || status == "rejected", IsTrue)
tk.MustExec("set @@tidb_evolve_plan_baselines=0")
}
Expand Down Expand Up @@ -2240,3 +2318,52 @@ func (s *testSuite) TestBindingLastUpdateTime(c *C) {
c.Assert(updateTime2, Equals, updateTime)
tk.MustQuery(`show global status like 'last_plan_binding_update_time';`).Check(testkit.Rows())
}

func (s *testSuite) TestGCBindRecord(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, key(a))")

tk.MustExec("create global binding for select * from t where a = 1 using select * from t use index(a) where a = 1")
rows := tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 1)
c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` = ?")
c.Assert(rows[0][3], Equals, "using")
tk.MustQuery("select status from mysql.bind_info where original_sql = 'select * from `test` . `t` where `a` = ?'").Check(testkit.Rows(
"using",
))

h := s.domain.BindHandle()
// bindinfo.Lease is set to 0 for test env in SetUpSuite.
c.Assert(h.GCBindRecord(), IsNil)
rows = tk.MustQuery("show global bindings").Rows()
c.Assert(len(rows), Equals, 1)
c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` = ?")
c.Assert(rows[0][3], Equals, "using")
tk.MustQuery("select status from mysql.bind_info where original_sql = 'select * from `test` . `t` where `a` = ?'").Check(testkit.Rows(
"using",
))

tk.MustExec("drop global binding for select * from t where a = 1")
tk.MustQuery("show global bindings").Check(testkit.Rows())
tk.MustQuery("select status from mysql.bind_info where original_sql = 'select * from `test` . `t` where `a` = ?'").Check(testkit.Rows(
"deleted",
))
c.Assert(h.GCBindRecord(), IsNil)
tk.MustQuery("show global bindings").Check(testkit.Rows())
tk.MustQuery("select status from mysql.bind_info where original_sql = 'select * from `test` . `t` where `a` = ?'").Check(testkit.Rows())
}

func (s *testSerialSuite) TestOptimizeOnlyOnce(c *C) {
tk := testkit.NewTestKit(c, s.store)
s.cleanBindingEnv(tk)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, index idxa(a))")
tk.MustExec("create global binding for select * from t using select * from t use index(idxa)")
c.Assert(failpoint.Enable("github.com/pingcap/tidb/planner/checkOptimizeCountOne", "return"), IsNil)
tk.MustQuery("select * from t").Check(testkit.Rows())
c.Assert(failpoint.Disable("github.com/pingcap/tidb/planner/checkOptimizeCountOne"), IsNil)
}
39 changes: 39 additions & 0 deletions bindinfo/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,45 @@ func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (e
return err
}

// GCBindRecord physically removes the deleted bind records in mysql.bind_info.
func (h *BindHandle) GCBindRecord() (err error) {
h.bindInfo.Lock()
h.sctx.Lock()
defer func() {
h.sctx.Unlock()
h.bindInfo.Unlock()
}()
exec, _ := h.sctx.Context.(sqlexec.SQLExecutor)
_, err = exec.ExecuteInternal(context.TODO(), "BEGIN PESSIMISTIC")
if err != nil {
return err
}
defer func() {
if err != nil {
_, err1 := exec.ExecuteInternal(context.TODO(), "ROLLBACK")
terror.Log(err1)
return
}

_, err = exec.ExecuteInternal(context.TODO(), "COMMIT")
if err != nil {
return
}
}()

// Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances.
if err = h.lockBindInfoTable(); err != nil {
return err
}

// To make sure that all the deleted bind records have been acknowledged to all tidb,
// we only garbage collect those records with update_time before 10 leases.
updateTime := time.Now().Add(-(10 * Lease))
updateTimeStr := types.NewTime(types.FromGoTime(updateTime), mysql.TypeTimestamp, 3).String()
_, err = exec.ExecuteInternal(context.TODO(), `DELETE FROM mysql.bind_info WHERE status = 'deleted' and update_time < %?`, updateTimeStr)
return err
}

// lockBindInfoTable simulates `LOCK TABLE mysql.bind_info WRITE` by acquiring a pessimistic lock on a
// special builtin row of mysql.bind_info. Note that this function must be called with h.sctx.Lock() held.
// We can replace this implementation to normal `LOCK TABLE mysql.bind_info WRITE` if that feature is
Expand Down
25 changes: 19 additions & 6 deletions domain/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -990,12 +990,13 @@ func (do *Domain) LoadBindInfoLoop(ctxForHandle sessionctx.Context, ctxForEvolve
return err
}

do.globalBindHandleWorkerLoop()
do.handleEvolvePlanTasksLoop(ctxForEvolve)
owner := do.newOwnerManager(bindinfo.Prompt, bindinfo.OwnerKey)
do.globalBindHandleWorkerLoop(owner)
do.handleEvolvePlanTasksLoop(ctxForEvolve, owner)
return nil
}

func (do *Domain) globalBindHandleWorkerLoop() {
func (do *Domain) globalBindHandleWorkerLoop(owner owner.Manager) {
do.wg.Add(1)
go func() {
defer func() {
Expand All @@ -1004,10 +1005,15 @@ func (do *Domain) globalBindHandleWorkerLoop() {
util.Recover(metrics.LabelDomain, "globalBindHandleWorkerLoop", nil, false)
}()
bindWorkerTicker := time.NewTicker(bindinfo.Lease)
defer bindWorkerTicker.Stop()
gcBindTicker := time.NewTicker(100 * bindinfo.Lease)
defer func() {
bindWorkerTicker.Stop()
gcBindTicker.Stop()
}()
for {
select {
case <-do.exit:
owner.Cancel()
return
case <-bindWorkerTicker.C:
err := do.bindHandle.Update(false)
Expand All @@ -1019,20 +1025,27 @@ func (do *Domain) globalBindHandleWorkerLoop() {
do.bindHandle.CaptureBaselines()
}
do.bindHandle.SaveEvolveTasksToStore()
case <-gcBindTicker.C:
if !owner.IsOwner() {
continue
}
err := do.bindHandle.GCBindRecord()
if err != nil {
logutil.BgLogger().Error("GC bind record failed", zap.Error(err))
}
}
}
}()
}

func (do *Domain) handleEvolvePlanTasksLoop(ctx sessionctx.Context) {
func (do *Domain) handleEvolvePlanTasksLoop(ctx sessionctx.Context, owner owner.Manager) {
do.wg.Add(1)
go func() {
defer func() {
do.wg.Done()
logutil.BgLogger().Info("handleEvolvePlanTasksLoop exited.")
util.Recover(metrics.LabelDomain, "handleEvolvePlanTasksLoop", nil, false)
}()
owner := do.newOwnerManager(bindinfo.Prompt, bindinfo.OwnerKey)
for {
select {
case <-do.exit:
Expand Down
Loading

0 comments on commit 9c6f681

Please sign in to comment.