FrankChen021 commented on code in PR #20403:
URL: https://github.com/apache/druid/pull/20403#discussion_r4082349260


##########
processing/src/main/java/org/apache/druid/java/util/http/client/pool/ResourcePool.java:
##########
@@ -62,8 +64,17 @@ public class ResourcePool<K, V> implements Closeable
   public ResourcePool(final ResourceFactory<K, V> factory, final 
ResourcePoolConfig config,
                       final boolean eagerInitialization)
   {
-    this.pool = CacheBuilder.newBuilder().build(
-        new CacheLoader<>()
+    this.pool = CacheBuilder.newBuilder()
+                            
.expireAfterAccess(config.getUnusedConnectionTimeoutMillis(), 
TimeUnit.MILLISECONDS)
+                            .removalListener(
+                                (RemovalNotification<K, 
ResourceHolderPerKey<K, V>> notification) -> {
+                                  if (notification.wasEvicted()) {
+                                    notification.getValue().close();

Review Comment:
   [P2] Ensure expired holders are closed during pool shutdown
   
   **Finding:** Adding expiration makes an entry eligible to be hidden from 
Cache.asMap().entrySet() before Guava removes it. ResourcePool.close() iterates 
that view but never calls cleanUp(), so an expired holder can be skipped and 
its removal notification never delivered; its idle resources then remain open 
when the HTTP client is stopped. This is a lifecycle leak on the new expiration 
path.
   
   **Suggestion:** Run cache maintenance and deliver pending removal 
notifications before or as part of close(), or explicitly invalidate and close 
every holder; add a test that lets a key expire and then closes the pool 
without another cache access.



##########
processing/src/main/java/org/apache/druid/java/util/http/client/pool/ResourcePool.java:
##########
@@ -62,8 +64,17 @@ public class ResourcePool<K, V> implements Closeable
   public ResourcePool(final ResourceFactory<K, V> factory, final 
ResourcePoolConfig config,
                       final boolean eagerInitialization)
   {
-    this.pool = CacheBuilder.newBuilder().build(
-        new CacheLoader<>()
+    this.pool = CacheBuilder.newBuilder()
+                            
.expireAfterAccess(config.getUnusedConnectionTimeoutMillis(), 
TimeUnit.MILLISECONDS)

Review Comment:
   [P1] Do not expire holders while resources are borrowed
   
   **Finding:** The cache entry's access time is updated when take() starts, 
not when a resource is returned. If an HTTP request keeps all resources for 
longer than unusedConnectionTimeout (the documented defaults are 15 minutes for 
readTimeout and 4 minutes for unusedConnectionTimeout), the next access expires 
the holder even though it still has lent resources. close() leaves those in-use 
resources open until their containers return, while the load immediately 
creates a fresh holder with maxPerKey new resources, so the configured per-key 
connection limit is violated and repeated long-lived requests can accumulate 
extra channels and SSL buffers.
   
   **Suggestion:** Tie key eviction to holder idleness, such as deferring 
eviction while resources are lent and closing the holder only after the last 
borrowed container returns; add a test that holds a resource past the timeout 
before taking another resource for the same key.



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