From add86d3c7c047215f886c51e2b29a4c7f1e86c0c Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 03 Sep 2026 08:21:14 +0000
Subject: [PATCH] [#877] Bound a statement of the JDBC backend by the class of the work it belongs to (#882)

---
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2ChildrenCount.java |   50 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2ChildrenCount.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2ChildrenCount.java
index 812300f..7eb800c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2ChildrenCount.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2ChildrenCount.java
@@ -57,9 +57,16 @@
     this.counter = new ShardedCounter(name);
   }
 
-  SequentialCursor<EntryID, Void> openCursor(ReadableTransaction txn)
+  /**
+   * Walks the children counts whole, which {@code verify-index} does and no client operation does:
+   * there is no overload of this method that would take the bound of an operation by accident.
+   * Reading the count of a single entry is another matter - see {@link #getCount}.
+   *
+   * @see ReadableTransaction#openBulkCursor(TreeName)
+   */
+  SequentialCursor<EntryID, Void> openBulkCursor(ReadableTransaction txn)
   {
-    return transformKeysAndValues(counter.openCursor(txn),
+    return transformKeysAndValues(counter.openBulkCursor(txn),
         TO_ENTRY_ID, CursorTransformer.<ByteString, Void> keepValuesUnchanged());
   }
 
@@ -141,7 +148,23 @@
    */
   long getCount(ReadableTransaction txn, EntryID entryID)
   {
-    return counter.getCount(txn, toKey(entryID));
+    return getCount(txn, entryID, false);
+  }
+
+  /**
+   * Get the number of children for the given entry, as part of a walk of a whole tree rather than
+   * of a client operation. {@code verify-index} reads one of these per DN while it walks dn2id
+   * whole, and no client is waiting on any of them.
+   *
+   * @param txn storage transaction
+   * @param entryID The entryID identifying to the counter
+   * @param partOfAWholeTreeWalk whether this read belongs to a walk of a whole tree
+   * @return Value of the counter. 0 if no counter is associated yet.
+   * @see ReadableTransaction#openBulkCursor(TreeName)
+   */
+  long getCount(ReadableTransaction txn, EntryID entryID, boolean partOfAWholeTreeWalk)
+  {
+    return counter.getCount(txn, toKey(entryID), partOfAWholeTreeWalk);
   }
 
   /**
@@ -151,7 +174,26 @@
    */
   long getTotalCount(ReadableTransaction txn)
   {
-    return getCount(txn, TOTAL_COUNT_ENTRY_ID);
+    return getTotalCount(txn, false);
+  }
+
+  /**
+   * The same total, told which kind of work it is part of. It is a read of this tree like any
+   * other - a cursor positioned on one key, which on a storage engine that walks a table rather
+   * than an index is a scan of it - so what it may take follows who is waiting on it:
+   * {@code verify-index} reads it once to size the progress report of a walk of the whole backend,
+   * with nobody waiting, while {@code cn=monitor} and the searches of {@code GroupManager} and
+   * {@code SubentryManager} read the same total for a client.
+   *
+   * @param txn storage transaction
+   * @param partOfAWholeTreeWalk whether this read belongs to a walk of a whole tree rather than to
+   *          a client operation
+   * @return Sum of all the counter contained in this tree
+   * @see ReadableTransaction#openBulkCursor(TreeName)
+   */
+  long getTotalCount(ReadableTransaction txn, boolean partOfAWholeTreeWalk)
+  {
+    return getCount(txn, TOTAL_COUNT_ENTRY_ID, partOfAWholeTreeWalk);
   }
 
   /**

--
Gitblit v1.10.0