[ 
https://issues.apache.org/jira/browse/HDDS-17355?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

ASF GitHub Bot updated HDDS-17355:
----------------------------------
    Labels: pull-request-available  (was: )

> Datanode stop aborts halfway when a schema V1/V2 container DB is still in 
> use, ContainerCache.shutdownCache throws IllegalArgumentException
> -------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17355
>                 URL: https://issues.apache.org/jira/browse/HDDS-17355
>             Project: Apache Ozone
>          Issue Type: Bug
>    Affects Versions: 1.3.0, 1.4.0, 2.0.0, 1.4.1, 2.1.0, 2.2.0, 2.1.1, 2.1.2, 
> 2.2.1
>            Reporter: Siyao Meng
>            Priority: Major
>              Labels: pull-request-available
>         Attachments: CR-8-skip-container-dbs-in-use-on-cache-shutdown.patch, 
> TestBugCR8ShutdownCacheAbortsStop.java
>
>
> h3. Mechanism
> {{ContainerCache.shutdownCache}} runs 
> {{Preconditions.checkArgument(cleanupDb(db), "refCount: %s", ...)}} for every 
> cached schema V1/V2 container DB, and {{ReferenceCountedDB.cleanup}} returns 
> false while the DB is referenced. Since HDDS-6540 this is on the datanode 
> stop path: {{OzoneContainer.stop}} calls {{KeyValueHandler.stop}}, then 
> {{BlockManagerImpl.shutdown}} and {{BlockUtils.shutdownCache}}. Nothing on 
> the way out catches the {{IllegalArgumentException}} 
> ({{DatanodeStateMachine.stopDaemon}} only catches {{IOException}}), so the 
> rest of the shutdown is skipped: the remaining cached V1/V2 DBs, 
> {{DatanodeStoreCache.shutdownCache}} (the schema V3 per volume DBs), the 
> volume sets and the other services after the handlers in 
> {{OzoneContainer.stop}}, and the remainder of {{DatanodeStateMachine.close}} 
> and {{HddsDatanodeService.stop}}. Nothing retries the skipped steps. The 
> message is {{refCount: 1}} at this commit and {{refCount: [1]}} in released 
> versions, whose format string has no placeholder.
> h3. Trigger
> A datanode holding schema V1/V2 containers (upgraded from a release before 
> schema V3, or with schema V3 disabled) stops while a thread holds a handle 
> from {{BlockUtils.getDB}}. {{OzoneContainer.stop}} stops the handlers before 
> {{blockDeletingService}}, so a running {{BlockDeletingTask}} is a natural 
> holder. HDDS-16131 hit the same exception in a test teardown that called 
> {{BlockUtils.shutdownCache}} while a {{BlockDeletingTask}} still held a DB, 
> and was fixed in the test only. {{DeleteBlocksCommandHandler}} workers are 
> another holder, since their pool is stopped after {{container.stop()}} (code 
> reading, not run).
> h3. Impact
> * On a normal SIGTERM the shutdown hook logs the exception and the JVM exits, 
> so the skipped DBs are closed by process exit and RocksDB recovers on the 
> next start.
> * When the datanode stops itself through 
> {{HddsDatanodeService.terminateDatanode}} ({{stop(); terminate(1);}}, used 
> when Ratis closes a container state machine in {{ContainerStateMachine}}, for 
> too many failed volumes, a critical state machine error, a failed certificate 
> rollback and a failure to persist a new certificate ID in {{saveNewCertId}}), 
> the exception escapes {{stop()}} and {{terminate(1)}} is not reached. Checked 
> locally with a test that starts an {{HddsDatanodeService}} with a schema V2 
> container, holds a {{BlockUtils.getDB}} handle and calls 
> {{terminateDatanode}}: {{stop()}} throws {{IllegalArgumentException}}, 
> {{terminate(1)}} is not called, {{isStopped}} is true, and two non daemon 
> threads (one of them Jetty's scheduler) stay alive, so the JVM does not exit. 
> With the handle released, or with the patch, {{terminate(1)}} is called and 
> no extra non daemon thread is left. The datanode is meant to exit there so 
> that a supervisor restarts it; instead it stays up with its services stopped 
> until someone restarts it by hand.
> h3. Reproduction
> PASS on unmodified source, deterministic. 
> [^TestBugCR8ShutdownCacheAbortsStop.java] ({{hadoop-hdds/container-service}}) 
> starts a real {{OzoneContainer}} with schema V3 enabled, creates two schema 
> V2 containers, holds a handle on one of them through {{BlockUtils.getDB}} and 
> calls {{OzoneContainer.stop}} on the same thread. {{stop}} throws 
> {{IllegalArgumentException: refCount: 1}}; afterwards the other V2 DB and the 
> schema V3 per volume DB are still open and the data volume was not shut down. 
> A second {{stop}} after the handle is released completes the shutdown. A 
> passing test means the defect is present. The {{terminateDatanode}} check 
> above is a separate local test, not attached.
> h3. Patch
> [^CR-8-skip-container-dbs-in-use-on-cache-shutdown.patch], against 
> bf3829e6c8ac69ae17585585703f91426dcdf6b3. It also applies to master at 
> 4fb68e96831.
> {{shutdownCache}} closes and removes every DB it can close and leaves a DB 
> that is still referenced open and in the cache, with one WARN line naming it, 
> instead of throwing. Closing it would make the user's next DB call fail and 
> break the reference count contract. A skipped DB is closed by a later 
> eviction, {{removeDB}} or {{shutdownCache}} once released, or by process exit 
> on a real stop, which is what happens to every DB today after the exception. 
> Nothing changes when no DB is referenced. Reordering {{OzoneContainer.stop}} 
> to stop background services first would only narrow the window, so it is not 
> part of the patch.
> Covered by {{testShutdownCacheKeepsDBInUseOpen}} in the existing 
> {{TestContainerCache}}, which fails without the change with 
> {{IllegalArgumentException: refCount: 1}}. With the change 
> {{TestContainerCache}}, {{TestOzoneContainer}}, {{TestBlockDeletingService}}, 
> {{TestContainerPersistence}}, the schema one and two backwards compatibility 
> suites, {{TestBlockManagerImpl}}, {{TestContainerReader}}, 
> {{TestDatanodeStoreCache}}, {{TestKeyValueBlockIterator}} and 
> {{TestStaleRecoveringContainerScrubbingService}} pass (586 tests), and 
> checkstyle is clean. With the patch the reproduction fails at its first 
> assertion because {{stop}} no longer throws.
> Found by code review of the RocksDB handle close and shutdown paths, as part 
> of the TLA+ verification effort under HDDS-15926, on commit 
> bf3829e6c8ac69ae17585585703f91426dcdf6b3. 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
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to