hyeon kim created FLINK-40470:
---------------------------------

             Summary: Savepoint redeploy loops forever when the deployment 
attempt fails
                 Key: FLINK-40470
                 URL: https://issues.apache.org/jira/browse/FLINK-40470
             Project: Flink
          Issue Type: Bug
          Components: Kubernetes Operator
    Affects Versions: kubernetes-operator-1.15.0
            Reporter: hyeon kim


When {{job.savepointRedeployNonce}} is changed, 
{{AbstractJobReconciler#redeployWithSavepoint}} cancels the running job 
(deleting the whole cluster) and redeploys from {{initialSavepointPath}}. 
Unlike every other deployment path (first deployment, suspend/resume, session 
deploy), the target spec is only recorded into the resource status *after* the 
deploy call succeeds:

{code:java}
cancelJob(ctx, SuspendMode.STATELESS);
...
deploy(...);                                             // <- if this throws...
ReconciliationUtils.updateStatusForDeployedSpec(...);    // <- ...the new nonce 
is never recorded
{code}

If {{deploy}} throws for any reason (transient API server error, or an error 
raised after the cluster resources were actually created — e.g. 
{{KubernetesClusterDescriptor#deployApplicationCluster}} fails resolving the 
rest endpoint of a freshly created LoadBalancer service on RBAC-restricted 
clusters where listing nodes is forbidden), the new nonce is never recorded in 
{{lastReconciledSpec}}. Every subsequent reconciliation classifies the diff as 
{{SAVEPOINT_REDEPLOY}} again and repeats the full delete + redeploy cycle 
indefinitely.

Observed in production on a managed Kubernetes platform where {{nodes}} list is 
forbidden for namespaced tenants: with {{kubernetes.rest-service.exposed.type: 
LoadBalancer}}, a savepoint redeploy looped delete/recreate for 23 minutes 
until manual intervention.

All other job deployment paths already call 
{{ReconciliationUtils.updateStatusBeforeDeploymentAttempt}} before deploying 
(see {{AbstractJobReconciler#reconcileSpecChange}} for the suspended->running 
transition and 
{{AbstractFlinkResourceReconciler#updateStatusBeforeFirstDeployment}}), which 
makes a failed attempt recover through the regular upgrading/observe flow 
instead of retriggering the redeploy.

*Proposed fix:* record the target spec with 
{{updateStatusBeforeDeploymentAttempt}} + 
{{statusRecorder.patchAndCacheStatus}} before the deploy call in 
{{redeployWithSavepoint}}, mirroring the suspend/resume path. A failed deploy 
then recovers on the next reconciliation by restoring from the recorded 
{{upgradeSavepointPath}} (which already points at {{initialSavepointPath}}), 
without repeating the delete + redeploy cycle.

*Open question:* with this fix, the retry recovers through the regular 
suspended->running restore path. That path only restores from 
{{upgradeSavepointPath}} when the effective upgrade mode is not STATELESS, so 
for a spec with {{upgradeMode: stateless}} the retry falls back to an 
empty-state restore instead of the requested savepoint (still strictly better 
than the current infinite delete/redeploy loop). Distinguishing "interrupted 
savepoint redeploy" from "user deliberately switched to a stateless resume" in 
{{reconcileSpecChange}} needs a semantic decision, so I left that corner as a 
follow-up — happy to address it in this ticket if there is a preferred approach.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to