Siyao Meng created HDDS-16916:
---------------------------------
Summary: New SCM leader blocks its Ratis log applying thread and
terminates when the root CA post-processing state is stale
Key: HDDS-16916
URL: https://issues.apache.org/jira/browse/HDDS-16916
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments:
CR-1-delete-post-rotation-state-off-the-notifying-thread.patch,
TestBugCR1LeaderReadyBlockingWrite.java
h3. Mechanism
{{SCMStateMachine.notifyLeaderReady}} runs on the Ratis {{StateMachineUpdater}}
thread and notifies the SCM services inline.
{{RootCARotationManager.notifyStatusChanged}} then calls
{{checkAndHandlePostProcessing}} on that thread. After a root CA rotation the
leader saves the new root certificate as the stateful configuration of
{{RootCARotationManager}} and schedules its deletion one
{{hdds.x509.rootca.certificate.polling.interval}} (default 2h) later. If a new
leader still finds the entry after that window, which it measures from the
{{notBefore}} of the root certificate, {{checkAndHandlePostProcessing}} calls
{{deleteConfiguration}}. That is a replicated call which waits until its log
entry is applied, and the only thread that can apply it is the one waiting.
After {{ozone.scm.ha.ratis.request.timeout}} (default 30s) the call fails,
{{notifyStatusChanged}} rethrows the failure as a {{RuntimeException}}, Ratis
closes the server because the {{StateMachineUpdater}} caught a {{Throwable}},
and {{SCMStateMachine.close}} terminates the SCM.
The same inline delete exists in the branch for an entry whose root certificate
is older than the local one (code reading, exercised at unit level by the test
in the patch).
The same inline call is in the release tags ozone-1.4.0, 1.4.1, 2.0.0, 2.1.0,
2.1.1, 2.1.2, 2.2.0 and 2.2.1 (read, not run). In 1.4.0 and 1.4.1 the services
are notified from {{SCMStateMachine.notifyTermIndexUpdated}}, which the stack
trace in HDDS-11750 shows on the same thread.
h3. Trigger
# Secure SCM HA with {{hdds.x509.ca.rotation.enabled=true}} (default false).
# A root CA rotation completes.
# The leader that ran the rotation loses leadership before its scheduled
deletion runs, which is one polling interval after the rotation finished. Run:
the leader stops one second before the end of the window. By code reading a
step down has the same effect, because it cancels the deletion task.
# The next SCM becomes leader ready, out of safe mode, later than one polling
interval after the {{notBefore}} of the new root certificate. A leader change
that is complete before that point is harmless, because the new leader then
only schedules the deletion for the rest of the window.
The points in time of steps 3 and 4 differ. The new root certificate is created
when the rotation starts and the leader schedules the deletion when the
rotation finishes, so the point of step 4 comes first by the time the rotation
took. That was about 1.3s in the run. By code reading it is limited by the wait
for the sub CA acknowledgements, {{hdds.x509.ca.rotation.ack.timeout}} (default
15 minutes), plus the replicated calls of the rotation, and a leader election
between the two points is enough, with no time without a leader. An admin
leadership transfer does not get there, because the leader refuses it while its
deletion is pending (run inside the window).
If the scheduled deletion itself fails, the failure is only logged and not
retried (code reading). The entry then stays, and the next SCM that becomes
leader ready out of safe mode is affected whenever that happens.
By code reading, an SCM that becomes leader while still in safe mode is not
affected, because the check then runs later on the thread that leaves safe mode.
h3. Impact
* The new leader applies no log entry for 30s and then stops. In the run the
SCM was stopped from its {{StateMachineUpdater}} thread and reached
{{ExitUtils.terminate}} with status 0 ("scm statemachine is closed by ratis,
terminate SCM"). The run used the mini cluster, where the JVM exit is disabled.
A separate SCM process was not run.
* Nothing brings the SCM back, it stays down until it is restarted.
* In the run the previous leader was down as well, so the three SCM ring was
left with one SCM and no quorum. If the previous leader is still up, two SCMs
remain and can elect a leader (argued, not run).
* No replicated state is damaged. The delete entry stays in the Ratis log, so
it should be applied under the next leader and the failure should not repeat
(argued, not run).
* Rotation happens about once per root certificate lifetime
({{hdds.x509.max.duration}}, default P1865D). The leader change has to straddle
the end of the window after it (the old leader loses leadership before its
deletion runs, the next leader becomes ready after the window), or come at any
later time if the scheduled deletion failed.
h3. Reproduction
PASS, unmodified source, nothing injected.
[^TestBugCR1LeaderReadyBlockingWrite.java] ({{hadoop-ozone/integration-test}})
starts a secure mini cluster with three SCMs. Certificate lifetimes are
shortened so that the leader rotates the root CA two minutes after SCM init,
and the polling interval is 20s instead of 2h. The test stops the leader one
second before the window ends and then finds the {{StateMachineUpdater}} thread
of the new leader inside {{deleteConfiguration}}, called from
{{SCMStateMachine.notifyLeaderReady}}. 30s later the Ratis division of the new
leader is closing and the SCM stops. The test takes about three minutes. A
passing test means the defect is present.
h3. Patch
[^CR-1-delete-post-rotation-state-off-the-notifying-thread.patch], against
b9b33851b20fa11baf4b07982de9afb8ea44f85a. It also applies to master at
5108acff480. The patch is written against that commit alone, so other fixes
proposed for {{RootCARotationManager}} and its unit test will need a manual
merge with it.
{{checkAndHandlePostProcessing}} hands its two replicated deletes (the entry
past its window and the entry older than the local root certificate) to the
executor of the manager, which already runs the scheduled deletion. A failed
delete is logged like a failed scheduled deletion, and the entry is checked
again after the next leader change. Everything else stays on the notifying
thread and is unchanged: reading the entry, setting the flag that blocks
certificate signing inside the window, and any failure of the read or of the
comparison, for example an entry naming a newer root certificate than the local
one, which ends the SCM as before.
{{SecretKeyManagerService.notifyStatusChanged}} in the same package also runs
its replicated call on its own executor.
Covered in the existing {{TestRootCARotationManager}} by
{{testStalePostProcessingStateIsDeletedOffNotifyingThread}}, which fails for
both deletes without the change because the delete runs on the notifying thread
and its failure is thrown to the caller, and by
{{testFailedPostProcessingCheckIsThrownToNotifier}}, which holds the unchanged
failure of the check in place and passes with and without the change. With the
change the SCM HA and root CA rotation unit suites of
{{hadoop-hdds/server-scm}} pass (115 tests) and checkstyle is clean. In the
reproduction the new leader is then ready 5.6s after the old leader stopped,
deletes the entry from the executor thread 6ms after the check and keeps
running, so the reproduction fails at its first assertion.
Related issues, none of which covers this call. HDDS-11750 reported the same
self wait for {{LegacyReplicationManager.notifyStatusChanged}} and was resolved
as Won't Fix without a code change, and that class has since been removed.
HDDS-16029 (open) is a different blocking call, {{ContainerBalancer}} joining
its thread when the SCM loses leadership. HDDS-16222 (open) proposes a static
check that {{notifyStatusChanged}} cannot block and names those two issues, not
this call.
Found by code review of the SCM root CA rotation paths, as part of the TLA+
verification effort under HDDS-15926, on commit
b9b33851b20fa11baf4b07982de9afb8ea44f85a. 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
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]