Wei-Chiu Chuang created HDDS-17254:
--------------------------------------

             Summary: Client-side non-daemon executors and timers can prevent 
JVM shutdown
                 Key: HDDS-17254
                 URL: https://issues.apache.org/jira/browse/HDDS-17254
             Project: Apache Ozone
          Issue Type: Bug
          Components: Ozone Client, EC Client
            Reporter: Wei-Chiu Chuang


h2. Problem

HDDS-10564 / [PR #6418|https://github.com/apache/ozone/pull/6418] made 
RpcClient output-stream executors use daemon threads so an unclosed 
FileSystem/client would not keep a command-line JVM alive. A source audit found 
four other client-side executor/timer creation sites with the same potential 
shutdown risk.

This report is based on source inspection at 
d0949f291791c399f3e54409fcb92656a80597a4. JVM-exit hangs have not yet been 
reproduced in a subprocess test. Not every invocation starts the relevant 
worker: the conditions for each path are listed below.

h2. Affected paths

h3. 1. Ratis streaming writes: BlockDataStreamOutput

The constructor creates its response executor with 
Executors.newSingleThreadExecutor(), whose default thread factory creates 
non-daemon threads. This is separate from RpcClient.writeExecutor, so 
HDDS-10564 does not cover it. After an asynchronous response starts the worker, 
an abandoned/unclosed streaming output can keep the JVM alive. Normal close 
calls cleanup in finally, and cleanup shuts down the executor.
* Executor creation: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockDataStreamOutput.java#L195
* Normal close/cleanup: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockDataStreamOutput.java#L689
* Executor shutdown: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/BlockDataStreamOutput.java#L736

h3. 2. Short-circuit reads: per-connection timer

XceiverClientShortCircuit.connect() creates new Timer(prefix + "-Timer") 
without the daemon argument. The timer thread starts when the connection is 
established. If client cleanup is omitted, it can keep the JVM alive. Normal 
close cancels the timer.
* 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/XceiverClientShortCircuit.java#L143

h3. 3. Short-circuit reads: singleton DomainSocketFactory timer

When short-circuit reads are configured and native support is available, 
DomainSocketFactory creates new Timer(name) without the daemon argument. This 
is a second non-daemon timer, separate from the connection timer above. Factory 
close cancels it, but an unclosed client/factory can leave it alive.
* Creation: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/DomainSocketFactory.java#L160
* Cancellation: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/storage/DomainSocketFactory.java#L272

h3. 4. Default low-level EC reader factory

BlockInputStreamFactoryImpl's no-argument constructor supplies 
Executors::newSingleThreadExecutor. If an EC reconstruction reader is 
instantiated through this default factory and executes reconstruction work, it 
can start non-daemon workers. The reconstruction stream's close method does not 
shut down the supplied executor. The normal RpcClient path supplies its own 
daemon executors and is not affected by this default-factory creation site.
* Default factory: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/ozone/client/io/BlockInputStreamFactoryImpl.java#L70
* Reconstruction executor acquisition: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/ozone/client/io/ECBlockInputStreamFactoryImpl.java#L95
* Reconstruction stream close: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-hdds/client/src/main/java/org/apache/hadoop/ozone/client/io/ECBlockReconstructedStripeInputStream.java#L738
* A production caller of the default factory is the checksum-verification tool: 
https://github.com/apache/ozone/blob/d0949f291791c399f3e54409fcb92656a80597a4/hadoop-ozone/cli-debug/src/main/java/org/apache/hadoop/ozone/debug/replicas/ChecksumVerifier.java#L61

h2. Expected behavior / suggested fix

Client housekeeping workers should not prevent JVM termination solely because a 
caller has finished its work but omitted explicit cleanup. Apply the 
daemon-thread principle used by HDDS-10564 to the affected background 
executors/timers, while preserving normal close/cancel behavior.
* Use a named daemon ThreadFactory for the streaming response executor.
* Use daemon timers for both short-circuit timer sites.
* Give the default EC reconstruction executor an explicit lifecycle/owner and 
daemon threads. Do not indiscriminately shut down caller-supplied shared 
executors from an individual stream's close method.

h2. Validation needed

Add isolated subprocess tests with bounded exit timeouts:
# Start streaming response work, omit stream/client close, return from main, 
and verify the JVM exits.
# Establish a short-circuit connection, omit client/factory close, return from 
main, and verify both timer threads do not retain the JVM (requires native 
short-circuit prerequisites).
# Trigger EC reconstruction through the no-argument factory, close the stream, 
and verify no orphan executor keeps the JVM alive; also check the 
abandoned-stream case.
# Verify explicit cleanup still terminates/releases the owned resources and 
does not break shared executors.

Daemon workers do not replace application responsibility to flush and close 
streams. No data-durability guarantee is inferred from changing thread daemon 
status.

Related prior fix: HDDS-10564.



--
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