From 129fc4e34608d8212e62435f07283132e76ee1d6 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 16 Sep 2026 11:32:23 +0000
Subject: [PATCH] [#983] Interrupt the listen thread after the ReplicaOfflineMsgs are forwarded (#987)

---
 opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java |   26 +++++++++++++++++++++-----
 1 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java
index c97a647..05232fe 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java
@@ -1660,12 +1660,9 @@
       connectThread.interrupt();
     }
 
-    // shutdown the listener thread
+    // Stop accepting connections. Closing the socket is what ends the accept() of the listen
+    // thread, and the loop of that thread already stops on its closed socket.
     close(listenSocket);
-    if (listenThread != null)
-    {
-      listenThread.interrupt();
-    }
 
     /*
      * Let the ReplicaOfflineMsgs a collocated DS sent be forwarded while every handler is still
@@ -1680,6 +1677,25 @@
      */
     awaitReplicaOfflineMsgsForwarded();
 
+    /*
+     * Only now interrupt the listen thread: the handshake of an incoming connection runs in it,
+     * and an interrupt sent before the wait tears down a peer replication server whose handshake
+     * is in flight - one of the very servers the message has to be forwarded to. Whether that
+     * peer is already registered in the domain and waiting on the startup of its session, or
+     * still owes its TopologyMsg - a receive no interrupt breaks, so the flag survives until the
+     * handshake has registered it and reaches the startup of its session - the abort unregisters
+     * it, and it is never told that the replica went offline.
+     * <p>
+     * The interrupt still precedes the shutdown of the domains, so a handshake which has not
+     * finished by then is still aborted before its reader and its writer are started. Without it
+     * such a handshake would register its peer after the domains were stopped and serve it: a
+     * writer parked on a cursor over the closed changelog, a heartbeat to a server which is gone.
+     */
+    if (listenThread != null)
+    {
+      listenThread.interrupt();
+    }
+
     for (ReplicationServerDomain domain : getReplicationServerDomains())
     {
       domain.shutdown();

--
Gitblit v1.10.0