mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
14 hours ago add86d3c7c047215f886c51e2b29a4c7f1e86c0c
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ShardedCounter.java
@@ -12,6 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2015 ForgeRock AS.
 * Portions Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.backends.pluggable;
@@ -72,16 +73,30 @@
    super(name);
  }
  SequentialCursor<ByteString, Void> openCursor(ReadableTransaction txn)
  /**
   * Walks this counter 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 a
   * single counter is another matter - {@link #getCount} opens a cursor of its own, and one of a
   * client operation unless the caller says otherwise.
   *
   * @see ReadableTransaction#openBulkCursor(TreeName)
   */
  SequentialCursor<ByteString, Void> openBulkCursor(ReadableTransaction txn)
  {
    return uniqueKeys(txn.openBulkCursor(getName()));
  }
  private SequentialCursor<ByteString, Void> uniqueKeys(Cursor<ByteString, ByteString> cursor)
  {
    return new UniqueKeysCursor<>(transformKeysAndValues(
        txn.openCursor(getName()), TO_KEY,
        cursor, TO_KEY,
        CursorTransformer.<ByteString, ByteString, Void> constant(null)));
  }
  private Cursor<ByteString, Long> openCursor0(ReadableTransaction txn)
  private Cursor<ByteString, Long> openCursor0(ReadableTransaction txn, boolean partOfAWholeTreeWalk)
  {
    return transformKeysAndValues(txn.openCursor(getName()), TO_KEY, TO_LONG);
    return transformKeysAndValues(
        partOfAWholeTreeWalk ? txn.openBulkCursor(getName()) : txn.openCursor(getName()), TO_KEY, TO_LONG);
  }
  void addCount(final WriteableTransaction txn, ByteSequence key, final long delta)
@@ -107,8 +122,35 @@
  long getCount(final ReadableTransaction txn, ByteSequence key)
  {
    return getCount(txn, key, false);
  }
  /**
   * The same read, told which kind of work it is part of. A client operation reads a counter of its
   * own and takes the bound of one - {@code numSubordinates} of a search
   * ({@code EntryContainer.getNumberOfChildren}), the entry count of a VLV index a search is paging
   * through ({@code VLVIndex.getEntryCount}) - while {@code verify-index} reads one per DN of the
   * tree it is walking, with nobody waiting on it: bounding those as client operations is what #877
   * exists to stop, and on the JDBC backend it aborted a verify of a backend large enough.
   * <p>
   * The third caller is {@code ID2ChildrenCount.getTotalCount}, which is read both ways and is told
   * which it is by its own caller: a verify sizes its progress report with it, {@code cn=monitor}
   * and the searches of {@code GroupManager} and {@code SubentryManager} read it for a client. A
   * delete and a modify DN reach neither form - they go through {@link #removeCount}, which is a
   * client operation by construction.
   *
   * @param txn storage transaction
   * @param key the counter to read
   * @param partOfAWholeTreeWalk whether this read belongs to a walk of a whole tree rather than to
   *          a client operation
   * @return Value of the counter. 0 if no counter is associated yet.
   * @see ReadableTransaction#openBulkCursor(TreeName)
   */
  long getCount(final ReadableTransaction txn, ByteSequence key, boolean partOfAWholeTreeWalk)
  {
    long counterValue = 0;
    try (final SequentialCursor<ByteString, Long> cursor = new ShardCursor(openCursor0(txn), key))
    try (final SequentialCursor<ByteString, Long> cursor =
        new ShardCursor(openCursor0(txn, partOfAWholeTreeWalk), key))
    {
      while (cursor.next())
      {
@@ -121,7 +163,8 @@
  long removeCount(final WriteableTransaction txn, ByteSequence key)
  {
    long counterValue = 0;
    try (final SequentialCursor<ByteString, Long> cursor = new ShardCursor(openCursor0(txn), key))
    // a removal is always a client operation: an entry is being deleted or moved
    try (final SequentialCursor<ByteString, Long> cursor = new ShardCursor(openCursor0(txn, false), key))
    {
      // Iterate over and remove all the thread local shards
      while (cursor.next())