Dennis-Mircea Ciupitu created FLINK-41157:
---------------------------------------------
Summary: Stateless jobs with HA enabled cannot be recovered or
restarted when HA metadata is missing
Key: FLINK-41157
URL: https://issues.apache.org/jira/browse/FLINK-41157
Project: Flink
Issue Type: Bug
Components: Kubernetes Operator
Reporter: Dennis-Mircea Ciupitu
h1. Summary
For a FlinkDeployment in application mode with {{job.upgradeMode: stateless}}
and Kubernetes HA enabled in {{{}spec.flinkConfiguration{}}}, the JobManager
deployment recovery path and the cluster health-check restart path require the
per-job HA ConfigMap ({{{}<cluster-id>--config-map{}}}) to exist. When it does
not, the operator fails the restart with {{RestoreFailed}} ("HA metadata is not
available to restore from last state") instead of redeploying the stateless job
from empty state. The eligibility checks of both paths explicitly treat a
stateless job as always restartable without HA metadata, so the gating and the
execution disagree.
Depending on the trigger, the deployment either stays {{FAILED}} and retries
the same failing restore on every reconcile, or ends up with no running cluster
at all while reporting lifecycle state {{STABLE}} and no error.
h1. Root cause
h2. Eligibility accepts stateless jobs without HA metadata
* {{{}AbstractFlinkResourceReconciler.shouldRecoverDeployment{}}}: a lost
JobManager deployment is recovered when the job is stateless or HA is enabled.
* {{{}ApplicationReconciler.shouldRestartJobBecauseUnhealthy{}}}: a stateless
job is always restarted, other upgrade modes only when
{{isHaMetadataAvailable}} is true.
h2. Execution requires HA metadata whenever HA is enabled
* {{ApplicationReconciler.reconcileOtherChanges}} calls {{resubmitJob(ctx,
HighAvailabilityMode.isHighAvailabilityModeActivated(observeConfig))}} - The
upgrade mode is not considered.
* {{AbstractJobReconciler.resubmitJob}} switches the spec to {{LAST_STATE}}
when {{requireHaMetadata}} is true and keeps the job id.
* {{AbstractFlinkService.submitApplicationCluster}} then calls
{{{}validateHaMetadataExists{}}}, which throws {{{}UpgradeFailureException("HA
metadata is not available to restore from last state. ...",
"RestoreFailed"){}}}.
h2. History
FLINK-31277 (operator 1.5.0) replaced the earlier call site {{requireHaMetadata
= upgradeMode != STATELESS}} with the HA-only condition and at the same time
widened {{shouldRecoverDeployment}} to "stateless or HA".
The combination: stateless + HA enabled + missing metadata has never been
covered.
h2. The availability check is existence-only
* {{FlinkUtils.isValidHaConfigMap}} accepts any ConfigMap whose name ends in
{{-config-map}} except {{{}-cluster-config-map{}}}, ignoring both content and
job id.
* {{FlinkUtils.checkpointExists}} is only used to decide whether the dummy
last-state savepoint path is set in {{{}deploy{}}}.
h2. When the per-job ConfigMap is missing
Flink creates it when the JobMaster builds its checkpoint store and deletes it
itself on global job cleanup. It is missing when a human or a cleanup script
removed the HA ConfigMaps, when the JobManager never reached job submission, or
after the job reached a terminal state (the FLINK-37392 flavour).
h1. Reproduction
The reproduction can be done on {{examples/basic-checkpoint-ha.yaml}} with a
single change: {{{}upgradeMode: stateless{}}}.
NOTE: On minikube the hostPath additionally needs {{type: DirectoryOrCreate}}
and a writable {{/tmp/flink}} on the node.
Common steps:
* Apply the deployment and wait for {{jobStatus.state=RUNNING}} and
{{{}lifecycleState=STABLE{}}}. ConfigMaps {{{}-{}}}{{{}cluster-config-map{}}}
and {{-config-map}} exist.
* Delete the per-job ConfigMap: {{{}kubectl delete cm --config-map{}}}. The
running job is not affected (no checkpointing configured in the example).
h2. Trigger A: JobManager deployment recovery
Delete the deployment: {{kubectl delete deployment
{{basic-checkpoint-ha-example}}}}
Observed on the next reconcile:
{code:java}
Event Warning Missing Missing JobManager deployment
Event Warning RecoverDeployment Recovering lost deployment
Log Deploying application cluster requiring last-state from HA metadata
Log Job graph in ConfigMap -cluster-config-map is deleted
Event Warning RestoreFailed HA metadata is not available to restore from last
state. It is possible that the job has finished or terminally failed, or the
configmaps have been deleted. {code}
Status: {{{}jobStatus.state=RECONCILING{}}},
{{{}jobManagerDeploymentStatus=MISSING{}}}, {{{}lifecycleState=FAILED{}}},
{{error}} set to the {{{}UpgradeFailureException{}}}. The {{RecoverDeployment}}
/ {{Submit}} / {{RestoreFailed}} sequence repeated on every reconcile (event
count 5 after 3.5 minutes). No pods running.
h2. Trigger B: cluster health-check restart
Additional configuration on the deployment:
{code:java}
kubernetes.operator.cluster.health-check.enabled: "true"
kubernetes.operator.cluster.health-check.restarts.threshold: "1"
kubernetes.operator.cluster.health-check.restarts.window: 10 min
kubernetes.operator.cluster.health-check.checkpoint-progress.enabled: "false"
{code}
Delete the TaskManager pod twice, waiting for the new TaskManager to register
in between ({{{}numRestarts{}}} goes from 1 to 3).
Observed within a single reconcile (operator log):
{code:java}
17:16:11.264 Restart count hit threshold: 1
17:16:11.292 Event Warning RestartUnhealthyJob
17:16:11.292 Deleting cluster with Foreground propagation
17:16:13.580 Keeping HA metadata for last-state restore
17:16:13.580 Resubmitting Flink job...
17:16:13.588 Deploying application cluster requiring last-state from HA metadata
17:16:13.684 Job graph in ConfigMap -cluster-config-map is deleted 17:16:13.696
Event Warning RestoreFailed HA metadata is not available to restore from last
state. ...
17:16:13.710 Status STABLE The resource deployment is considered to be stable
{code}
End state: {{{}jobStatus.state=FINISHED{}}},
{{{}jobManagerDeploymentStatus=MISSING{}}}, {{{}lifecycleState=STABLE{}}},
{{error}} empty, no pods, {{RestoreFailed}} event count still 1 after 6
minutes. {{cleanupAfterFailedJob}} marks the job {{FINISHED}} before the
restore attempt, so neither {{shouldRecoverDeployment}} nor
{{shouldRestartJobBecauseUnhealthy}} fires again, and
{{clearErrorsIfDeploymentIsHealthy}} removes the error on the immediate
re-reconcile. The resource looks healthy while nothing runs.
In both triggers the attempted submit also removed the {{executionPlan-*}}
entry from {{-cluster-config-map}} (5 keys to 0) before the validation threw.
h1. Impact
The impact is different per each trigger:
* Trigger A: a stateless job with HA enabled cannot recover from a lost
JobManager deployment without manual intervention, and the operator repeats a
failing deploy attempt on every reconcile.
* Trigger B: a stateless job with HA enabled and the health check turned on is
terminated by the operator and never restarted, with no error recorded and
lifecycle {{{}STABLE{}}}. Alerting on {{lifecycleState}} or {{status.error}}
does not catch it.
The documented semantics of {{stateless}} ("upgrades from empty state") are not
honoured by the recovery paths.
h1. Workaround
The workaroung for the described issue is represented by any spec change, for
example bumping {{{}restartNonce{}}}.
* In the trigger A state (job {{{}RECONCILING{}}}) this cancels in stateless
mode, deletes the HA ConfigMaps and redeploys with a new job id (verified,
{{RUNNING}} about 30 s after the patch).
* In the trigger B state (job {{{}FINISHED{}}}) the suspend step is a no-op,
the cluster is redeployed with a new job id and the HA ConfigMaps are left
behind.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)