@@ -28,6 +28,7 @@ import (
28
28
"github.com/pingcap/parser/model"
29
29
"github.com/pingcap/parser/mysql"
30
30
"github.com/pingcap/tidb/kv"
31
+ "github.com/pingcap/tidb/sessionctx"
31
32
"github.com/pingcap/tidb/store/tikv"
32
33
"github.com/pingcap/tidb/table/tables"
33
34
"github.com/pingcap/tidb/tablecodec"
@@ -48,10 +49,37 @@ type SplitIndexRegionExec struct {
48
49
upper []types.Datum
49
50
num int
50
51
valueLists [][]types.Datum
52
+
53
+ done bool
54
+ splitRegionResult
55
+ }
56
+
57
+ type splitRegionResult struct {
58
+ splitRegions int
59
+ finishScatterNum int
60
+ }
61
+
62
+ // Open implements the Executor Open interface.
63
+ func (e * SplitIndexRegionExec ) Open (ctx context.Context ) error {
64
+ return e .splitIndexRegion (ctx )
51
65
}
52
66
53
67
// Next implements the Executor Next interface.
54
- func (e * SplitIndexRegionExec ) Next (ctx context.Context , _ * chunk.Chunk ) error {
68
+ func (e * SplitIndexRegionExec ) Next (ctx context.Context , chk * chunk.Chunk ) error {
69
+ chk .Reset ()
70
+ if e .done {
71
+ return nil
72
+ }
73
+ appendSplitRegionResultToChunk (chk , e .splitRegions , e .finishScatterNum )
74
+ e .done = true
75
+ return nil
76
+ }
77
+
78
+ // checkScatterRegionFinishBackOff is the back off time that used to check if a region has finished scattering before split region timeout.
79
+ const checkScatterRegionFinishBackOff = 50
80
+
81
+ // splitIndexRegion is used to split index regions.
82
+ func (e * SplitIndexRegionExec ) splitIndexRegion (ctx context.Context ) error {
55
83
store := e .ctx .GetStore ()
56
84
s , ok := store .(kv.SplitableStore )
57
85
if ! ok {
@@ -62,10 +90,15 @@ func (e *SplitIndexRegionExec) Next(ctx context.Context, _ *chunk.Chunk) error {
62
90
return err
63
91
}
64
92
93
+ start := time .Now ()
65
94
ctxWithTimeout , cancel := context .WithTimeout (ctx , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
66
95
defer cancel ()
67
96
regionIDs := make ([]uint64 , 0 , len (splitIdxKeys ))
68
97
for _ , idxKey := range splitIdxKeys {
98
+ if isCtxDone (ctxWithTimeout ) {
99
+ break
100
+ }
101
+
69
102
regionID , err := s .SplitRegion (idxKey , true )
70
103
if err != nil {
71
104
logutil .Logger (context .Background ()).Warn ("split table index region failed" ,
@@ -74,28 +107,17 @@ func (e *SplitIndexRegionExec) Next(ctx context.Context, _ *chunk.Chunk) error {
74
107
zap .Error (err ))
75
108
continue
76
109
}
110
+ if regionID == 0 {
111
+ continue
112
+ }
77
113
regionIDs = append (regionIDs , regionID )
78
114
79
- if isCtxDone (ctxWithTimeout ) {
80
- return errors .Errorf ("wait split region timeout(%v)" , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
81
- }
82
115
}
116
+ e .splitRegions = len (regionIDs )
83
117
if ! e .ctx .GetSessionVars ().WaitSplitRegionFinish {
84
118
return nil
85
119
}
86
- for _ , regionID := range regionIDs {
87
- err := s .WaitScatterRegionFinish (regionID )
88
- if err != nil {
89
- logutil .Logger (context .Background ()).Warn ("wait scatter region failed" ,
90
- zap .Uint64 ("regionID" , regionID ),
91
- zap .String ("table" , e .tableInfo .Name .L ),
92
- zap .String ("index" , e .indexInfo .Name .L ),
93
- zap .Error (err ))
94
- }
95
- if isCtxDone (ctxWithTimeout ) {
96
- return errors .Errorf ("wait split region timeout(%v)" , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
97
- }
98
- }
120
+ e .finishScatterNum = waitScatterRegionFinish (ctxWithTimeout , e .ctx , start , s , regionIDs , e .tableInfo .Name .L , e .indexInfo .Name .L )
99
121
return nil
100
122
}
101
123
@@ -225,16 +247,35 @@ type SplitTableRegionExec struct {
225
247
upper types.Datum
226
248
num int
227
249
valueLists [][]types.Datum
250
+
251
+ done bool
252
+ splitRegionResult
253
+ }
254
+
255
+ // Open implements the Executor Open interface.
256
+ func (e * SplitTableRegionExec ) Open (ctx context.Context ) error {
257
+ return e .splitTableRegion (ctx )
228
258
}
229
259
230
260
// Next implements the Executor Next interface.
231
- func (e * SplitTableRegionExec ) Next (ctx context.Context , _ * chunk.Chunk ) error {
261
+ func (e * SplitTableRegionExec ) Next (ctx context.Context , chk * chunk.Chunk ) error {
262
+ chk .Reset ()
263
+ if e .done {
264
+ return nil
265
+ }
266
+ appendSplitRegionResultToChunk (chk , e .splitRegions , e .finishScatterNum )
267
+ e .done = true
268
+ return nil
269
+ }
270
+
271
+ func (e * SplitTableRegionExec ) splitTableRegion (ctx context.Context ) error {
232
272
store := e .ctx .GetStore ()
233
273
s , ok := store .(kv.SplitableStore )
234
274
if ! ok {
235
275
return nil
236
276
}
237
277
278
+ start := time .Now ()
238
279
ctxWithTimeout , cancel := context .WithTimeout (ctx , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
239
280
defer cancel ()
240
281
@@ -244,48 +285,78 @@ func (e *SplitTableRegionExec) Next(ctx context.Context, _ *chunk.Chunk) error {
244
285
}
245
286
regionIDs := make ([]uint64 , 0 , len (splitKeys ))
246
287
for _ , key := range splitKeys {
288
+ failpoint .Inject ("mockSplitRegionTimeout" , func (val failpoint.Value ) {
289
+ if val .(bool ) {
290
+ time .Sleep (time .Second * 1 + time .Millisecond * 10 )
291
+ }
292
+ })
293
+ if isCtxDone (ctxWithTimeout ) {
294
+ break
295
+ }
247
296
regionID , err := s .SplitRegion (key , true )
248
297
if err != nil {
249
298
logutil .Logger (context .Background ()).Warn ("split table region failed" ,
250
299
zap .String ("table" , e .tableInfo .Name .L ),
251
300
zap .Error (err ))
252
301
continue
253
302
}
303
+ if regionID == 0 {
304
+ continue
305
+ }
254
306
regionIDs = append (regionIDs , regionID )
255
307
256
- failpoint .Inject ("mockSplitRegionTimeout" , func (val failpoint.Value ) {
257
- if val .(bool ) {
258
- time .Sleep (time .Second * 1 )
259
- }
260
- })
261
-
262
- if isCtxDone (ctxWithTimeout ) {
263
- return errors .Errorf ("split region timeout(%v)" , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
264
- }
265
308
}
309
+ e .splitRegions = len (regionIDs )
266
310
if ! e .ctx .GetSessionVars ().WaitSplitRegionFinish {
267
311
return nil
268
312
}
313
+
314
+ e .finishScatterNum = waitScatterRegionFinish (ctxWithTimeout , e .ctx , start , s , regionIDs , e .tableInfo .Name .L , "" )
315
+ return nil
316
+ }
317
+
318
+ func waitScatterRegionFinish (ctxWithTimeout context.Context , sctx sessionctx.Context , startTime time.Time , store kv.SplitableStore , regionIDs []uint64 , tableName , indexName string ) int {
319
+ remainMillisecond := 0
320
+ finishScatterNum := 0
269
321
for _ , regionID := range regionIDs {
270
- err := s .WaitScatterRegionFinish (regionID )
271
- if err != nil {
272
- logutil .Logger (context .Background ()).Warn ("wait scatter region failed" ,
273
- zap .Uint64 ("regionID" , regionID ),
274
- zap .String ("table" , e .tableInfo .Name .L ),
275
- zap .Error (err ))
322
+ if isCtxDone (ctxWithTimeout ) {
323
+ // Do not break here for checking remain regions scatter finished with a very short backoff time.
324
+ // Consider this situation - Regions 1, 2, and 3 are to be split.
325
+ // Region 1 times out before scattering finishes, while Region 2 and Region 3 have finished scattering.
326
+ // In this case, we should return 2 Regions, instead of 0, have finished scattering.
327
+ remainMillisecond = checkScatterRegionFinishBackOff
328
+ } else {
329
+ remainMillisecond = int ((sctx .GetSessionVars ().GetSplitRegionTimeout ().Seconds () - time .Since (startTime ).Seconds ()) * 1000 )
276
330
}
277
331
278
- failpoint .Inject ("mockScatterRegionTimeout" , func (val failpoint.Value ) {
279
- if val .(bool ) {
280
- time .Sleep (time .Second * 1 )
332
+ err := store .WaitScatterRegionFinish (regionID , remainMillisecond )
333
+ if err == nil {
334
+ finishScatterNum ++
335
+ } else {
336
+ if len (indexName ) == 0 {
337
+ logutil .Logger (context .Background ()).Warn ("wait scatter region failed" ,
338
+ zap .Uint64 ("regionID" , regionID ),
339
+ zap .String ("table" , tableName ),
340
+ zap .Error (err ))
341
+ } else {
342
+ logutil .Logger (context .Background ()).Warn ("wait scatter region failed" ,
343
+ zap .Uint64 ("regionID" , regionID ),
344
+ zap .String ("table" , tableName ),
345
+ zap .String ("index" , indexName ),
346
+ zap .Error (err ))
281
347
}
282
- })
283
-
284
- if isCtxDone (ctxWithTimeout ) {
285
- return errors .Errorf ("wait split region scatter timeout(%v)" , e .ctx .GetSessionVars ().GetSplitRegionTimeout ())
286
348
}
287
349
}
288
- return nil
350
+ return finishScatterNum
351
+ }
352
+
353
+ func appendSplitRegionResultToChunk (chk * chunk.Chunk , totalRegions , finishScatterNum int ) {
354
+ chk .AppendInt64 (0 , int64 (totalRegions ))
355
+ if finishScatterNum > 0 && totalRegions > 0 {
356
+ chk .AppendFloat64 (1 , float64 (finishScatterNum )/ float64 (totalRegions ))
357
+ } else {
358
+ chk .AppendFloat64 (1 , float64 (0 ))
359
+ }
289
360
}
290
361
291
362
func isCtxDone (ctx context.Context ) bool {
0 commit comments