You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f83312e
Browse filesBrowse the repository at this point in the historyBrowse files
[SPARK-60052] Suspend and requeue a running SparkApplication on Kueue eviction
### What changes were proposed in this pull request?
This PR aims to suspend a running `SparkApplication` whose Kueue `Workload` is evicted or deactivated, and to queue it again with a new attempt, like a running `SparkCluster`.
- `AppSuspendStep` moves such an application to `Suspended` and releases its driver and executors. Then it deletes the `Workload` after the requeue backoff, or keeps a deactivated one until it is reactivated.
- The new attempt starts with `ApplicationStatus#resume`, so it does not count as a restart.
- A `PodsReadyTimeout` is handled like any other eviction, and `spec.suspend` takes precedence.
- `AppKueueEvictionStep` and the `KueueEvictionIgnored` event are removed.
### Why are the changes needed?
Currently, the driver and executors keep running after an eviction. So preemption, cohort quota reclamation and `waitForPodsReady` do not work, and a deactivated `Workload` over-commits the quota.
### Does this PR introduce _any_ user-facing change?
No for the released versions, because the Kueue integration is not released yet in [1.0.0](https://github.com/apache/spark-kubernetes-operator/releases/tag/1.0.0) (2026-07-23).
### How was this patch tested?
Pass the CIs with the newly added test cases.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Opus 5.5
Closes#952 from dongjoon-hyun/SPARK-60052.
Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
Copy file name to clipboardExpand all lines: docs/config_properties.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,7 +53,7 @@
53
53
| spark.kubernetes.operator.reconciler.retry.initialIntervalSeconds | Integer | 5 | false | Initial interval(in seconds) of retries on unhandled controller errors. |
54
54
| spark.kubernetes.operator.reconciler.retry.intervalMultiplier | Double | 1.5 | false | Interval multiplier of retries on unhandled controller errors. Setting this to 1 for linear retry. |
55
55
| spark.kubernetes.operator.reconciler.retry.maxIntervalSeconds | Integer | -1 | false | Max interval(in seconds) of retries on unhandled controller errors. Set to non-positive for unlimited. |
56
-
| spark.kubernetes.operator.reconciler.suspendHoldRequeueIntervalSeconds | Long | 1800 | true | Requeue interval (in seconds) at which a resource held by spec.suspend is reconciled, so that its SuspendHeld event is republished. A SparkApplication or SparkCluster suspended while running publishes no such event, since its Suspended status says so, and uses this interval only to look at its released resources again. The hold ends only when a user clears spec.suspend, which arrives as a watch event and reconciles right away, so nothing waits for this interval. It is deliberately much coarser than spark.kubernetes.operator.reconciler.intervalSeconds, since a suspended resource has nothing to observe while each republish costs a read and a write on the API server. Keep it below the '--event-ttl' of the API server (one hour by default), or the event expires between repeats and the hold stops being visible, and keep 'spark.kubernetes.operator.events.minIntervalSeconds' within the rest of that TTL, that is at most the TTL minus this interval, or a repeat is dropped and the next one lands after the event expired. |
56
+
| spark.kubernetes.operator.reconciler.suspendHoldRequeueIntervalSeconds | Long | 1800 | true | Requeue interval (in seconds) at which a resource held by spec.suspend is reconciled, so that its SuspendHeld event is republished. A SparkApplication or SparkCluster suspended while running publishes no such event, since its Suspended status says so, and uses this interval only to look at its released resources again. The hold ends only when a user clears spec.suspend, which arrives as a watch event and reconciles right away, so nothing waits for this interval. It also paces a SparkApplication or SparkCluster which is suspended by a Kueue eviction and whose Workload is deactivated. That hold ends when the Workload is reactivated, which arrives as a watch event as well. It is deliberately much coarser than spark.kubernetes.operator.reconciler.intervalSeconds, since a suspended resource has nothing to observe while each republish costs a read and a write on the API server. Keep it below the '--event-ttl' of the API server (one hour by default), or the event expires between repeats and the hold stops being visible, and keep 'spark.kubernetes.operator.events.minIntervalSeconds' within the rest of that TTL, that is at most the TTL minus this interval, or a repeat is dropped and the next one lands after the event expired. |
57
57
| spark.kubernetes.operator.reconciler.terminationTimeoutSeconds | Integer | 30 | false | Grace period for operator shutdown before reconciliation threads are killed. |
58
58
| spark.kubernetes.operator.reconciler.trimStateTransitionHistoryEnabled | Boolean | true | true | When enabled, operator would trim state transition history when a new attempt starts, keeping previous attempt summary only. It also drops the history of a suspended SparkCluster when it is resumed. |
59
59
| spark.kubernetes.operator.watchedNamespaces | String | default | true | Comma-separated list of namespaces that the operator would be watching for Spark resources. If set to '*', operator would watch all namespaces. |
Copy file name to clipboardExpand all lines: docs/configuration.md
+2-3Lines changed: 2 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -102,7 +102,7 @@ replacing its `message`. A repeat carrying the same `message` may be paced by
102
102
The initial `Submitted` status of a new resource is not persisted to the API server on its own, so
103
103
no event is published for it. A `Submitted` event is published only when a `SparkApplication` or a
104
104
`SparkCluster`moves from `Suspended` back to `Submitted`, after `spec.suspend` is set back to
105
-
`false`, or after the eviction of a `SparkCluster` by Kueue.
105
+
`false`, or after its eviction by Kueue.
106
106
107
107
In addition, the operator publishes the following `Warning` events.
108
108
@@ -115,9 +115,8 @@ In addition, the operator publishes the following `Warning` events.
115
115
| `ClusterRequestFailed` | Applying the Services, StatefulSets, NetworkPolicy, HorizontalPodAutoscaler or PodDisruptionBudget of a `SparkCluster` fails in a way which may yet succeed, such as a throttled request or an internal server error from an admission webhook, so it is retried with the default interval instead of failing the cluster. Transport-level errors are skipped. |
116
116
| `SuspendReleaseFailed` | Deleting the driver pod (`SparkApplication`), the master and worker StatefulSets or the HorizontalPodAutoscaler or PodDisruptionBudget of the workers (`SparkCluster`), or the Kueue `Workload` of a resource suspended while running fails, or its pods cannot be listed. A failure which may clear on its own, such as a timeout, is skipped. Every failure is retried with the default interval. |
117
117
| `KueueResourceFlavorReadFailed` | Reading the `ResourceFlavor`s which Kueue assigned to an admitted `Workload` fails, so the node selector and the tolerations of the flavors cannot be applied. Transport-level errors are skipped and retried every 5 seconds, while a persistent failure is retried with the default interval. |
118
-
| `KueuePodsReadyUpdateFailed` | Recording the `PodsReady` condition on the Kueue `Workload` of a running `SparkApplication` or `SparkCluster` fails, e.g. without the permission for the `workloads/status` subresource, so Kueue may evict it by a `PodsReadyTimeout`, which releases a running `SparkCluster` and queues it againeven if its master and workers are ready. Transport-level errors and conflicts are skipped. A `SparkCluster` retries them every 5 seconds and a persistent failure with the default interval, while a `SparkApplication` retries any failure with its next reconciliation, so that the recording does not hold back the observation of its driver. See [Kueue](spark_custom_resources.md#kueue). |
118
+
| `KueuePodsReadyUpdateFailed` | Recording the `PodsReady` condition on the Kueue `Workload` of a running `SparkApplication` or `SparkCluster` fails, e.g. without the permission for the `workloads/status` subresource, so Kueue may evict it by a `PodsReadyTimeout`, which releases it and queues it again, a `SparkApplication` with a new attempt, even if its pods are ready. Transport-level errors and conflicts are skipped. A `SparkCluster` retries them every 5 seconds and a persistent failure with the default interval, while a `SparkApplication` retries any failure with its next reconciliation, so that the recording does not hold back the observation of its driver. See [Kueue](spark_custom_resources.md#kueue). |
119
119
| `KueueDisabled` | A `SparkApplication` or `SparkCluster` labeled with `kueue.x-k8s.io/queue-name` is not queued, since `spark.kubernetes.operator.kueue.enabled` is disabled, so the driver (or master and worker) is requested without the Kueue admission. |
120
-
| `KueueEvictionIgnored` | Kueue evicted or deactivated the `Workload` of a `SparkApplication` whose driver is requested and whose attempt has neither stopped nor been suspended. The operator does not act on it, so the driver and executors keep running. An evicted `Workload` keeps holding its quota, while a deactivated one no longer counts against it. It is republished on every reconciliation while it lasts, subject to [`minIntervalSeconds`](#event-frequency). See [Kueue](spark_custom_resources.md#kueue). |
121
120
122
121
For a resource held by [`spec.suspend`](spark_custom_resources.md#suspend) or queued by
123
122
[Kueue](spark_custom_resources.md#kueue), the operator also publishes the following `Normal`
0 commit comments