From 92d88ca699cd8090a26b92cbe46789d2b848195f Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Mon, 07 Sep 2026 09:30:36 +0000
Subject: [PATCH] [#889] Keep a change the replay could not apply out of the ServerState (#892)

---
 opendj-server-legacy/src/test/java/org/opends/server/plugins/ShortCircuitPlugin.java |   68 +++++++++++++++++++++++++++++++--
 1 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ShortCircuitPlugin.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ShortCircuitPlugin.java
index 2a04473..751daac 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ShortCircuitPlugin.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ShortCircuitPlugin.java
@@ -13,6 +13,7 @@
  *
  * Copyright 2006-2008 Sun Microsystems, Inc.
  * Portions Copyright 2014-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
  */
 package org.opends.server.plugins;
 
@@ -25,6 +26,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.config.server.ConfigException;
@@ -614,11 +616,18 @@
     }
 
     // Check for registered short circuits.
-    Integer resultCode = shortCircuits.get(
-         operation.getOperationType() + "/" + section.toLowerCase());
+    final String key = operation.getOperationType() + "/" + section.toLowerCase();
+    Integer resultCode = shortCircuits.get(key);
     if (resultCode != null)
     {
-      return resultCode;
+      final int reached = shortCircuitCounts.computeIfAbsent(key, k -> new AtomicInteger()).incrementAndGet();
+      final Integer maxTimes = shortCircuitLimits.get(key);
+      if (maxTimes == null || reached <= maxTimes)
+      {
+        return resultCode;
+      }
+      // The short circuit was applied as many times as it was asked for: from now on the
+      // operations are let through, which is how a transient failure is simulated.
     }
 
     // If we've gotten here, then we shouldn't short-circuit the operation
@@ -662,6 +671,28 @@
   /** Registered short circuits for operations regardless of controls. */
   private static Map<String, Integer> shortCircuits = new ConcurrentHashMap<>();
 
+  /** How many times a registered short circuit was reached. */
+  private static final Map<String, AtomicInteger> shortCircuitCounts = new ConcurrentHashMap<>();
+
+  /** How many times a registered short circuit must be applied, when it is limited. */
+  private static final Map<String, Integer> shortCircuitLimits = new ConcurrentHashMap<>();
+
+  /**
+   * Returns how many times the short circuit registered for the given operation type and
+   * plugin point was reached. A short circuit registered for a limited number of times is
+   * counted as reached by the operations it let through once that number was used up.
+   *
+   * @param operation The type of operation the short circuit applies to.
+   * @param section The plugin point the short circuit applies to.
+   * @return the number of operations which reached the short circuit
+   */
+  public static int getShortCircuitCount(OperationType operation, String section)
+  {
+    final AtomicInteger count = shortCircuitCounts.get(operation + "/" + section.toLowerCase());
+    return count != null ? count.get() : 0;
+  }
+
+
   /**
    * Register a short circuit for the given operation type and plugin point.
    * @param operation The type of operation the short circuit applies to.
@@ -670,7 +701,29 @@
    */
   public static void registerShortCircuit(OperationType operation, String section, int resultCode)
   {
-    shortCircuits.put(operation + "/" + section.toLowerCase(), resultCode);
+    final String key = operation + "/" + section.toLowerCase();
+    // This registration applies to every operation, and it counts from zero: a limit or
+    // a count left behind by a previous registration is not part of it.
+    shortCircuitCounts.remove(key);
+    shortCircuitLimits.remove(key);
+    shortCircuits.put(key, resultCode);
+  }
+
+  /**
+   * Register a short circuit which only applies to the given number of operations, the
+   * ones which follow being let through: this is how a transient failure is simulated.
+   *
+   * @param operation The type of operation the short circuit applies to.
+   * @param section The plugin point the short circuit applies to.
+   * @param resultCode The result code to be returned for the short circuit.
+   * @param maxTimes How many operations must be short circuited.
+   */
+  public static void registerShortCircuit(OperationType operation, String section, int resultCode, int maxTimes)
+  {
+    final String key = operation + "/" + section.toLowerCase();
+    shortCircuitCounts.remove(key);
+    shortCircuitLimits.put(key, maxTimes);
+    shortCircuits.put(key, resultCode);
   }
 
   /**
@@ -680,6 +733,11 @@
    */
   public static void deregisterShortCircuit(OperationType operation, String section)
   {
-    shortCircuits.remove(operation + "/" + section.toLowerCase());
+    final String key = operation + "/" + section.toLowerCase();
+    shortCircuits.remove(key);
+    shortCircuitLimits.remove(key);
+    // The count belongs to the registration which is being removed: a test which counts
+    // the operations it short circuits must not inherit the count of the previous one.
+    shortCircuitCounts.remove(key);
   }
 }

--
Gitblit v1.10.0