| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2015 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.backends.pluggable; |
| | | |
| | |
| | | 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) |
| | |
| | | |
| | | 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()) |
| | | { |
| | |
| | | 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()) |