New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
executor: fix a order by bug #4470
Changes from 3 commits
d845575
0609325
fcfaa31
1da6833
439799a
0ee2c22
435d16b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -300,7 +300,7 @@ type IndexLookUpExecutor struct { | |
tableWorker | ||
finished chan struct{} | ||
|
||
resultChan <-chan *lookupTableTask | ||
resultCh chan *lookupTableTask | ||
resultCurr *lookupTableTask | ||
} | ||
|
||
|
@@ -317,28 +317,33 @@ func (e *IndexLookUpExecutor) startIndexWorker(kvRanges []kv.KeyRange, workCh ch | |
return errors.Trace(err) | ||
} | ||
result.Fetch(e.ctx.GoCtx()) | ||
ih := &e.indexWorker | ||
ih.wg.Add(1) | ||
worker := &e.indexWorker | ||
worker.wg.Add(1) | ||
go func() { | ||
ctx, cancel := goctx.WithCancel(e.ctx.GoCtx()) | ||
ih.fetchHandles(e, result, workCh, ctx, finished) | ||
worker.fetchHandles(e, result, workCh, ctx, finished) | ||
cancel() | ||
if err := result.Close(); err != nil { | ||
log.Error("close SelectDAG result failed:", errors.ErrorStack(err)) | ||
} | ||
close(workCh) | ||
ih.wg.Done() | ||
close(e.resultCh) | ||
worker.wg.Done() | ||
}() | ||
return nil | ||
} | ||
|
||
// fetchHandles fetches a batch of handles from index data and builds the index lookup tasks. | ||
func (ih *indexWorker) fetchHandles(e *IndexLookUpExecutor, result distsql.SelectResult, workCh chan<- *lookupTableTask, ctx goctx.Context, finished <-chan struct{}) { | ||
// The tasks are sent to workCh to be further processed by tableWorker, and sent to e.resultCh | ||
// at the same time to keep data ordered. | ||
func (worker *indexWorker) fetchHandles(e *IndexLookUpExecutor, result distsql.SelectResult, workCh chan<- *lookupTableTask, ctx goctx.Context, finished <-chan struct{}) { | ||
for { | ||
handles, finish, err := extractHandlesFromIndexResult(result) | ||
if err != nil { | ||
workCh <- &lookupTableTask{ | ||
tasksErr: errors.Trace(err), | ||
doneCh := make(chan error, 1) | ||
doneCh <- errors.Trace(err) | ||
e.resultCh <- &lookupTableTask{ | ||
doneCh: doneCh, | ||
} | ||
return | ||
} | ||
|
@@ -353,64 +358,45 @@ func (ih *indexWorker) fetchHandles(e *IndexLookUpExecutor, result distsql.Selec | |
case <-finished: | ||
return | ||
case workCh <- task: | ||
e.resultCh <- task | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there need another But it only need to select The old implementation before refactor have this issue, it's very hard to trigger though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
} | ||
} | ||
|
||
func (ih *indexWorker) close() { | ||
ih.wg.Wait() | ||
} | ||
|
||
func (e *IndexLookUpExecutor) waitIndexWorker() { | ||
e.indexWorker.close() | ||
func (worker *indexWorker) close() { | ||
worker.wg.Wait() | ||
} | ||
|
||
// tableWorker is used by IndexLookUpExecutor to maintain table lookup background goroutines. | ||
type tableWorker struct { | ||
resultCh chan<- *lookupTableTask | ||
wg sync.WaitGroup | ||
wg sync.WaitGroup | ||
} | ||
|
||
// startTableWorker launch some background goroutines which pick tasks from workCh, | ||
// execute the task and store the results in IndexLookUpExecutor's resultCh. | ||
// startTableWorker launch some background goroutines which pick tasks from workCh and execute the task. | ||
func (e *IndexLookUpExecutor) startTableWorker(workCh <-chan *lookupTableTask, finished <-chan struct{}) { | ||
resultCh := make(chan *lookupTableTask, atomic.LoadInt32(&LookupTableTaskChannelSize)) | ||
th := &e.tableWorker | ||
th.resultCh = resultCh | ||
e.resultChan = resultCh | ||
worker := &e.tableWorker | ||
lookupConcurrencyLimit := e.ctx.GetSessionVars().IndexLookupConcurrency | ||
th.wg.Add(lookupConcurrencyLimit) | ||
worker.wg.Add(lookupConcurrencyLimit) | ||
for i := 0; i < lookupConcurrencyLimit; i++ { | ||
ctx, cancel := goctx.WithCancel(e.ctx.GoCtx()) | ||
go func() { | ||
th.pickAndExecTask(e, workCh, ctx, finished) | ||
worker.pickAndExecTask(e, workCh, ctx, finished) | ||
cancel() | ||
th.wg.Done() | ||
worker.wg.Done() | ||
}() | ||
} | ||
go func() { | ||
th.wg.Wait() | ||
close(th.resultCh) | ||
}() | ||
} | ||
|
||
// pickAndExecTask picks tasks from workCh, execute them, and send the result to tableWorker's taskCh. | ||
func (th *tableWorker) pickAndExecTask(e *IndexLookUpExecutor, workCh <-chan *lookupTableTask, ctx goctx.Context, finished <-chan struct{}) { | ||
// pickAndExecTask picks tasks from workCh, and execute them. | ||
func (worker *tableWorker) pickAndExecTask(e *IndexLookUpExecutor, workCh <-chan *lookupTableTask, ctx goctx.Context, finished <-chan struct{}) { | ||
for { | ||
select { | ||
case task, ok := <-workCh: | ||
if !ok { | ||
return | ||
} | ||
if task.tasksErr != nil { | ||
th.resultCh <- task | ||
return | ||
} | ||
|
||
// TODO: The results can be simplified when new_distsql.go replace distsql.go totally. | ||
e.executeTask(task, ctx) | ||
th.resultCh <- task | ||
case <-ctx.Done(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If ctx is done, and then there can be remaining tasks in the One solution is to remove The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't matter that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the session thread blocking in |
||
return | ||
case <-finished: | ||
|
@@ -419,9 +405,8 @@ func (th *tableWorker) pickAndExecTask(e *IndexLookUpExecutor, workCh <-chan *lo | |
} | ||
} | ||
|
||
func (e *IndexLookUpExecutor) waitTableWorker() { | ||
for range e.resultChan { | ||
} | ||
func (worker *tableWorker) close() { | ||
worker.wg.Wait() | ||
} | ||
|
||
// Open implements the Executor Open interface. | ||
|
@@ -437,6 +422,7 @@ func (e *IndexLookUpExecutor) open(kvRanges []kv.KeyRange) error { | |
e.finished = make(chan struct{}) | ||
e.indexWorker = indexWorker{} | ||
e.tableWorker = tableWorker{} | ||
e.resultCh = make(chan *lookupTableTask, atomic.LoadInt32(&LookupTableTaskChannelSize)) | ||
|
||
// indexWorker will write to workCh and tableWorker will read from workCh, | ||
// so fetching index and getting table data can run concurrently. | ||
|
@@ -563,8 +549,10 @@ func (e *IndexLookUpExecutor) Schema() *expression.Schema { | |
func (e *IndexLookUpExecutor) Close() error { | ||
if e.finished != nil { | ||
close(e.finished) | ||
e.waitIndexWorker() | ||
e.waitTableWorker() | ||
for range e.resultCh { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need comment for this line. |
||
} | ||
e.indexWorker.close() | ||
e.tableWorker.close() | ||
e.finished = nil | ||
} | ||
return nil | ||
|
@@ -574,13 +562,10 @@ func (e *IndexLookUpExecutor) Close() error { | |
func (e *IndexLookUpExecutor) Next() (Row, error) { | ||
for { | ||
if e.resultCurr == nil { | ||
resultCurr, ok := <-e.resultChan | ||
resultCurr, ok := <-e.resultCh | ||
if !ok { | ||
return nil, nil | ||
} | ||
if resultCurr.tasksErr != nil { | ||
return nil, errors.Trace(resultCurr.tasksErr) | ||
} | ||
e.resultCurr = resultCurr | ||
} | ||
row, err := e.resultCurr.getRow() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to select
finished
channel.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
finished
is closed, means we've close theIndexLookUpExecutor
and don't care the result any more.IndexLookUpExecutor.Close()
would drain the remain lookupTableTask and discard them.Both the same result, so select
finished
here or not doesn't make any difference.