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

gavinchou pushed a commit to branch bdbje
in repository https://gitbox.apache.org/repos/asf/doris-thirdparty.git


The following commit(s) were added to refs/heads/bdbje by this push:
     new 70324a2452e [fix](bdbje) release SSL certificate monitor threads on 
factory shutdown (#380)
70324a2452e is described below

commit 70324a2452e4ff717c872e431b5b385c82a54f84
Author: Siyang Tang <[email protected]>
AuthorDate: Mon Feb 9 20:31:40 2026 +0800

    [fix](bdbje) release SSL certificate monitor threads on factory shutdown 
(#380)
---
 pom.xml                                            |  2 +-
 .../java/com/sleepycat/je/rep/impl/RepImpl.java    | 33 ++++++++++++
 .../java/com/sleepycat/je/rep/monitor/Monitor.java |  9 ++++
 .../sleepycat/je/rep/net/DataChannelFactory.java   |  9 ++++
 .../com/sleepycat/je/rep/util/DbGroupAdmin.java    | 60 ++++++++++++----------
 .../java/com/sleepycat/je/rep/util/DbPing.java     | 32 ++++++++++--
 .../je/rep/util/ReplicationGroupAdmin.java         | 27 +++++++++-
 .../je/rep/utilint/net/SSLChannelFactory.java      | 35 +++++++++++--
 8 files changed, 171 insertions(+), 36 deletions(-)

diff --git a/pom.xml b/pom.xml
index 729aaf748e0..387f898e85d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
     </parent>
     <groupId>org.apache.doris</groupId>
     <artifactId>je</artifactId>
-    <version>18.3.17-doris-SNAPSHOT</version>
+    <version>18.3.18-doris-SNAPSHOT</version>
     <name>bdb-je apache doris release</name>
     <url>https://doris.apache.org/</url>
     <description>fork from bdb-je 18.3.12 from maven with starrocks bdbje 
patches</description>
diff --git a/src/main/java/com/sleepycat/je/rep/impl/RepImpl.java 
b/src/main/java/com/sleepycat/je/rep/impl/RepImpl.java
index a37a93d4704..09d817207b9 100644
--- a/src/main/java/com/sleepycat/je/rep/impl/RepImpl.java
+++ b/src/main/java/com/sleepycat/je/rep/impl/RepImpl.java
@@ -645,6 +645,33 @@ public class RepImpl
         }
     }
 
+    private synchronized void shutdownChannelFactory(PrintWriter errors) {
+        if (channelFactory == null) {
+            return;
+        }
+
+        try {
+            channelFactory.shutdown();
+        } catch (RuntimeException e) {
+            if (errors != null) {
+                appendException(errors, e,
+                                "shutting down channel factory " +
+                                nameIdPair);
+            } else {
+                LoggerUtils.warning(
+                    envLogger, this,
+                    "Unexpected exception shutting down channel factory: " +
+                    e.getMessage());
+            }
+        } finally {
+            channelFactory = null;
+        }
+    }
+
+    private void shutdownChannelFactory() {
+        shutdownChannelFactory(null);
+    }
+
     @Override
     protected Environment createInternalEnvironment() {
         return new InternalReplicatedEnvironment
@@ -680,6 +707,8 @@ public class RepImpl
             }
         } catch (InterruptedException e) {
             appendException(errors, e, "shutting down node " + nameIdPair);
+        } finally {
+            shutdownChannelFactory(errors);
         }
     }
 
@@ -774,6 +803,8 @@ public class RepImpl
                 repNode = null;
             }
         } catch (Exception ignore) {
+        } finally {
+            shutdownChannelFactory();
         }
 
         super.doCloseAfterInvalid();
