Siyao Meng created HDDS-16925:
---------------------------------
Summary: SCM forgets all container replicas when it installs a
Ratis snapshot, until the next full container reports
Key: HDDS-16925
URL: https://issues.apache.org/jira/browse/HDDS-16925
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments: CR-8-keep-container-replicas-across-reinitialize.patch,
TestBugCR8ReplicaWipeOnReinitialize.java
h3. Mechanism
{{ContainerStateManagerImpl.reinitialize}}, which runs when an SCM installs a
checkpoint from the leader ({{SCMHAManagerImpl.startServices}}), replaces its
{{ContainerStateMap}} with an empty one and reloads only the {{ContainerInfo}}
rows from the installed DB. Replicas are not in the DB; they come from datanode
reports. So after the install the SCM knows no replica of any container,
although the datanodes still hold them all. They come back with incremental
reports for changed containers and with each datanode's next full container
report ({{hdds.container.report.interval}}, 60 minutes by default). Nothing
asks for an earlier full report, and safe mode is not entered again on an
install or on a leader change. Replica updates take only their stripe lock, not
the global lock that {{reinitialize}} holds, so updates that run during the
swap are lost as well.
Recon's SCM DB snapshot sync
({{ReconStorageContainerManagerFacade.initializeNewRdbStore}}) calls the same
method and wipes Recon's own replica view the same way; the sync is a Recon
admin operation and Recon sends no commands (by code reading, not run).
h3. Trigger
# A follower SCM that is already processing datanode reports installs a
checkpoint from the leader. With the default
{{ozone.scm.ha.ratis.log.purge.enabled}}=false the leader purges its log only
up to the lowest commit index of its peers, so a lagging follower normally
catches up from the log. It installs a checkpoint when log purge is enabled and
it falls behind the purged index, for example after a long pause. A newly
bootstrapped or restarted SCM is not affected, because it starts its datanode
server only after it has caught up.
# Within the next report interval, that SCM becomes leader (failover or
leadership transfer).
h3. Impact
While the new leader has not received the full reports (argued from the code
unless stated otherwise):
* {{getContainerWithPipeline}} for a closed container returns an empty
pipeline, because the pipeline of a closed container is built from its
replicas. Reads that need a fresh lookup fail.
* After {{hdds.scm.wait.time.after.safemode.exit}} (5 minutes),
ReplicationManager sees the containers as MISSING (no replica known) or
UNDER_REPLICATED (some added back by incremental reports), replicates or
reconstructs them and later deletes the extra copies as over replicated. A
DELETING container with no known replica is marked DELETED while datanodes
still hold it.
* Decommission and maintenance: the monitor takes the node's containers from
NodeManager (not reset) and their replicas from ContainerManager (reset). This
monitor is not delayed by {{hdds.scm.wait.time.after.safemode.exit}}: the new
leader resumes tracking the nodes in {{NodeDecommissionManager.onBecomeLeader}}
and checks them every {{ozone.scm.datanode.admin.monitor.interval}} (30
seconds), so it acts on the wiped view right after the failover (by code
reading). A Ratis container then holds the decommission until the reports
return. An EC container with no known replica is classified MISSING, not
UNDER_REPLICATED, so it counts as sufficiently replicated, and the node can
finish decommission or enter maintenance without its EC replicas being copied.
Shown in the reproduction with the real check handler; the monitor and the
failover were not run.
* Everything returns to normal after the full reports. I found no path to data
loss on its own.
h3. Reproduction
PASS on the unmodified source. [^TestBugCR8ReplicaWipeOnReinitialize.java]
({{hadoop-hdds/server-scm}}) builds a real {{ContainerStateManagerImpl}} on a
real SCM DB with a CLOSED Ratis THREE container and a CLOSED EC 3+2 container,
each with a replica on a DECOMMISSIONING datanode, and calls {{reinitialize}}
with the same container table. It then applies the per container decision of
{{DatanodeAdminMonitorImpl.checkContainersReplicatedOnNode}} with the real
{{RatisContainerReplicaCount}}, {{ECContainerReplicaCount}} and
{{ECReplicationCheckHandler}}. Before: the EC container is UNDER_REPLICATED and
holds the decommission. After: {{Ratis replicas 0 (was 3), EC replicas 0 (was
5)}}, the EC container is MISSING and passes the decommission check. A passing
test means the defect is present.
h3. Not covered by the patch
Containers created on the leader while the follower lagged still have no
replicas on the new SCM until the datanodes report them. A leader readiness
gate after an install (wait for full reports, or ask the datanodes for one
before acting as leader) would cover those, as a follow up.
h3. Patch
[^CR-8-keep-container-replicas-across-reinitialize.patch], against
b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at
52812559dad.
The patch keeps the replica sets of the containers that are still in the
installed table. Inside the existing global write lock it also takes the write
lock of every stripe, so a replica update cannot run during the swap and be
overwritten by the copied set. Containers that are no longer in the table lose
their replicas with them. Lock order is unchanged (global, then stripe). Report
processing on that SCM waits while the container table reloads, which already
blocks container reads. With the patch the reproduction fails because the
replicas are still there.
Covered by {{testReinitializeKeepsReplicasOfRemainingContainers}} and
{{testReplicaUpdateDuringReinitializeIsNotLost}} in the existing
{{TestContainerStateManager}}. Without the change both fail (empty replica
sets). Copying the replicas without the stripe locks fails the second test,
because a replica removed during the swap comes back. With the patch the
container, report handler and replica suites in {{hadoop-hdds/server-scm}} (140
tests), the Recon container suites (22 tests) and
{{TestSCMInstallSnapshotWithHA}} and {{TestSCMInstallSnapshot}} (5 tests) pass,
and checkstyle is clean.
Found by code review of the SCM Ratis snapshot and checkpoint install paths, as
part of the TLA+ verification effort under HDDS-15926, on commit
b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. Checked against HDDS issues and
apache/ozone pull requests for duplicates before filing. HDDS-16896 (safe mode
rules of a bootstrapped SCM) is related but a different mechanism. 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]