LuciferYang commented on PR #58163:
URL: https://github.com/apache/spark/pull/58163#issuecomment-5365610350

   Pushed as `3e2480aaf92`. Items 1, 3, 5 and 6 are done as asked. Item 2 takes 
a slightly different route to the same end, and item 4 changes the remaining 
sentence rather than only deleting the clause; both are explained below.
   
   **1.** Five lines now, close to your version:
   
   ```scala
   // The credentials secret takes precedence over the driver service account: 
this branch never
   // applies the account, so warn that the pod keeps whatever its spec names, 
or the namespace
   // default. Stay quiet when the spec already names the same account. Both 
spec fields are
   // read, `serviceAccountName` winning, matching Kubernetes' 
SetDefaults_PodSpec: a pod
   // template is deserialized with no API-server defaulting, so it can leave 
either one null.
   ```
   
   The `caCertFile` example and the warn versus require reasoning are in the 
JIRA and the PR description. The test comments came down the same way, and the 
commit is net negative overall (+37/-41).
   
   **2.** Done, with one deviation. The constant goes in `MDC(PREFIX, 
KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX)` with the `.*` as literal text, 
rather than `MDC(CONFIG2, s"$KUBERNETES_AUTH_DRIVER_MOUNTED_CONF_PREFIX.*")`. I 
had the string hardcoded on purpose: all 15 other `MDC(CONFIG2, ...)` sites in 
the tree hold a single real config key, so a glob there turns `config2` into a 
value nothing can be joined on once structured logging is enabled. `PREFIX` 
takes exactly the prefix, which uses the constant and keeps the MDC value 
meaningful. Switching to `CONFIG2` is one line if you prefer it.
   
   **3.** Done. The message names the account, and the no-account case now says 
what happens instead of offering both:
   
   ```
   ... take precedence: the pod keeps other, named by its spec. To have Spark 
apply ...
   ... take precedence: the pod falls back to the namespace's default account. 
To have Spark apply ...
   ```
   
   Two assertions cover it, each with its own mutation: blanking the name fails 
with `did not contain "other"`, blanking the fallback wording fails with `did 
not contain "namespace's default"`.
   
   **4.** Clause dropped. I did not leave the rest as "Spark logs a warning 
about that", because with the guard in place that sentence is not always true. 
It reads "Spark logs a warning when the account is dropped" now, which is 
accurate without documenting the heuristic, since in the same-account case 
nothing is dropped. The two table rows are untouched.
   
   **5.** Done. It was five call sites with two discarding the value; those two 
call `withLogAppender` directly now, and `withLogAppenderReturning` is left 
only where the pod is used. The logger name is a suite-level val.
   
   **6.** Checked it. Worth stating the blast radius first: the warning needs 
`spark.kubernetes.authenticate.driver.serviceAccountName` set, one of the four 
submitted 
`spark.kubernetes.authenticate.driver.{oauthToken,caCertFile,clientKeyFile,clientCertFile}`
 values rather than the `mounted.*` variants, and a pod spec that does not 
already name that same account 
(`DriverKubernetesCredentialsFeatureStep.scala:73`, `:91`). So whatever the 
cadence, it applies to the misconfiguration the warning is about, not to 
applications generally.
   
   It is not per reconcile. Only two steps ever ask for the spec, the result is 
memoized per context (`SparkAppContext.java:117-126`), and each of those two 
wraps all three getters in a single try/catch that returns 
(`AppInitStep.java:98-140`, `AppCleanUpStep.java:140-157`), so a reconcile 
builds at most once. `AppInitStep` returns before its getters unless the state 
`isInitializing()`, which is exactly `Submitted` or `ScheduledToRestart` 
(`:66-67`, `ApplicationStateSummary.java:131-133`). `AppCleanUpStep` reaches 
its getters only when the last state observed before termination was 
`SchedulingFailure` (`:137`, `:317-321`), one check that covers a live app and 
a terminated one; `cleanup()` runs that same step 
(`SparkAppReconciler.java:214`) against a context of its own (`:211`), so the 
finalizer shares the gate and its build is not deduplicated against the 
reconcile path's. `AppResourceObserveStep`, `AppRunningStep`, `AppValidateStep` 
and `AppUnknownStateStep` never ask at all, so a r
 unning application never rebuilds. That part is measured, not only read: 
