@@ -35,6 +35,7 @@ import (
35
35
"github.com/pingcap/tidb/pkg/util"
36
36
"github.com/pingcap/tidb/pkg/util/logutil"
37
37
"go.uber.org/zap"
38
+ "golang.org/x/sync/singleflight"
38
39
)
39
40
40
41
type statsWrapper struct {
@@ -46,7 +47,7 @@ type statsWrapper struct {
46
47
type StatsLoad struct {
47
48
NeededItemsCh chan * NeededItemTask
48
49
TimeoutItemsCh chan * NeededItemTask
49
- WorkingColMap map [model. TableItemID ][] chan stmtctx. StatsLoadResult
50
+ Singleflight singleflight. Group
50
51
SubCtxs []sessionctx.Context
51
52
sync.Mutex
52
53
}
@@ -221,46 +222,60 @@ func (h *Handle) HandleOneTask(sctx sessionctx.Context, lastTask *NeededItemTask
221
222
} else {
222
223
task = lastTask
223
224
}
224
- return h .handleOneItemTask (sctx , task )
225
+ resultChan := h .StatsLoad .Singleflight .DoChan (task .TableItemID .Key (), func () (any , error ) {
226
+ return h .handleOneItemTask (sctx , task )
227
+ })
228
+ timeout := time .Until (task .ToTimeout )
229
+ select {
230
+ case result := <- resultChan :
231
+ if result .Err == nil {
232
+ slr := result .Val .(* stmtctx.StatsLoadResult )
233
+ if slr .Error != nil {
234
+ return task , slr .Error
235
+ }
236
+ task .ResultCh <- * slr
237
+ return nil , nil
238
+ }
239
+ return task , result .Err
240
+ case <- time .After (timeout ):
241
+ return task , nil
242
+ }
225
243
}
226
244
227
- func (h * Handle ) handleOneItemTask (sctx sessionctx.Context , task * NeededItemTask ) (* NeededItemTask , error ) {
228
- result := stmtctx.StatsLoadResult {Item : task .TableItemID }
245
+ func (h * Handle ) handleOneItemTask (sctx sessionctx.Context , task * NeededItemTask ) (result * stmtctx.StatsLoadResult , err error ) {
246
+ defer func () {
247
+ // recover for each task, worker keeps working
248
+ if r := recover (); r != nil {
249
+ logutil .BgLogger ().Error ("handleOneItemTask panicked" , zap .Any ("recover" , r ), zap .Stack ("stack" ))
250
+ err = errors .Errorf ("stats loading panicked: %v" , r )
251
+ }
252
+ }()
253
+ result = & stmtctx.StatsLoadResult {Item : task .TableItemID }
229
254
item := result .Item
230
255
tbl , ok := h .Get (item .TableID )
231
256
if ! ok {
232
- h .writeToResultChan (task .ResultCh , result )
233
- return nil , nil
257
+ return result , nil
234
258
}
235
- var err error
236
259
wrapper := & statsWrapper {}
237
260
if item .IsIndex {
238
261
index , ok := tbl .Indices [item .ID ]
239
262
if ! ok || index .IsFullLoad () {
240
- h .writeToResultChan (task .ResultCh , result )
241
- return nil , nil
263
+ return result , nil
242
264
}
243
265
wrapper .idx = index
244
266
} else {
245
267
col , ok := tbl .Columns [item .ID ]
246
268
if ! ok || col .IsFullLoad () {
247
- h .writeToResultChan (task .ResultCh , result )
248
- return nil , nil
269
+ return result , nil
249
270
}
250
271
wrapper .col = col
251
272
}
252
- // to avoid duplicated handling in concurrent scenario
253
- working := h .setWorking (result .Item , task .ResultCh )
254
- if ! working {
255
- h .writeToResultChan (task .ResultCh , result )
256
- return nil , nil
257
- }
258
273
t := time .Now ()
259
274
needUpdate := false
260
275
wrapper , err = h .readStatsForOneItem (sctx , item , wrapper )
261
276
if err != nil {
262
277
result .Error = err
263
- return task , err
278
+ return result , err
264
279
}
265
280
if item .IsIndex {
266
281
if wrapper .idx != nil {
@@ -273,9 +288,8 @@ func (h *Handle) handleOneItemTask(sctx sessionctx.Context, task *NeededItemTask
273
288
}
274
289
metrics .ReadStatsHistogram .Observe (float64 (time .Since (t ).Milliseconds ()))
275
290
if needUpdate && h .updateCachedItem (item , wrapper .col , wrapper .idx ) {
276
- h . writeToResultChan ( task . ResultCh , result )
291
+ return result , nil
277
292
}
278
- h .finishWorking (result )
279
293
return nil , nil
280
294
}
281
295
@@ -466,32 +480,3 @@ func (h *Handle) updateCachedItem(item model.TableItemID, colHist *statistics.Co
466
480
h .UpdateStatsCache ([]* statistics.Table {tbl }, nil )
467
481
return true
468
482
}
469
-
470
- func (h * Handle ) setWorking (item model.TableItemID , resultCh chan stmtctx.StatsLoadResult ) bool {
471
- h .StatsLoad .Lock ()
472
- defer h .StatsLoad .Unlock ()
473
- chList , ok := h .StatsLoad .WorkingColMap [item ]
474
- if ok {
475
- if chList [0 ] == resultCh {
476
- return true // just return for duplicate setWorking
477
- }
478
- h .StatsLoad .WorkingColMap [item ] = append (chList , resultCh )
479
- return false
480
- }
481
- chList = []chan stmtctx.StatsLoadResult {}
482
- chList = append (chList , resultCh )
483
- h .StatsLoad .WorkingColMap [item ] = chList
484
- return true
485
- }
486
-
487
- func (h * Handle ) finishWorking (result stmtctx.StatsLoadResult ) {
488
- h .StatsLoad .Lock ()
489
- defer h .StatsLoad .Unlock ()
490
- if chList , ok := h .StatsLoad .WorkingColMap [result .Item ]; ok {
491
- list := chList [1 :]
492
- for _ , ch := range list {
493
- h .writeToResultChan (ch , result )
494
- }
495
- }
496
- delete (h .StatsLoad .WorkingColMap , result .Item )
497
- }
0 commit comments