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

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


The following commit(s) were added to refs/heads/master by this push:
     new 70336088d RATIS-1946. PeerProxyMap: Stack trace may not be printed. 
(#976)
70336088d is described below

commit 70336088d3a5bb488b6b91330ebda20ff1448e90
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Fri Dec 1 09:23:02 2023 -0800

    RATIS-1946. PeerProxyMap: Stack trace may not be printed. (#976)
---
 .../java/org/apache/ratis/util/PeerProxyMap.java   | 28 ++++++++++---
 .../java/org/apache/ratis/util/StringUtils.java    | 17 ++++++++
 .../src/test/java/org/apache/ratis/BaseTest.java   |  5 ++-
 .../apache/ratis/server/impl/LeaderElection.java   |  6 +--
 .../ratis/server/leader/LogAppenderDefault.java    |  4 +-
 .../server/storage/RaftStorageDirectoryImpl.java   | 12 ++----
 .../server/simulation/SimulatedServerRpc.java      |  3 +-
 .../org/apache/ratis/util/TestPeerProxyMap.java    | 49 ++++++++++++++++++++++
 8 files changed, 101 insertions(+), 23 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/util/PeerProxyMap.java 
b/ratis-common/src/main/java/org/apache/ratis/util/PeerProxyMap.java
index c5a08f56a..105ecbfb4 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/PeerProxyMap.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/PeerProxyMap.java
@@ -27,7 +27,11 @@ import org.slf4j.LoggerFactory;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -79,7 +83,7 @@ public class PeerProxyMap<PROXY extends Closeable> implements 
RaftPeer.Add, Clos
 
     @Override
     public String toString() {
-      return peer.toString();
+      return peer.getId().toString();
     }
   }
 
@@ -142,7 +146,8 @@ public class PeerProxyMap<PROXY extends Closeable> 
implements RaftPeer.Add, Clos
       }
     }
     // close proxy without holding the reset lock
-    optional.ifPresent(proxy -> closeProxy(proxy, pp));
+    optional.map(proxy -> closeProxy(proxy, pp))
+        .ifPresent(e -> LOG.warn("Failed to close the previous proxy", e));
   }
 
   /** @return true if the given throwable is handled; otherwise, the call is a 
no-op, return false. */
@@ -156,19 +161,30 @@ public class PeerProxyMap<PROXY extends Closeable> 
implements RaftPeer.Add, Clos
 
   @Override
   public void close() {
+    final List<IOException> exceptions = Collections.synchronizedList(new 
ArrayList<>());
     ConcurrentUtils.parallelForEachAsync(peers.values(),
-        pp -> pp.setNullProxyAndClose().ifPresent(proxy -> closeProxy(proxy, 
pp)),
+        pp -> pp.setNullProxyAndClose().map(proxy -> closeProxy(proxy, 
pp)).ifPresent(exceptions::add),
         r -> new Thread(r).start()
     ).join();
+
+    final int size = exceptions.size();
+    if (size > 0) {
+      final Iterator<IOException> i = exceptions.iterator();
+      final IOException e = new IOException(name + ": Failed to close proxy 
map", i.next());
+      for(; i.hasNext(); ) {
+        e.addSuppressed(i.next());
+      }
+      LOG.warn("{}: {} exception(s)", name, size, e);
+    }
   }
 
-  private void closeProxy(PROXY proxy, PeerAndProxy pp) {
+  private IOException closeProxy(PROXY proxy, PeerAndProxy pp) {
     try {
       LOG.debug("{}: Closing proxy for peer {}", name, pp);
       proxy.close();
+      return null;
     } catch (IOException e) {
-      LOG.warn("{}: Failed to close proxy for peer {}, proxy class: {}",
-          name, pp, proxy.getClass(), e);
+      return new IOException(name + ": Failed to close proxy for peer " + pp + 
", " + proxy.getClass(), e);
     }
   }
 }
\ No newline at end of file
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/StringUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/util/StringUtils.java
index e1dae790c..0f3266e65 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/StringUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/StringUtils.java
@@ -27,10 +27,12 @@ import java.io.StringWriter;
 import java.nio.ByteBuffer;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 public final class StringUtils {
@@ -176,4 +178,19 @@ public final class StringUtils {
       return "" + future.join();
     }
   }
+
+  public static <T> String array2String(T[] array, Function<T, String> 
toStringMethod) {
+    if (array == null) {
+      return null;
+    } else if (array.length == 0) {
+      return "[]";
+    }
+
+    final StringBuilder b = new StringBuilder(128).append('[');
+    Arrays.stream(array).map(toStringMethod).forEach(s -> 
b.append(s).append(", "));
+    final int length = b.length();
+    b.setCharAt(length - 2, ']');
+    b.setLength(length - 1);
+    return b.toString();
+  }
 }
