@@ -39,6 +39,8 @@ import (
39
39
// RetryCount is the max retry count for a sync load task.
40
40
const RetryCount = 3
41
41
42
+ var globalStatsSyncLoadSingleFlight singleflight.Group
43
+
42
44
type statsWrapper struct {
43
45
col * statistics.Column
44
46
idx * statistics.Index
@@ -79,25 +81,26 @@ func (h *Handle) SendLoadRequests(sc *stmtctx.StatementContext, neededHistItems
79
81
}
80
82
sc .StatsLoad .Timeout = timeout
81
83
sc .StatsLoad .NeededItems = remainedItems
82
- sc .StatsLoad .ResultCh = make (chan stmtctx.StatsLoadResult , len (remainedItems ))
83
- tasks := make ([]* NeededItemTask , 0 )
84
+ sc .StatsLoad .ResultCh = make ([]<- chan singleflight.Result , 0 , len (remainedItems ))
84
85
for _ , item := range remainedItems {
85
- task := & NeededItemTask {
86
- TableItemID : item ,
87
- ToTimeout : time .Now ().Local ().Add (timeout ),
88
- ResultCh : sc .StatsLoad .ResultCh ,
89
- }
90
- tasks = append (tasks , task )
91
- }
92
- timer := time .NewTimer (timeout )
93
- defer timer .Stop ()
94
- for _ , task := range tasks {
95
- select {
96
- case h .StatsLoad .NeededItemsCh <- task :
97
- continue
98
- case <- timer .C :
99
- return errors .New ("sync load stats channel is full and timeout sending task to channel" )
100
- }
86
+ localItem := item
87
+ resultCh := globalStatsSyncLoadSingleFlight .DoChan (localItem .Key (), func () (any , error ) {
88
+ timer := time .NewTimer (timeout )
89
+ defer timer .Stop ()
90
+ task := & NeededItemTask {
91
+ TableItemID : localItem ,
92
+ ToTimeout : time .Now ().Local ().Add (timeout ),
93
+ ResultCh : make (chan stmtctx.StatsLoadResult , 1 ),
94
+ }
95
+ select {
96
+ case h .StatsLoad .NeededItemsCh <- task :
97
+ result := <- task .ResultCh
98
+ return result , nil
99
+ case <- timer .C :
100
+ return nil , errors .New ("sync load stats channel is full and timeout sending task to channel" )
101
+ }
102
+ })
103
+ sc .StatsLoad .ResultCh = append (sc .StatsLoad .ResultCh , resultCh )
101
104
}
102
105
sc .StatsLoad .LoadStartTime = time .Now ()
103
106
return nil
@@ -123,26 +126,34 @@ func (h *Handle) SyncWaitStatsLoad(sc *stmtctx.StatementContext) error {
123
126
metrics .SyncLoadCounter .Inc ()
124
127
timer := time .NewTimer (sc .StatsLoad .Timeout )
125
128
defer timer .Stop ()
126
- for {
129
+ for _ , resultCh := range sc . StatsLoad . ResultCh {
127
130
select {
128
- case result , ok := <- sc .StatsLoad .ResultCh :
129
- if ok {
130
- if result .HasError () {
131
- errorMsgs = append (errorMsgs , result .ErrorMsg ())
132
- }
133
- delete (resultCheckMap , result .Item )
134
- if len (resultCheckMap ) == 0 {
135
- metrics .SyncLoadHistogram .Observe (float64 (time .Since (sc .StatsLoad .LoadStartTime ).Milliseconds ()))
136
- return nil
137
- }
138
- } else {
131
+ case result , ok := <- resultCh :
132
+ if ! ok {
139
133
return errors .New ("sync load stats channel closed unexpectedly" )
140
134
}
135
+ // this error is from statsSyncLoad.SendLoadRequests which start to task and send task into worker,
136
+ // not the stats loading error
137
+ if result .Err != nil {
138
+ errorMsgs = append (errorMsgs , result .Err .Error ())
139
+ } else {
140
+ val := result .Val .(stmtctx.StatsLoadResult )
141
+ // this error is from the stats loading error
142
+ if val .HasError () {
143
+ errorMsgs = append (errorMsgs , val .ErrorMsg ())
144
+ }
145
+ delete (resultCheckMap , val .Item )
146
+ }
141
147
case <- timer .C :
142
148
metrics .SyncLoadTimeoutCounter .Inc ()
143
149
return errors .New ("sync load stats timeout" )
144
150
}
145
151
}
152
+ if len (resultCheckMap ) == 0 {
153
+ metrics .SyncLoadHistogram .Observe (float64 (time .Since (sc .StatsLoad .LoadStartTime ).Milliseconds ()))
154
+ return nil
155
+ }
156
+ return nil
146
157
}
147
158
148
159
// removeHistLoadedColumns removed having-hist columns based on neededColumns and statsCache.
@@ -240,28 +251,17 @@ func (h *Handle) HandleOneTask(lastTask *NeededItemTask, readerCtx *StatsReaderC
240
251
task = lastTask
241
252
}
242
253
result := stmtctx.StatsLoadResult {Item : task .TableItemID }
243
- resultChan := h .StatsLoad .Singleflight .DoChan (task .TableItemID .Key (), func () (any , error ) {
244
- err := h .handleOneItemTask (task , readerCtx , ctx )
245
- return nil , err
246
- })
247
- timeout := time .Until (task .ToTimeout )
248
- select {
249
- case sr := <- resultChan :
250
- // sr.Val is always nil.
251
- if sr .Err == nil {
252
- task .ResultCh <- result
253
- return nil , nil
254
- }
255
- if ! isVaildForRetry (task ) {
256
- result .Error = sr .Err
257
- task .ResultCh <- result
258
- return nil , nil
259
- }
260
- return task , sr .Err
261
- case <- time .After (timeout ):
262
- task .ToTimeout .Add (time .Duration (h .mu .ctx .GetSessionVars ().StatsLoadSyncWait .Load ()) * time .Microsecond )
263
- return task , nil
254
+ err = h .handleOneItemTask (task , readerCtx , ctx )
255
+ if err == nil {
256
+ task .ResultCh <- result
257
+ return nil , nil
258
+ }
259
+ if ! isVaildForRetry (task ) {
260
+ result .Error = err
261
+ task .ResultCh <- result
262
+ return nil , nil
264
263
}
264
+ return task , err
265
265
}
266
266
267
267
func isVaildForRetry (task * NeededItemTask ) bool {
0 commit comments