Siyao Meng created HDDS-16878:
---------------------------------

             Summary: OM checkpoint servlets keep using the DB store closed by 
a checkpoint install and return HTTP 500 once that OM is leader
                 Key: HDDS-16878
                 URL: https://issues.apache.org/jira/browse/HDDS-16878
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: CR-1-checkpoint-servlets-read-current-db-store.patch, 
TestBugCR1CheckpointServletClosedStore.java

h3. Mechanism
{{DBCheckpointServlet.initialize}} stores the {{DBStore}} it is given, and 
{{OMDBCheckpointServlet.init}} and {{OMDBCheckpointServletInodeBasedXfer.init}} 
pass {{om.getMetadataManager().getStore()}}. Both servlets then take every 
checkpoint, the DB location and the {{RocksDBCheckpointDiffer}} from 
{{getDbStore()}}, which returns that stored field. 
{{OzoneManager.installCheckpoint}} stops the old metadata manager, which closes 
its store, and {{reloadOMState}} creates a new {{OmMetadataManagerImpl}}. The 
HTTP server and its servlets are created once in {{OzoneManager.start}} and are 
never initialized again, so after the install the servlets still use the closed 
store.

Jetty initializes each servlet separately, on its own first request. Each 
endpoint is affected if it served at least one request before the install in 
the same process lifetime: for example while that OM was leader earlier (a 
follower install, an OM bootstrap or a Recon full snapshot), or a request that 
reached it as a follower and got 503. Followers and bootstrapping OMs download 
from {{/v2/dbCheckpoint}}.

The same code is in every release since 2.0.0 (code reading of the release 
tags).

h3. Trigger
# OM X serves a request on a checkpoint endpoint (as leader, or 503 as 
follower). Only the endpoints that served a request are affected.
# X falls behind far enough that the leader sends a snapshot, and X installs 
the checkpoint through {{installSnapshotFromLeader}}.
# X becomes leader (election or leadership transfer).

h3. Impact
* While X is leader, every request to {{/dbCheckpoint}} and 
{{/v2/dbCheckpoint}} returns HTTP 500 and the OM logs {{RocksDatabaseException: 
Rocks Database is closed}} from {{RDBStore.getCheckpoint}}. This lasts until X 
restarts.
* A follower that needs a checkpoint from X cannot get one: 
{{OmRatisSnapshotProvider.downloadDBSnapshotFromLeader}} fails with "ErrorCode: 
500" (run). A bootstrapping OM and a Recon full snapshot use the same endpoints 
and would fail the same way (code reading, not run). Recon incremental sync 
through {{getDBUpdates}} reads the current store and is not affected.
* No client data is affected.

h3. Reproduction
PASS on the unmodified source, deterministic. 
[^TestBugCR1CheckpointServletClosedStore.java] 
({{hadoop-ozone/integration-test}}) starts three OMs with one held back, 
advances the leader past the Ratis log purge gap, and starts the late OM with 
the existing {{OzoneManager.setTestInstallSnapshot}} hook set only until both 
servlets have answered one request (503). Ratis then installs the checkpoint 
through the real path, leadership is transferred to that OM, and both endpoints 
plus a real follower download are tried. Output:
{noformat}
Before install, omNode-3 answered /dbCheckpoint -> 503, /v2/dbCheckpoint -> 
503. After install: store replaced = true, old store closed = true
After omNode-3 became leader: /dbCheckpoint -> 500, /v2/dbCheckpoint -> 500, 
follower download failed: Unexpected exception when trying to reach OM to 
download latest checkpoint. Checkpoint URL: 
http://127.0.0.1:15017/v2/dbCheckpoint?includeSnapshotData=true&flushBeforeCheckpoint=true.
 ErrorCode: 500
{noformat}
A passing test means the defect is present. With the patch both endpoints 
return 200, the download succeeds and the test fails at its first defect 
assertion.

h3. Patch
[^CR-1-checkpoint-servlets-read-current-db-store.patch], against 
a6b7bdb937109ba2893688b41a89470bc95c88cf. It also applies to master at 
6818707698c.

Both OM servlets override {{getDbStore()}} to return the store of the OM's 
current metadata manager, the same way their bootstrap lock and 
{{getSnapshotDir()}} already read the OM's current state. Nothing changes for 
an OM that never installed a checkpoint. {{DBCheckpointServlet}} and the SCM 
servlet are not changed. Four places in the existing servlet tests injected a 
spy store through {{initialize}}; they now also stub {{getDbStore()}} with it.

Covered by the new 
{{TestOMRatisSnapshots#testDbCheckpointServletsServeInstalledDbAfterLeaderChange}},
 which fails without the change with {{[/dbCheckpoint] expected: 200 but was: 
500}}. With the change {{TestOMRatisSnapshots}}, {{TestOMDbCheckpointServlet}}, 
{{TestOMDbCheckpointServletInodeBasedXfer}}, {{TestGetDBUpdatesAuthorization}} 
and {{TestOMDBCheckpointServletInodeBasedXferNonLeader}} pass (37 tests) and 
checkstyle is clean.

{{SCMDBCheckpointServlet}} caches the SCM store the same way, and an SCM 
checkpoint install also creates a new store (code reading, not run). Recon 
fetches the SCM DB over gRPC, so only direct use of the SCM endpoint is 
affected; it is left for a separate change. Other holders of the old metadata 
manager, such as the snapshot directory metrics and the multi tenant Ranger 
sync service (HDDS-16853), are out of scope here.

Found by code review of the OM Ratis snapshot (checkpoint) install paths, as 
part of the TLA+ verification effort under HDDS-15926, on commit 
a6b7bdb937109ba2893688b41a89470bc95c88cf. 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]

Reply via email to