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

ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 94aabdee49f IGNITE-23548 Add default exception handlers to log 
unexpected Throwables (#6881)
94aabdee49f is described below

commit 94aabdee49f9bad0faeff5410afb04a97f17460a
Author: Vladimir Dmitrienko <[email protected]>
AuthorDate: Wed Nov 12 08:51:56 2025 +0100

    IGNITE-23548 Add default exception handlers to log unexpected Throwables 
(#6881)
---
 .../internal/client/ClientTimeoutWorker.java       | 10 ++---
 .../ignite/internal/client/TcpClientChannel.java   |  2 +-
 .../client/io/netty/DefaultExceptionHandler.java   | 49 ++++++++++++++++++++++
 .../io/netty/NettyClientConnectionMultiplexer.java |  3 +-
 .../handlers/StopNodeOrHaltFailureHandler.java     | 17 ++++++--
 .../network/netty/DefaultExceptionHandler.java     | 42 +++++++++++++++++++
 .../internal/network/netty/PipelineUtils.java      |  1 +
 .../raft/jraft/storage/impl/RocksDBLogStorage.java |  5 ++-
 .../logit/util/concurrent/ShutdownAbleThread.java  |  2 +
 .../system/SystemDisasterRecoveryManagerImpl.java  |  2 +
 10 files changed, 121 insertions(+), 12 deletions(-)

diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ClientTimeoutWorker.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ClientTimeoutWorker.java
index 77e73cceee3..f13217c8e4b 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ClientTimeoutWorker.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ClientTimeoutWorker.java
@@ -24,8 +24,8 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import org.apache.ignite.client.IgniteClientConfiguration;
 import org.apache.ignite.internal.future.timeout.TimeoutWorker;
-import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.thread.NamedThreadFactory;
 import org.jetbrains.annotations.Nullable;
 
@@ -44,12 +44,12 @@ final class ClientTimeoutWorker {
         // No-op.
     }
 
-    synchronized void registerClientChannel(TcpClientChannel ch) {
+    synchronized void registerClientChannel(TcpClientChannel ch, 
IgniteClientConfiguration clientCfg) {
         channels.add(ch);
         emptyCount = 0;
 
         if (executor == null) {
-            executor = createExecutor();
+            executor = createExecutor(clientCfg);
             emptyCount = 0;
 
             long sleepInterval = TimeoutWorker.getSleepInterval();
@@ -68,11 +68,11 @@ final class ClientTimeoutWorker {
         }
     }
 
-    private static ScheduledExecutorService createExecutor() {
+    private static ScheduledExecutorService 
createExecutor(IgniteClientConfiguration clientCfg) {
         return Executors.newSingleThreadScheduledExecutor(
                 new NamedThreadFactory(
                         "TcpClientChannel-timeout-worker",
-                        Loggers.voidLogger()));
+                        ClientUtils.logger(clientCfg, 
ClientTimeoutWorker.class)));
     }
 
     private void checkTimeouts() {
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
index 9e19da2613c..4557c63d544 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/TcpClientChannel.java
@@ -200,7 +200,7 @@ class TcpClientChannel implements ClientChannel, 
ClientMessageHandler, ClientCon
 
                     tcpConnectionEstablished = true;
 
-                    ClientTimeoutWorker.INSTANCE.registerClientChannel(this);
+                    ClientTimeoutWorker.INSTANCE.registerClientChannel(this, 
cfg.clientConfiguration());
 
                     sock = s;
 
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/DefaultExceptionHandler.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/DefaultExceptionHandler.java
new file mode 100644
index 00000000000..5cbcf9632d1
--- /dev/null
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/DefaultExceptionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.client.io.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.ignite.client.IgniteClientConfiguration;
+import org.apache.ignite.internal.client.ClientUtils;
+import org.apache.ignite.internal.logger.IgniteLogger;
+
+/**
+ * Default Netty exception handler.
+ *
+ * <p>Intended to be the last inbound handler in a channel pipeline.
+ */
+class DefaultExceptionHandler extends ChannelInboundHandlerAdapter {
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /**
+     * Constructor.
+     *
+     * @param clientCfg Client configuration.
+     */
+    DefaultExceptionHandler(IgniteClientConfiguration clientCfg) {
+        this.log = ClientUtils.logger(clientCfg, 
DefaultExceptionHandler.class);
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        log.warn("Unhandled exception in channel {}: {}", ctx.channel(), 
cause.getMessage(), cause);
+        ctx.close();
+    }
+}
diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
index 3ed2238ed95..5b0a25ed82f 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
@@ -92,7 +92,8 @@ public class NettyClientConnectionMultiplexer implements 
ClientConnectionMultipl
                     ch.pipeline().addLast(
                             new 
FlushConsolidationHandler(FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES,
 true),
                             new ClientMessageDecoder(),
-                            new NettyClientMessageHandler());
+                            new NettyClientMessageHandler(),
+                            new DefaultExceptionHandler(clientCfg));
                 }
             });
 
