From e193d73b6b8d893cebf46d0eb2ee9d3735b690f6 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Fri, 11 Sep 2026 13:07:27 +0000
Subject: [PATCH] [#967] Keep the bookkeeping of a domain whose base entry is missing out of its configuration entry (#972)

---
 opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/LDAPReplicationDomain.java |   27 +++++++++++++++++++--------
 1 files changed, 19 insertions(+), 8 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 56ec859..8daed89 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
@@ -371,6 +371,13 @@
    * stops the session as its first act, so what they wait for is a session which is about
    * to be stopped again. The wait between the stop and the start is deliberately left
    * outside the lock, so the waiting is bounded by a connect rather than by the backoff.
+   * <p>
+   * It comes after the configuration backend's update lock and never before it: a write to
+   * the domain configuration entry holds that lock while it calls
+   * {@link #applyConfigurationChange(ReplicationDomainCfg)}, which takes this one. So
+   * nothing may write a configuration entry while holding this lock - that is why neither
+   * the state {@link #disable()} saves nor the generationId {@link #enable()} stores falls
+   * back to the domain configuration entry when the base entry of the suffix is missing.
    */
   private final Object serviceStateLock = new Object();
   /**
@@ -4267,6 +4274,17 @@
 
   /**
    * Stores the value of the generationId.
+   * <p>
+   * A base entry which is not in the backend leaves the generationId unstored until the
+   * entry appears, and is not an error - it is what a suffix waiting to be initialized by
+   * an import looks like. The generationId used to be stored on the domain configuration
+   * entry instead, and must not be again: that write reaches
+   * {@link #applyConfigurationChange(ReplicationDomainCfg)} with the configuration
+   * backend's update lock held, and so takes {@link #serviceStateLock} in the order
+   * opposite to the one {@link #disable()} takes the two in. The generationId of a suffix
+   * with no entry is a constant which {@code loadGenerationId()} computes again for free,
+   * and a value a former version left on the configuration entry is still read back.
+   *
    * @param generationId The value of the generationId.
    * @return a ResultCode indicating if the method was successful.
    */
@@ -4276,14 +4294,7 @@
     if (result != ResultCode.SUCCESS)
     {
       generationIdSavedStatus = false;
-      if (result == ResultCode.NO_SUCH_OBJECT)
-      {
-        // If the base entry does not exist, save the generation
-        // ID in the config entry
-        result = runSaveGenerationId(config.dn(), generationId);
-      }
-
-      if (result != ResultCode.SUCCESS)
+      if (result != ResultCode.NO_SUCH_OBJECT)
       {
         logger.error(ERR_UPDATING_GENERATION_ID, getBaseDN(), result.getName());
       }

--
Gitblit v1.10.0