From 6477a7e28301b6d5d9746cf4bfb29ae4b748258d Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Fri, 11 Sep 2026 12:44:36 +0000
Subject: [PATCH] [#911] Report the replication connections which used to be dropped in silence (#935)

---
 opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationBroker.java |   63 +++++++++++++++++++++++++++----
 1 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationBroker.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationBroker.java
index adf6c7e..b99c5bb 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationBroker.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationBroker.java
@@ -1161,8 +1161,18 @@
     catch (Exception e)
     {
       logger.traceException(e);
-      errorMessage = WARN_EXCEPTION_STARTING_SESSION_PHASE.get(
-          getServerId(), serverURL, getBaseDN(), stackTraceToSingleLineString(e));
+      /*
+       * The whole trace, on one line, for the server this broker is electing, which is
+       * what this message carried before it was reported for the others as well; the
+       * message alone for a server which was only contacted, whose report is not
+       * throttled. An SSLException lands here, is not paced by a connect timeout the way
+       * a refused connection is, and collectReplicationServersInfo() reruns over every
+       * URL on each reconnection: one bad certificate among several replication servers
+       * would otherwise write a full stack trace per flap cycle. The trace of those is in
+       * the trace log, which traceException() above wrote it to.
+       */
+      errorMessage = WARN_EXCEPTION_STARTING_SESSION_PHASE.get(getServerId(), serverURL, getBaseDN(),
+          keepSession ? stackTraceToSingleLineString(e) : getExceptionMessage(e));
     }
     finally
     {
@@ -1172,16 +1182,51 @@
         close(socket);
       }
 
-      if (!hasConnected && errorMessage != null && !connectionError)
+      if (!hasConnected && errorMessage != null)
       {
-        // There was no server waiting on this host:port
-        // Log a notice and will try the next replicationServer in the list
-        if (keepSession) // Log error message only for final connection
+        if (!connectionError)
         {
-          // log the error message only once to avoid overflowing the error log
-          logger.error(errorMessage);
+          /*
+           * Report the cause for every replication server contacted, and not only for the
+           * elected one: none is ever elected when none of them answers, and this is then
+           * the only place naming why -- a refused connection, a rejected certificate, a
+           * wrong port -- next to the "unable to connect to any replication servers"
+           * summary which names none of them.
+           *
+           * connectionError is what bounds the volume, and it bounds it to one line per
+           * replication server and per attempt to connect this broker makes. It is set
+           * when an attempt reaches no replication server at all, and stays set until a
+           * session is established, so the 500 ms loop which retries a total outage
+           * reports its first pass only.
+           *
+           * It is not set while this broker is connected, so the unreachable servers of a
+           * topology which still serves this broker are reported again on each
+           * reconnection -- one line each, so a reconnection costs as many lines as there
+           * are servers it could not reach, where it used to cost none. That is the volume
+           * this reporting is worth: a broker reconnects when its session is lost, not on
+           * a schedule, and a server which cannot be reached over several reconnections is
+           * a server whose configuration or certificate needs looking at.
+           *
+           * The severity says what the failure cost this broker, not what the message is
+           * named: the elected server keeps the error it was reported with, and a server
+           * which was only contacted is a warning, so that a broker which does find a
+           * server to work with does not raise an error over the one it did not need.
+           * ERR_DS_DN_DOES_NOT_MATCH is the one message which reaches the second branch
+           * under an ERR_ name -- it is set without setting hasConnected -- and it is a
+           * permanent misconfiguration rather than a transient. It still goes out as an
+           * error for the server this broker is electing, which is the one it cannot work
+           * without, and where it is only contacted the broker has another server to work
+           * with. Before this, that path logged nothing above trace either way.
+           */
+          if (keepSession)
+          {
+            logger.error(errorMessage);
+          }
+          else
+          {
+            logger.warn(errorMessage);
+          }
         }
-
         logger.trace(errorMessage);
       }
     }

--
Gitblit v1.10.0