diff --git 
a/modules/failure-handler/src/main/java/org/apache/ignite/internal/failure/handlers/StopNodeOrHaltFailureHandler.java
 
b/modules/failure-handler/src/main/java/org/apache/ignite/internal/failure/handlers/StopNodeOrHaltFailureHandler.java
index 8671c15e98f..267deea3f8f 100644
--- 
a/modules/failure-handler/src/main/java/org/apache/ignite/internal/failure/handlers/StopNodeOrHaltFailureHandler.java
+++ 
b/modules/failure-handler/src/main/java/org/apache/ignite/internal/failure/handlers/StopNodeOrHaltFailureHandler.java
@@ -22,6 +22,9 @@ import java.util.concurrent.TimeUnit;
 import org.apache.ignite.internal.failure.FailureContext;
 import org.apache.ignite.internal.failure.NodeStopper;
 import 
org.apache.ignite.internal.failure.handlers.configuration.StopNodeOrHaltFailureHandlerView;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.thread.LogUncaughtExceptionHandler;
 import org.apache.ignite.internal.tostring.IgniteToStringExclude;
 import org.apache.ignite.internal.tostring.S;
 
@@ -31,6 +34,8 @@ import org.apache.ignite.internal.tostring.S;
  * then JVM process will be terminated forcibly using {@code 
Runtime.getRuntime().halt()}.
  */
 public class StopNodeOrHaltFailureHandler extends AbstractFailureHandler {
+    private static final IgniteLogger LOG = 
Loggers.forClass(StopNodeOrHaltFailureHandler.class);
+
     /**
      * This is kill code that can be used by external tools, like Shell 
scripts,
      * to auto-stop the Ignite JVM process without restarting.
@@ -77,16 +82,18 @@ public class StopNodeOrHaltFailureHandler extends 
AbstractFailureHandler {
         if (tryStop) {
             CountDownLatch latch = new CountDownLatch(1);
 
-            new Thread(
+            Thread stopperThread = new Thread(
                     () -> {
                         nodeStopper.stopNode();
 
                         latch.countDown();
                     },
                     "node-stopper"
-            ).start();
+            );
+            stopperThread.setUncaughtExceptionHandler(new 
LogUncaughtExceptionHandler(LOG));
+            stopperThread.start();
 
-            new Thread(
+            Thread haltOnStopTimeoutThread = new Thread(
                     () -> {
                         try {
                             if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
@@ -97,7 +104,9 @@ public class StopNodeOrHaltFailureHandler extends 
AbstractFailureHandler {
                         }
                     },
                     "jvm-halt-on-stop-timeout"
-            ).start();
+            );
+            haltOnStopTimeoutThread.setUncaughtExceptionHandler(new 
LogUncaughtExceptionHandler(LOG));
+            haltOnStopTimeoutThread.start();
         } else {
             Runtime.getRuntime().halt(KILL_EXIT_CODE);
         }
diff --git 
a/modules/network/src/main/java/org/apache/ignite/internal/network/netty/DefaultExceptionHandler.java
 
b/modules/network/src/main/java/org/apache/ignite/internal/network/netty/DefaultExceptionHandler.java
new file mode 100644
index 00000000000..e937221ab01
--- /dev/null
+++ 
b/modules/network/src/main/java/org/apache/ignite/internal/network/netty/DefaultExceptionHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.network.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+
+/**
+ * Default Netty exception handler.
+ *
+ * <p>Intended to be the last inbound handler in a channel pipeline.
+ */
+class DefaultExceptionHandler extends ChannelInboundHandlerAdapter {
+    /** Handler name. */
+    public static final String NAME = "default-exception-handler";
+
+    /** Logger. */
+    private static final IgniteLogger LOG = 
Loggers.forClass(DefaultExceptionHandler.class);
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+        LOG.warn("Unhandled exception in channel {}: {}", ctx.channel(), 
cause.getMessage(), cause);
+        ctx.close();
+    }
+}
diff --git 
a/modules/network/src/main/java/org/apache/ignite/internal/network/netty/PipelineUtils.java
 
