[
https://issues.apache.org/jira/browse/HDDS-17575?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-17575:
------------------------------
Attachment: TestBugMC1LeaderReadyAfterStepDown.java
> SCM that steps down while notifyLeaderReady runs stays leader ready as a
> follower and is already ready when it is elected again
> -------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-17575
> URL: https://issues.apache.org/jira/browse/HDDS-17575
> Project: Apache Ozone
> Issue Type: Bug
> Affects Versions: 1.2.0, 1.3.0, 1.2.1, 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-1-set-scm-leader-ready-only-in-the-same-leader-term.patch,
> TestBugMC1LeaderReadyAfterStepDown.java
>
>
> h3. Mechanism
> Ratis calls {{SCMStateMachine.notifyLeaderReady}} on the state machine apply
> thread and {{SCMStateMachine.notifyNotLeader}} from {{changeToFollower}} on
> another thread, and nothing orders the two. {{notifyLeaderReady}} flushes the
> transaction buffer, reloads the deleted block summary and then calls
> {{SCMContext.setLeaderReady}}, which sets the flag without checking that this
> SCM is still the leader, or still in the same term. If the step down
> ({{updateLeaderAndTerm(false, 0)}}, which clears the flag) lands before that
> call, the follower ends up with {{isLeader=false}} and
> {{isLeaderReady=true}}. {{updateLeaderAndTerm(true, term)}} does not clear
> the flag, so it stays set until the SCM next steps down from a leadership,
> including through the next election of this SCM.
> The {{SCMContext.isLeaderReady}} Javadoc says "True - it is leader and ready".
> h3. Trigger
> A step down of a newly elected SCM (higher term election, {{ozone admin scm
> transfer}}, Ratis admin step down) that lands between Ratis declaring the
> leader ready and {{setLeaderReady}}. On master since HDDS-16143 (fix version
> 2.3.0, not released yet), which moved the transaction buffer flush and the
> deleted block summary reload into {{notifyLeaderReady}}, that window contains
> both. In released versions it is only the few calls between Ratis' readiness
> check and {{setLeaderReady}}, so the practical exposure is master. An earlier
> run that stepped each new leader down as soon as it appeared, without timing
> help, did not hit it in 5 tries, so in practice it needs a slow flush or
> unlucky timing.
> h3. Impact
> * On the follower, every service gated on {{SCMContext.isLeaderReady}} alone
> becomes RUNNING: {{ReplicationManager}}, {{SCMBlockDeletingService}},
> {{BackgroundPipelineCreator}}, {{SecretKeyManagerService}}, the container
> balancer check. Shown for the first two ({{shouldRun()}} true on the
> follower). They cannot send datanode commands, because
> {{SCMContext.getTermOfLeader}} throws on a follower, and Ratis writes fail
> there, so on the follower this is wasted work and errors.
> * {{SCMDatanodeProtocolServer.register}} uses the flag to decide whether a
> follower's {{errorNodeNotPermitted}} is final (HDDS-17090). With the stale
> flag a lagging follower's rejection is final again and the datanode shuts
> down (code reading, not run).
> * When the SCM is elected again it is leader ready from
> {{updateLeaderAndTerm(true, term)}} on, before it has applied the previous
> terms' entries and before that term's {{notifyLeaderReady}} reloads the
> deleted block summary, which is the ordering HDDS-10690 established. The
> reproduction shows the flag already true inside the new term's
> {{notifyLeaderReady}}; the earlier part follows from the code.
> {{ReplicationManager}} and {{SCMBlockDeletingService}} are already RUNNING,
> so they also skip the {{hdds.scm.wait.time.after.safemode.exit}} wait after
> the election.
> h3. Reproduction
> [^TestBugMC1LeaderReadyAfterStepDown.java]
> ({{hadoop-ozone/integration-test}}, target path and Maven command in the
> class comment) runs three SCMs, one OM and three datanodes on unmodified
> source. A test side log4j appender holds the apply thread of the new leader
> at the INFO line logged during {{DeletedBlockLogImpl.onLeaderReady}}, after
> the flush and before {{setLeaderReady}}, while leadership is moved away with
> the Ratis admin {{transferLeadership}} API. The hold only changes timing. It
> passes on unmodified source when the bug is present (35s):
> {noformat}
> After step down: ... ratis{role=FOLLOWER term=4 ready=false}
> scmContext{isLeader=false isLeaderReady=true}
> ReplicationManager.shouldRun=true SCMBlockDeletingService.shouldRun=true
> Re-elected, inside notifyLeaderReady: ... ratis{role=LEADER term=5
> ready=true} scmContext{isLeader=true isLeaderReady=true} ...
> {noformat}
> In the same run the new leader held at the same point in its first election
> reports {{isLeaderReady=false}}, as expected. With the patch the test fails:
> the follower stays {{isLeaderReady=false}} and both services report
> {{shouldRun()}} false.
> h3. Patch
> [^MC-1-set-scm-leader-ready-only-in-the-same-leader-term.patch], against
> 424482eb9cfc4b835e1bf73bcdaa9e7d70df59f0. It also applies to master at
> 257a71ab277.
> {{SCMContext.setLeaderReady}} takes the term and sets the flag under its
> write lock only if the SCM is still the leader of that term, and returns
> whether it did. {{notifyLeaderReady}} reads the Ratis current term when it
> starts (the same source {{notifyLeaderChanged}} uses) and notifies the
> services only if the flag was set. Since {{notifyNotLeader}} updates
> {{SCMContext}} under the same lock, either the step down comes first and the
> flag is not set, or it comes after and clears it. A step down plus a new
> election inside the callback fails the term check, and the new term's own
> {{notifyLeaderReady}} sets the flag. The term is read when the callback
> starts, because the {{StateMachine}} API does not pass the term of the entry
> Ratis checked; a step down and a re-election that both fit between Ratis'
> check and that read would still pass, which needs at least one election
> timeout with no Ozone code running (argued, not run). Nothing changes for an
> SCM that stays leader. The existing test callers are updated for the new
> signature.
> Covered by {{testLeaderReadyNotSetWhenLeadershipChangesDuringLeaderReady}} in
> the existing {{TestSCMStateMachine}} (step down during the flush, step down
> and new term during the flush, no change). Without the change it fails
> because the follower is leader ready. With it {{TestSCMStateMachine}},
> {{TestSCMContext}}, {{TestContainerBalancer}}, {{TestSCMNodeManager}},
> {{TestSCMHAManagerImpl}} and {{TestReplicationManager}} pass (181 tests),
> checkstyle is clean.
> Found by TLA+ model checking and code review of the OM and SCM Ratis state
> machine integration (leader readiness, snapshot install and log failure
> handling) under HDDS-15926, on commit
> 424482eb9cfc4b835e1bf73bcdaa9e7d70df59f0. 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]