Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ public Void run(ReadableTransaction txn) throws Exception
long undefined = 0;
long count = 0;
BackendTreeKeyValue keyDecoder = new BackendTreeKeyValue(index);
try (Cursor<ByteString, EntryIDSet> cursor = index.openCursor(txn))
try (Cursor<ByteString, EntryIDSet> cursor = index.openBulkCursor(txn))
{
while (cursor.next())
{
Expand Down Expand Up @@ -1286,7 +1286,8 @@ public TreeStats run(ReadableTransaction txn) throws Exception
long count = 0;
long totalKeySize = 0;
long totalDataSize = 0;
try (final Cursor<ByteString, ByteString> cursor = txn.openCursor(target.getTreeName()))
// dbtest walks the tree whole, on the command line of an operator: bulk work either way
try (final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(target.getTreeName()))
{
ByteString key;
ByteString maxKey = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2012-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand Down Expand Up @@ -131,7 +132,19 @@ public String valueToString(ByteString value)
public final Cursor<ByteString, EntryIDSet> openCursor(ReadableTransaction txn)
{
checkNotNull(txn, "txn must not be null");
return CursorTransformer.transformValues(txn.openCursor(getName()),
return decoding(txn.openCursor(getName()));
}

@Override
public final Cursor<ByteString, EntryIDSet> openBulkCursor(ReadableTransaction txn)
{
checkNotNull(txn, "txn must not be null");
return decoding(txn.openBulkCursor(getName()));
}

private Cursor<ByteString, EntryIDSet> decoding(Cursor<ByteString, ByteString> cursor)
{
return CursorTransformer.transformValues(cursor,
new ValueTransformer<ByteString, ByteString, EntryIDSet, NeverThrowsException>()
{
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2012-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand Down Expand Up @@ -172,7 +173,10 @@ private void exportContainer(ReadableTransaction txn, EntryContainer entryContai
throws StorageRuntimeException, IOException, LDIFException
{
ID2Entry id2entry = entryContainer.getID2Entry();
try (final Cursor<ByteString, ByteString> cursor = txn.openCursor(id2entry.getName()))
// The whole of id2entry with nobody waiting on the walk: an export-ldif, or the generation ID
// a replicated domain computes for itself the first time it starts (LDAPReplicationDomain
// .computeGenerationId), which is why this must not be bounded as the work of an operation.
try (final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(id2entry.getName()))
{
while (cursor.next())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ SequentialCursor<EntryID, Void> openCursor(ReadableTransaction txn)
TO_ENTRY_ID, CursorTransformer.<ByteString, Void> keepValuesUnchanged());
}

/** @see ReadableTransaction#openBulkCursor(TreeName) */
SequentialCursor<EntryID, Void> openBulkCursor(ReadableTransaction txn)
{
return transformKeysAndValues(counter.openBulkCursor(txn),
TO_ENTRY_ID, CursorTransformer.<ByteString, Void> keepValuesUnchanged());
}

/**
* Updates the number of children for a given entry without updating the total number of entries.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,11 @@ void afterOpen(WriteableTransaction txn, boolean createOnDemand) throws StorageR
{
// Make sure the tree is there and readable, even if the storage is READ_ONLY.
// Would be nice if there were a better way...
try (final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
// Bulk: the first batch of a cursor carries no seek predicate, so this is a walk of the whole
// tree as far as the storage is concerned, and it runs on every open of the backend. A bound
// meant for an entry read would keep a large backend from opening at all on an engine where
// such a batch is not a step along an index.
try (final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(getName()))
{
cursor.next();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2012-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand All @@ -37,6 +38,17 @@ interface Index extends Tree

Cursor<ByteString, EntryIDSet> openCursor(ReadableTransaction txn);

/**
* Opens a cursor over the whole index for a task no client operation is waiting on, such as
* {@code verify-index} or {@code dbtest}.
*
* @param txn
* the transaction to read the index with
* @return a cursor over every key of this index
* @see ReadableTransaction#openBulkCursor(org.opends.server.backends.pluggable.spi.TreeName)
*/
Cursor<ByteString, EntryIDSet> openBulkCursor(ReadableTransaction txn);

boolean setIndexEntryLimit(int indexEntryLimit);

boolean setConfidential(boolean indexConfidential);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* Copyright 2008-2009 Sun Microsystems, Inc.
* Portions Copyright 2013-2016 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable;

Expand Down Expand Up @@ -146,7 +147,8 @@ private void load(WriteableTransaction txn, boolean shouldCreate)
// Cursor through the object class database and load the object class set
// definitions. At the same time, figure out the highest token value and
// initialize the object class counter to one greater than that.
try (Cursor<ByteString, ByteString> ocCursor = txn.openCursor(ocTreeName))
// Both trees are read whole while the backend opens, with no client operation waiting on it.
try (Cursor<ByteString, ByteString> ocCursor = txn.openBulkCursor(ocTreeName))
{
while (ocCursor.next())
{
Expand All @@ -169,7 +171,7 @@ private void load(WriteableTransaction txn, boolean shouldCreate)
}

// Cursor through the attribute description database and load the attribute set definitions.
try (Cursor<ByteString, ByteString> adCursor = txn.openCursor(adTreeName))
try (Cursor<ByteString, ByteString> adCursor = txn.openBulkCursor(adTreeName))
{
while (adCursor.next())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -73,9 +74,20 @@ public ByteString apply(ByteString shardedKey)
}

SequentialCursor<ByteString, Void> openCursor(ReadableTransaction txn)
{
return uniqueKeys(txn.openCursor(getName()));
}

/** @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)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,15 @@ public Cursor<ByteString, ByteString> openCursor(final TreeName name)
return new TracedCursor(cursor);
}

@Override
public Cursor<ByteString, ByteString> openBulkCursor(final TreeName name)
{
traceEnter("openBulkCursor", "name", name);
final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(name);
traceLeave("openBulkCursor", "name", name);
return new TracedCursor(cursor);
}

@Override
public ByteString read(final TreeName name, final ByteSequence key)
{
Expand Down Expand Up @@ -374,6 +383,15 @@ public Cursor<ByteString, ByteString> openCursor(final TreeName name)
return new TracedCursor(cursor);
}

@Override
public Cursor<ByteString, ByteString> openBulkCursor(final TreeName name)
{
traceEnter("openBulkCursor", "name", name);
final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(name);
traceLeave("openBulkCursor", "name", name);
return new TracedCursor(cursor);
}

@Override
public void openTree(final TreeName name, boolean createOnDemand)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ else if(lowerName.startsWith("vlv."))
*/
private void iterateID2Entry(ReadableTransaction txn) throws StorageRuntimeException
{
try(final Cursor<ByteString, ByteString> cursor = txn.openCursor(id2entry.getName()))
// Every tree this job walks, it walks whole, and no client operation is waiting on it.
try(final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(id2entry.getName()))
{
long storedEntryCount = id2entry.getRecordCount(txn);
while (cursor.next())
Expand Down Expand Up @@ -442,7 +443,7 @@ private void iterateDN2ID(ReadableTransaction txn) throws StorageRuntimeExceptio
final Deque<ChildrenCount> childrenCounters = new LinkedList<>();
ChildrenCount currentNode = null;

try(final Cursor<ByteString, ByteString> cursor = txn.openCursor(dn2id.getName()))
try(final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(dn2id.getName()))
{
while (cursor.next())
{
Expand Down Expand Up @@ -525,7 +526,7 @@ private void verifyID2ChildrenCount(ReadableTransaction txn, ChildrenCount paren

private void iterateID2ChildrenCount(ReadableTransaction txn) throws StorageRuntimeException
{
try (final SequentialCursor<EntryID, Void> cursor = id2childrenCount.openCursor(txn))
try (final SequentialCursor<EntryID, Void> cursor = id2childrenCount.openBulkCursor(txn))
{
while (cursor.next())
{
Expand Down Expand Up @@ -607,7 +608,7 @@ private void iterateVLVIndex(ReadableTransaction txn, VLVIndex vlvIndex, boolean
return;
}

try(final Cursor<ByteString, ByteString> cursor = txn.openCursor(vlvIndex.getName()))
try(final Cursor<ByteString, ByteString> cursor = txn.openBulkCursor(vlvIndex.getName()))
{
while (cursor.next())
{
Expand Down Expand Up @@ -655,7 +656,7 @@ private void iterateAttrIndex(ReadableTransaction txn, MatchingRuleIndex index)
return;
}

try(final Cursor<ByteString,EntryIDSet> cursor = index.openCursor(txn))
try(final Cursor<ByteString,EntryIDSet> cursor = index.openBulkCursor(txn))
{
while (cursor.next())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2014-2015 ForgeRock AS.
* Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends.pluggable.spi;

Expand Down Expand Up @@ -43,6 +44,26 @@ public interface ReadableTransaction
*/
Cursor<ByteString, ByteString> openCursor(TreeName treeName);

/**
* Opens a cursor on the tree whose name is provided, for a walk of that whole tree with no
* client operation waiting on it: an export, a verify, a rebuild, or the load of a tree while
* the backend opens.
* <p>
* A storage engine that bounds how long a statement may take must not bound such a walk as it
* bounds the work of a client operation: what this legitimately takes follows the size of the
* tree, and cutting it short fails an administrative task that would otherwise have run to the
* end. An engine with no such bound - every one but the JDBC backend - answers this exactly as
* {@link #openCursor(TreeName)} does.
*
* @param treeName
* the tree name
* @return a new cursor
*/
default Cursor<ByteString, ByteString> openBulkCursor(TreeName treeName)
{
return openCursor(treeName);
}

/**
* Returns the number of key/value pairs in the provided tree.
*
Expand Down
Loading
Loading