This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-kubernetes-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new deff77fb [SPARK-60080] Add `Kueue Admission and Workload Lifecycle` 
section to `architecture.md`
deff77fb is described below

commit deff77fbe1c8eb162f2962eaf1c2e5bd4a6a14de
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Thu Oct 8 05:01:18 2026 -0700

    [SPARK-60080] Add `Kueue Admission and Workload Lifecycle` section to 
`architecture.md`
    
    ### What changes were proposed in this pull request?
    
    This PR aims to add a `Kueue Admission and Workload Lifecycle` section to 
`docs/architecture.md`. The section covers two things:
    
    - How a queued `SparkApplication` or `SparkCluster` waits for the Kueue 
admission, with a mermaid flowchart of the decision order in `AppInitStep` and 
`ClusterInitStep`.
    - How the operator handles its Kueue `Workload` until it is released.
    
    The `Application State Transition` and `Cluster State Transition` sections 
now link to it.
    
    ### Why are the changes needed?
    
    The admission wait is a hold within `Submitted` (or `ScheduledToRestart`), 
not a state. So the state diagrams alone read as if the driver or master were 
created right after `Submitted`.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. This is a documentation-only change.
    
    ### How was this patch tested?
    
    Manual review against the code. The doc also passed `markdownlint`, and all 
its mermaid diagrams render with `mmdc`.
    
    - 
https://github.com/dongjoon-hyun/spark-kubernetes-operator/blob/SPARK-60080/docs/architecture.md#kueue-admission-and-workload-lifecycle
    
    <img width="788" height="618" alt="Screenshot 2026-10-07 at 20 55 00" 
src="https://github.com/user-attachments/assets/d9ebdd8a-0a86-468b-9bda-40d7c0169dc7";
 />
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5.5
    
    Closes #957 from dongjoon-hyun/SPARK-60080.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 docs/architecture.md | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/docs/architecture.md b/docs/architecture.md
index bfccdc5a..529835da 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -91,6 +91,109 @@ Multiple Operator instances can coexist on the same cluster 
(for example one per
 tenant) as long as each uses a distinct ClusterRole / ClusterRoleBinding name 
and a disjoint set of
 watched namespaces. See [operations.md](operations.md) for a concrete 
multi-instance example.
 
