diff --git a/config/config.go b/config/config.go index 2d522fa73737c..d75166cbb1ea4 100644 --- a/config/config.go +++ b/config/config.go @@ -91,7 +91,8 @@ type Config struct { TreatOldVersionUTF8AsUTF8MB4 bool `toml:"treat-old-version-utf8-as-utf8mb4" json:"treat-old-version-utf8-as-utf8mb4"` // 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. @@ -327,6 +328,7 @@ var defaultConf = Config{ CheckMb4ValueInUTF8: true, TreatOldVersionUTF8AsUTF8MB4: true, EnableTableLock: false, + DelayCleanTableLock: 0, TxnLocalLatches: TxnLocalLatches{ Enabled: true, Capacity: 2048000, @@ -592,6 +594,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 d7bba079cd6d8..ae6fbfa3da454 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -57,6 +57,9 @@ treat-old-version-utf8-as-utf8mb4 = true # 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 a648556d3a9c9..41514fc081b49 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -61,6 +61,7 @@ unrecognized-option-test = true _, err = f.WriteString(` token-limit = 0 enable-table-lock = true +delay-clean-table-lock = 5 [performance] [tikv-client] commit-timeout="41s" @@ -80,6 +81,7 @@ max-batch-size=128 c.Assert(conf.TiKVClient.MaxBatchSize, Equals, uint(128)) c.Assert(conf.TokenLimit, Equals, uint(1000)) 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 efb48308e1f2c..cb6f54e9c6811 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -3063,6 +3063,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 b7df1bae0566f..4deeca79d1ee3 100644 --- a/session/session.go +++ b/session/session.go @@ -1361,6 +1361,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 {