From e87bb52a6af2a7cfc2e0621562fcfb270eb351ed Mon Sep 17 00:00:00 2001 From: Chengpeng Yan <41809508+Reminiscent@users.noreply.github.com> Date: Tue, 15 Mar 2022 20:55:23 +0800 Subject: [PATCH] bindinfo: refactor some codes in bindinfo package (#33094) --- bindinfo/bind_record.go | 29 ++++++++++++++++++++++++----- bindinfo/handle.go | 16 ++++++++-------- bindinfo/session_handle.go | 2 +- planner/core/common_plans.go | 12 ++++++------ planner/optimize.go | 2 +- session/bootstrap.go | 10 ++++++++++ session/bootstrap_test.go | 24 ++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 21 deletions(-) diff --git a/bindinfo/bind_record.go b/bindinfo/bind_record.go index 5b9c9f2040b43..bdb8301befb0f 100644 --- a/bindinfo/bind_record.go +++ b/bindinfo/bind_record.go @@ -87,6 +87,13 @@ func (b *Binding) IsBindingEnabled() bool { return b.Status == Enabled || b.Status == Using } +// IsBindingAvailable returns whether the binding is available. +// The available means the binding can be used or can be converted into a usable status. +// It includes the 'Enabled', 'Using' and 'Disabled' status. +func (b *Binding) IsBindingAvailable() bool { + return b.IsBindingEnabled() || b.Status == Disabled +} + // SinceUpdateTime returns the duration since last update time. Export for test. func (b *Binding) SinceUpdateTime() (time.Duration, error) { updateTime, err := b.UpdateTime.GoTime(time.Local) @@ -104,8 +111,8 @@ type BindRecord struct { Bindings []Binding } -// HasUsingBinding checks if there are any using bindings in bind record. -func (br *BindRecord) HasUsingBinding() bool { +// HasEnabledBinding checks if there are any enabled bindings in bind record. +func (br *BindRecord) HasEnabledBinding() bool { for _, binding := range br.Bindings { if binding.IsBindingEnabled() { return true @@ -114,9 +121,21 @@ func (br *BindRecord) HasUsingBinding() bool { return false } -// FindUsingBinding gets the using binding. -// There is at most one binding that can be used now -func (br *BindRecord) FindUsingBinding() *Binding { +// HasAvailableBinding checks if there are any available bindings in bind record. +// The available means the binding can be used or can be converted into a usable status. +// It includes the 'Enabled', 'Using' and 'Disabled' status. +func (br *BindRecord) HasAvailableBinding() bool { + for _, binding := range br.Bindings { + if binding.IsBindingAvailable() { + return true + } + } + return false +} + +// FindEnabledBinding gets the enabled binding. +// There is at most one binding that can be used now. +func (br *BindRecord) FindEnabledBinding() *Binding { for _, binding := range br.Bindings { if binding.IsBindingEnabled() { return &binding diff --git a/bindinfo/handle.go b/bindinfo/handle.go index cca2897f50774..7712089537011 100644 --- a/bindinfo/handle.go +++ b/bindinfo/handle.go @@ -188,7 +188,7 @@ func (h *BindHandle) Update(fullLoad bool) (err error) { // When the memory capacity of bing_cache is not enough, // there will be some memory-related errors in multiple places. // Only needs to be handled once. - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err)) + logutil.BgLogger().Warn("[sql-bind] BindHandle.Update", zap.Error(memExceededErr)) } return nil } @@ -280,7 +280,7 @@ func (h *BindHandle) AddBindRecord(sctx sessionctx.Context, record *BindRecord) if oldRecord != nil { binding := oldRecord.FindBinding(record.Bindings[0].ID) if binding != nil { - // There is already a binding with status `Using`, `PendingVerify` or `Rejected`, we could directly cancel the job. + // There is already a binding with status `Enabled`, `Disabled`, `PendingVerify` or `Rejected`, we could directly cancel the job. if record.Bindings[0].Status == PendingVerify { return nil } @@ -595,30 +595,30 @@ func (h *BindHandle) newBindRecord(row chunk.Row) (string, *BindRecord, error) { func (h *BindHandle) setBindRecord(hash string, meta *BindRecord) { newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() if err0 != nil { - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err0)) + logutil.BgLogger().Warn("[sql-bind] BindHandle.setBindRecord", zap.Error(err0)) } oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) err1 := newCache.SetBindRecord(hash, meta) if err1 != nil && err0 == nil { - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err1)) + logutil.BgLogger().Warn("[sql-bind] BindHandle.setBindRecord", zap.Error(err1)) } h.bindInfo.Value.Store(newCache) updateMetrics(metrics.ScopeGlobal, oldRecord, meta, false) } -// appendBindRecord addes the BindRecord to the cache, all the stale BindRecords are +// appendBindRecord adds the BindRecord to the cache, all the stale BindRecords are // removed from the cache after this operation. func (h *BindHandle) appendBindRecord(hash string, meta *BindRecord) { newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() if err0 != nil { - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err0)) + logutil.BgLogger().Warn("[sql-bind] BindHandle.appendBindRecord", zap.Error(err0)) } oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) newRecord := merge(oldRecord, meta) err1 := newCache.SetBindRecord(hash, newRecord) if err1 != nil && err0 == nil { // Only need to handle the error once. - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err1)) + logutil.BgLogger().Warn("[sql-bind] BindHandle.appendBindRecord", zap.Error(err1)) } h.bindInfo.Value.Store(newCache) updateMetrics(metrics.ScopeGlobal, oldRecord, newRecord, false) @@ -783,7 +783,7 @@ func (h *BindHandle) CaptureBaselines() { } dbName := utilparser.GetDefaultDB(stmt, bindableStmt.Schema) normalizedSQL, digest := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmt, dbName, bindableStmt.Query)) - if r := h.GetBindRecord(digest.String(), normalizedSQL, dbName); r != nil && r.HasUsingBinding() { + if r := h.GetBindRecord(digest.String(), normalizedSQL, dbName); r != nil && r.HasAvailableBinding() { continue } bindSQL := GenerateBindSQL(context.TODO(), stmt, bindableStmt.PlanHint, true, dbName) diff --git a/bindinfo/session_handle.go b/bindinfo/session_handle.go index 946697380104c..742a45473c096 100644 --- a/bindinfo/session_handle.go +++ b/bindinfo/session_handle.go @@ -46,7 +46,7 @@ func (h *SessionHandle) appendBindRecord(hash string, meta *BindRecord) { oldRecord := h.ch.GetBindRecord(hash, meta.OriginalSQL, meta.Db) err := h.ch.SetBindRecord(hash, meta) if err != nil { - logutil.BgLogger().Warn("[sql-bind] ", zap.Error(err)) + logutil.BgLogger().Warn("[sql-bind] SessionHandle.appendBindRecord", zap.Error(err)) } updateMetrics(metrics.ScopeSession, oldRecord, meta, false) } diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index fd6e1ef717798..e401716ed2857 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -405,9 +405,9 @@ func GetBindSQL4PlanCache(sctx sessionctx.Context, preparedStmt *CachedPrepareSt sessionHandle := sctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) bindRecord := sessionHandle.GetBindRecord(preparedStmt.SQLDigest4PC, preparedStmt.NormalizedSQL4PC, "") if bindRecord != nil { - usingBinding := bindRecord.FindUsingBinding() - if usingBinding != nil { - return usingBinding.BindSQL + enabledBinding := bindRecord.FindEnabledBinding() + if enabledBinding != nil { + return enabledBinding.BindSQL } } globalHandle := domain.GetDomain(sctx).BindHandle() @@ -416,9 +416,9 @@ func GetBindSQL4PlanCache(sctx sessionctx.Context, preparedStmt *CachedPrepareSt } bindRecord = globalHandle.GetBindRecord(preparedStmt.SQLDigest4PC, preparedStmt.NormalizedSQL4PC, "") if bindRecord != nil { - usingBinding := bindRecord.FindUsingBinding() - if usingBinding != nil { - return usingBinding.BindSQL + enabledBinding := bindRecord.FindEnabledBinding() + if enabledBinding != nil { + return enabledBinding.BindSQL } } return "" diff --git a/planner/optimize.go b/planner/optimize.go index a57a71f4d0c16..cdb039945f049 100644 --- a/planner/optimize.go +++ b/planner/optimize.go @@ -456,7 +456,7 @@ func getBindRecord(ctx sessionctx.Context, stmt ast.StmtNode) (*bindinfo.BindRec sessionHandle := ctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) bindRecord := sessionHandle.GetBindRecord(hash, normalizedSQL, "") if bindRecord != nil { - if bindRecord.HasUsingBinding() { + if bindRecord.HasEnabledBinding() { return bindRecord, metrics.ScopeSession, nil } return nil, "", nil diff --git a/session/bootstrap.go b/session/bootstrap.go index bff4bcc06324d..c64091a10fff6 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -581,6 +581,8 @@ const ( version83 = 83 // version84 adds the tables mysql.stats_meta_history version84 = 84 + // version85 updates bindings with status 'using' in mysql.bind_info table to 'enabled' status + version85 = 85 ) // currentBootstrapVersion is defined as a variable, so we can modify its value for testing. @@ -673,6 +675,7 @@ var ( upgradeToVer82, upgradeToVer83, upgradeToVer84, + upgradeToVer85, } ) @@ -1742,6 +1745,13 @@ func upgradeToVer84(s Session, ver int64) { doReentrantDDL(s, CreateStatsMetaHistory) } +func upgradeToVer85(s Session, ver int64) { + if ver >= version85 { + return + } + mustExecute(s, fmt.Sprintf("UPDATE HIGH_PRIORITY mysql.bind_info SET status= '%s' WHERE status = '%s'", bindinfo.Enabled, bindinfo.Using)) +} + func writeOOMAction(s Session) { comment := "oom-action is `log` by default in v3.0.x, `cancel` by default in v4.0.11+" mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE= %?`, diff --git a/session/bootstrap_test.go b/session/bootstrap_test.go index 9a886ae50fc8c..581068a9f56bd 100644 --- a/session/bootstrap_test.go +++ b/session/bootstrap_test.go @@ -1070,3 +1070,27 @@ func TestIndexMergeUpgradeFrom400To540(t *testing.T) { }() } } + +func TestUpgradeToVer85(t *testing.T) { + ctx := context.Background() + store, dom := createStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := createSessionAndSetID(t, store) + mustExec(t, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'using', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + mustExec(t, se, `insert into mysql.bind_info values('select * from t1', 'select /*+ use_index(t1, idx_a)*/ * from t1', 'test', 'enabled', '2021-01-05 14:50:58.257', '2021-01-05 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + mustExec(t, se, `insert into mysql.bind_info values('select * from t2', 'select /*+ use_index(t2, idx_a)*/ * from t2', 'test', 'disabled', '2021-01-06 14:50:58.257', '2021-01-06 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + mustExec(t, se, `insert into mysql.bind_info values('select * from t3', 'select /*+ use_index(t3, idx_a)*/ * from t3', 'test', 'deleted', '2021-01-07 14:50:58.257', '2021-01-07 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + mustExec(t, se, `insert into mysql.bind_info values('select * from t4', 'select /*+ use_index(t4, idx_a)*/ * from t4', 'test', 'invalid', '2021-01-08 14:50:58.257', '2021-01-08 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + upgradeToVer85(se, version84) + + r := mustExec(t, se, `select count(*) from mysql.bind_info where status = 'enabled'`) + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, int64(2), row.GetInt64(0)) + + require.NoError(t, r.Close()) + mustExec(t, se, "delete from mysql.bind_info where default_db = 'test'") +}