@@ -811,6 +842,8 @@ public class RepImpl
             }
         } catch (InterruptedException ignore) {
             /* ignore */
+        } finally {
+            shutdownChannelFactory();
         }
 
         try {
diff --git a/src/main/java/com/sleepycat/je/rep/monitor/Monitor.java 
b/src/main/java/com/sleepycat/je/rep/monitor/Monitor.java
index 7d2a7250abb..c0b3b9defae 100644
--- a/src/main/java/com/sleepycat/je/rep/monitor/Monitor.java
+++ b/src/main/java/com/sleepycat/je/rep/monitor/Monitor.java
@@ -552,6 +552,15 @@ public class Monitor {
         if (serviceDispatcher != null) {
             serviceDispatcher.shutdown();
         }
+
+        if (repGroupAdmin != null) {
+            repGroupAdmin.close();
+        }
+
+        if (channelFactory != null) {
+            channelFactory.shutdown();
+            channelFactory = null;
+        }
     }
 
     /**
diff --git a/src/main/java/com/sleepycat/je/rep/net/DataChannelFactory.java 
b/src/main/java/com/sleepycat/je/rep/net/DataChannelFactory.java
index b8af5dda396..c22642ce1ba 100644
--- a/src/main/java/com/sleepycat/je/rep/net/DataChannelFactory.java
+++ b/src/main/java/com/sleepycat/je/rep/net/DataChannelFactory.java
@@ -215,4 +215,13 @@ public interface DataChannelFactory {
                         InetSocketAddress localAddr,
                         ConnectOptions connectOptions)
         throws IOException;
+
+    /**
+     * Releases resources associated with this factory.
+     *
+     * Implementations that do not hold resources can use the default no-op
+     * behavior.
+     */
+    default void shutdown() {
+    }
 }
