Siyao Meng created HDDS-17422:
---------------------------------

             Summary: OM bootstrapped into a pre-finalized OM ring runs at its 
software version, exposes unfinalized features and stalls finalization while it 
leads
                 Key: HDDS-17422
                 URL: https://issues.apache.org/jira/browse/HDDS-17422
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-2-bootstrapping-om-adopts-peer-apparent-version.patch, 
TestBugMC2FreshBootstrappedOmPreFinalizedRing.java

h3. Mechanism
A new OM initializes its storage with a VERSION file at 
{{OzoneManagerVersion.SOFTWARE_VERSION}} ({{OMStorage}} constructor). When it 
joins an existing ring with {{ozone om --bootstrap}}, nothing moves it to the 
ring's apparent version: the bootstrap handshake does not carry a version, 
{{RatisBasedVersionManager.validateDBVersion}} only logs a mismatch, and 
{{finalizeFromSnapshotIfRequired}} can only raise the version. A ring that has 
not been finalized since it was installed has no {{#LAYOUTVERSION}} in its DB 
at all. So if the ring is pre-finalized, the new OM is finalized on its own 
while its peers are not. The ZDU design requires all OMs of a ring to run at 
the same apparent version.

The rest follows from that one replica:
* {{OzoneManager.getServiceList}} reports the local apparent version for every 
OM, on the assumption that peers share it.
* {{OMUpgradeFinalizeService}} is only created when the local OM needs 
finalization, and only the leader's instance submits 
{{CompleteFinalizeUpgrade}}. The new OM has none.
* {{OzoneManager.queryUpgradeStatus}} reports the answering OM's own state.

h3. Trigger
# The OM ring runs the new software and is not finalized yet (apparent version 
ZDU, or a legacy layout version after an upgrade from an older release).
# An operator adds or replaces an OM with {{ozone om --bootstrap}} (which 
initializes the OM storage first), or with {{ozone om --bootstrap --force}} 
while a peer is down.
# The new OM becomes leader through an election or {{ozone admin om transfer}}.

The documented recovery of a faulty OM reaches the same state: delete its DB 
and VERSION file, run {{ozone om --init}}, and restart it normally 
(HDDS-16534). That path does not use bootstrap (see Patch).

h3. Impact
* While the new OM leads, a client that connects learns the new OM's version 
from the service list and keeps it for its lifetime. {{RpcClient}} then allows 
If-None-Match writes, and they are accepted although three of four OMs are not 
finalized for them (run). An S3 Gateway that first connects in that window 
would do the same (argued, not run).
* If the admin finalizes while the new OM leads, SCM finalizes and the other 
OMs record the in-progress marker, but no {{CompleteFinalizeUpgrade}} is ever 
submitted. The other OMs stay pre-finalized, and {{queryUpgradeStatus}} reports 
FINALIZED for the cluster and for OM (run, 5 s window with a 10 ms check 
interval). Moving leadership to an original OM completes finalization.
* Features gated on the new version are usable before finalization, so the 
downgrade that the pre-finalized state promises is no longer safe (argued). 
Apart from the finalization in progress marker, which only the original OMs 
write, I found no replica divergence: with identical software, each replica 
evaluates If-None-Match at apply time. A replica on older software would ignore 
the unknown field (argued, not run).
* Releases 1.3.0 through 2.2.1 have the same root cause in an older form (read 
at the release tags, not run). A fresh OM starts at the newest layout version 
and bootstrap does not lower it, so while it leads a pre-finalized ring, it 
admits requests gated by {{@DisallowedUntilLayoutVersion}} before finalization. 
There finalization is applied on every replica, so it does not stall, and the 
service list reports the software version.

