Saurabh Kanawade created FLINK-41033:
----------------------------------------

             Summary:  Savepoint history cleanup disposes the savepoint 
referenced by status.jobStatus.upgradeSavepointPath, causing restore failures 
(FileNotFoundException)
                 Key: FLINK-41033
                 URL: https://issues.apache.org/jira/browse/FLINK-41033
             Project: Flink
          Issue Type: Bug
          Components: Kubernetes Operator
    Affects Versions: kubernetes-operator-1.13.0
            Reporter: Saurabh Kanawade


Flink Kubernetes Operator 1.13.0 (upgraded from 1.8.0), Flink 1.18.1, 
application mode (FlinkDeployment), upgradeMode: savepoint, Kubernetes HA 
(ConfigMap based), adaptive scheduler, FlinkStateSnapshot CRs not used (legacy 
savepointHistory), AWS EKS, checkpoints/savepoints/HA on S3, state backend

We recently upgraded the Flink Kubernetes Operator from 1.8.0 to 1.13.0. Since 
then, one of our production jobs went down for about 9.5 hours and could not 
recover by itself. The restore failed with {{FileNotFoundException}} on a 
savepoint that the operator's own savepoint-history cleanup had deleted 28 days 
earlier. The operator still listed that savepoint as 
{{{}status.jobStatus.upgradeSavepointPath{}}}. We could only recover by 
manually redeploying with {{initialSavepointPath}} pointing at a newer 
savepoint. The day after the manual fix, the operator deleted that savepoint 
too, so the job is again in the same position.

We have not found a configuration that keeps periodic savepoint cleanup and 
still guarantees the job can be recovered. We would appreciate guidance from 
the community. Our questions are at the end.
{code:java}
||Time||Event||
|09 Sep 22:13:52|Spec change → savepoint upgrade. Job {{2804d6…}} suspended 
with {{savepoint-2804d6-c6cd134a569d}}.|
|09 Sep 22:15:36|New job {{61c71cfe5ef7d6b55c3586c04bd1b68f}} starts from that 
savepoint; the operator sets {{upgradeSavepointPath}} to it.|
|10 Sep 19:12:25|The operator ({{SnapshotObserver}}) disposes 
{{savepoint-2804d6-c6cd134a569d}}, about 21h after it was written. It dropped 
out of the 96-entry history.|
|15 Sep … 3 Oct|Status still shows {{upgradeSavepointPath: 
…/savepoint-2804d6-c6cd134a569d}}. No event or warning that the file is gone.|
|24 Sep 22:23:51|JobManager restart; the job recovers normally from an HA 
checkpoint (same job ID, new start time).|
|07 Oct ≈22:41|Last periodic savepoint taken: 
{{savepoint-61c71c-a40dbf62613a}}.|
|07 Oct 22:50–22:58|Node disruption in the cluster. Both JobManagers, 
TaskManagers and both operator pods are rescheduled. 5 other Flink jobs hit by 
the same event recover within minutes.|
|07 Oct 22:51:24|Our job goes to RESTARTING on the old leader JM after losing a 
TaskManager.|
|07 Oct 22:52:48|New JM leader takes over. It finds *no completed checkpoint in 
the HA store* and falls back to {{execution.savepoint.path}} (the 9 Sep 
savepoint).|
|07 Oct 22:53:43|*Restore fails: FileNotFoundException. Job goes to FAILED.* 
{{jobManagerDeploymentStatus}} stays READY.|
|08 Oct 08:08:23|A spec change makes the operator redeploy the FAILED job ("Job 
is in terminal state, ready for upgrade from observed latest state").|
|08 Oct 08:09:52|*Fails again with the same FileNotFoundException.* The 
operator restored from {{upgradeSavepointPath}}, not from the newer 22:41 
savepoint recorded in {{savepointInfo.lastSavepoint}}.|
|08 Oct 08:23:36|Manual {{initialSavepointPath = 
…/savepoint-61c71c-a40dbf62613a}} plus {{savepointRedeployNonce}} bump. Job 
RUNNING at 08:24:53.|
|08 Oct 22:42:16|The operator disposes {{savepoint-61c71c-a40dbf62613a}}, the 
savepoint the new job was just started from. It is still the 
{{upgradeSavepointPath}}.|
{code}
h3. Failure logs

1) Savepoint created on upgrade, then disposed by the operator:
{noformat}
2026-09-09 22:13:52  AbstractFlinkService  Job successfully suspended with 
savepoint 
s3://<bucket>/<namespace>/<job>/savepoint/savepoint-2804d6-c6cd134a569d
2026-09-10 19:12:25  SnapshotObserver      Disposing savepoint 
s3://<bucket>/<namespace>/<job>/savepoint/savepoint-2804d6-c6cd134a569d
{noformat}
2) Operator status 23 days later, still pointing to the deleted savepoint:
{noformat}
2026-10-03 09:25:33 (status logged by operator)
"jobId":"61c71cfe5ef7d6b55c3586c04bd1b68f","state":"RUNNING","startTime":"1790288631452",
"upgradeSavepointPath":"s3://<bucket>/<namespace>/<job>/savepoint/savepoint-2804d6-c6cd134a569d"
{noformat}
3) JobManager failover: restore failure:
{noformat}
2026-10-07 22:51:24  Job restarting after TaskManager loss: 
FlinkExpectedException: The TaskExecutor is shutting down
2026-10-07 22:52:39  Operator: Exception while getting job status (JM 
deployment DEPLOYED_NOT_READY)
2026-10-07 22:52:48  New JobManager leader; job RECONCILING -> INITIALIZING
2026-10-07 22:53:43  Event[Job] | Warning | ERROR | 
java.util.concurrent.CompletionException:
  java.io.FileNotFoundException: Cannot find checkpoint or savepoint 
