From b304b3c5a12546f7b8a0288b60e2a05cad10e326 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sat, 3 Oct 2026 11:16:58 +0900 Subject: [PATCH 1/3] Describe Spark cancellation as usually taking effect The cancellation list stated that a running Spark job always stops within seconds and ends in the CANCELED state. A stop request sent right after the calculation starts can occasionally have no effect (measured for #841: 1 of 6 attempts), so qualify the statement and list that case. Co-Authored-By: Claude Opus 5.5 --- docs/spark.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/spark.md b/docs/spark.md index 956a929d5..1ef9c2b51 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -256,8 +256,10 @@ The `cancel()` method sends a [StopCalculationExecution](https://docs.aws.amazon request for the calculation. It does not terminate the session. Athena cancels the calculation on a best-effort basis: -- A running Spark job, such as a DataFrame action, stops within seconds. - The calculation ends in the `CANCELED` state, and the session remains usable for later calculations. +- A running Spark job, such as a DataFrame action, usually stops within seconds. + The calculation then ends in the `CANCELED` state, and the session remains usable for later calculations. +- A request sent right after the calculation starts can occasionally have no effect. + The calculation then runs as if it had not been canceled. - Python code that runs on the driver without a Spark job, such as `time.sleep()`, runs to completion. The calculation ends in the `COMPLETED` state, and the session rejects new calculations until then. - Canceling a calculation that has already finished does not raise an error or change its state. From 0e6657343e78c47f3245df4f4d337bac0537e524 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sat, 3 Oct 2026 11:22:35 +0900 Subject: [PATCH 2/3] Say when a Spark start request is abandoned on an interrupt Co-Authored-By: Claude Opus 5.5 --- docs/spark.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/spark.md b/docs/spark.md index 1ef9c2b51..73217e2a1 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -291,6 +291,7 @@ A `KeyboardInterrupt` while `execute()` is still starting the calculation first [StartCalculationExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StartCalculationExecution.html) request to finish, and then cancels the calculation it started in the same way. The `calculation_id` property returns that calculation's ID. +If `execute()` has not begun the request when the interrupt is handled, the request is never sent and `calculation_id` is `None`. A second `KeyboardInterrupt` during this wait propagates at once without cancelling the calculation. A cancellation request sent right after a calculation starts can occasionally have no effect, so the calculation can still end in the `COMPLETED` state. From 0c64829d34e36dd87a896196f44f88e267117734 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Sat, 3 Oct 2026 11:26:36 +0900 Subject: [PATCH 3/3] Include a failed wait in the cancellation failure notes Co-Authored-By: Claude Opus 5.5 --- docs/aio.md | 2 +- docs/spark.md | 3 ++- docs/usage.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/aio.md b/docs/aio.md index d48895a69..9feffb7d6 100644 --- a/docs/aio.md +++ b/docs/aio.md @@ -138,7 +138,7 @@ With `kill_on_interrupt` enabled, which is the default, cancelling the task whil requests cancellation of the query, waits until it reaches a terminal state, and then raises `asyncio.CancelledError`. Cancellation is a best-effort request, so the query can still end as `SUCCEEDED` or `FAILED`. The `query_id` property keeps the ID of the cancelled query. -If the cancellation request fails, `asyncio.CancelledError` is raised with the error as its cause. +If the cancellation request or that wait fails, `asyncio.CancelledError` is raised with the error as its cause. Cancelling the task while `execute()` is still starting the query first waits for the start request to finish, and then cancels the query it started in the same way. If the task is cancelled before `execute()` begins the request, the request is never sent. diff --git a/docs/spark.md b/docs/spark.md index 73217e2a1..ed05caa3f 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -285,7 +285,8 @@ with conn.cursor() as cursor: With `kill_on_interrupt` enabled, which is the default, a `KeyboardInterrupt` while `execute()` waits for the calculation requests cancellation, waits until the calculation reaches a terminal state, and then propagates. The `state` property returns that terminal state. -If the cancellation request fails, the `KeyboardInterrupt` propagates with the error as its cause. +If the cancellation request or that wait fails, the `KeyboardInterrupt` propagates with the error as its cause, +and the `state` property returns `None`. A `KeyboardInterrupt` while `execute()` is still starting the calculation first waits for the [StartCalculationExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StartCalculationExecution.html) diff --git a/docs/usage.md b/docs/usage.md index 0638fc489..885ef3e1e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -507,7 +507,7 @@ With `kill_on_interrupt` enabled, which is the default, a `KeyboardInterrupt` wh requests cancellation, waits until the query reaches a terminal state, and then propagates. Cancellation is a best-effort request, so the query can still end as `SUCCEEDED` or `FAILED`. The `query_id` property keeps the ID of the interrupted query. -If the cancellation request fails, the `KeyboardInterrupt` propagates with the error as its cause. +If the cancellation request or that wait fails, the `KeyboardInterrupt` propagates with the error as its cause. A `KeyboardInterrupt` while `execute()` is still starting the query first waits for the [StartQueryExecution](https://docs.aws.amazon.com/athena/latest/APIReference/API_StartQueryExecution.html)