From 624543783ec748154bbcaf59af825e0121812ec6 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Tue, 8 Sep 2026 21:55:05 +0300 Subject: [PATCH] [#924] Clear the disabled flag before a domain enables its session disable() sets the flag before it stops the session; enable() cleared it two statements after starting one. enableService() ends with startListenService(), so the listener can list a delivery and hand it to a replay thread while the flag still says the domain is going away: that thread gives the change up at the top of its replay loop, and abandonReplay() does not ask for it again - a domain on its way down owns its session. The change is left listed, uncommitted and owned by nobody, so this domain's ServerState, and every change parked behind it, is held back until something else restarts the session. The flag is now cleared before the session is started, and put back if it could not be started: a domain which has no session owns it the way a disabled one does. --- .../plugin/LDAPReplicationDomain.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java index 7fa68c0ddc..efd209c49c 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java @@ -3936,10 +3936,39 @@ public void enable() return; } - enableService(); - sessionGeneration++; - + /* + * The flag is cleared before the session is started, where disable() sets it before + * stopping one: enableService() ends with startListenService(), so the listener it + * starts can list a delivery and hand it to a replay thread while this method is + * still running. A replay thread which reads a flag that still says "disabled" + * gives the change up at the top of its replay loop, and abandonReplay() does not + * ask for it again - a domain on its way down owns its session - so the change is + * left listed, uncommitted and owned by nobody. Nothing would replay it: the + * replication server only sends it again over a session which is restarted, so this + * domain's ServerState, and every change which depends on that one, would be held + * back for as long as the session lives. + */ disabled = false; + boolean started = false; + try + { + enableService(); + sessionGeneration++; + started = true; + } + finally + { + if (!started) + { + /* + * The other half of the same invariant: a domain whose session could not be + * started owns that session the way a disabled one does, so the flag goes back + * where it was rather than leave the replay threads believing there is a session + * of theirs to restart. + */ + disabled = true; + } + } } }