Siyao Meng created HDDS-17523:
---------------------------------

             Summary: Container Balancer is not resumed after SCM restart if 
datanodes are still registering at safe mode exit, and shouldRun stays true
                 Key: HDDS-17523
                 URL: https://issues.apache.org/jira/browse/HDDS-17523
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng


h3. Mechanism
After an SCM restart or a leader change, 
{{ContainerBalancer.notifyStatusChanged}} calls {{ContainerBalancer.start}} 
when the persisted {{shouldRun}} is true. {{start}} runs the same 
{{validateConfiguration}} as a start requested by the user, and that now 
includes checks against the current cluster state: include and exclude 
datanodes must be registered (HDDS-14662), included containers must exist, and 
the number of healthy eligible datanodes times 
{{hdds.container.balancer.datanodes.involved.max.percentage.per.iteration}} 
must allow at least 2 datanodes (both HDDS-15535). If a check fails, 
{{notifyStatusChanged}} only logs "Could not start ContainerBalancer on 
raft/safe-mode status change." The persisted {{shouldRun}} stays true, no stop 
reason is recorded, and nothing retries: the notification is sent only on safe 
mode exit, leader ready and step down.

The safe mode exit notification comes as soon as the exit rules pass, which can 
be before every datanode has registered (at least 
{{hdds.scm.safemode.min.datanode}} datanodes, default 3, and 99% of Ratis 
containers with at least one reported replica, among other rules). The 
balancing task already waits {{hdds.scm.wait.time.after.safemode.exit}} 
(default 5m) before its first iteration and evaluates the datanodes again in 
every iteration, but the check in {{start}} runs before that wait.

h3. Trigger
# On a cluster of 10 datanodes, run {{ozone admin containerbalancer start}} 
with default settings.
# Restart SCM. Safe mode exits while 9 of the 10 datanodes have registered. 
With 3 replica containers, every container already has a reported replica once 
8 of 10 datanodes have registered, and the other safe mode rules also pass with 
9 of 10.
# {{start}} throws "max-datanodes-percentage-to-involve-per-iteration=20 allows 
at most 1 datanode(s) per iteration with 9 eligible datanode(s)". The last 
datanode registers moments later, but the balancer stays stopped.

With the default 20% and no exclusions, a resume is rejected whenever fewer 
than 10 eligible datanodes have registered at the notification: one late 
datanode on a 10 datanode cluster, two on 11, three on 12. Larger clusters need 
more late datanodes, which gets less likely as the cluster grows. Exclusions 
and a non default percentage change which cluster sizes are exposed. An 
excluded or included datanode that has not registered yet also skips the 
resume, on any cluster size and also in 2.2.x. Two variants fail on every 
restart until the user acts: such a datanode that is down and does not register 
again, and an included container that was deleted in the meantime.

h3. Impact
A balancing run started by the user silently does not resume after a restart. 
{{ozone admin containerbalancer status}} reports it as not running with no stop 
reason, while the persisted state still says it should run, so the next restart 
or leader change tries again. Nothing is lost and the user can start it again. 
A leader change between running SCMs is less exposed, because followers also 
receive datanode heartbeats. I did not run a mini cluster restart; the 
scenarios were run on the mock SCM of {{TestContainerBalancer}}.

h3. Reproduction
PASS on unmodified source, deterministic. 
[^TestBugCR2BalancerNotResumedAfterRestart.java] ({{hadoop-hdds/server-scm}}) 
uses the mock SCM of {{TestContainerBalancer}} with the persisted balancer 
state in memory. It starts the balancer with {{startBalancer}}, stops it as SCM 
shutdown does, then creates a second {{ContainerBalancer}} over the same 
persisted state and calls {{notifyStatusChanged}}. Three tests: 9 of 10 
datanodes healthy at the notification with default settings; an excluded 
datanode that has not registered; an included container that no longer exists 
(two restarts). Each asserts the warning, status STOPPED and {{shouldRun}} 
true. A passing test means the defect is present.

h3. Patch
[^CR-2-resume-balancer-while-datanodes-register.patch], against 
9118d5a85a37db8c64404d56703b7bacb040e063. It also applies to master at 
f59f8d3d123.

The cluster state checks move from {{validateConfiguration}} into a new 
{{validateClusterState}}, which only {{startBalancer}} calls, right after 
{{validateConfiguration}}. A start requested by the user is validated exactly 
as before, in the same order and with the same messages, so the failures 
HDDS-15535 reports to the CLI are unchanged. A resume still runs the size and 
timeout checks, and leaves the cluster state to the task, which looks at it 
after the post safe mode wait. If the cluster is still too small or nothing can 
be selected then, the task stops and persists {{shouldRun=false}}, with stop 
reason {{CAN_NOT_BALANCE_ANY_MORE}} (pool too small, no included container 
left) or {{INITIALIZATION_FAILED}} (included datanode not registered), as it 
does today when the cluster changes during a run and as resumes did before 
HDDS-14662.

Covered by {{testNotifyStartsWhileDatanodesAreStillRegistering}} in the 
existing {{TestContainerBalancer}}: after the restart only 5 of 10 datanodes 
are healthy and the excluded datanode has not registered, {{startBalancer}} is 
still rejected, and {{notifyStatusChanged}} resumes the balancer with the 
persisted configuration. Without the change it fails with "expected: <RUNNING> 
but was: <STOPPED>". 
{{testNotifyResumeWithMissingIncludedContainerStopsWithReason}}, on the 20 
datanode mock cluster of the balancer task tests, resumes a run whose included 
container no longer exists: the task stops with {{CAN_NOT_BALANCE_ANY_MORE}}, 
schedules no move and persists {{shouldRun=false}}. Without the change it fails 
because {{shouldRun}} stays true.

Found by code review of the SCM container balancer start, stop and failover 
resume paths, as part of the TLA+ verification effort 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]

Reply via email to