[
https://issues.apache.org/jira/browse/HDDS-17423?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-17423:
------------------------------
Attachment: TestBugMC1BootstrappedScmInPrefinalizedRing.java
> SCM bootstrapped into a ring that is not finalized starts finalized,
> finalizes datanodes without an admin finalize and gets them fenced
> ---------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-17423
> URL: https://issues.apache.org/jira/browse/HDDS-17423
> 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.3.0,
> 2.1.2, 2.2.1
> Reporter: Siyao Meng
> Priority: Major
> Attachments:
> MC-1-bootstrapped-scm-adopts-ring-apparent-version.patch,
> TestBugMC1BootstrappedScmInPrefinalizedRing.java
>
>
> h3. Mechanism
> {{StorageContainerManager.scmBootstrap}} takes only the cluster ID and the
> primary SCM ID from the running ring and writes a new VERSION file through
> {{new SCMStorageConfig(conf)}}, whose apparent version is
> {{HDDSVersion.SOFTWARE_VERSION}} unless the testing key
> {{testing.hdds.scm.init.apparent.version}} is set. {{ScmVersionManager}} is
> built from the VERSION file only, and the snapshot path
> ({{RatisBasedVersionManager.finalizeFromSnapshotIfRequired}}) and the
> FINALIZE apply can only raise the version. Nothing on the bootstrap or start
> path brings the new SCM down to the version of the other SCMs, so an SCM
> added while the ring is upgraded but not finalized runs finalized
> ({{needsFinalization() == false}}) next to SCMs that are not.
> h3. Trigger
> # All SCMs and datanodes run the new software and are not finalized yet.
> Today this is the state of a ring upgraded from 2.2.x to this code (apparent
> version is the legacy layout version); with zero downtime upgrade it is the
> state between the rolling upgrade of the binaries and the admin finalize.
> # An SCM is added with {{ozone scm --bootstrap}}, with production
> configuration.
> # The datanodes are configured with the new SCM, and the new SCM becomes
> leader (election or {{ozone admin scm transfer}}).
> h3. Impact
> * As leader, the new SCM sends {{FinalizeVersionCommand}} to every datanode
> ({{SCMNodeManager.sendFinalizeToDatanodeIfNeeded}}). The datanodes finalize
> although no admin finalize was issued and the other SCMs are still not
> finalized, so the downgrade option is lost (run).
> * A finalized datanode that restarts is rejected by each SCM that is not
> finalized ({{SCMNodeManager.shouldFenceDatanode}}, apparent version above
> SCM) and shuts down (run, three rejections, datanode stopped).
> * For an upgrade from a release that already has the zero downtime upgrade
> versions, the new SCM also rejects every datanode still on the previous
> release, which the other SCMs accept (run by sending the registration request
> of such a datanode to the SCM datanode protocol server, since no such release
> exists yet).
> * This lasts until an admin finalize. No data loss is expected.
> * Releases 1.3.0 to 2.2.1 have the same root cause: {{scmBootstrap}}
> initializes the new SCM at the newest layout version, so as leader it sends
> {{FinalizeNewLayoutVersionCommand}} to the datanodes of a cluster that is not
> finalized. Datanodes are not fenced there, because registration checks only
> the software layout version (read from code at the release tags, not run).
> h3. Reproduction
> PASS on the unmodified commit, about 64 s.
> [^TestBugMC1BootstrappedScmInPrefinalizedRing.java]
> ({{hadoop-ozone/integration-test}}) starts three SCMs and three datanodes at
> apparent version {{ZDU}} through the testing key, bootstraps a fourth SCM
> with {{MiniOzoneHAClusterImpl.bootstrapSCM}} without that key, adds it to the
> datanodes by reconfiguration and transfers leadership to it. Output on the
> unmodified code: the new SCM is at {{STREAM_PUTBLOCK_WITHOUT_RAFT (101)}}
> while the original SCMs are at {{ZDU (100)}}, all datanodes finalize to 101
> with no finalize command, a restarted datanode logs "SCM rejected this
> datanode's registration" and stops, and a registration with software and
> apparent version 100 is accepted by an original SCM and rejected by the new
> one. A passing test means the defect is present.
> h3. Patch
> [^MC-1-bootstrapped-scm-adopts-ring-apparent-version.patch], against
> 5f7111a27ea07ce6b7bf5284a4213ebacb65f2ae. It also applies to master at
> e03ba37c41d. It targets master only; the release branches use the older
> layout version framework, where the same idea would need a separate change.
> {{GetScmInfoResponseProto}}, which {{scmBootstrap}} already fetches from the
> leader, gets an optional {{apparentVersion}} field filled by
> {{SCMBlockProtocolServer.getScmInfo}}. When it is lower than the initial
> apparent version of the new VERSION file, {{scmBootstrap}} writes it instead.
> A value that is too low is safe, because the new SCM finalizes with the ring
> when it applies the FINALIZE log entry (run) or installs a snapshot of a
> finalized ring (from code). A finalized ring and an existing SCM directory
> are unchanged. The field is only in a read RPC response and not in the Raft
> log; a response without it (a leader without the patch) keeps today's
> behaviour. A ring version above this SCM's software version is not used and
> is logged as a warning.
> Not covered: the FINALIZE log entry has no target version, so a FINALIZE
> entry from an earlier upgrade that is still in the retained Raft log would
> raise a replaying new SCM to its own software version. That cannot happen at
> this commit: the entry is written only by this code, and a ring is
> unfinalized here only during its first upgrade into this framework, from a
> release whose log has no such entry.
> The existing {{queryUpgradeStatus}} RPC already returns the SCM apparent
> version, but it fails while SCM is in safe mode, so using it would make
> bootstrap fail in a case it supports today.
> Covered by {{testBootstrappedScmJoinsRingAtRingApparentVersion}} in the
> existing {{TestScmHAFinalization}}: a ring at {{INITIAL_VERSION}} bootstraps
> an SCM without the testing key, which must join at {{INITIAL_VERSION}} and
> then finalize with the ring; a second SCM bootstrapped into the finalized
> ring must start at the software version. {{testGetScmInfoApparentVersion}} in
> the existing {{TestScmBlockLocationProtocolPB}} checks that a response
> without the field gives no version, so bootstrap keeps today's behaviour.
> Without the change it fails with {{expected: INITIAL_VERSION (0) but was:
> STREAM_PUTBLOCK_WITHOUT_RAFT (101)}}. With it, {{TestScmHAFinalization}},
> {{TestStorageContainerManagerHA}}, {{TestDatanodeSCMNodesReconfiguration}},
> {{TestFailoverWithSCMHA}}, {{TestMiniOzoneHACluster}},
> {{TestSecureOzoneCluster}} and the SCM unit suites for the block protocol
> server, both block protocol translators, the version manager and SCM startup
> pass, and checkstyle is clean. With the patch the reproduction fails at its
> first assertion on the new SCM, because it starts at {{ZDU (100)}}.
> Related: HDDS-17089 (an SCM restarted after an interrupted snapshot install
> stays behind its finalized DB) and HDDS-17090 (a lagging follower rejects
> datanodes the leader finalized) are different mechanisms and touch different
> code; the patches do not overlap.
> Found by TLA+ model checking and code review of the zero downtime upgrade
> version gating in SCM, OM and the datanode write path under HDDS-15926, on
> commit 5f7111a27ea07ce6b7bf5284a4213ebacb65f2ae. 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]