diff --git a/config/config.go b/config/config.go index c06a998119c07..529239bc95715 100644 --- a/config/config.go +++ b/config/config.go @@ -99,7 +99,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. @@ -358,6 +359,7 @@ var defaultConf = Config{ TreatOldVersionUTF8AsUTF8MB4: true, SplitRegionMaxNum: 1000, EnableTableLock: false, + DelayCleanTableLock: 0, TxnLocalLatches: TxnLocalLatches{ Enabled: false, Capacity: 2048000, @@ -635,6 +637,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) diff --git a/config/config.toml.example b/config/config.toml.example index d7700278465be..ef0e82fb496e4 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -76,6 +76,9 @@ server-version = "" enable-table-lock = false >>>>>>> 612936bbd... *: Support LOCK/UNLOCK TABLES feature (#10343) +# 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 3a3ee82289f12..0942343629b35 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -68,6 +68,7 @@ alter-primary-key = true split-region-max-num=10000 server-version = "test_version" enable-table-lock = true +delay-clean-table-lock = 5 [performance] txn-entry-count-limit=2000 txn-total-size-limit=2000 @@ -114,6 +115,7 @@ history-size=100 c.Assert(conf.StmtSummary.RefreshInterval, Equals, 100) c.Assert(conf.StmtSummary.HistorySize, Equals, 100) 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 2927cff560cba..82cb5d8cfe5b2 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -3404,6 +3404,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 f937116bb66f5..eebe70460c424 100644 --- a/session/session.go +++ b/session/session.go @@ -1407,6 +1407,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 {