[
https://issues.apache.org/jira/browse/HDDS-16930?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-16930:
------------------------------
Attachment:
CR-6-flush-scm-transaction-buffer-before-checkpoint-install.patch
> Follower SCM exits when it installs a Ratis snapshot after applying a delete
> transaction removal that its buffer has not flushed yet
> ------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-16930
> URL: https://issues.apache.org/jira/browse/HDDS-16930
> Project: Apache Ozone
> Issue Type: Bug
> Reporter: Siyao Meng
> Priority: Major
> Attachments:
> CR-6-flush-scm-transaction-buffer-before-checkpoint-install.patch,
> TestBugCR6InstallAfterDeleteTxRemoval.java
>
>
> h3. Mechanism
> {{DeletedBlockLogStateManagerImpl.removeTransactionsFromDB}} adds the removed
> transaction IDs to the in memory {{deletingTxIDs}} and queues the deletes in
> the SCM transaction buffer. Only {{onFlush()}}, called when the buffer is
> flushed, clears the set. {{DeletedBlockLogStateManagerImpl.reinitialize}}
> runs {{Preconditions.checkArgument(deletingTxIDs.isEmpty())}}, and its
> comment says that a flush is called before the reload. No flush happens on
> the install path: Ratis calls {{SCMStateMachine.pause}}, which only changes
> the lifecycle state, and then {{SCMStateMachine.reinitialize}}, and
> {{SCMHAManagerImpl.installCheckpoint}} stops the store, replaces the DB and
> calls {{startServices}}, which calls {{DeletedBlockLog.reinitialize}}. If the
> set is not empty the check throws. The rollback puts the old DB back and
> reloads again, the set is still not empty, the same check throws, and SCM
> calls {{exitSystem(1)}} ("Failed to reload SCM state and instantiate
> services.").
> On a follower the buffer is flushed by a Ratis snapshot (every
> {{ozone.scm.ha.ratis.snapshot.threshold}} entries, default 1000), at the
> pending operation limit (default 10000), when it becomes leader, when it
> serves a checkpoint download ({{InterSCMGrpcService.download}}) and when it
> applies a replicated {{StatefulServiceStateManager.saveConfiguration}}. The
> periodic flush ({{ozone.scm.ha.dbtransactionbuffer.flush.interval}}) runs on
> the leader only. So after a follower applies a removal (sent for every delete
> acknowledgement from datanodes and by the scanner cleanup), the set stays non
> empty until the next of these flushes, which for a follower that stops
> applying may never come.
> h3. Trigger
> # {{ozone.scm.ha.ratis.log.purge.enabled}} is true (default false) and the
> leader has purged its log past the follower's position. The leader purges
> only after {{ozone.scm.ha.ratis.log.purge.gap}} entries (default 1000000)
> past its last purge.
> # The follower applied a delete transaction removal since its last buffer
> flush, then fell behind the purged log while running (for example paused or
> cut off from the leader).
> # Ratis asks the follower to install a snapshot.
> With default settings Ratis purges only below every peer's commit index, so a
> running follower is not sent a snapshot, and a newly bootstrapped SCM has an
> empty set. I did not find a default configuration route.
> h3. Impact
> * The follower SCM process exits during the install (run with the exit
> replaced by a recording exit manager; the process exit itself not run).
> * A restart does not recover it. In a run with the exit replaced, stopping
> and restarting that SCM right after the failed install failed: the stop could
> not flush the transaction buffer ({{RocksDatabaseException: Rocks Database is
> closed}} from {{SCMHADBTransactionBufferImpl.flush}}), and the restarted SCM
> could not open the restored {{scm.db}} (RocksDB reported a missing {{.sst}}
> file and a possibly corrupted MANIFEST). The rollback restores the old DB
> while the store opened by the failed reload is still open, which is
> HDDS-16921: with the patch of that issue applied, the same restart opened the
> DB (2 of 2 runs). A real process exit was not run.
> * Through the real Ratis path the failure comes after Ratis has already
> purged the follower's log up to the snapshot index and replied
> SNAPSHOT_INSTALLED to the leader ({{SnapshotInstallationHandler}} does both
> before {{reinitialize}} runs on the state machine updater thread). So even
> with a DB that opens, the restarted follower is left in the wedge reported in
> HDDS-16928 (code reading for this trigger; the wedge itself is run there). HA
> redundancy stays reduced until that SCM is repaired.
> h3. Reproduction
> PASS on the unmodified source, default configuration.
> [^TestBugCR6InstallAfterDeleteTxRemoval.java]
> ({{hadoop-ozone/integration-test}}) starts three SCMs, has the leader add one
> delete transaction and remove it again ({{removeTransactions}}, the request a
> datanode acknowledgement sends), and checks that both followers applied the
> removal without a flush. Both followers then install the leader's checkpoint
> through the state machine calls Ratis makes ({{pause}},
> {{setInstallingSnapshotData}}, {{reinitialize}}); the log purge notification
> itself is not exercised. The control follower flushes its buffer first and
> installs fine. The other one fails at
> {{DeletedBlockLogStateManagerImpl.reinitialize}} in the reload and in the
> rollback reload, and the exit is requested with the
> {{IllegalArgumentException}}. A passing test means the defect is present.
> With the patch both installs succeed and the test fails at its
> {{assertThatThrownBy}}.
> h3. Patch
> [^CR-6-flush-scm-transaction-buffer-before-checkpoint-install.patch], against
> b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at
> 52812559dad.
> {{SCMHAManagerImpl.installCheckpoint}} flushes the transaction buffer before
> it stops the store. This clears {{deletingTxIDs}} through {{onFlush()}} as
> the comments in {{DeletedBlockLogStateManagerImpl.reinitialize}} and
> {{SCMDeletedBlockTransactionStatusManager.reinitialize}} already assume, and
> it leaves the old DB, which a rollback restores, consistent with the
> follower's applied index. Ratis calls {{reinitialize}} on its state machine
> updater thread, so no transaction is applied during the flush. Resetting the
> set in {{reinitialize}} instead would also avoid the crash, but it drops the
> check and leaves a restored old DB behind the applied index.
> Covered by {{testInstallCheckpointAfterDeleteTransactionRemoval}} in the
> existing {{TestSCMInstallSnapshotWithHA}}: the follower applies a removal
> that is not flushed yet and then installs the leader's checkpoint. Without
> the change the install fails with the {{IllegalArgumentException}} above in
> both reloads. With it {{TestSCMInstallSnapshotWithHA}},
> {{TestSCMInstallSnapshot}}, {{TestSCMSnapshot}}, {{TestScmHAFinalization}},
> {{TestSecretKeySnapshot}} and the server-scm {{TestDeletedBlockLog}},
> {{TestSCMHADBTransactionBufferImpl}}, {{TestSCMHAManagerImpl}} and
> {{TestSCMStateMachine}} pass, and checkstyle is clean.
> {{TestSCMInstallSnapshotWithHA}} is tagged flaky (HDDS-5631), so the new test
> does not run in the regular CI checks.
> 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. 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]