b/modules/network/src/main/java/org/apache/ignite/internal/network/netty/PipelineUtils.java
index e8670e5c1c5..97e3d7724f6 100644
--- 
a/modules/network/src/main/java/org/apache/ignite/internal/network/netty/PipelineUtils.java
+++ 
b/modules/network/src/main/java/org/apache/ignite/internal/network/netty/PipelineUtils.java
@@ -70,6 +70,7 @@ public class PipelineUtils {
         pipeline.addLast(CHUNKED_WRITE_HANDLER_NAME, new 
ChunkedWriteHandler());
         pipeline.addLast(OutboundEncoder.NAME, new 
OutboundEncoder(messageFormat, serializationService));
         pipeline.addLast(IoExceptionSuppressingHandler.NAME, new 
IoExceptionSuppressingHandler());
+        pipeline.addLast(DefaultExceptionHandler.NAME, new 
DefaultExceptionHandler());
     }
 
     /**
diff --git 
a/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/impl/RocksDBLogStorage.java
 
b/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/impl/RocksDBLogStorage.java
index 013eb674764..188d176c8a5 100644
--- 
a/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/impl/RocksDBLogStorage.java
+++ 
b/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/impl/RocksDBLogStorage.java
@@ -30,6 +30,7 @@ import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.thread.IgniteThreadFactory;
 import org.apache.ignite.raft.jraft.conf.Configuration;
 import org.apache.ignite.raft.jraft.conf.ConfigurationEntry;
 import org.apache.ignite.raft.jraft.conf.ConfigurationManager;
@@ -149,7 +150,9 @@ public class RocksDBLogStorage implements LogStorage, 
Describer {
     private LogEntryEncoder logEntryEncoder;
     private LogEntryDecoder logEntryDecoder;
 
-    private final ExecutorService executor = 
Executors.newSingleThreadExecutor();
+    private final ExecutorService executor = Executors.newSingleThreadExecutor(
+        
IgniteThreadFactory.createWithFixedPrefix("rocks-db-log-storage-thread-", 
false, LOG)
+    );
 
     public RocksDBLogStorage(final String path, final RaftOptions raftOptions) 
{
         super();
diff --git 
a/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/logit/util/concurrent/ShutdownAbleThread.java
 
b/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/logit/util/concurrent/ShutdownAbleThread.java
index ac04aaebfca..2bce38f35c3 100644
--- 
a/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/logit/util/concurrent/ShutdownAbleThread.java
+++ 
b/modules/raft/src/main/java/org/apache/ignite/raft/jraft/storage/logit/util/concurrent/ShutdownAbleThread.java
@@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.thread.LogUncaughtExceptionHandler;
 
 public abstract class ShutdownAbleThread implements Runnable {
     private static final IgniteLogger LOG = 
Loggers.forClass(ShutdownAbleThread.class);
@@ -47,6 +48,7 @@ public abstract class ShutdownAbleThread implements Runnable {
         }
         this.stopped = false;
         this.thread = new Thread(this, getServiceName());
+        this.thread.setUncaughtExceptionHandler(new 
LogUncaughtExceptionHandler(LOG));
         this.thread.start();
     }
 
diff --git 
a/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
 
b/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
index d2b2a59f267..f8cdab0671e 100644
--- 
a/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
+++ 
b/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
@@ -61,6 +61,7 @@ import org.apache.ignite.internal.network.InternalClusterNode;
 import org.apache.ignite.internal.network.MessagingService;
 import org.apache.ignite.internal.network.NetworkMessage;
 import org.apache.ignite.internal.network.TopologyService;
+import org.apache.ignite.internal.thread.LogUncaughtExceptionHandler;
 import org.apache.ignite.internal.util.CompletableFutures;
 import org.apache.ignite.internal.vault.VaultManager;
 import org.jetbrains.annotations.Nullable;
@@ -554,6 +555,7 @@ public class SystemDisasterRecoveryManagerImpl implements 
SystemDisasterRecovery
 
             thread.setName(threadNamePrefix + thread.getId());
             thread.setDaemon(true);
+            thread.setUncaughtExceptionHandler(new 
LogUncaughtExceptionHandler(LOG));
 
             thread.start();
         }

Reply via email to