[
https://issues.apache.org/jira/browse/HDDS-17525?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-17525:
------------------------------
Affects Version/s: 2.2.1
2.0.0
1.4.0
2.1.2
2.1.0
1.4.1
2.1.1
1.3.0
2.2.0
> Container balancer that stops on its own keeps persisted shouldRun=true if
> the save fails, so a later leader change starts a new run
> ------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-17525
> URL: https://issues.apache.org/jira/browse/HDDS-17525
> Project: Apache Ozone
> Issue Type: Bug
> Affects Versions: 1.3.0, 1.4.0, 2.0.0, 1.4.1, 2.1.0, 2.2.0, 2.1.1, 2.1.2,
> 2.2.1
> Reporter: Siyao Meng
> Priority: Minor
> Attachments: MC-2-retry-failed-save-on-balancer-self-stop.patch,
> TestBugMC2TerminalSaveFailureResume.java
>
>
> h3. Mechanism
> When the balancing task stops on its own (INITIALIZATION_FAILED,
> CAN_NOT_BALANCE_ANY_MORE, COMPLETED_ALL_ITERATIONS),
> {{ContainerBalancerTask.tryStopWithSaveConfiguration}} saves
> {{shouldRun=false}} through the Ratis replicated
> {{StatefulServiceStateManager}}. If that save throws, the catch only records
> the stop reason and logs a WARN. It does not retry, and nothing writes
> {{false}} later without an admin command. The task still ends STOPPED, so
> {{ContainerBalancer.getBalancerStatusInfo}} reports {{shouldRun=false}} while
> the persisted value is {{true}}.
> The next {{ContainerBalancer.notifyStatusChanged}} on a leader out of safe
> mode (failover back to this SCM, failover to another SCM, or a restart) reads
> the persisted {{true}} and calls {{start()}}, which starts a new balancing
> run for a run that had already ended.
> For comparison, a failed save in the admin {{stopBalancer}} path is returned
> to the caller and the balancer keeps running, so the admin knows the stop did
> not take effect.
> h3. Trigger
> # An admin starts the balancer ({{shouldRun=true}} is saved).
> # The task stops on its own, for example because the cluster is already
> balanced.
> # The {{shouldRun=false}} write is rejected by Ratis before it is appended
> while the SCM stays leader, for example {{ResourceUnavailableException}}
> (pending write limit, Ratis default 4096 requests or 64MB, not overridden by
> SCM) or {{LeaderSteppingDownException}} during a leadership transfer that
> then fails. {{ScmInvoker}} turns the reply into an {{SCMException}}.
> # Later, after the cluster has drifted, any leader change or restart starts a
> new run.
> Step 3 is narrow under default settings: SCM Ratis writes are synchronous, so
> the pending limit is only reached after commits stall for minutes while the
> leader keeps its majority, and the terminal save has to land then. A save
> that times out while the SCM stays leader commits later and ends durable as
> {{false}}, which is harmless. An exception while applying the entry
> terminates the SCM. These points are from reading the code, not run.
> h3. Impact
> * A balancing run nobody asked for starts after the next leader change or
> restart, with the last admin config, and moves containers.
> * Until then the status command says the balancer will not run, while the
> persisted state says it will. Only the WARN {{Save configuration failed}}
> shows it. Running {{stopBalancer}} fixes the state.
> * For COMPLETED_ALL_ITERATIONS the new run starts at the persisted next
> iteration index. It stops after the start delay without moving data only if
> the index save of the last iteration succeeded, which runs just before the
> terminal save.
> h3. Reproduction
> PASS, deterministic, unmodified source, injected save failure.
> [^TestBugMC2TerminalSaveFailureResume.java] ({{hadoop-hdds/server-scm}}) uses
> a mocked SCM as {{TestContainerBalancer}} does, with {{ContainerBalancer}}
> and {{ContainerBalancerTask}} unmodified on a real {{SCMContext}}. The
> cluster is balanced, so the first iteration fails to initialize, and the
> first {{shouldRun=false}} save throws the {{SCMException}} that wraps a
> {{ResourceUnavailableException}}. After the task stops, status reports
> {{shouldRun=false}} and the persisted value is {{true}} on a leader. The
> cluster is then made unbalanced and a step-down and step-up are driven
> through {{SCMContext.updateLeaderAndTerm}} and {{notifyStatusChanged}}: a new
> task starts and selects one over-utilized and one under-utilized datanode. A
> passing test means the defect is present. A real Ratis rejection was not
> exercised.
> h3. Patch
> [^MC-2-retry-failed-save-on-balancer-self-stop.patch], against
> 9118d5a85a37db8c64404d56703b7bacb040e063. It also applies to master at
> f59f8d3d123.
> {{tryStopWithSaveConfiguration}} retries a failed {{shouldRun=false}} save
> while the task is still running and the SCM is still leader ready and out of
> safe mode, waiting 1 second before the first retry and doubling the wait up
> to 60 seconds. The first failure is logged with its stack trace, later ones
> as one line. It gives up, leaving {{shouldRun=true}} so the next leader
> resumes as on any step down today, when the SCM steps down or enters safe
> mode or the task is stopped by someone else. The task lock is not held while
> waiting, so a step down or an admin stop waits at most for one attempt as
> today and then ends the wait. While it retries, the task stays RUNNING and
> status shows {{shouldRun=true}}, which matches the persisted value, the same
> way a failed admin stop leaves the balancer running. During that time an
> admin {{startBalancer}} is refused as already running, and an admin
> {{stopBalancer}} ends the retry (if its own save succeeds).
> Covered by {{testFailedSaveOnSelfStopIsRetried}} and
> {{testStepDownWhileRetryingFailedSaveOnSelfStop}} in the existing
> {{TestContainerBalancer}}. Without the change the first fails because the
> persisted value is still {{true}} and the second times out because nothing
> retries. With it the balancer unit suites in {{hadoop-hdds/server-scm}} pass
> (437 tests) and checkstyle is clean. With the patch the reproduction fails
> because the persisted value is {{false}} after the stop.
> Not covered: when the save fails because the SCM is stepping down, the next
> leader still resumes the ended run, as in the step-down case today. That run
> starts within minutes on almost the same cluster state, so it usually stops
> again with the same reason.
> Found by TLA+ model checking and code review of the SCM container balancer
> start, stop and failover resume paths under HDDS-15926, on commit
> 9118d5a85a37db8c64404d56703b7bacb040e063. Checked against HDDS issues and
> apache/ozone pull requests for duplicates before filing. The attached patch
> is a proposal for review. Generated with Specula (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]