driving the real reconciler and its real steps with the submission worker 
mocked to count `getResourceSpec` calls gives 1 build at `Submitted`, 0 at 
`DriverRequested` and `DriverStarted`, 0 across five consecutive 
`RunningHealthy` reconciles, 0 for a `Failed` app entering restart, and 1 at 
`ScheduledToRestart`, so two builds across ten reconciles for one attempt plus 
one restart. A `cleanup()` of a healthy app is 0, and an app in 
`SchedulingFailure` is 1 whether reached through `reconcile()` or `cleanup()`.
   
   It does repeat without bound and with no backoff of its own in one failure 
mode, which I suspect is the case you had in mind. When `persistStatus` cannot 
patch the status it swallows the `KubernetesClientException` and returns false 
(`StatusRecorder.java:146-155`), the step then leaves the state where it was 
and requeues with no added delay (`AppReconcileStep.java:155-160`, 
`ReconcileProgress.java:87-89`), and the cached status is not advanced either, 
since `StatusRecorder.java:131` is reached only on success. The next reconcile 
is therefore the same state again, and that hits all three building paths: 
`Submitted`, `ScheduledToRestart` once its backoff window has elapsed, and the 
`SchedulingFailure` release path, which an ordinary reconcile takes as well, 
since `AppCleanUpStep` is the second step of every one. This last paragraph is 
reasoning from the source rather than measurement, unlike the counts above.
   
   I would still leave the warning as it is. The loop is paced from more than 
one direction. Each round sleeps inside `persistStatus`, which attempts the 
patch `API_STATUS_PATCH_MAX_ATTEMPTS` times (default 3) and sleeps 
`API_RETRY_ATTEMPT_AFTER_SECONDS` (default 1) after each failure including the 
last, on the reconcile thread (`StatusRecorder.java:96-129`), so a round costs 
at least about three seconds, and considerably more once the fabric8 client's 
own retries engage on a 429 or a 5xx. On top of that the operator gives this 
controller a JOSDK rate limiter of 5 reconciles per 15 seconds per resource 
(`SparkOperator.java:332`, `SparkOperatorConf.java:382-405`). Every round also 
already logs an ERROR of the operator's own (`StatusRecorder.java:152`) plus a 
WARN from the step (`AppReconcileStep.java:158`, or `:201` on the 
append-and-persist paths). A feature-step line riding along is not what to fix 
there. The cadence has precedent as well: `KerberosConfDriverFeatureStep` logs 
from i
 ts class body whenever neither krb5 config is set (`:81-84`), and that step is 
constructed on every build (`KubernetesDriverBuilder.scala:86`), before the 
excluded-steps filter can drop it. That one is INFO rather than WARN, so an 
operator running at WARN would see mine and not it.
   
   On the executor step, I read it the same way, and it is worse there than on 
the driver side. The guard is followed by `buildPodWithServiceAccount`, which 
writes both fields, so an executor template naming only `serviceAccountName` 
does not merely get a stray warning, it runs under a different identity. It 
dates from `7912ab85a6f`. Filed as SPARK-58910, with the repro and the reason 
`ExecutorKubernetesCredentialsFeatureStepSuite` misses it: all three cases 
start from `SparkPod.initialPod()`, so both fields are null and the `isEmpty` 
branch always wins.
   
   On warn versus require, agreed, and config-gated enforcement in its own 
ticket sounds right if it comes up.
   
   On CI, I read the failure on the commit you reviewed the same way: 
`StateDataSourceTransformWithStateSuiteCheckpointV2` twice with "There are 2 
possibly leaked file streams", nothing in `kubernetes`. The push above has no 
failures so far, with one job still running.
   


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