Skip to content

Wait for the rows when re-executing an expired result - #50

Merged
ivanvyd merged 1 commit into
mainfrom
fix/re-execute-waits-for-the-rows
Aug 6, 2026
Merged

Wait for the rows when re-executing an expired result#50
ivanvyd merged 1 commit into
mainfrom
fix/re-execute-waits-for-the-rows

Conversation

@ivanvyd

@ivanvyd ivanvyd commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Closing the last live-coverage gap I could reach found a real defect — and the test that was
supposed to cover it had been agreeing with the bug.

What the live workspace says

Call Response
POST …/attachments/{id}/execute-query HTTP 200, state: PENDING, no manifest, no rows
GET …/attachments/{id}/query-result moments later state: SUCCEEDED, rows present

execute-query only starts the re-execution.

The defect

ReExecuteQueryAsync returned that first acknowledgement. FetchQueryResultAsync returns null
when there is no manifest, so the documented QUERY_RESULT_EXPIRED recovery produced nothing —
and ExportCommand then threw:

Databricks returned no result for that query, even after re-running it. Ask the question again.

The user is told to start over while the warehouse query they just paid for completes successfully
a second later and is discarded. It now polls for the rows, with the same backoff and timeout as
every other wait in this client.

Why the existing test did not catch it

The contract test stubbed a completed response — a shape Databricks does not return. The test
encoded the same wrong assumption as the code, so it passed.

A stub cannot catch an error in the stub. That is the second time in this cycle a fixture
encoded a shape the service does not produce, so the replacement is both:

  • a contract test reproducing the real PENDING acknowledgement, which fails without the fix
  • a live test, A_re_executed_query_returns_its_rows, run against the real workspace

What is still not reached, and why

The QUERY_RESULT_EXPIRED state that triggers recovery. Databricks expires the cache on its own
schedule, hours later, and there is no way to force it. So the recovery is verified and the
condition it recovers from is simulated — which is what compatibility.md and ROADMAP.md now
say, rather than implying both are covered.

Verification

230 offline tests and 9 live tests green — the live suite grew from 8. Build clean under
warnings-as-errors; format clean.

Exercising execute-query against a live workspace corrected a wire
assumption and, with it, a real defect.

execute-query only starts the re-execution. It answers HTTP 200 with state
PENDING and no manifest, and the rows appear on the ordinary query-result
endpoint a moment later. The client returned that first acknowledgement, so
ReExecuteQueryAsync produced null and `export last` told the user to ask
the question again -- while the warehouse query they had just paid for
completed and was discarded. It now polls for the rows, using the same
backoff and timeout as every other wait in this client.

The contract test covering this path stubbed a completed response, which
Databricks does not return, so the test agreed with the bug. That is the
general failure worth naming: a stub cannot catch an error in the stub.
The new contract test reproduces the real PENDING acknowledgement, and
fails without the fix. Alongside it is a live test, because this is the
second time in this cycle that a fixture encoded a shape the service does
not actually produce.

The live suite is 9 tests, all passing. Still unreached is the
QUERY_RESULT_EXPIRED state that triggers recovery: Databricks expires the
cache on its own schedule, hours later, and there is no way to force it.
So the recovery is verified and the condition it recovers from is
simulated -- which is what the docs now say rather than implying both.
@ivanvyd
ivanvyd merged commit ab1f8cf into main Aug 6, 2026
10 checks passed
@ivanvyd
ivanvyd deleted the fix/re-execute-waits-for-the-rows branch August 6, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant