This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit bb18e63b37c87f200de629ca747736acbc4a3fc4
Author: Nazerke Seidan <[email protected]>
AuthorDate: Sat May 9 08:17:53 2026 +0500

    SOLR-18218: Improve ExecutorUtil error logging (#4399)
---
 .../manager/consumer/KafkaCrossDcConsumer.java     |  3 ++-
 .../org/apache/solr/common/util/ExecutorUtil.java  | 31 ++++++++++++++++++++--
 .../solr/common/util/SolrNamedThreadFactory.java   |  7 +++++
 .../apache/solr/common/util/ExecutorUtilTest.java  | 13 +++++++++
 4 files changed, 51 insertions(+), 3 deletions(-)

diff --git 
a/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
 
b/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
index d368151c6af..31c2615e77f 100644
--- 
a/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
+++ 
b/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
@@ -51,6 +51,7 @@ import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.IOUtils;
+import org.apache.solr.common.util.SolrNamedThreadFactory;
 import org.apache.solr.crossdc.common.CrossDcConf;
 import org.apache.solr.crossdc.common.IQueueHandler;
 import org.apache.solr.crossdc.common.KafkaCrossDcConf;
@@ -96,7 +97,7 @@ public class KafkaCrossDcConsumer extends 
Consumer.CrossDcConsumer {
   private final ThreadPoolExecutor executor;
 
   private final ExecutorService offsetCheckExecutor =
-      ExecutorUtil.newMDCAwareCachedThreadPool(r -> new Thread(r, 
"offset-check-thread"));
+      ExecutorUtil.newMDCAwareCachedThreadPool(new 
SolrNamedThreadFactory("offset-check-thread"));
   private final PartitionManager partitionManager;
 
   private final BlockingQueue<Runnable> queue = new BlockingQueue<>(10);
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java 
b/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
index e6e1cd44941..48ac61f55d8 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/ExecutorUtil.java
@@ -162,8 +162,9 @@ public class ExecutorUtil {
         pool.shutdownNow();
         // Wait again for forced threads to stop.
         if (!pool.awaitTermination(timeout, unit)) {
-          log.error("Threads from pool {} did not forcefully stop.", pool);
-          throw new RuntimeException("Timeout waiting for pool " + pool + " to 
shutdown.");
+          String executorDetails = describeExecutorForLogging(pool);
+          log.error("Threads from pool did not forcefully stop. {}", 
executorDetails);
+          throw new RuntimeException("Timeout waiting for pool to shutdown. " 
+ executorDetails);
         }
       }
     } catch (InterruptedException ie) {
@@ -174,6 +175,20 @@ public class ExecutorUtil {
     }
   }
 
+  /** Executor logging details which include pool name when executor fails to 
terminate. */
+  public static String describeExecutorForLogging(ExecutorService pool) {
+    if (pool == null) return "";
+    if (pool instanceof ThreadPoolExecutor poolExecutor) {
+      ThreadFactory threadFactory = poolExecutor.getThreadFactory();
+      String poolName =
+          threadFactory instanceof SolrNamedThreadFactory 
solrNamedThreadFactory
+              ? solrNamedThreadFactory.getPoolName()
+              : "";
+      return "[" + "poolName=" + poolName + "]" + poolExecutor;
+    }
+    return "";
+  }
+
   /**
    * Await the termination of an {@link ExecutorService} until all threads are 
complete, or until we
    * are interrupted, at which point the {@link ExecutorService} will be 
interrupted as well.
@@ -322,6 +337,18 @@ public class ExecutorUtil {
       this.enableSubmitterStackTrace = true;
     }
 
+    /** When the thread factory is a {@link SolrNamedThreadFactory}, prefixes 
the pool name. */
+    @Override
+    public String toString() {
+      ThreadFactory threadFactory = getThreadFactory();
+      String base = super.toString();
+      if (threadFactory instanceof SolrNamedThreadFactory 
solrNamedThreadFactory) {
+        String poolName = solrNamedThreadFactory.getPoolName();
+        return "[" + "poolName=" + poolName + "] " + base;
+      }
+      return base;
+    }
+
     @Override
     public void execute(final Runnable command) {
       final Map<String, String> submitterContext = MDC.getCopyOfContextMap();
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/util/SolrNamedThreadFactory.java 
b/solr/solrj/src/java/org/apache/solr/common/util/SolrNamedThreadFactory.java
index 32950971cc7..e36bb54db5a 100644
--- 
a/solr/solrj/src/java/org/apache/solr/common/util/SolrNamedThreadFactory.java
+++ 
b/solr/solrj/src/java/org/apache/solr/common/util/SolrNamedThreadFactory.java
@@ -25,10 +25,17 @@ public class SolrNamedThreadFactory implements 
ThreadFactory {
   private final ThreadGroup group;
   private final AtomicInteger threadNumber = new AtomicInteger(1);
   private final String prefix;
+  private final String poolName;
 
   public SolrNamedThreadFactory(String namePrefix) {
     group = getThreadGroup();
     prefix = namePrefix + "-" + poolNumber.getAndIncrement() + "-thread-";
+    poolName = namePrefix;
+  }
+
+  /** Returns the name prefix passed to the constructor as a pool name. */
+  public String getPoolName() {
+    return poolName;
   }
 
   @SuppressWarnings("removal")
diff --git 
a/solr/solrj/src/test/org/apache/solr/common/util/ExecutorUtilTest.java 
b/solr/solrj/src/test/org/apache/solr/common/util/ExecutorUtilTest.java
index f9d6026edd0..ed739632e84 100644
--- a/solr/solrj/src/test/org/apache/solr/common/util/ExecutorUtilTest.java
+++ b/solr/solrj/src/test/org/apache/solr/common/util/ExecutorUtilTest.java
@@ -275,4 +275,17 @@ public class ExecutorUtilTest extends SolrTestCase {
       ExecutorUtil.shutdownNowAndAwaitTermination(service);
     }
   }
+
+  @Test
+  public void mdcAwarePoolToStringIncludesPoolName() {
+    ExecutorService service = 
ExecutorUtil.newMDCAwareCachedThreadPool("test-async-task");
+    try {
+      assertTrue(service.toString(), 
service.toString().contains("[poolName=test-async-task]"));
+
+      String executorForLogging = 
ExecutorUtil.describeExecutorForLogging(service);
+      assertTrue(executorForLogging, 
executorForLogging.contains("[poolName=test-async-task]"));
+    } finally {
+      ExecutorUtil.shutdownNowAndAwaitTermination(service);
+    }
+  }
 }

Reply via email to