@@ -23,7 +23,9 @@ import (
2323 "time"
2424
2525 "github.com/pingcap/tidb/pkg/parser/ast"
26+ "github.com/pingcap/tidb/pkg/sessionctx/vardef"
2627 "github.com/pingcap/tidb/pkg/testkit"
28+ "github.com/pingcap/tidb/pkg/testkit/testfailpoint"
2729 "github.com/pingcap/tidb/pkg/testkit/testflag"
2830 "github.com/pingcap/tidb/pkg/ttl/cache"
2931 "github.com/pingcap/tidb/pkg/ttl/ttlworker"
@@ -97,3 +99,87 @@ func TestCancelWhileScan(t *testing.T) {
9799 close (delCh )
98100 wg .Wait ()
99101}
102+
103+ func TestCancelWhileScanAtStatementBoundary (t * testing.T ) {
104+ store , dom := testkit .CreateMockStoreAndDomain (t )
105+ tk := testkit .NewTestKit (t , store )
106+
107+ origBatchSize := vardef .TTLScanBatchSize .Load ()
108+ vardef .TTLScanBatchSize .Store (30 )
109+ t .Cleanup (func () {
110+ vardef .TTLScanBatchSize .Store (origBatchSize )
111+ })
112+
113+ tk .MustExec ("create table test.t (id int primary key, created_at datetime) TTL= created_at + interval 1 hour" )
114+ tk .MustExec ("split table test.t between (0) and (30000) regions 30" )
115+ for i := range 30 {
116+ tk .MustExec (fmt .Sprintf ("insert into test.t values (%d, NOW() - INTERVAL 24 HOUR)" , i * 1000 ))
117+ }
118+ testTable , err := dom .InfoSchema ().TableByName (context .Background (), ast .NewCIStr ("test" ), ast .NewCIStr ("t" ))
119+ require .NoError (t , err )
120+ testPhysicalTableCache , err := cache .NewPhysicalTable (ast .NewCIStr ("test" ), testTable .Meta (), ast .NewCIStr ("" ))
121+ require .NoError (t , err )
122+
123+ testfailpoint .Enable (t , "github.com/pingcap/tidb/pkg/store/copr/sleepCoprRequest" , "return(2000)" )
124+
125+ taskCtx , cancelTask := context .WithCancel (context .Background ())
126+ defer cancelTask ()
127+ ttlTask := ttlworker .NewTTLScanTask (taskCtx , testPhysicalTableCache , & cache.TTLTask {
128+ JobID : "test" ,
129+ TableID : testTable .Meta ().ID ,
130+ ScanID : 1 ,
131+ ScanRangeStart : nil ,
132+ ScanRangeEnd : nil ,
133+ ExpireTime : time .Now ().Add (- 12 * time .Hour ),
134+ OwnerID : "test" ,
135+ OwnerAddr : "test" ,
136+ OwnerHBTime : time .Now (),
137+ Status : cache .TaskStatusRunning ,
138+ StatusUpdateTime : time .Now (),
139+ State : & cache.TTLTaskState {},
140+ CreatedTime : time .Now (),
141+ })
142+
143+ triggerCancel := make (chan struct {})
144+ var cancelOnce sync.Once
145+ testfailpoint .EnableCall (t , "github.com/pingcap/tidb/pkg/executor/beforeResetSQLKillerForTTLScan" , func (stmt ast.StmtNode ) {
146+ if _ , ok := stmt .(* ast.SelectStmt ); ! ok {
147+ return
148+ }
149+
150+ cancelOnce .Do (func () {
151+ cancelTask ()
152+ close (triggerCancel )
153+ time .Sleep (100 * time .Millisecond )
154+ })
155+ })
156+
157+ delCh := make (chan * ttlworker.TTLDeleteTask )
158+ doneCh := make (chan struct {})
159+ go func () {
160+ defer close (doneCh )
161+ for range delCh {
162+ }
163+ }()
164+
165+ doScanDone := make (chan struct {})
166+ go func () {
167+ defer close (doScanDone )
168+ ttlTask .DoScan (context .Background (), delCh , dom .AdvancedSysSessionPool ())
169+ }()
170+
171+ select {
172+ case <- triggerCancel :
173+ case <- time .After (10 * time .Second ):
174+ require .FailNow (t , "TTL scan SELECT was not reached" )
175+ }
176+
177+ select {
178+ case <- doScanDone :
179+ case <- time .After (time .Second ):
180+ require .FailNow (t , "TTL scan was not canceled within 1s after statement-boundary cancel" )
181+ }
182+
183+ close (delCh )
184+ <- doneCh
185+ }
0 commit comments