yandrey321 opened a new pull request, #10747:
URL: https://github.com/apache/ozone/pull/10747

   ## What changes were proposed in this pull request?
   
   XceiverClientGrpc.close() performs a blocking graceful channel shutdown, and 
because clients are torn down through XceiverClientManager's cache eviction, 
that blocking wait runs while the clientCache monitor is held — serializing 
every concurrent acquireClient() / releaseClient() call.
   HDDS-14571 changed close() from a forced shutdownNow() (which cancels 
in-flight RPCs and terminates in ~1 ms) to a graceful shutdown() followed by a 
polling loop that always sleeps at least SHUTDOWN_WAIT_INTERVAL_MILLIS (100 ms) 
before it can observe termination:
   
   ```
   for (ChannelInfo channelInfo : dnChannelInfoMap.values()) {
     channelInfo.getChannel().shutdown();
   }
   while (!nonTerminatedChannels.isEmpty() && System.nanoTime() < deadline) {
     nonTerminatedChannels.removeIf(ManagedChannel::isTerminated);
     Thread.sleep(SHUTDOWN_WAIT_INTERVAL_MILLIS); // >= 100ms floor per close()
   }
   ```
   
   Every close() now costs ≥100 ms instead of ~1 ms. The eviction path that 
invokes it holds the cache monitor throughout:
   
   ```
   acquireClient()                      // synchronized (clientCache)
     -> getClient()
       -> clientCache.get(key, newClient)
          -> Guava evicts an entry on the calling thread
             -> RemovalListener.onRemoval()   // synchronized (clientCache)
                -> XceiverClientSpi.setEvicted() -> cleanup() -> close()  // 
blocks here
   ```
   
   So concurrent readers that create/evict short-lived clients (e.g. EC 
read/checksum collection, which uses a distinct client per placement group) 
serialize behind a ≥100 ms lock hold per closed channel and degrade 
non-linearly with concurrency.
   Fix: restore immediate termination in close() — use channel.shutdownNow() 
followed by channel.awaitTermination(SHUTDOWN_WAIT_MAX_SECONDS, ...), and drop 
the mandatory-sleep polling loop. This keeps HDDS-14571's goal (no synchronized 
methods on XceiverClientGrpc) while eliminating the blocking wait under the 
cache lock.
   
   
   Co authored with Claude Code
   
   ## What is the link to the Apache JIRA
   
   https://issues.apache.org/jira/browse/HDDS-15849
   
   
   ## How was this patch tested?
   
   Added TestXceiverClientManagerCloseContention, a unit test that exercises 
the real XceiverClientManager and XceiverClientGrpc.close() (real 
lazily-connected plaintext gRPC channels to random local datanode addresses; no 
MiniOzoneCluster and no live datanode required, since close() only shuts 
channels down). maxCacheSize=1 plus a distinct pipeline per iteration forces an 
eviction-driven close on nearly every acquisition; 8 threads × 8 cycles produce 
63 closes. It measures average wall-clock per close and asserts it stays well 
under the pre-fix ~100 ms floor.
   The same test, unmodified, against the code before and after this change:
   
   run  | wall-clock | ms / close | result
   -- | -- | -- | --
   Before (graceful shutdown() + 100 ms poll) | 6874 ms | 109 ms | ❌ fails
   After (shutdownNow() + awaitTermination) | 201 ms | 3 ms | ✅ passes
   
   ~34× faster per close


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to