[
https://issues.apache.org/jira/browse/HDDS-16931?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-16931:
------------------------------
Attachment: TestBugMC7CorruptCheckpointAcceptedAndInstalled.java
> SCM checks only the TransactionInfo of a downloaded checkpoint before
> acknowledging it, so a checkpoint with corrupt SST blocks is installed
> --------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-16931
> URL: https://issues.apache.org/jira/browse/HDDS-16931
> Project: Apache Ozone
> Issue Type: Bug
> Reporter: Siyao Meng
> Priority: Minor
> Attachments:
> MC-7-verify-scm-checkpoint-checksums-before-install.patch,
> TestBugMC7CorruptCheckpointAcceptedAndInstalled.java
>
>
> h3. Mechanism
> Before a follower SCM acknowledges a downloaded checkpoint to Ratis,
> {{SCMHAManagerImpl.verifyCheckpointFromLeader}} opens it read only and reads
> only the TransactionInfo. A checkpoint with a damaged data block in another
> table passes. {{installCheckpoint}} then calls
> {{HAUtils.replaceDBWithCheckpoint}}, which moves {{scm.db}} to a backup,
> copies the checkpoint in and deletes {{dbInconsistentMarker}} before any
> service has read the new DB. Only then does {{reloadSCMState}} read it; its
> failure triggers a rollback to the old DB. If the process stops before that
> rollback (kill, OOM, power loss during the reload, which iterates every
> container and pipeline), {{scm.db}} is the corrupt checkpoint, there is no
> marker, and the old DB is only in {{scm.db.backup.*}}. Every start then fails
> on the first read of the damaged block, and nothing restores the backup.
> h3. Trigger
> # The checkpoint a follower downloads has a corrupt SST block outside the
> TransactionInfo, for example media corruption of a rarely read SST on the
> leader (the checkpoint hard links the leader's files) or a write error on the
> follower while unpacking it.
> # The follower SCM stops after {{replaceDBWithCheckpoint}} returned and
> before the rollback in {{installCheckpoint}} completed. A failed copy during
> the rollback (for example out of disk space) reaches the same state without a
> stop: its reset branch moves the corrupt checkpoint back into {{scm.db}} and
> deletes the marker (by code reading, not run).
> Both steps are uncommon, and a stop alone (with a good checkpoint) is
> harmless today: the restart opens the installed DB and catches up.
> h3. Impact
> * If the damaged block is in a table the reload iterates (containers,
> pipelines), the scan ends early without an error, because
> {{RDBStoreAbstractIterator.hasNext}} checks only {{isValid()}} and not
> {{status()}}. The reload can then report success with part of the table
> loaded, and the backup of the old DB is deleted (by code reading, not run).
> The iterator behaviour affects every table scan and is worth its own issue.
> * If the damaged block is read by key during the reload and the SCM stops
> before the rollback: the SCM does not start until an operator repairs its DB.
> There is no marker, so the error is a RocksDB corruption, not the
> "inconsistent state" message that points at the cause.
> * No data is lost cluster wide; the other SCMs have the state and the old DB
> is intact in the backup. Moving the backup back makes the DB open, but the
> follower acknowledged the install and its Raft log now starts at the snapshot
> index, so the old DB alone does not let it rejoin (argued, see HDDS-16928).
> Replacing the DB from another SCM does.
> h3. Reproduction
> PASS on the unmodified source.
> [^TestBugMC7CorruptCheckpointAcceptedAndInstalled.java]
> ({{hadoop-ozone/integration-test}}) runs a one SCM mini cluster. It copies a
> clean checkpoint of the SCM DB to a separate directory as the follower's DB,
> and makes a second checkpoint with a raised TransactionInfo and the first 16
> bytes of its meta table SST overwritten. {{verifyCheckpointFromLeader}} on
> the SCM accepts it: {{(t:1, i:1004)}}. {{HAUtils.replaceDBWithCheckpoint}} on
> the follower's directory then returns with no marker and the old DB in
> {{scm.db.backup.4_...}}. Opening that directory with {{SCMMetadataStoreImpl}}
> and reading the apparent layout version, as SCM start does, fails with
> {{Corruption: ... Failed to get #LAYOUTVERSION from ColumnFamily-meta}}. The
> stop itself is not simulated with a kill: {{replaceDBWithCheckpoint}} has
> returned and nothing else writes to the directory before the reload, so the
> directory is in the state a stop at that point leaves. A passing test means
> the defect is present.
> h3. Patch
> [^MC-7-verify-scm-checkpoint-checksums-before-install.patch], against
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at
> 52812559dad.
> The patch makes {{verifyCheckpointFromLeader}} also run RocksDB
> {{verifyChecksum}} on the read only checkpoint (a new
> {{RocksDatabase.verifyChecksum}} and {{HAUtils.verifyCheckpointChecksums}}).
> A damaged checkpoint is then handled like a failed download: the follower
> does not acknowledge it, its DB is not touched, and Ratis retries the
> install. This removes the precondition instead of narrowing the stop window,
> and it also keeps checkpoints with damaged blocks in iterated tables out of
> the install. The check is unconditional, with no switch, because it runs only
> during an install, on the install thread, at a cost of the same order as the
> download just before it, and installing a checkpoint with damaged blocks is
> never the right outcome. The cost is one read of the downloaded checkpoint
> per install; nothing changes for SCMs that do not install a checkpoint, and
> the shared {{HAUtils.getTrxnInfoFromCheckpoint}} used by OM is unchanged.
> Keeping the marker until the reload succeeds was not chosen: it would also
> refuse the start after a stop during the reload of a good checkpoint, which
> recovers on its own today. With the patch the reproduction fails at its first
> assertion: {{verifyCheckpointFromLeader}} returns null and logs {{Corruption:
> ... Failed to verifyChecksum}}.
> Covered by {{testVerifyCheckpointFromLeaderRejectsCorruptCheckpoint}} in the
> existing {{TestSCMHAManagerImpl}}: a clean checkpoint of a real SCM DB is
> accepted, and the same checkpoint with damaged SST blocks (except the one
> with the TransactionInfo) is rejected. Without the change it fails with
> "expected: null but was: (t:1, i:1000)". With it the SCM HA unit suites
> ({{TestSCMHAManagerImpl}}, {{TestSCMStateMachine}},
> {{TestSCMSnapshotProvider}}, 22 tests) and {{TestRDBStore}} and
> {{TestRDBSnapshotProvider}} (18 tests) pass. The mini cluster suites
> {{TestSCMInstallSnapshotWithHA}} and {{TestSCMInstallSnapshot}} (5 tests,
> including an install through Ratis) pass and checkstyle is clean.
> Found by TLA+ model checking and code review of the SCM Ratis snapshot and
> checkpoint install paths under HDDS-15926, on commit
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. 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]