Andrey Yarovoy created HDDS-17491:
-------------------------------------

             Summary: XceiverClientManager should close idle clients even when 
its cache is no longer used
                 Key: HDDS-17491
                 URL: https://issues.apache.org/jira/browse/HDDS-17491
             Project: Apache Ozone
          Issue Type: Improvement
          Components: Ozone Client
            Reporter: Andrey Yarovoy


`XceiverClientManager` keeps datanode clients in a Guava cache with 
`expireAfterAccess(scm.container.client.idle.threshold)`
(default 10 s). Evicting a client marks it evicted, and it is closed once no 
one holds it. Guava doesn't evict on a
timer, though. It removes expired entries only during cache operations, and 
only in the segment the operation
touches (the cache has 4 segments by default). As a result:

- **An idle JVM never closes its clients.** Once the application stops writing, 
nothing uses the cache again, so
connections, channels and their threads stay open until 
`XceiverClientManager.close()` runs.
- **A busy JVM closes idle clients only by chance.** When some clients are idle 
and others are still used, each
cache write cleans up only one segment. With 16 idle clients, one write after 
the threshold closed 5, 5 and 6 of
them in three runs.

For Ratis data streaming this means a JVM that doesn't close its FileSystem 
never exits (HDDS-17278). The streaming
client holds the shared Netty worker group, and its non-daemon threads keep the 
JVM alive. Hadoop's shutdown hook
would close the FileSystem, but it never runs because the JVM never starts 
shutting down. If idle clients were
closed, the worker group would be released and its threads would stop.

#### Proposed change

- Give `XceiverClientManager` a single-thread `ScheduledExecutorService` that 
runs `clientCache.cleanUp()`
periodically, for example every idle threshold. Expired clients that no one 
holds are then closed about one
threshold after their last use.
- The thread must be a **daemon** thread, so it doesn't keep a JVM alive 
itself. Give it a recognizable name, for
example `XceiverClientManager-cleanup`.
- Stop it in `XceiverClientManager.close()`, before the final `cleanUp()`.
- Clients that are still referenced stay open until they are released; the 
existing ref-count rule is unchanged.
- As a side benefit, idle clients are no longer closed by whichever thread 
happens to use the cache, under the
`clientCache` lock (see HDDS-15849), but by the cleanup thread.

#### Effect on HDDS-17278

A JVM that wrote with data streaming and doesn't close its FileSystem exits 
about 12 s after its last write. That is
the 10 s idle threshold plus Netty's default 2 s quiet period for 
`shutdownGracefully`. Today it doesn't exit until
it is killed: 60 s for a MapReduce task (`mapreduce.task.exit.timeout`), or 
never for other clients. An immediate
exit needs the Ratis fix (linked RATIS issue).

#### Testing

- **`TestXceiverClientManagerIdleEviction`** (`hdds-client`, uses a 200 ms 
threshold and real, lazily connected gRPC
clients, no datanode needed). The first two tests fail today:
- a released client is closed without any further cache use;
- 16 idle clients are all closed when another client is used;
- an explicit `cleanUp()` closes an expired, released client;
- an expired client that is still referenced stays open until it is released.
- Check that the cleanup thread is a daemon thread and stops on `close()`.
- **Integration:** a child JVM writes a file with 
`ozone.fs.datastream.enabled=true`, returns from `main` without
closing the FileSystem, and must exit within the idle threshold plus a margin. 
The test should use a short
threshold.



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