file/directory
  's3://<bucket>/<namespace>/<job>/savepoint/savepoint-2804d6-c6cd134a569d' on 
file system 's3'.
2026-10-07 22:53:43  Event[Job] | Info | JOBSTATUSCHANGED | Job status changed 
from INITIALIZING to FAILED
{noformat}
4) Operator redeploy of the FAILED job: same deleted path:
{noformat}
2026-10-08 08:08:23  Job is in terminal state, ready for upgrade from observed 
latest state
2026-10-08 08:09:52  Event[Job] | Warning | ERROR | 
java.io.FileNotFoundException: Cannot find checkpoint or savepoint
  file/directory 
's3://<bucket>/<namespace>/<job>/savepoint/savepoint-2804d6-c6cd134a569d' on 
file system 's3'.
{noformat}
5) Manual recovery, and the new restore savepoint deleted again:
{noformat}
Here after manually updated the initial savepoint path & redeployNounce so we 
have recovered the application.{noformat}
h3. Questions for the community
 # Is it expected that the operator's savepoint-history cleanup disposes the 
savepoint currently stored in {{{}status.jobStatus.upgradeSavepointPath{}}}, 
which is also the running cluster's {{{}execution.savepoint.path{}}}? If so, 
what retention setup do you recommend so that a job can always be recovered?
 # For a job in terminal FAILED state with no observable checkpoint, why does 
the operator restore from {{upgradeSavepointPath}} instead of the newer 
{{{}savepointInfo.lastSavepoint{}}}? Is there a configuration that makes it use 
the latest completed savepoint?
 # Is there a supported way, such as 
{{{}kubernetes.operator.job.restart.failed{}}}, to automatically recover a 
FAILED job in this situation without manual {{initialSavepointPath}} changes? 
Would it also hit the deleted path?
 # Could the empty HA checkpoint store after the JobManager failover at 22:52 
be explained by FLINK-34518 (Flink 1.18.1 plus adaptive scheduler, JM lost 
while the job was RESTARTING)? Are there other known causes? Is upgrading to 
Flink 1.18.2+ the right fix?
 # Would enabling FlinkStateSnapshot CRs change this behaviour, or are UPGRADE 
snapshots cleaned up the same way there?
 # Would you recommend {{upgradeMode: last-state}} instead of {{savepoint}} for 
this kind of setup?
 # If this is considered a bug, would the maintainers accept a fix that skips 
disposing the savepoint referenced by {{{}upgradeSavepointPath{}}}? We are 
happy to contribute a PR.

Thank you for any guidance.



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

Reply via email to