From cc3bbed3d80821627d47b19d46fc4f906b61866e Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 10 Jul 2026 11:25:39 +0300 Subject: [PATCH 1/2] Fix intermittent GenerationIdTest.testMultiRS by re-advertising genId on change The generationId gossip between replication servers is event-driven with no periodic re-advertisement: changeGenerationId() updated the field silently and relied on a single topology broadcast from the surrounding connect/status event. A peer RS that missed or raced that broadcast stayed stuck on a stale generationId (-1), which is the intermittent testMultiRS failure (expected:<48> but was:<-1>). Re-advertise the topology (sendTopoInfoToAll) on every real generationId transition so the gossip is self-healing and every connected peer converges. --- .../replication/server/ReplicationServerDomain.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java index 6463087ef9..ca7140d69f 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyrighted 2026 3A Systems, LLC. */ package org.opends.server.replication.server; @@ -1722,6 +1723,17 @@ public long changeGenerationId(long generationId) this.generationId = generationId; this.generationIdSavedStatus = false; + + // The generationId gossip between replication servers is purely + // event-driven: it is carried in the topology messages sent on + // connect/disconnect/status events, and there is no periodic + // re-advertisement. A peer RS that misses (or races) the single + // topology broadcast following a generationId change would otherwise + // stay stuck with a stale generationId (typically -1) indefinitely - + // the intermittent GenerationIdTest.testMultiRS failure. Re-advertising + // the topology on every real generationId transition makes the gossip + // self-healing so every connected peer converges on the new value. + sendTopoInfoToAll(); } return oldGenerationId; } From 17d2ae17490a8772db0476f399794250a17b8093 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 10 Jul 2026 18:05:24 +0300 Subject: [PATCH 2/2] Apply review suggestion: shorter comment, re-advertise only valid generationIds Transitions to -1 need no topology re-advertisement: mayResetGenerationId() is a local cleanup each RS performs independently, and the ResetGenerationIdMsg path already forwards the reset to all connected RSs. --- .../server/ReplicationServerDomain.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java index ca7140d69f..ebe734624f 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServerDomain.java @@ -1724,16 +1724,13 @@ public long changeGenerationId(long generationId) this.generationId = generationId; this.generationIdSavedStatus = false; - // The generationId gossip between replication servers is purely - // event-driven: it is carried in the topology messages sent on - // connect/disconnect/status events, and there is no periodic - // re-advertisement. A peer RS that misses (or races) the single - // topology broadcast following a generationId change would otherwise - // stay stuck with a stale generationId (typically -1) indefinitely - - // the intermittent GenerationIdTest.testMultiRS failure. Re-advertising - // the topology on every real generationId transition makes the gossip - // self-healing so every connected peer converges on the new value. - sendTopoInfoToAll(); + // generationId gossip is purely event-driven: it only travels in the + // topology messages sent on connect/disconnect/status events. Re-advertise + // on every real transition so a peer that missed one converges on the next. + if (generationId > 0) + { + sendTopoInfoToAll(); + } } return oldGenerationId; }