peter-toth commented on code in PR #892:
URL: 
https://github.com/apache/spark-kubernetes-operator/pull/892#discussion_r4111659418


##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppInitStep.java:
##########
@@ -220,15 +221,28 @@ private Optional<ReconcileProgress> holdForKueueAdmission(
    * the case where the driver was created but the status update to 
DriverRequested failed, so that
    * a suspended application still completes its initialization instead of 
being held with a live
    * driver. See {@link SparkAppContext#getCurrentAttemptDriverPod()} for how 
a pod left
-   * from a previous attempt is told apart.
+   * from a previous attempt is told apart. The informer cache may not have 
seen a driver created
+   * in the previous reconcile yet, so a driver missing from it is looked up 
on the API server as
+   * well if the Kueue Workload is evicted or deactivated, since that Workload 
would be released or
+   * held under the driver. Otherwise, the driver spec is not built for the 
lookup.
    *
    * @param context The SparkAppContext for the application.
    * @return True if the driver pod of the current attempt exists, false 
otherwise.
    * @throws KubernetesClientException if the lookup fails, so that a running 
driver is not
    *     mistaken for one that was never requested.
    */
   private boolean isDriverRequested(SparkAppContext context) {
-    return context.getCurrentAttemptDriverPod().isPresent();
+    if (context.getCurrentAttemptDriverPod().isPresent()) {
+      return true;
+    }
+    Optional<Workload> workload = context.getCachedKueueWorkload();
+    if (workload.isEmpty()

Review Comment:
   **Finding 1.** This guard admits a `Workload` that was never admitted, and 
on that path the fallback's answer makes the resource start rather than wait.
   
   A driver can only exist on this path after `holdForAdmission` returned 
`ADMITTED`, so `isAdmitted` is a necessary condition for the question the 
fallback is asking. It is not checked. And `Evicted=True` with no `Admitted` is 
a state Kueue produces routinely: the `!IsActive` branch of 
`workload_controller.go` calls `workloadevict.Evict` unconditionally, so a 
`Workload` deactivated while still pending gets `Evicted=True` with reason 
`Deactivated` and no admission. That is the same state your own `holdsQuota` 
guard in `requestAdmission` was written to recognise.
   
   Measured in `AppInitStepTest`, with a labeled application whose `Workload` 
has `active: false` and `Evicted=True/Deactivated` but no `Admitted` condition, 
a pod carrying the driver name present and not terminating, and 
`getCurrentAttemptDriverPod()` returning empty:
   
   | arm | probe result | `driverMissingFromCacheKeepsItsEvictedKueueWorkload` |
   |---|---|---|
   | `AppInitStep.java` at base `f2720a4c` | `admitted=false`, state 
`Submitted` | FAILS, so your test is effective |
   | this PR | `admitted=false`, state **`DriverRequested`** | passes |
   | this PR + the gate below | `admitted=false`, state `Submitted` | passes |
   
   So the application reports `DriverRequested` while its `Workload` is 
deactivated and Kueue counts nothing for it, which is the state the PR exists 
to prevent. Base holds, so this arrives with the PR.
   
   The reachability is the narrow part, and I want to be straight about it. The 
fallback keeps only the pod's name and `deletionTimestamp`; it drops the 
driver-label filter and the cache-candidate gate that 
`SparkAppContext.getCurrentAttemptDriverPod()` documents as how a previous 
attempt's pod is told apart ("The informer cache is only used to find a 
candidate ... the candidate is verified against the API server"). So the pod it 
finds is the current attempt's driver only by assumption. Reusing the driver 
pod name across attempts, which that javadoc names as the case it guards, plus 
a retained pod, is the concrete way to get a foreign pod under that name.
   
   Gating on the admission closes it without touching the case you added:
   
   ```java
       Optional<Workload> workload = context.getCachedKueueWorkload();
       if (workload.isEmpty()
           || !KueueWorkloadUtils.isAdmitted(workload.get())
           || KueueWorkloadUtils.findEviction(workload.get()).isEmpty()
               && !KueueWorkloadUtils.isDeactivated(workload.get())) {
         return false;
       }
   ```
   
   `driverMissingFromCacheKeepsItsEvictedKueueWorkload` still passes with it, 
since that `Workload` is admitted, and `spark-operator`'s Kueue and 
reconcile-step suites plus `checkstyleMain`, `pmdMain` and `spotlessCheck` are 
green.
   
   It buys a second thing. A `Workload` deactivated while pending is the common 
shape of `kueuectl stop workload` on a queued resource, and that hold can last 
hours. Without the gate, every reconcile in it runs 
`context.getDriverPodSpec()`, which builds the whole driver submission spec, 
plus one API `GET`, to answer a question whose answer cannot be yes. Your 
javadoc already flags that cost ("Otherwise, the driver spec is not built for 
the lookup"); the admission gate is what makes the scoping match it.
   



##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/kueue/v1beta2/WorkloadStatus.java:
##########
@@ -85,4 +86,19 @@ public boolean isFinished() {
                     "Finished".equalsIgnoreCase(c.getType())
                         && "True".equalsIgnoreCase(c.getStatus()));
   }
+
+  /**
+   * Checks if Kueue evicted the workload. Kueue keeps the "Admitted" 
condition of an evicted
+   * workload until the controller of its resources stops them and releases 
the quota.
+   *
+   * @return true if an "Evicted" condition exists with status "True", false 
otherwise.
+   */
+  public boolean isEvicted() {

Review Comment:
   **Finding 2.** Nothing in `src/main` calls this, and it is a second copy of 
a predicate the PR also writes in `KueueWorkloadUtils`.
   
   `grep -rn "isEvicted" spark-operator/src` returns only 
`WorkloadTest.java:143,161,164,167` plus the declaration here and the javadoc 
reference at `KueueWorkloadUtils.java:83`. Every production read of the 
eviction goes through `KueueWorkloadUtils.findEviction`, which matches 
`CONDITION_EVICTED` with its own stream rather than calling this.
   
   That inverts the convention the file already sets. `isAdmitted`, 
`isQuotaReserved` and `isFinished` live on `WorkloadStatus`, and 
`KueueWorkloadUtils.isAdmitted(Workload)` is a thin wrapper over 
`status.isAdmitted()`. The `CONDITION_FINISHED` constant is not a 
counterexample: it exists because `addFinishedCondition` *writes* that 
condition, while the operator only ever reads `Evicted`.
   
   The javadoc at `KueueWorkloadUtils.java:83` makes the split visible: "Type 
of the Kueue condition of an evicted Workload, as {@link 
WorkloadStatus#isEvicted}" points the reader at the method that is not the one 
doing the work.
   
   Two ways out, either is fine. Drop `isEvicted()` and its four `WorkloadTest` 
assertions, and drop the `{@link}` from the constant's javadoc, since 
`findEviction` needs the `Condition` for the event message anyway. Or move the 
lookup down here, which keeps the predicate in one place:
   
   ```java
     public Optional<Condition> findEviction() {
       return conditions == null
           ? Optional.empty()
           : conditions.stream()
               .filter(
                   c ->
                       "Evicted".equalsIgnoreCase(c.getType())
                           && "True".equalsIgnoreCase(c.getStatus()))
               .findFirst();
     }
   
     public boolean isEvicted() {
       return findEviction().isPresent();
     }
   ```
   
   with `KueueWorkloadUtils.findEviction(workload)` delegating to it for a null 
status, the way `isAdmitted` already does.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to