+## Kueue Admission and Workload Lifecycle
+
+A `SparkApplication` or a `SparkCluster` labeled with 
`kueue.x-k8s.io/queue-name` is queued by
+[Kueue](https://kueue.sigs.k8s.io/) when 
`spark.kubernetes.operator.kueue.enabled` is set, which the
+Helm chart sets with `operatorRbac.kueue.enabled` along with the RBAC rules 
for Kueue. The setting
+also registers the `Workload` informer, while without it, the label is ignored 
with the
+`KueueDisabled` warning event and the Operator does not access Kueue at all. 
For a queued resource,
+the Operator creates a Kueue `Workload` with the pod sets of its driver and 
executors (or master and
+workers), and requests the driver (or the master and workers) only once Kueue 
admits the
+`Workload`. Applications and clusters share this mechanism. This section 
relates it to the
+reconciliation and the state machines below, while 
[Suspend](spark_custom_resources.md#suspend)
+and [Kueue](spark_custom_resources.md#kueue) describe the behavior in detail.
+
+Waiting for the admission is not a state but a hold within the initialization 
step, like waiting
+for `spec.suspend` to be cleared, so the resource stays in `Submitted`, or 
`ScheduledToRestart` for
+a restarted application attempt, meanwhile. `AppInitStep` and 
`ClusterInitStep` decide as follows.
+
+```mermaid
+flowchart TD
+    init([Submitted or ScheduledToRestart]) --> suspend{spec.suspend?}
+    suspend -->|set, nothing requested yet| suspendHold[Release the Workload, 
SuspendHeld event]
+    suspend -->|unset, or requested already| queued{Queue label and Kueue 
enabled?}
+    queued -->|no| request[Request the driver, or the master and workers]
+    queued -->|yes| requested{Driver or master requested already?}
+    requested -->|yes| reapply[Apply the flavors of the earlier admission]
+    requested -->|no| admission{Admission of the Workload}
+    admission -->|PENDING or BACKOFF| kueueHold[KueueAdmissionPending event]
+    admission -->|STALE| stale[Delete the Workload to create it again]
+    admission -->|ADMITTED| admitted[Apply the flavors and pod set updates, 
KueueAdmitted event]
+    reapply --> request
+    admitted --> request
+    request --> next([DriverRequested or RunningHealthy])
+    suspendHold --> hold([Stay in the same state until the next 
reconciliation])
+    kueueHold --> hold
+    stale --> hold
+```
+
+* `spec.suspend` comes first (`SuspendUtils`), so a suspended resource is held 
without a
+  `Workload`, and suspending a queued one releases its `Workload`. A cluster 
resumed from
+  `Suspended` moves back to `Suspended` instead of being held. After the 
restart backoff of a
+  restarted attempt, `KueueWorkloadUtils` creates the `Workload` unless it 
exists, and finds it
+  `ADMITTED`, `PENDING`, `BACKOFF` if it is evicted and its requeue backoff 
has not elapsed, or
+  `STALE` if it is owned by another resource, still pending for an outdated 
spec, evicted after the
+  backoff, or being deleted.
+* Once the `Workload` is admitted, the `nodeLabels` and `tolerations` of the 
`ResourceFlavor`s
+  assigned to it and the pod set updates of its admission checks are applied 
to the driver and
+  executor pod templates of a copy of the `SparkApplication`, which the driver 
resources are built
+  from, or to the pod templates of the master and worker `StatefulSet`s. 
Neither the custom
+  resource nor the `Workload` is changed.
+* A driver or master which was requested already, e.g. when the status update 
after the request
+  failed, is not held and requests no admission, since it has to complete the 
initialization
+  rather than run unobserved. Its resources are applied again in that 
reconciliation, so the
+  flavors of the earlier admission are applied again as well, rather than 
dropped from them.
+* A hold changes no state. The initial `Submitted` state of a first attempt is 
not persisted to the
+  API server until the next state, so `kubectl get` shows an empty `Current 
State`, and the
+  `SuspendHeld`, `KueueAdmissionPending` and `KueueAdmitted`
+  [events](configuration.md#kubernetes-events) show the progress instead. A 
hold completes the
+  reconciliation with a requeue, after 
`spark.kubernetes.operator.reconciler.intervalSeconds` while
+  the `Workload` is pending, or after
+  `spark.kubernetes.operator.reconciler.suspendHoldRequeueIntervalSeconds` for 
`spec.suspend`, and
+  the next reconciliation decides again in the same order, which requests the 
admission again and
+  republishes the event.
+* The `Workload` informer reconciles the resource as soon as the admission, 
the eviction or the
+  activation of its `Workload` changes, or the `Workload` is deleted, so that 
an admission starts
+  the resource right away rather than at the next requeue. It ignores the 
creation of the
+  `Workload` and its other updates, e.g. of the status of a pending one, which 
would use up the
+  per-resource rate limit.
+* A `Workload` which Kueue evicts before the driver or master is requested is 
kept until the
+  requeue backoff which Kueue records on it elapses, since its deletion would 
drop the backoff, and
+  then it is deleted and created again, which queues the resource again. A 
deactivated `Workload`
+  is kept as it is until it is reactivated, since Kueue neither counts nor 
admits it, and a new one
+  would come back active.
+* While the resource runs, the Operator records the `PodsReady` condition on 
its `Workload` once
+  as many pods of each pod set as its `count` are ready, so that the 
`waitForPodsReady` timeout of
+  Kueue does not evict it. `AppRunningStep` records it from `DriverReady` on, 
and
+  `ClusterSuspendStep` while the cluster is `RunningHealthy`, which a cluster 
enters without
+  waiting for its pods. It is recorded only once and never set back to 
`False`, since an eviction
+  for a lost executor or worker, which Spark replaces, would run the resource 
again from scratch.
+* The `Workload` is released at the following points. Kueue would admit 
another workload into the
+  quota which terminating pods still occupy, so the Operator deletes it only 
after the pods are
+  gone, unless they are stuck terminating, and the resource keeps its state 
until then.
+  * When an application releases its resources, at the end of an attempt, when 
their retention
+    expires, or when the application is deleted, `AppCleanUpStep` deletes the 
`Workload` before the
+    application moves to `ResourceReleased` or `ScheduledToRestart`. The 
`Workload` name is fixed
+    per resource, so a restarted attempt is queued with a new `Workload` 
rather than run on this
+    admission.
+  * When an application retains its resources as 
`TerminatedWithoutReleaseResources` after
+    `Succeeded`, `Failed` or `DriverEvicted`, its `Workload` gets the Kueue 
`Finished` condition
+    instead, which releases the quota while keeping the `Workload` with the 
resources, since the
+    driver pod is in a terminal phase and no attempt follows.
+  * When a resource enters `Suspended` by `spec.suspend` or by the eviction of 
its `Workload`,
+    `AppSuspendStep` or `ClusterSuspendStep` releases the driver, or the 
master and workers, and
+    then the `Workload`, an evicted one after its requeue backoff, and a 
deactivated one after it is
+    reactivated. The resource then moves to `Submitted` and is queued again 
with a new `Workload`,
+    once `spec.suspend` is cleared if it is set. The `suspendReason` of the 
`Suspended` state,
+    `SpecSuspend` or `KueueEviction`, tells the two causes apart.
+  * When a `SparkCluster` is deleted, its `Workload` is garbage collected 
through its
+    `ownerReference`, like the other resources of the cluster.
+* The Operator writes none of the `Workload` status which Kueue owns, such as 
the admission or the
+  requeue state. It records only the `PodsReady` and `Finished` conditions, 
and releases or
+  requeues a resource by deleting its `Workload` and creating a new one, so 
that the integration
+  does not depend on how Kueue manages that status.
+
 ## Application State Transition
 
 ```mermaid
@@ -165,6 +268,9 @@ stateDiagram-v2
 ```
 
 * Spark applications are expected to run from submitted to succeeded before 
releasing resources
+* An application stays in `Submitted` or `ScheduledToRestart` while it is held 
by `spec.suspend` or
+  waits for the Kueue admission, since neither is a state of its own. See
+  [Kueue Admission and Workload 
Lifecycle](#kueue-admission-and-workload-lifecycle).
 * Once the driver is requested, an application moves to `Succeeded`, `Failed` 
or `DriverEvicted`
   from any of the states above whenever the driver pod terminates or is 
evicted. It also moves to
   `Failed` if the driver pod is removed unexpectedly.
@@ -225,6 +331,9 @@ stateDiagram-v2
   [Kueue](spark_custom_resources.md#kueue) is suspended as well when Kueue 
evicts its `Workload`,
   and moves to `Submitted` once its master and workers are released and the 
requeue backoff of its
   `Workload` elapsed, or once its `Workload` is reactivated if it was 
deactivated.
+* Likewise, a cluster stays in `Submitted` while it waits for the Kueue 
admission, or while it is
+  held by `spec.suspend` before it ever ran. See
+  [Kueue Admission and Workload 
Lifecycle](#kueue-admission-and-workload-lifecycle).
 * Apart from `spec.suspend` and a Kueue eviction, a cluster leaves 
`RunningHealthy`, `Suspended` or
   `Failed` only when its custom resource is deleted. At that point, the K8s 
resources created for the cluster are
   garbage collected through their `ownerReference` to the `SparkCluster` 
custom resource.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to