diff --git a/ratis-common/src/test/java/org/apache/ratis/BaseTest.java 
b/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
index b05387b7b..821369461 100644
--- a/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
+++ b/ratis-common/src/test/java/org/apache/ratis/BaseTest.java
@@ -23,6 +23,7 @@ import org.apache.ratis.util.ExitUtils;
 import org.apache.ratis.util.FileUtils;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.Slf4jUtils;
+import org.apache.ratis.util.StringUtils;
 import org.apache.ratis.util.TimeDuration;
 import org.apache.ratis.util.function.CheckedRunnable;
 import org.junit.After;
@@ -130,7 +131,9 @@ public abstract class BaseTest {
       Class<? extends Throwable> expectedThrowableClass, Logger log,
       Class<? extends Throwable>... expectedCauseClasses) {
     if (log != null) {
-      log.info("The test \"{}\" throws {}", description,  
JavaUtils.getClassSimpleName(t.getClass()), t);
+      log.info("Expected the test \"{}\" to throw {} with cause(s) {}",
+          description, expectedThrowableClass.getSimpleName(),
+          StringUtils.array2String(expectedCauseClasses, 
Class::getSimpleName));
     }
     Assert.assertEquals(expectedThrowableClass, t.getClass());
 
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/LeaderElection.java 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/LeaderElection.java
index 43b778057..53e83d866 100644
--- 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/LeaderElection.java
+++ 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/LeaderElection.java
@@ -254,12 +254,10 @@ class LeaderElection implements Runnable {
       }
       final LifeCycle.State state = lifeCycle.getCurrentState();
       if (state.isClosingOrClosed()) {
-        LOG.info("{}: {} is safely ignored since this is already {}",
-            this, JavaUtils.getClassSimpleName(e.getClass()), state, e);
+        LOG.info(this + ": since this is already " + state + ", safely ignore 
" + e);
       } else {
         if (!server.getInfo().isAlive()) {
-          LOG.info("{}: {} is safely ignored since the server is not alive: 
{}",
-              this, JavaUtils.getClassSimpleName(e.getClass()), server, e);
+          LOG.info(this + ": since the server is not alive, safely ignore " + 
e);
         } else {
           LOG.error("{}: Failed, state={}", this, state, e);
         }
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/leader/LogAppenderDefault.java
 
b/ratis-server/src/main/java/org/apache/ratis/server/leader/LogAppenderDefault.java
index 6f38f5009..21ef70d4d 100644
--- 
a/ratis-server/src/main/java/org/apache/ratis/server/leader/LogAppenderDefault.java
+++ 
b/ratis-server/src/main/java/org/apache/ratis/server/leader/LogAppenderDefault.java
@@ -87,7 +87,7 @@ class LogAppenderDefault extends LogAppenderBase {
       } catch (IOException ioe) {
         // TODO should have more detailed retry policy here.
         if (retry++ % 10 == 0) { // to reduce the number of messages
-          LOG.warn("{}: Failed to appendEntries (retry={}): {}", this, 
retry++, ioe);
+          LOG.warn("{}: Failed to appendEntries (retry={})", this, retry, ioe);
         }
         handleException(ioe);
       }
@@ -114,7 +114,7 @@ class LogAppenderDefault extends LogAppenderBase {
     } catch (InterruptedIOException iioe) {
       throw iioe;
     } catch (Exception ioe) {
-      LOG.warn("{}: Failed to installSnapshot {}: {}", this, snapshot, ioe);
+      LOG.warn("{}: Failed to installSnapshot {}", this, snapshot, ioe);
       handleException(ioe);
       return null;
     }
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftStorageDirectoryImpl.java
 
b/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftStorageDirectoryImpl.java
index 8cf7dd326..e7f69d1e2 100644
--- 
a/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftStorageDirectoryImpl.java
+++ 
b/ratis-server/src/main/java/org/apache/ratis/server/storage/RaftStorageDirectoryImpl.java
@@ -157,10 +157,10 @@ class RaftStorageDirectoryImpl implements 
RaftStorageDirectory {
     }
 
     // check enough space
-    if (!hasEnoughSpace()) {
-      LOG.warn("There are not enough space left for directory {}"
-          + " free space min required: {} free space actual: {}",
-          rootPath, freeSpaceMin, root.getFreeSpace());
+    final long freeSpace = root.getFreeSpace();
+    if (freeSpace < freeSpaceMin.getSize()) {
+      LOG.warn("{} in directory {}: free space = {} < required = {}",
+          StorageState.NO_SPACE, rootPath, freeSpace, freeSpaceMin);
       return StorageState.NO_SPACE;
     }
 
@@ -177,10 +177,6 @@ class RaftStorageDirectoryImpl implements 
RaftStorageDirectory {
     return getMetaFile().exists();
   }
 
-  private boolean hasEnoughSpace() {
-    return root.getFreeSpace() >= freeSpaceMin.getSize();
-  }
-
   /**
    * Lock storage to provide exclusive access.
    *
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
index 863432d9c..85e2620a5 100644
--- 
a/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/simulation/SimulatedServerRpc.java
@@ -211,8 +211,7 @@ class SimulatedServerRpc implements RaftServerRpc {
               : 
IOUtils.asIOException(JavaUtils.unwrapCompletionException(exception));
           clientHandler.getRpc().sendReply(request, reply, e);
         } catch (IOException e) {
-          LOG.warn("Failed to send reply {} for request {} due to exception 
{}",
-              reply, request, e);
+          LOG.warn("Failed to send reply {} for request {}", reply, request, 
e);
         }
       }, executor);
       return null;
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/util/TestPeerProxyMap.java 
b/ratis-test/src/test/java/org/apache/ratis/util/TestPeerProxyMap.java
index 62ec35400..549fbc53f 100644
--- a/ratis-test/src/test/java/org/apache/ratis/util/TestPeerProxyMap.java
+++ b/ratis-test/src/test/java/org/apache/ratis/util/TestPeerProxyMap.java
@@ -24,6 +24,9 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import java.io.Closeable;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.SocketAddress;
 
 /** Tests for {@link PeerProxyMap}. */
 public class TestPeerProxyMap extends BaseTest {
@@ -77,4 +80,50 @@ public class TestPeerProxyMap extends BaseTest {
     map.resetProxy(id); // hold resetLock and then call close() with requires 
proxy lock
     t.join();
   }
+
+  /** Copied from {@link 
org.apache.ratis.thirdparty.io.netty.channel.AbstractChannel}. */
+  private static final class AnnotatedConnectException extends 
ConnectException {
+    private static final long serialVersionUID = 3901958112696433556L;
+
+    AnnotatedConnectException(ConnectException exception, SocketAddress 
remoteAddress) {
+      super(exception.getMessage() + ": " + remoteAddress);
+      initCause(exception);
+    }
+
+    // Suppress a warning since this method doesn't need synchronization
+    @Override
+    public Throwable fillInStackTrace() {
+      return this;
+    }
+  }
+
+  private static class ExceptionProxy implements Closeable {
+    private final RaftPeer peer;
+
+    ExceptionProxy(RaftPeer peer) {
+      this.peer = peer;
+    }
+
+    @Override
+    public void close() throws IOException {
+      throw new AnnotatedConnectException(new ConnectException("Failed to 
connect to " + peer.getId()), null);
+    }
+
+    @Override
+    public String toString() {
+      return peer.getId().toString();
+    }
+  }
+
+  @Test(timeout = 1000)
+  public void testStackTrace() {
+    final RaftPeerId id = RaftPeerId.valueOf("s0");
+    final RaftPeer peer = RaftPeer.newBuilder().setId(id).build();
+    try(final PeerProxyMap<ExceptionProxy> map = new PeerProxyMap<>("test", 
ExceptionProxy::new);
+        final ExceptionProxy ignored = map.computeIfAbsent(peer).get()) {
+    } catch (IOException e) {
+      assertThrowable("closeProxy", e, AnnotatedConnectException.class, LOG, 
ConnectException.class);
+      Assert.assertEquals(0, e.getStackTrace().length);
+    }
+  }
 }

Reply via email to