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

xiangfu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 325446cabb5 Fix non-daemon threads blocking JVM shutdown in 
RenewableTlsUtils (#17221)
325446cabb5 is described below

commit 325446cabb5c2cca2ecbcbd9893913eebd66720f
Author: 2dmurali <[email protected]>
AuthorDate: Wed Nov 19 15:39:00 2025 -0800

    Fix non-daemon threads blocking JVM shutdown in RenewableTlsUtils (#17221)
    
    Co-authored-by: Murali D <[email protected]>
---
 .../apache/pinot/common/utils/NamedThreadFactory.java    | 16 ++++++++++++++--
 .../apache/pinot/common/utils/tls/RenewableTlsUtils.java | 11 ++++++++---
 .../pinot/common/utils/tls/RenewableTlsUtilsTest.java    |  5 ++++-
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/NamedThreadFactory.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/NamedThreadFactory.java
index 4c9fd7e7df8..c579dcfe170 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/NamedThreadFactory.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/NamedThreadFactory.java
@@ -53,6 +53,7 @@ public class NamedThreadFactory implements ThreadFactory {
   private final ThreadGroup _group;
   private final AtomicInteger _threadNumber = new AtomicInteger(1);
   private final String _threadNamePrefix;
+  private final boolean _daemon;
 
   /**
    * Creates a new {@link NamedThreadFactory} instance
@@ -60,10 +61,21 @@ public class NamedThreadFactory implements ThreadFactory {
    * @param threadNamePrefix the name prefix assigned to each thread created.
    */
   public NamedThreadFactory(String threadNamePrefix) {
+    this(threadNamePrefix, false);
+  }
+
+  /**
+   * Creates a new {@link NamedThreadFactory} instance
+   *
+   * @param threadNamePrefix the name prefix assigned to each thread created.
+   * @param daemon if true, creates daemon threads
+   */
+  public NamedThreadFactory(String threadNamePrefix, boolean daemon) {
     final SecurityManager s = System.getSecurityManager();
     _group = (s != null) ? s.getThreadGroup() : 
Thread.currentThread().getThreadGroup();
     _threadNamePrefix =
-        String.format(NAME_PATTERN, checkPrefix(threadNamePrefix), 
THREAD_POOL_NUMBER.getAndIncrement());
+            String.format(NAME_PATTERN, checkPrefix(threadNamePrefix), 
THREAD_POOL_NUMBER.getAndIncrement());
+    _daemon = daemon;
   }
 
   private static String checkPrefix(String prefix) {
@@ -78,7 +90,7 @@ public class NamedThreadFactory implements ThreadFactory {
   public Thread newThread(Runnable r) {
     final Thread t =
         new Thread(_group, r, String.format("%s-%d", _threadNamePrefix, 
_threadNumber.getAndIncrement()), 0);
-    t.setDaemon(false);
+    t.setDaemon(_daemon);
     t.setPriority(Thread.NORM_PRIORITY);
     return t;
   }
diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/tls/RenewableTlsUtils.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/tls/RenewableTlsUtils.java
index 32903c8471a..47966aec0eb 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/utils/tls/RenewableTlsUtils.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/utils/tls/RenewableTlsUtils.java
@@ -43,6 +43,7 @@ import 
nl.altindag.ssl.keymanager.HotSwappableX509ExtendedKeyManager;
 import nl.altindag.ssl.trustmanager.HotSwappableX509ExtendedTrustManager;
 import nl.altindag.ssl.util.SSLFactoryUtils;
 import org.apache.pinot.common.config.TlsConfig;
+import org.apache.pinot.common.utils.NamedThreadFactory;
 import org.apache.pinot.spi.utils.retry.RetryPolicies;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,6 +56,8 @@ public class RenewableTlsUtils {
   private static final String FILE_SCHEME = "file";
   private static final int CERT_RELOAD_JOB_INTERVAL_IN_MINUTES = 1440;
   private static final int CERT_RELOAD_JOB_INITAL_DELAY_IN_MINUTES = 20;
+  private static final String SSL_FILE_WATCHER_THREAD_PREFIX = 
"ssl-file-watcher";
+  private static final String SSL_DAILY_RELOAD_THREAD_PREFIX = 
"ssl-daily-reload";
 
   private RenewableTlsUtils() {
     // left blank
@@ -209,7 +212,8 @@ public class RenewableTlsUtils {
       // The reloadSslFactoryWhenFileStoreChanges is a blocking call, so we 
need to create a new thread to run it.
       // Creating a new thread to run the reloadSslFactoryWhenFileStoreChanges 
is costly; however, unless we
       // invoke the createAutoRenewedSSLFactoryFromFileStore method crazily, 
this should not be a problem.
-      Executors.newSingleThreadExecutor().execute(() -> {
+      // Use daemon thread to allow JVM to exit when on-demand processes 
complete.
+      Executors.newSingleThreadExecutor(new 
NamedThreadFactory(SSL_FILE_WATCHER_THREAD_PREFIX, true)).execute(() -> {
         try {
           reloadSslFactoryWhenFileStoreChanges(sslFactory,
               keyStoreType, keyStorePath, keyStorePassword,
@@ -227,8 +231,9 @@ public class RenewableTlsUtils {
       // it was never detected and run in others. This will result in few 
components with
       // stale certificates. In order to prevent this issue, we are adding a 
new scheduled
       // thread which will run once a day and reload the certs.
-
-      Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
+      // Use daemon thread to allow JVM to exit when on-demand processes 
complete.
+      Executors.newSingleThreadScheduledExecutor(new 
NamedThreadFactory(SSL_DAILY_RELOAD_THREAD_PREFIX, true))
+              .scheduleAtFixedRate(() -> {
         LOGGER.info("Creating a scheduled thread to reloadSsl once a day");
         try {
           reloadSslFactory(sslFactory,
diff --git 
a/pinot-common/src/test/java/org/apache/pinot/common/utils/tls/RenewableTlsUtilsTest.java
 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/tls/RenewableTlsUtilsTest.java
index 3225dced401..c39a206e0ab 100644
--- 
a/pinot-common/src/test/java/org/apache/pinot/common/utils/tls/RenewableTlsUtilsTest.java
+++ 
b/pinot-common/src/test/java/org/apache/pinot/common/utils/tls/RenewableTlsUtilsTest.java
@@ -54,6 +54,7 @@ import 
nl.altindag.ssl.trustmanager.HotSwappableX509ExtendedTrustManager;
 import nl.altindag.ssl.trustmanager.UnsafeX509ExtendedTrustManager;
 import org.apache.commons.io.FileUtils;
 import org.apache.pinot.common.config.TlsConfig;
+import org.apache.pinot.common.utils.NamedThreadFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -84,6 +85,7 @@ public class RenewableTlsUtilsTest {
 
   private static final String TLS_KEYSTORE_FILE_PATH = DEFAULT_TEST_TLS_DIR + 
"/" + TLS_KEYSTORE_FILE;
   private static final String TLS_TRUSTSTORE_FILE_PATH = DEFAULT_TEST_TLS_DIR 
+ "/" + TLS_TRUSTSTORE_FILE;
+  private static final String SSL_RELOAD_THREAD_PREFIX = "ssl-reload-test";
 
   @BeforeMethod
   public void setUp()
@@ -183,7 +185,8 @@ public class RenewableTlsUtilsTest {
 
     // Start a new thread to reload the ssl factory when the tls files change
     // Avoid early finalization by not using Executors.newSingleThreadExecutor 
(java <= 20, JDK-8145304)
-    ExecutorService executorService = Executors.newFixedThreadPool(1);
+    ExecutorService executorService = Executors.newFixedThreadPool(1,
+            new NamedThreadFactory(SSL_RELOAD_THREAD_PREFIX, true));
     executorService.execute(
         () -> {
           try {


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

Reply via email to