Compare commits

...
Author SHA1 Message Date
Joe LiandGitHub 17ff845b9c Merge branch 'master' into fix-sqllab-async-query-stuck-running 2026-08-11 12:17:33 -07:00
Joe LiandGitHub d69cb61f05 Merge branch 'master' into fix-sqllab-async-query-stuck-running 2026-08-10 21:38:56 -07:00
sadpandajoeandJoe Li 2f88576079 fix(sqllab): async queries no longer stuck at Running forever
REFRESH_QUERIES's stale-poll guard blocked any incoming Success update
whenever the locally stored state was Running, Fetching, or Success.
Fetching/Success make sense to guard (a late poll shouldn't clobber a
state that's already at or past Success), but Running is strictly
before Success, so an incoming Success there is genuine new
information, not staleness.

For async-mode queries (allow_run_async databases), this poller
(QueryAutoRefresh, via REFRESH_QUERIES) is the only path that ever
updates their state - runQuery only dispatches querySuccess directly
for the synchronous case. Once such a query was observed Running, it
could never reach Success: every subsequent poll re-applied the same
guard against the same stuck prevState.

Drop Running from the blocked-state list so a genuine Running ->
Success transition is no longer discarded.
2026-08-07 17:51:55 +00:00
2 changed files with 28 additions and 6 deletions
@@ -695,6 +695,27 @@ describe('sqlLabReducer', () => {
);
expect(newState.queries['sync-query'].state).toBe(QueryState.Fetching);
});
test('should move an async query from running to success when polling reports it finished', () => {
const asyncQuery = {
...query,
id: 'async-query',
state: QueryState.Running,
runAsync: true,
};
newState = sqlLabReducer(
{
...newState,
queries: { 'async-query': asyncQuery },
},
actions.refreshQueries({
'async-query': {
...asyncQuery,
state: QueryState.Success,
},
}),
);
expect(newState.queries['async-query'].state).toBe(QueryState.Success);
});
});
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
describe('CLEAR_INACTIVE_QUERIES', () => {
@@ -769,14 +769,15 @@ export default function sqlLabReducer(
}),
// race condition:
// because of async behavior, sql lab may still poll a couple of seconds
// when it started fetching or finished rendering results
// after it started fetching or finished rendering results. Guard only
// against re-applying a redundant Success onto a state that's already at
// or past Success (Fetching/Success) — Running is strictly before
// Success, so an incoming Success there is new information, not a stale
// poll, and must be allowed through (otherwise an async query can never
// leave Running once observed there).
state:
currentState === QueryState.Success &&
[
QueryState.Fetching,
QueryState.Success,
QueryState.Running,
].includes(prevState)
[QueryState.Fetching, QueryState.Success].includes(prevState)
? prevState
: currentState,
};