Repository navigation
fix(scheduler): let a caller that holds a slot work on the task it waits for - #1482
Merged
Merged
Conversation
…its for IMPORT DATABASE and PROJECT_GRAPH run a query from inside their own task. When every slot is held by such callers, the nested task is queued and nobody can take it. On a one-thread database IMPORT DATABASE hangs this way when the query has a timeout (the import then runs on the worker) or when the connection's threads setting is above one (its statements then want more than one thread). A thread that holds a slot (a worker, a caller running a task inline, or a launched thread that took over its caller's slot) now registers on the queued task and runs it before waiting; idle workers can still join. With launchNewWorkerThread the new thread already works in the caller's place, so that path is unchanged and the number of threads in tasks stays within max_num_threads. Tests: two imports on a one-thread database, one under a timeout and one with threads set to two, and scheduler tests for a nested task with no free slot, under a timeout, and on a launched thread; all five hang without this change. A sixth fails if the caller also works on a launched thread's task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IMPORT DATABASE runs each statement through executeNoLock, which clears the interrupt flag and restarts the timer. A caller that saw the deadline pass interrupted once and then waited without a timeout, so an interrupt that landed between two statements, or in a phase that does not check the flag, was lost. Before the parent commit the worker running the import still timed each statement in its own wait loop when another worker ran it; now that worker runs the statement itself. The caller now checks the clock again every 100 ms after interrupting, so a statement that started after the deadline is interrupted when its own time runs out. Test: a statement that the worker runs itself after the deadline is interrupted; it fails on the parent commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Two non-blocking follow-ups (from batch-4 review):
|
Contributor
Author
|
Both are in #1556.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This work was produced with the help of language models.
Fixes #1481.
IMPORT DATABASEandPROJECT_GRAPHrun a query inside their own task and wait for it; when such tasks hold every slot, nobody can run the queued one. #1297 fixed the plain case. On a one-thread database,IMPORT DATABASEstill hangs when the query has a timeout, because the import then runs on the worker, or whenCALL threadsis above one, because its statements then want more than one thread.Fix. A thread that holds a slot (a worker, a caller running a task inline, or a launched thread that took over its caller's slot) registers on the queued task and runs it before waiting; idle workers can still join. With
launchNewWorkerThreadthe new thread already works in the caller's place, so that path is unchanged and threads stay withinmax_num_threads. Ordinary queries pay one thread-local read per queued pipeline. No layout change:TaskScheduler's layout is extension ABI.Timeouts. Each statement of an import restarts the timer and clears the interrupt. The worker running the import used to watch each statement's clock while another worker ran it; it now runs the statement itself. So the second commit keeps the waiting client thread watching after its first interrupt: it checks the clock again every 100 ms, and a statement that runs past the timeout is interrupted even if it started after the first deadline. A query past its deadline therefore wakes its waiting thread every 100 ms until it stops; the interval bounds how late such a statement is noticed. The two commits go in together: the first alone loses that per-statement check with two or more threads. The waiting thread reads the statement timer without synchronization while the worker restarts it; that race already exists on main with two or more threads, and both timeout tests exercise it.
Tests.
ApiTest.ImportDatabaseWithOneThreadTimesOut(expectsImport database failed: Interrupted.) andImportDatabaseWithOneThreadAndThreadsSetToTwo, andTaskSchedulerTest.NestedTaskRunsOnCallerWhenNoSlotIsFree,NestedTaskWithTimeoutRunsOnWorkerandNestedTaskRunsOnLaunchedThread, hang on main.LaunchedThreadStandsInForCallerfails if the caller also runs a launched thread's task, andQueuedTasksRunInParallelOnceInlineSlotsFreefails without the slot-holder check.TimeoutInterruptsStatementStartedAfterDeadlinefails without the second commit. Full ctest passes; the export/import, copy_large, gds and rec_joins tests pass underMAX_NUM_THREADS=1.