diff --git a/src/main/java/com/sleepycat/je/rep/util/DbGroupAdmin.java 
b/src/main/java/com/sleepycat/je/rep/util/DbGroupAdmin.java
index b9e26c342e5..78f69c7b2bd 100644
--- a/src/main/java/com/sleepycat/je/rep/util/DbGroupAdmin.java
+++ b/src/main/java/com/sleepycat/je/rep/util/DbGroupAdmin.java
@@ -277,40 +277,46 @@ public class DbGroupAdmin {
 
         createGroupAdmin();
 
-        if (actions.size() == 0) {
-            return;
-        }
+        try {
+            if (actions.size() == 0) {
+                return;
+            }
 
-        for (Command action : actions) {
-            switch (action) {
+            for (Command action : actions) {
+                switch (action) {
 
-                /* Dump the group information. */
-            case DUMP:
-                dumpGroup();
-                break;
+                    /* Dump the group information. */
+                case DUMP:
+                    dumpGroup();
+                    break;
 
-                /* Remove a member. */
-            case REMOVE:
-                removeMember(nodeName);
-                break;
+                    /* Remove a member. */
+                case REMOVE:
+                    removeMember(nodeName);
+                    break;
 
-                /* Transfer the current mastership to a specified node. */
-            case TRANSFER_MASTER:
-                transferMaster(nodeName, timeout);
-                break;
+                    /* Transfer the current mastership to a specified node. */
+                case TRANSFER_MASTER:
+                    transferMaster(nodeName, timeout);
+                    break;
 
-                /* Update the network address of a specified node. */
-            case UPDATE_ADDRESS:
-                updateAddress(nodeName, newHostName, newPort);
-                break;
+                    /* Update the network address of a specified node. */
+                case UPDATE_ADDRESS:
+                    updateAddress(nodeName, newHostName, newPort);
+                    break;
 
-                /* Delete a member */
-            case DELETE:
-                deleteMember(nodeName);
-                break;
+                    /* Delete a member */
+                case DELETE:
+                    deleteMember(nodeName);
+                    break;
 
-            default:
-                throw new AssertionError();
+                default:
+                    throw new AssertionError();
+                }
+            }
+        } finally {
+            if (groupAdmin != null) {
+                groupAdmin.close();
             }
         }
     }
diff --git a/src/main/java/com/sleepycat/je/rep/util/DbPing.java 
b/src/main/java/com/sleepycat/je/rep/util/DbPing.java
index 52ce91a9b5c..d32d5ea6045 100644
--- a/src/main/java/com/sleepycat/je/rep/util/DbPing.java
+++ b/src/main/java/com/sleepycat/je/rep/util/DbPing.java
@@ -50,6 +50,7 @@ public class DbPing {
     private int socketTimeout = 10000;
     /* The factory for channel creation */
     private DataChannelFactory channelFactory;
+    private boolean ownsChannelFactory = false;
 
     private static final String undocumentedUsageString =
         "  -netProps <optional>    # name of a property file containing\n" +
@@ -93,8 +94,12 @@ public class DbPing {
         throws Exception {
 
         DbPing ping = new DbPing();
-        ping.parseArgs(args);
-        System.out.println(ping.getNodeState());
+        try {
+            ping.parseArgs(args);
+            System.out.println(ping.getNodeState());
+        } finally {
+            ping.close();
+        }
     }
 
     /**
@@ -201,6 +206,7 @@ public class DbPing {
         }
 
         this.channelFactory = initializeFactory(repNetConfig, nodeName);
+        this.ownsChannelFactory = true;
     }
 
     private DbPing() {
@@ -269,7 +275,8 @@ public class DbPing {
                   int socketTimeout,
                   ReplicationNetworkConfig netConfig) {
         this(repNode, groupName, socketTimeout,
-             initializeFactory(netConfig, repNode.getName()));
+             initializeFactory(netConfig, repNode.getName()),
+             true);
     }
 
     /**
@@ -289,11 +296,20 @@ public class DbPing {
                   String groupName,
                   int socketTimeout,
                   DataChannelFactory channelFactory) {
+        this(repNode, groupName, socketTimeout, channelFactory, false);
+    }
+
+    private DbPing(ReplicationNode repNode,
+                   String groupName,
+                   int socketTimeout,
+                   DataChannelFactory channelFactory,
+                   boolean ownsChannelFactory) {
         this.nodeName = repNode.getName();
         this.groupName = groupName;
         this.socketAddress = repNode.getSocketAddress();
         this.socketTimeout = socketTimeout;
         this.channelFactory = channelFactory;
+        this.ownsChannelFactory = ownsChannelFactory;
     }
 
     /* Get the state of the specified node. */
@@ -333,6 +349,16 @@ public class DbPing {
         }
     }
 
+    /**
+     * Releases resources owned by this instance.
+     */
+    public void close() {
+        if (ownsChannelFactory && channelFactory != null) {
+            channelFactory.shutdown();
+            channelFactory = null;
+        }
+    }
+
     private static ReplicationNetworkConfig makeRepNetConfig(File propFile)
         throws FileNotFoundException {
 
diff --git a/src/main/java/com/sleepycat/je/rep/util/ReplicationGroupAdmin.java 
b/src/main/java/com/sleepycat/je/rep/util/ReplicationGroupAdmin.java
index b1edb797209..ecb88536164 100644
--- a/src/main/java/com/sleepycat/je/rep/util/ReplicationGroupAdmin.java
+++ b/src/main/java/com/sleepycat/je/rep/util/ReplicationGroupAdmin.java
@@ -77,6 +77,8 @@ public class ReplicationGroupAdmin {
     private final Logger logger;
     private final Formatter formatter;
     private final DataChannelFactory channelFactory;
+    private final boolean ownsChannelFactory;
+    private volatile boolean closed = false;
 
     /**
      * Constructs a group admin object.
@@ -104,7 +106,8 @@ public class ReplicationGroupAdmin {
                                  Set<InetSocketAddress> helperSockets,
                                  ReplicationNetworkConfig repNetConfig) {
         this(groupName, helperSockets,
-             initializeFactory(repNetConfig, groupName));
+             initializeFactory(repNetConfig, groupName),
+             true);
     }
 
     /**
@@ -119,9 +122,17 @@ public class ReplicationGroupAdmin {
     public ReplicationGroupAdmin(String groupName,
                                  Set<InetSocketAddress> helperSockets,
                                  DataChannelFactory channelFactory) {
+        this(groupName, helperSockets, channelFactory, false);
+    }
+
+    private ReplicationGroupAdmin(String groupName,
+                                  Set<InetSocketAddress> helperSockets,
+                                  DataChannelFactory channelFactory,
+                                  boolean ownsChannelFactory) {
         this.groupName = groupName;
         this.helperSockets = helperSockets;
         this.channelFactory = channelFactory;
+        this.ownsChannelFactory = ownsChannelFactory;
 
         electionsProtocol =
             new Protocol(TimebasedProposalGenerator.getParser(),
@@ -138,6 +149,20 @@ public class ReplicationGroupAdmin {
         formatter = new ReplicationFormatter(NameIdPair.NOCHECK);
     }
 
+    /**
+     * Releases resources owned by this instance.
+     */
+    public synchronized void close() {
+        if (closed) {
+            return;
+        }
+        closed = true;
+
+        if (ownsChannelFactory) {
+            channelFactory.shutdown();
+        }
+    }
+
     /**
      * Returns the helper sockets being used to contact a replication group
      * member, in order to query for the information.
diff --git 
a/src/main/java/com/sleepycat/je/rep/utilint/net/SSLChannelFactory.java 
b/src/main/java/com/sleepycat/je/rep/utilint/net/SSLChannelFactory.java
index ae0c351abe9..01709b13dac 100644
--- a/src/main/java/com/sleepycat/je/rep/utilint/net/SSLChannelFactory.java
+++ b/src/main/java/com/sleepycat/je/rep/utilint/net/SSLChannelFactory.java
@@ -378,10 +378,17 @@ public class SSLChannelFactory implements 
DataChannelFactory {
                 return;
             }
 
-            // Only monitor PEM configuration
-            final String pemCert = config.getSSLPemCertFile();
-            final String pemKey = config.getSSLPemKeyFile();
-            final String pemCa = config.getSSLPemCaCertFile();
+            if (!isPemMonitoringEnabled(config)) {
+                logger.log(INFO,
+                           "Certificate monitoring disabled " +
+                           "(PEM certificate/key/CA files are not fully 
configured " +
+                           "or keystore mode is in use)");
+                return;
+            }
+
+            if (certificateCheckExecutor != null) {
+                return;
+            }
 
             // Create scheduled executor for periodic certificate checking
             certificateCheckExecutor = 
Executors.newSingleThreadScheduledExecutor(r -> {
@@ -408,6 +415,25 @@ public class SSLChannelFactory implements 
DataChannelFactory {
         }
     }
 
+    private boolean isPemMonitoringEnabled(ReplicationSSLConfig config) {
+        final String keyStore = config.getSSLKeyStore();
+        final String keyStoreProperty =
+            System.getProperty("javax.net.ssl.keyStore");
+
+        if ((keyStore != null && !keyStore.isEmpty()) ||
+            (keyStoreProperty != null && !keyStoreProperty.isEmpty())) {
+            return false;
+        }
+
+        return isNonEmpty(config.getSSLPemCaCertFile()) &&
+               isNonEmpty(config.getSSLPemKeyFile()) &&
+               isNonEmpty(config.getSSLPemCertFile());
+    }
+
+    private static boolean isNonEmpty(String value) {
+        return value != null && !value.isEmpty();
+    }
+
     /**
      * Initialize file modification times on startup.
      */
@@ -635,6 +661,7 @@ public class SSLChannelFactory implements 
DataChannelFactory {
     /**
      * Stop the certificate monitoring executor and clean up resources.
      */
+    @Override
     public void shutdown() {
         if (certificateCheckExecutor != null) {
             certificateCheckExecutor.shutdown();


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

Reply via email to