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();
}