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


##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/reconciler/reconcilesteps/AppRunningStep.java:
##########
@@ -49,6 +46,10 @@ public final class AppRunningStep extends AppReconcileStep {
   @Override
   public ReconcileProgress reconcile(
       SparkAppContext context, SparkAppStatusRecorder statusRecorder) {
+    // The driver and executors are observed from DriverReady on, so the Kueue 
Workload learns here
+    // that they are ready, before a state transition ends the reconciliation. 
A failed recording is
+    // retried by a later reconciliation rather than holding back the 
observation of the driver.
+    KueueWorkloadUtils.recordPodsReady(context);

Review Comment:
   **Finding 1.** The return value is dropped here, so on the 
`SparkApplication` path neither retry the description promises happens.
   
   The description says:
   
   > A conflict or a transport-level failure is retried after 5 seconds. A 
persistent failure publishes the new `KueuePodsReadyUpdateFailed` warning event 
and is retried with the default interval.
   
   Both hold for `ClusterSuspendStep`, which returns 
`recordPodsReady(context).orElse(proceed())`. For an application:
   
   - a conflict or a transient failure gives up its 
`completeAndRequeueAfter(STALE_WORKLOAD_REQUEUE_INTERVAL)`, and the retry lands 
on whatever this reconciliation ends with instead;
   - a persistent failure gives up its `completeAndDefaultRequeue()`, still 
publishes the event, and again inherits the step's own progress.
   
   What that inherits is not one interval. When the step changes nothing it 
falls through to `observeDriver`, which proceeds, and 
`SparkAppReconciler.reconcile` ends with `completeAndDefaultRequeue()`, i.e. 
`spark.kubernetes.operator.reconciler.intervalSeconds` (120s) rather than 5s. 
When the step transitions state it returns `completeAndImmediateRequeue()`. 
When the driver is missing from the cache, `observeDriver` returns 
`MISSING_DRIVER_REQUEUE_INTERVAL_SECONDS`. Against the 30-minute default 
`waitForPodsReady` timeout none of these is dangerous, so this is about the 
contract rather than about a broken resource.
   
   The comment above the call gives a good reason not to *hold back* the driver 
observation, and that reason is separable from discarding the value. The step 
can keep it and apply it on the branch that would otherwise proceed:
   
   ```java
       Optional<ReconcileProgress> podsReadyRetry = 
KueueWorkloadUtils.recordPodsReady(context);
   ```
   
   then at the bottom:
   
   ```java
       if (proposedStateSummary == prevStateSummary) {
         ReconcileProgress progress =
             observeDriver(context, statusRecorder, List.of(new 
AppDriverRunningObserver()));
         // A failed recording is retried on its own schedule, but only where 
the driver observation
         // has nothing of its own to say.
         return progress.isCompleted() ? progress : 
podsReadyRetry.orElse(progress);
       }
   ```
   
   If you would rather keep the drop as it is, the description's retry sentence 
needs the qualification, and so does the `KueuePodsReadyUpdateFailed` row in 
`docs/configuration.md:97`. That row reads "Transport-level errors and 
conflicts are skipped, and a persistent failure is retried with the default 
interval", which drops the "retried every 5 seconds" half its 
`KueueResourceFlavorReadFailed` neighbour carries — accurate for an 
application, not for a cluster.
   
   Either way this branch is the one path through `recordPodsReady` with no 
test: `AppRunningStepTest.immediateRequeueWhenRunningStateChanges` verifies the 
call is made, and nothing pins what happens to a failed one.
   



##########
spark-operator/src/main/java/org/apache/spark/k8s/operator/context/BaseContext.java:
##########
@@ -104,6 +111,30 @@ public Optional<Workload> getCachedKueueWorkload() {
     return josdkContext.getSecondaryResource(Workload.class);
   }
 
+  /**
+   * Counts the ready pods of the resource per Spark role in the informer 
cache, once for the
+   * lifetime of this context, since a reconciliation may check them more than 
once.
+   *
+   * @return The number of the ready pods by the value of their `spark-role` 
label, e.g. `executor`
+   *     or `worker`.
+   */
+  public Map<String, Long> countReadyPodsByRole() {
+    synchronized (this) {
+      if (readyPodsByRole == null) {
+        readyPodsByRole =
+            josdkContext
+                .getSecondaryResourcesAsStream(Pod.class)
+                .filter(pod -> 
pod.getMetadata().getLabels().containsKey(LABEL_SPARK_ROLE_NAME))

Review Comment:
   **Finding 2.** This filter is not just narrowing to Spark pods, it is what 
keeps the collector from throwing, and that is worth a clause of comment since 
the next reader may take it for a tidy-up and inline it into the `groupingBy` 
classifier.
   
   `Collectors.groupingBy` throws `NullPointerException` when the classifier 
returns null, so a pod in the stream without `spark-role` would fail the 
reconciliation rather than be ignored. Such a pod is reachable on the cluster 
side: `SparkClusterReconciler.prepareEventSources` selects the pod informer on 
`LABEL_SPARK_CLUSTER_NAME` alone, with no operator-name term, so anything 
carrying that one label is a secondary resource of the cluster. The application 
informer is narrower (`commonResourceLabelsStr()` plus the app-name mapper), 
but the helper is shared.
   
   ```java
                   // Not only a narrowing to the Spark pods: `groupingBy` 
throws on a null key, and
                   // the cluster pod informer selects on the cluster name 
label alone.
                   .filter(pod -> 
pod.getMetadata().getLabels().containsKey(LABEL_SPARK_ROLE_NAME))
   ```
   



-- 
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