From 918aed143217bf25967a2832d567588ae2c5c10c Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 22 Sep 2026 10:44:48 +0000
Subject: [PATCH] [#1038] Pin the session lock and generation of #974, and the restart a configuration change does run (#1043)

---
 opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationDomainTest.java |   79 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 79 insertions(+), 0 deletions(-)

diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationDomainTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationDomainTest.java
index f9b1b1f..37569cb 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationDomainTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/service/ReplicationDomainTest.java
@@ -238,6 +238,85 @@
   }
 
   /**
+   * The session generation is the identity of a session: a replay thread which stopped the
+   * session and let the lock go compares the generation it read then with the one it reads
+   * when it comes back, and starts the session only if the two are the same. Both halves
+   * of a restart have to move it, or a session stopped or started by something else in the
+   * meantime would look like the one that thread stopped.
+   */
+  @Test
+  public void everyStopAndEveryStartOfTheSessionIsCounted() throws Exception
+  {
+    final DN testService = DN.valueOf("o=test");
+    ReplicationServer replServer = null;
+    FakeReplicationDomain domain = null;
+    try
+    {
+      final int replServerPort = TestCaseUtils.findFreePort();
+      replServer = createReplicationServer(1, replServerPort, "ReplicationDomainTestDbGeneration", 100);
+      domain = new FakeReplicationDomain(testService, 2, newTreeSet("localhost:" + replServerPort), 1000, 1);
+
+      final long live = sessionGenerationOf(domain);
+      domain.disableService();
+      final long stopped = sessionGenerationOf(domain);
+      assertNotEquals(stopped, live, "stopping the session left its generation where it was");
+
+      domain.enableService();
+      final long started = sessionGenerationOf(domain);
+      assertNotEquals(started, stopped, "starting the session left its generation where it was");
+    }
+    finally
+    {
+      disable(domain);
+      remove(replServer);
+    }
+  }
+
+  /**
+   * What {@code restartSession()} of the LDAP domain does, minus the wait: the session is
+   * stopped, the generation is read as the claim on it, and the lock is let go. A
+   * {@link ReplicationDomain#restartService()} run by something else in the meantime - a
+   * configuration change - has to leave that claim stale, or the thread which comes back
+   * from its wait could not tell the session it stopped from the one which replaced it.
+   */
+  @Test
+  public void aClaimOnAStoppedSessionIsStaleOnceSomethingElseRestartedIt() throws Exception
+  {
+    final DN testService = DN.valueOf("o=test");
+    ReplicationServer replServer = null;
+    FakeReplicationDomain domain = null;
+    try
+    {
+      final int replServerPort = TestCaseUtils.findFreePort();
+      replServer = createReplicationServer(1, replServerPort, "ReplicationDomainTestDbStaleClaim", 100);
+      domain = new FakeReplicationDomain(testService, 2, newTreeSet("localhost:" + replServerPort), 1000, 1);
+
+      domain.disableService();
+      final long claim = sessionGenerationOf(domain);
+
+      domain.restartService();
+
+      assertNotEquals(sessionGenerationOf(domain), claim,
+          "a restart of the session by something else left the generation where it was,"
+              + " so the claim of the thread which stopped it still looks current");
+    }
+    finally
+    {
+      disable(domain);
+      remove(replServer);
+    }
+  }
+
+  /** Read under the lock, as {@link ReplicationDomain#getSessionGeneration()} asks. */
+  private static long sessionGenerationOf(ReplicationDomain domain)
+  {
+    synchronized (domain.serviceStateLock)
+    {
+      return domain.getSessionGeneration();
+    }
+  }
+
+  /**
    * Publish performance test.
    * The test loops calling the publish methods of the ReplicationDomain.
    * It should not be enabled by default as it will use a lot of time.

--
Gitblit v1.10.0