[
https://issues.apache.org/jira/browse/HDDS-17295?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-17295:
------------------------------
Attachment: MC-8-release-om-read-locks-on-the-acquired-lock.patch
> OzoneManager read paths release their volume or bucket read lock on a lock
> that a checkpoint install may have replaced
> ----------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-17295
> URL: https://issues.apache.org/jira/browse/HDDS-17295
> Project: Apache Ozone
> Issue Type: Bug
> Affects Versions: 2.2.0, 2.2.1
> Reporter: Siyao Meng
> Priority: Minor
> Attachments: MC-8-release-om-read-locks-on-the-acquired-lock.patch,
> TestBugMC8VolumeOwnerLockAcrossInstall.java
>
>
> h3. Mechanism
> {{OzoneManager.getVolumeOwner(String)}} takes the VOLUME read lock on
> {{metadataManager.getLock()}} and releases it in its {{finally}} block on a
> second read of the {{metadataManager}} field.
> {{OzoneManager.installCheckpoint}} replaces that field, and with it the lock,
> in {{reloadOMState}}. If the replacement happens between the acquire and the
> release, the release goes to the new lock and throws
> {{IllegalMonitorStateException}}, and the read hold on the replaced lock is
> never released. {{getBucketOwner}}, {{getLifecycleConfiguration}},
> {{listUsersInTenant}}, {{getS3VolumeContext}} and
> {{SstFilteringService.markSSTFilteredFlagForSnapshot}} use the same pattern.
> h3. Trigger
> A read with an ACL check (it calls {{getVolumeOwner}} or {{getBucketOwner}}
> through {{OmMetadataReader.checkAcls}}), or one of the other methods above,
> is in progress on an OM while that OM installs a checkpoint from the leader,
> and it reaches the lock acquire before the swap and the release after it. A
> follower installs a checkpoint when it lags behind the purged Raft log. On a
> follower such reads are served only with follower reads (off by default on
> client and server). {{installCheckpoint}} stops the Hadoop RPC server before
> the swap, but not the OM gRPC server ({{ozone.om.s3.grpc.server_enabled}}, on
> by default), so gRPC requests keep being served during the install. A Hadoop
> RPC call that is already running when the server stops loses its connection
> anyway. Any authenticated client can send such a read; it cannot make the
> install happen. For {{SstFilteringService}} it needs a filtering task that
> outlives the shutdown wait of {{KeyManager.stop}}.
> h3. Impact
> * A read over the OM gRPC server fails with {{IllegalMonitorStateException}}
> (returned as {{Status.INTERNAL}}) although the volume exists. The failed call
> is shown in the reproduction, in process. Argued from code, not run over a
> real connection: the gRPC follower read client treats this error as a reason
> to try another OM.
> * The read hold on the replaced lock is never released. Nothing uses the
> replaced lock after the install, so this holds no current request up.
> * {{OzoneManagerLock}} updates its per thread lock tracker after the unlock,
> so the failed release also leaves a stale bit on that thread. A stale VOLUME
> bit only refuses {{S3_BUCKET_LOCK}}, which no OM code acquires. A stale
> BUCKET bit (from {{getBucketOwner}} or {{getLifecycleConfiguration}}) also
> refuses {{VOLUME_LOCK}} on that thread until the thread next releases a
> BUCKET lock. Hadoop RPC handler threads exit with the stopped server; a gRPC
> handler thread keeps the bit (code reading).
> h3. Reproduction
> PASS, deterministic, unmodified source.
> [^TestBugMC8VolumeOwnerLockAcrossInstall.java]
> ({{hadoop-ozone/ozone-manager}}) starts one OM through {{OmTestManagers}} and
> creates a volume. It holds the VOLUME write lock of the current lock, starts
> {{getVolumeOwner}} on another thread so that it waits in its read lock
> acquire, installs a checkpoint of the OM's own DB with {{installCheckpoint}}
> (presented with a newer transaction index so that it proceeds), and then
> releases the write lock. The lookup fails with
> {{IllegalMonitorStateException}}. A passing test means the defect is present.
> h3. Patch
> [^MC-8-release-om-read-locks-on-the-acquired-lock.patch], against
> e0de543cbe9d73f3bf5473a45e1c59cd6eae7173. It also applies to master at
> 669912118cf.
> The six methods read the lock once into a local variable and release on that
> same lock. Nothing else changes: the same lock is taken as before, and the
> data is read as before.
> Covered by {{testOwnerLookupsAcrossInstallReleaseTheirLocks}} in the existing
> {{TestOMRatisSnapshots}}: on the active follower of the suite's HA cluster it
> puts a {{getVolumeOwner}} and a {{getBucketOwner}} call across an install in
> the same way (the install fails at its first backup move with the suite's
> existing fault injector and reloads the old DB, which also replaces the
> metadata manager and the lock) and checks that both calls return the owner.
> Without the change the test fails with {{IllegalMonitorStateException}}. With
> it the test, {{TestOMRatisSnapshots}}, {{TestSstFilteringService}},
> {{TestMultiTenantVolume}}, {{TestOMMetadataReader}} and
> {{TestOzoneIntegrationNonHA}} pass, and checkstyle is clean. With the patch
> the reproduction fails at its assertion, because the lookup returns the owner.
> Found by TLA+ model checking and code review of the Ozone Manager lock
> ordering paths under HDDS-15926, on commit
> e0de543cbe9d73f3bf5473a45e1c59cd6eae7173. Checked against HDDS issues and
> apache/ozone pull requests for duplicates before filing. Related but not
> duplicates: HDDS-4088 (added the owner lookup for the authorizer), HDDS-9279
> (follower reads, which make this reachable), HDDS-6833 (caching to reduce
> lock contention in {{getVolumeOwner}}), HDDS-17120 and HDDS-16878 (checkpoint
> servlets that keep a DB store an install replaced, the same pattern for
> another object). 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]