h3. Reproduction
PASS on unmodified source. 
[^TestBugMC2FreshBootstrappedOmPreFinalizedRing.java] 
({{hadoop-ozone/integration-test}}) starts a mini cluster with three OMs at 
apparent version ZDU (test only 
{{OMStorage.TESTING_INIT_APPARENT_VERSION_KEY}}) and one datanode. It removes 
the key, bootstraps a fourth OM through the normal init and bootstrap path, and 
transfers leadership to it. Output:
{noformat}
Bootstrapped omNode-bootstrap-1 at apparent version S3_IF_NONE_MATCH_ETAG 
(104); peers at [ZDU (100), ZDU (100), ZDU (100)]; its service list reports 
[S3_IF_NONE_MATCH_ETAG (104), S3_IF_NONE_MATCH_ETAG (104), 
S3_IF_NONE_MATCH_ETAG (104), S3_IF_NONE_MATCH_ETAG (104)]
If-None-Match write accepted while 3 of 4 OMs are at apparent version ZDU (100)
After 5000 ms: leader omNode-bootstrap-1, cluster status FINALIZED, OM status 
FINALIZED, peers at [ZDU (100), ZDU (100), ZDU (100)]
After leadership moved to omNode-3, the original OMs finalized to 
[S3_IF_NONE_MATCH_ETAG (104), S3_IF_NONE_MATCH_ETAG (104), 
S3_IF_NONE_MATCH_ETAG (104)]
{noformat}
As a control, the If-None-Match write is refused client side before the 
bootstrap. A passing test means the defect is present.

h3. Patch
[^MC-2-bootstrapping-om-adopts-peer-apparent-version.patch], against 
5f7111a27ea07ce6b7bf5284a4213ebacb65f2ae. It also applies to master at 
e03ba37c41d.

When an OM starts with {{ozone om --bootstrap}} (with or without {{--force}}) 
and has no {{om.db}} yet, it asks each configured peer for its apparent version 
before it builds its version manager, and lowers its VERSION file to the lowest 
answer. Lowering is safe because nothing has been persisted yet. A lagging peer 
can only make it start lower, and finalization over Ratis or from a snapshot 
then raises it like any follower (how far that raise goes is HDDS-16862, which 
also applies to an older {{CompleteFinalizeUpgrade}} entry replayed from a Raft 
log that was never purged). The peers answer through the OM admin RPC 
{{getPeerUpgradeStatus}}, which gets a new optional field 
{{omApparentVersion}}. That RPC is OM to OM, is not applied through Ratis, and 
was added after 2.2.0.

The query does not retry connections or socket timeouts and uses a 5 s RPC 
timeout. The client's fixed retry of 10 attempts at 1 s still applies. A 
stopped peer costs about 10 s (measured in a force bootstrap with one OM down). 
A peer host that does not answer at all can cost up to 11 times 
{{ipc.client.connect.timeout}} (20 s by default), about 4 minutes (calculated, 
not run). With default settings and no bound, it would be minutes per stopped 
peer and up to hours per silent host (calculated). If no peer reports a 
version, the OM logs a warning and keeps today's behaviour. Refusing to 
bootstrap in that case would be the stricter alternative.

Not covered: the re-init and normal restart path in Trigger. I ran it on the 
patched code with a ring at a legacy layout version. The re-initialized OM 
caught up at {{S3_IF_NONE_MATCH_ETAG (104)}}, and after a leadership transfer 
it led at 104 and reported 104 for every OM. A follow-up would run the same 
bounded query on a regular HA start without an {{om.db}}, falling back to the 
software version when no peer answers, because the first start of a new ring 
has no peers up yet. That changes the first start of every new HA cluster, so 
it is left out of this patch.

Covered by {{testBootstrappedOmAdoptsApparentVersionOfPreFinalizedRing}} in the 
existing {{TestOMUpgradeFinalization}}. It bootstraps an OM into a ring at a 
legacy layout version, makes it leader and finalizes. Without the change it 
fails with {{expected: INITIAL_VERSION (0) but was: S3_IF_NONE_MATCH_ETAG 
(104)}}. {{TestOMAdminProtocolServerSideImpl}} checks the new field. With the 
patch, these pass, and so do {{TestOMUpgradeFinalization}} (4 tests), 
{{TestAddRemoveOzoneManager}} (8 tests, including force bootstrap with an OM 
down) and the OM upgrade and startup unit suites (81 tests); checkstyle is 
clean. The reproduction then fails at its first assertion, because the new OM 
starts at ZDU.

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; HDDS-16862 and 
HDDS-17140 are related but different. 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