diff --git a/config/config.go b/config/config.go index 0549be42cb539..b4f512da2914a 100644 --- a/config/config.go +++ b/config/config.go @@ -105,7 +105,8 @@ type Config struct { StmtSummary StmtSummary `toml:"stmt-summary" json:"stmt-summary"` // EnableTableLock indicate whether enable table lock. // TODO: remove this after table lock features stable. - EnableTableLock bool `toml:"enable-table-lock" json:"enable-table-lock"` + EnableTableLock bool `toml:"enable-table-lock" json:"enable-table-lock"` + DelayCleanTableLock uint64 `toml:"delay-clean-table-lock" json:"delay-clean-table-lock"` } // Log is the log section of config. @@ -380,6 +381,7 @@ var defaultConf = Config{ TreatOldVersionUTF8AsUTF8MB4: true, SplitRegionMaxNum: 1000, EnableTableLock: false, + DelayCleanTableLock: 0, TxnLocalLatches: TxnLocalLatches{ Enabled: false, Capacity: 2048000, @@ -661,6 +663,11 @@ func TableLockEnabled() bool { return GetGlobalConfig().EnableTableLock } +// TableLockDelayClean uses to get the time of delay clean table lock. +var TableLockDelayClean = func() uint64 { + return GetGlobalConfig().DelayCleanTableLock +} + // ToLogConfig converts *Log to *logutil.LogConfig. func (l *Log) ToLogConfig() *logutil.LogConfig { return logutil.NewLogConfig(l.Level, l.Format, l.SlowQueryFile, l.File, l.DisableTimestamp, func(config *zaplog.Config) { config.DisableErrorVerbose = l.DisableErrorStack }) diff --git a/config/config.toml.example b/config/config.toml.example index 594d1886672e9..afdc1d27b1782 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -77,6 +77,9 @@ server-version = "" # enable-table-lock is used to control table lock feature. Default is false, indicate the table lock feature is disabled. enable-table-lock = false +# delay-clean-table-lock is used to control whether delayed-release the table lock in the abnormal situation. (Milliseconds) +delay-clean-table-lock = 0 + [log] # Log level: debug, info, warn, error, fatal. level = "info" diff --git a/config/config_test.go b/config/config_test.go index 9a864c1c3c74c..c259ca3ed1901 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -70,6 +70,7 @@ split-region-max-num=10000 server-version = "test_version" max-index-length = 3080 enable-table-lock = true +delay-clean-table-lock = 5 [performance] txn-entry-count-limit=2000 txn-total-size-limit=2000 @@ -117,6 +118,7 @@ history-size=100 c.Assert(conf.StmtSummary.HistorySize, Equals, 100) c.Assert(conf.MaxIndexLength, Equals, 3080) c.Assert(conf.EnableTableLock, IsTrue) + c.Assert(conf.DelayCleanTableLock, Equals, uint64(5)) c.Assert(f.Close(), IsNil) c.Assert(os.Remove(configFile), IsNil) diff --git a/ddl/db_test.go b/ddl/db_test.go index ccba0f1012188..3731bfbeeb4d8 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -3401,6 +3401,39 @@ func (s *testDBSuite2) TestLockTables(c *C) { tk2.MustExec("unlock tables") } +func (s *testDBSuite2) TestTablesLockDelayClean(c *C) { + if israce.RaceEnabled { + c.Skip("skip race test") + } + s.tk = testkit.NewTestKit(c, s.store) + tk := s.tk + tk2 := testkit.NewTestKit(c, s.store) + tk2.MustExec("use test") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1,t2") + defer tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (a int)") + tk.MustExec("create table t2 (a int)") + + tk.MustExec("lock tables t1 write") + checkTableLock(c, tk.Se, "test", "t1", model.TableLockWrite) + config.GetGlobalConfig().DelayCleanTableLock = 100 + var wg sync.WaitGroup + wg.Add(1) + var startTime time.Time + go func() { + startTime = time.Now() + tk.Se.Close() + wg.Done() + }() + time.Sleep(50) + checkTableLock(c, tk.Se, "test", "t1", model.TableLockWrite) + wg.Wait() + c.Assert(time.Since(startTime).Seconds() > 0.1, IsTrue) + checkTableLock(c, tk.Se, "test", "t1", model.TableLockNone) + config.GetGlobalConfig().DelayCleanTableLock = 0 +} + // TestConcurrentLockTables test concurrent lock/unlock tables. func (s *testDBSuite4) TestConcurrentLockTables(c *C) { if israce.RaceEnabled { diff --git a/session/session.go b/session/session.go index 9fcccf32981aa..a0f37976486be 100644 --- a/session/session.go +++ b/session/session.go @@ -1405,6 +1405,9 @@ func (s *session) Close() { // TODO: do clean table locks when session exited without execute Close. // TODO: do clean table locks when tidb-server was `kill -9`. if s.HasLockedTables() && config.TableLockEnabled() { + if ds := config.TableLockDelayClean(); ds > 0 { + time.Sleep(time.Duration(ds) * time.Millisecond) + } lockedTables := s.GetAllTableLocks() err := domain.GetDomain(s).DDL().UnlockTables(s, lockedTables) if err != nil {