From 03f5d1bc3610d8f891bdf407ec3fbfbc2f16d524 Mon Sep 17 00:00:00 2001
From: Maxim Thomas <maxim.thomas@gmail.com>
Date: Tue, 03 Feb 2026 14:05:47 +0000
Subject: [PATCH] Fix three and more nodes replication process stuck error (#584)

---
 opendj-packages/opendj-docker/Dockerfile-alpine                                                 |   10 +++-
 .github/workflows/build.yml                                                                     |   86 ++++++++++++++++++++++++++++++++++++++----
 opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationDomain.java |   10 ----
 opendj-packages/opendj-docker/Dockerfile                                                        |   10 +++-
 4 files changed, 92 insertions(+), 24 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8e3e02f..c6492b9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -160,6 +160,61 @@
         opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000
         opendj-server-legacy/target/package/opendj/bin/stop-ds
         rm -rf opendj-server-legacy/target/package/opendj/{config,db,changelogDb,logs,tmp}
+    - name: Test replication
+      if: runner.os != 'Windows'
+      run: |
+        cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj1
+        cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj2
+        cp -r ./opendj-server-legacy/target/package/opendj ./opendj-server-legacy/target/package/opendj3
+        
+        echo "Setup OpenDJ-1"
+        
+        opendj-server-legacy/target/package/opendj1/setup -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS \
+        --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
+        --sampleData 100000 --cli --acceptLicense --no-prompt
+        
+        echo "Setup OpenDJ-2 with replication"
+        
+        opendj-server-legacy/target/package/opendj2/setup -h localhost -p 2389 --ldapsPort 2636 --adminConnectorPort 24444 --enableStartTLS \
+        --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
+        --addBaseEntry --cli --acceptLicense --no-prompt
+        
+        opendj-server-legacy/target/package/opendj2/bin/dsreplication enable --no-prompt --host1 localhost --port1 4444 --bindDN1 "cn=Directory Manager"  --bindPassword1 password --replicationPort1 8989 \
+        --host2 localhost --port2 24444 --bindDN2 "cn=Directory Manager" --bindPassword2 password --replicationPort2 28989 \
+        --adminUID admin --adminPassword password --baseDN dc=example,dc=com --trustAll --noPropertiesFile
+        
+        opendj-server-legacy/target/package/opendj2/bin/dsreplication initialize --baseDN dc=example,dc=com --adminUID admin --adminPassword password --hostSource localhost \
+        --portSource 4444 --hostDestination localhost --portDestination 24444 -X -n
+        
+        opendj-server-legacy/target/package/opendj2/bin/ldapsearch --port 2636 --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll \
+        --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn  | grep ^dn: | wc -l | grep -q 100000
+        
+        echo "Setup OpenDJ-3 with replication"
+        
+        opendj-server-legacy/target/package/opendj3/setup -h localhost -p 3389 --ldapsPort 3636 --adminConnectorPort 34444 --enableStartTLS \
+        --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com \
+        --addBaseEntry --cli --acceptLicense --no-prompt
+        
+        opendj-server-legacy/target/package/opendj3/bin/dsreplication enable --no-prompt --host1 localhost --port1 24444 --bindDN1 "cn=Directory Manager"  --bindPassword1 password --replicationPort1 28989 \
+        --host2 localhost --port2 34444 --bindDN2 "cn=Directory Manager" --bindPassword2 password --replicationPort2 38989 \
+        --adminUID admin --adminPassword password --baseDN dc=example,dc=com --trustAll --noPropertiesFile
+        
+        opendj-server-legacy/target/package/opendj3/bin/dsreplication initialize --baseDN dc=example,dc=com --adminUID admin --adminPassword password --hostSource localhost \
+        --portSource 24444 --hostDestination localhost --portDestination 34444 -X -n
+        
+        opendj-server-legacy/target/package/opendj2/bin/ldapsearch --port 3636 --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll \
+        --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn  | grep ^dn: | wc -l | grep -q 100000
+        
+        echo "Stopping and removing OpenDJ instances"
+        
+        opendj-server-legacy/target/package/opendj1/bin/stop-ds
+        opendj-server-legacy/target/package/opendj2/bin/stop-ds
+        opendj-server-legacy/target/package/opendj3/bin/stop-ds
+        
+        rm -rf ./opendj-server-legacy/target/package/opendj1
+        rm -rf ./opendj-server-legacy/target/package/opendj2
+        rm -rf ./opendj-server-legacy/target/package/opendj3
+
     - name: Test on Windows
       if: runner.os == 'Windows'
       run:   |
@@ -196,6 +251,7 @@
             opendj-dsml-servlet/target/*.war
             opendj-rest2ldap-servlet/target/*.war
   build-docker:
+    needs: build-maven
     runs-on: 'ubuntu-latest'
     services:
       registry:
@@ -203,10 +259,10 @@
         ports:
           - 5000:5000
     steps:
-      - uses: actions/checkout@v4
+      - name: Download artifacts
+        uses: actions/download-artifact@v4
         with:
-          fetch-depth: 0
-          submodules: recursive
+          name: ubuntu-latest-11
       - name: Get latest release version
         shell: bash
         run:   |
@@ -226,9 +282,14 @@
         uses: docker/setup-buildx-action@v3
         with:
           driver-opts: network=host
+      - name: Prepare Dockerfile
+        shell: bash
+        run: |
+          unzip -d ./opendj-packages/opendj-docker ./opendj-packages/opendj-docker/target/Dockerfile.zip 
+          cp ./opendj-server-legacy/target/package/opendj-*.zip ./opendj-packages/opendj-docker
+          sed -i -E '/^#COPY opendj/s/^#//' ./opendj-packages/opendj-docker/Dockerfile
       - name: Build image (default)
         uses: docker/build-push-action@v5
-        continue-on-error: true
         with:
           context: ./opendj-packages/opendj-docker
           file: ./opendj-packages/opendj-docker/Dockerfile
@@ -251,8 +312,10 @@
           docker exec test 'sh' '-c' '/opt/opendj/bin/start-ds'
           docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll'
           docker exec test 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000'
+          docker kill test
 
   build-docker-alpine:
+    needs: build-maven
     runs-on: 'ubuntu-latest'
     services:
       registry:
@@ -260,10 +323,10 @@
         ports:
           - 5000:5000
     steps:
-      - uses: actions/checkout@v4
+      - name: Download artifacts
+        uses: actions/download-artifact@v4
         with:
-          fetch-depth: 0
-          submodules: recursive
+          name: ubuntu-latest-11
       - name: Get latest release version
         shell: bash
         run:   |
@@ -284,8 +347,13 @@
         uses: docker/setup-buildx-action@v3
         with:
           driver-opts: network=host
+      - name: Prepare Dockerfile
+        shell: bash
+        run: |
+          unzip -d ./opendj-packages/opendj-docker ./opendj-packages/opendj-docker/target/Dockerfile.zip 
+          cp ./opendj-server-legacy/target/package/opendj-*.zip ./opendj-packages/opendj-docker
+          sed -i -E '/^#COPY opendj/s/^#//' ./opendj-packages/opendj-docker/Dockerfile-alpine
       - name: Build image
-        continue-on-error: true
         uses: docker/build-push-action@v5
         with:
           context: ./opendj-packages/opendj-docker
@@ -309,4 +377,4 @@
           docker exec test 'sh' '-c' '/opt/opendj/bin/start-ds'
           docker exec test 'sh' '-c' '/opt/opendj/bin/rebuild-index --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll'
           docker exec test 'sh' '-c' '/opt/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 10000'
-
+          docker kill test
diff --git a/opendj-packages/opendj-docker/Dockerfile b/opendj-packages/opendj-docker/Dockerfile
index bd3fcdb..643769c 100644
--- a/opendj-packages/opendj-docker/Dockerfile
+++ b/opendj-packages/opendj-docker/Dockerfile
@@ -19,18 +19,22 @@
 ENV BACKEND_DB_DIRECTORY="db"
 #ENV SETUP_ARGS
 
+ARG OPENDJ_DIST_FILENAME=opendj.zip
+
 ARG VERSION
 
 WORKDIR /opt
 
+#COPY opendj-*.zip $OPENDJ_DIST_FILENAME
+
 RUN  apt-get update \
  && apt-get install -y --no-install-recommends curl unzip \
  && if [ -z "$VERSION" ] ; then VERSION="$(curl -i -o - --silent https://api.github.com/repos/OpenIdentityPlatform/OpenDJ/releases/latest | grep -m1 "\"name\"" | cut -d\" -f4)"; fi \
- && curl -L https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/$VERSION/opendj-$VERSION.zip --output opendj-$VERSION.zip \
- && unzip opendj-$VERSION.zip \
+ && if [ ! -f "$OPENDJ_DIST_FILENAME" ]; then echo file exists && curl -L https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/$VERSION/opendj-$VERSION.zip --output $OPENDJ_DIST_FILENAME; fi \
+ && unzip $OPENDJ_DIST_FILENAME \
  && apt-get remove -y --purge curl unzip \
  && rm -rf /var/lib/apt/lists/* \
- && rm -r opendj-*.zip \
+ && rm -r $OPENDJ_DIST_FILENAME \
  && groupadd $OPENDJ_USER \
  && useradd -m -r -u 1001 -g $OPENDJ_USER $OPENDJ_USER \
  && install -d -o $OPENDJ_USER /opt/opendj/data \
diff --git a/opendj-packages/opendj-docker/Dockerfile-alpine b/opendj-packages/opendj-docker/Dockerfile-alpine
index a6b191a..1a3ece3 100644
--- a/opendj-packages/opendj-docker/Dockerfile-alpine
+++ b/opendj-packages/opendj-docker/Dockerfile-alpine
@@ -19,20 +19,24 @@
 ENV BACKEND_DB_DIRECTORY="db"
 #ENV SETUP_ARGS
 
+ARG OPENDJ_DIST_FILENAME=opendj.zip
+
 ARG VERSION
 
 WORKDIR /opt
 
+#COPY opendj-*.zip $OPENDJ_DIST_FILENAME
+
 RUN apk add --update --no-cache --virtual builddeps curl unzip \
  && apk upgrade --update --no-cache \
  && apk add bash openjdk11 \
  && if [ -z "$VERSION" ] ; then VERSION="$(curl -i -o - --silent https://api.github.com/repos/OpenIdentityPlatform/OpenDJ/releases/latest | grep -m1 "\"name\"" | cut -d\" -f4)"; fi \
- && curl -L https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/$VERSION/opendj-$VERSION.zip --output opendj-$VERSION.zip \
- && unzip opendj-$VERSION.zip \
+ && if [ ! -f "$OPENDJ_DIST_FILENAME" ]; then echo file exists && curl -L https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/$VERSION/opendj-$VERSION.zip --output $OPENDJ_DIST_FILENAME; fi \
+ && unzip $OPENDJ_DIST_FILENAME \
  && apk del builddeps \
  && apk del curl \
  && apk del unzip \
- && rm -r opendj-$VERSION.zip \
+ && rm -r $OPENDJ_DIST_FILENAME \
  && addgroup -S $OPENDJ_USER \
  && adduser -S -u 1001 -G $OPENDJ_USER $OPENDJ_USER \
  && install -d -o $OPENDJ_USER /opt/opendj/data \
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationDomain.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationDomain.java
index 9c5bed7..8781e70 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationDomain.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/service/ReplicationDomain.java
@@ -13,7 +13,7 @@
  *
  * Copyright 2008-2010 Sun Microsystems, Inc.
  * Portions Copyright 2011-2016 ForgeRock AS.
- * Portions Copyright 2025 3A Systems LLC.
+ * Portions Copyright 2025-2026 3A Systems LLC.
  */
 package org.opends.server.replication.service;
 
@@ -42,7 +42,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -3031,13 +3030,6 @@
       {
         broker.stop();
       }
-      try {
-        exportThreadPool.shutdown();
-        boolean timedOut = exportThreadPool.awaitTermination(100, TimeUnit.SECONDS);
-        logger.info(LocalizableMessage.raw("export pool termination timed out: " + timedOut));
-      } catch (InterruptedException e) {
-        // Give up waiting.
-      }
 
       // Stop the listener thread
       if (listenerThread != null)

--
Gitblit v1.10.0