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

Jean-Noel Rouvignac
21.25.2015 167b0dde925af61a07bed4ce3cc30def39a802b9
OPENDJ-2016 Implement new on disk merge import strategy based on storage engine

OnDiskMergeStorageImporter now writes all keys/values to the Importer.
Added various toKey() and toValue() methods to the index classes.


OnDiskMergeStorageImporter.java
Write all keys/values to the Importer.
Rewrote the code inherited from OnDiskMergeBufferImporter + simplified the code


DN2ID.java:
Renamed dnToKey() to toKey().

DN2URI.java:
Added toKey() and toValue().
Used try-with-resources + diamond operator.

VLVIndex.java:
Extracted toKey() from encodeVLVKey().
Used try-with-resources.


VerifyJob.java:
Consequence of the change to VLVIndex.encodeKey().
Code cleanup.

IndexBuffer.java:
Code cleanup
6 files modified
385 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java 20 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2URI.java 124 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/IndexBuffer.java 55 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeStorageImporter.java 73 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VLVIndex.java 41 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java 72 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java
@@ -48,6 +48,7 @@
 * for each entry.  The key is the normalized entry DN and the value
 * is the entry ID.
 */
@SuppressWarnings("javadoc")
class DN2ID extends AbstractTree
{
  private static final Function<ByteString, Void, DirectoryException> TO_VOID_KEY =
@@ -72,7 +73,6 @@
  private final DN baseDN;
  /**
   * Create a DN2ID instance for in a given entryContainer.
   *
@@ -96,12 +96,12 @@
   */
  void put(final WriteableTransaction txn, DN dn, final EntryID entryID) throws StorageRuntimeException
  {
    txn.put(getName(), dnToKey(dn), entryID.toByteString());
    txn.put(getName(), toKey(dn), entryID.toByteString());
  }
  boolean insert(final WriteableTransaction txn, DN dn, final EntryID entryID) throws StorageRuntimeException
  {
    return txn.update(getName(), dnToKey(dn), new UpdateFunction()
    return txn.update(getName(), toKey(dn), new UpdateFunction()
    {
      @Override
      public ByteSequence computeNewValue(ByteSequence oldEntryID)
@@ -117,7 +117,8 @@
    });
  }
  private ByteString dnToKey(DN dn) {
  ByteString toKey(DN dn)
  {
    return dnToDNKey(dn, baseDN.size());
  }
@@ -131,7 +132,7 @@
   */
  boolean remove(WriteableTransaction txn, DN dn) throws StorageRuntimeException
  {
    return txn.delete(getName(), dnToKey(dn));
    return txn.delete(getName(), toKey(dn));
  }
  /**
@@ -143,7 +144,7 @@
   */
  EntryID get(ReadableTransaction txn, DN dn) throws StorageRuntimeException
  {
    final ByteString value = txn.read(getName(), dnToKey(dn));
    final ByteString value = txn.read(getName(), toKey(dn));
    return value != null ? new EntryID(value) : null;
  }
@@ -154,7 +155,7 @@
  private Cursor<ByteString, ByteString> openCursor0(ReadableTransaction txn, DN dn) {
    final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
    cursor.positionToKey(dnToKey(dn));
    cursor.positionToKey(toKey(dn));
    return cursor;
  }
@@ -167,7 +168,6 @@
    return new SubtreeCursor(openCursor0(txn, dn));
  }
  /**
   * Check if two DN have a parent-child relationship.
   *
@@ -215,11 +215,11 @@
    public boolean next()
    {
      if (cursorOnParent) {
        /** Go to the first children */
        // Go to the first children
        delegate.next();
        cursorOnParent = false;
      } else {
        /** Go to the next sibling */
        // Go to the next sibling
        delegate.positionToKeyOrNext(nextSibling());
      }
      return isDefined() && delegate.getKey().startsWith(parentDN);
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2URI.java
@@ -74,6 +74,7 @@
 * as in dn2id so that all referrals in a subtree can be retrieved by cursoring
 * through a range of the records.
 */
@SuppressWarnings("javadoc")
class DN2URI extends AbstractTree
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
@@ -84,17 +85,14 @@
   * The standard attribute type that is used to specify the set of referral
   * URLs in a referral entry.
   */
  private final AttributeType referralType =
       DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
  private final AttributeType referralType = DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
  /**
   * A flag that indicates whether there are any referrals contained in this
   * tree.  It should only be set to {@code false} when it is known that
   * there are no referrals.
   */
  private volatile ConditionResult containsReferrals =
       ConditionResult.UNDEFINED;
  private volatile ConditionResult containsReferrals = ConditionResult.UNDEFINED;
  /**
   * Create a new object representing a referral tree in a given
@@ -113,11 +111,14 @@
    prefixRDNComponents = entryContainer.getBaseDN().size();
  }
  /** Encodes the value. */
  private ByteSequence encode(DN dn, Collection<String> col)
  {
    if (col != null && !col.isEmpty())
    {
      ByteStringBuilder b = new ByteStringBuilder();
      // encode the dn inside the value
      // because the dn is encoded in a non reversible way in the key
      byte[] dnBytes = StaticUtils.getBytes(dn.toString());
      b.append(dnBytes.length);
      b.append(dnBytes);
@@ -133,6 +134,7 @@
    return null;
  }
  /** Decodes the value as a pair where the first element is the DN key and the second is the actual value. */
  private Pair<DN, List<String>> decode(ByteSequence bs) throws StorageRuntimeException
  {
    if (!bs.isEmpty())
@@ -149,7 +151,7 @@
        throw new StorageRuntimeException("Unable to decode DN from binary value", e);
      }
      final int nbElems = r.getInt();
      List<String> results = new ArrayList<String>(nbElems);
      List<String> results = new ArrayList<>(nbElems);
      for (int i = 0; i < nbElems; i++)
      {
        final int stringLength = r.getInt();
@@ -258,8 +260,7 @@
   */
  private ConditionResult containsReferrals(ReadableTransaction txn)
  {
    Cursor<?, ?> cursor = txn.openCursor(getName());
    try
    try (Cursor<?, ?> cursor = txn.openCursor(getName()))
    {
      return ConditionResult.valueOf(cursor.next());
    }
@@ -269,10 +270,6 @@
      return ConditionResult.UNDEFINED;
    }
    finally
    {
      cursor.close();
    }
  }
  /**
@@ -335,7 +332,7 @@
  private List<String> toStrings(Attribute a)
  {
    List<String> results = new ArrayList<String>(a.size());
    List<String> results = new ArrayList<>(a.size());
    for (ByteString v : a)
    {
      results.add(v.toString());
@@ -414,8 +411,7 @@
    Set<String> referralURLs = entry.getReferralURLs();
    if (referralURLs != null)
    {
      throwReferralException(entry.getName(), entry.getName(), referralURLs,
                             searchScope);
      throwReferralException(entry.getName(), entry.getName(), referralURLs, searchScope);
    }
  }
@@ -433,10 +429,9 @@
   * in the referral entry.
   */
  private void throwReferralException(DN targetDN, DN referralDN, Collection<String> labeledURIs,
      SearchScope searchScope)
      throws DirectoryException
      SearchScope searchScope) throws DirectoryException
  {
    ArrayList<String> URIList = new ArrayList<String>(labeledURIs.size());
    ArrayList<String> URIList = new ArrayList<>(labeledURIs.size());
    for (String labeledURI : labeledURIs)
    {
      // Remove the label part of the labeled URI if there is a label.
@@ -456,10 +451,7 @@
          DN urlBaseDN = targetDN;
          if (!referralDN.equals(ldapurl.getBaseDN()))
          {
            urlBaseDN =
                 EntryContainer.modDN(targetDN,
                                      referralDN.size(),
                                      ldapurl.getBaseDN());
            urlBaseDN = EntryContainer.modDN(targetDN, referralDN.size(), ldapurl.getBaseDN());
          }
          ldapurl.setBaseDN(urlBaseDN);
          if (searchScope == null)
@@ -523,28 +515,20 @@
      return;
    }
    try
    try (Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
    {
      final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
      try
      // Go up through the DIT hierarchy until we find a referral.
      for (DN dn = getParentWithinBase(targetDN); dn != null; dn = getParentWithinBase(dn))
      {
        // Go up through the DIT hierarchy until we find a referral.
        for (DN dn = getParentWithinBase(targetDN); dn != null; dn = getParentWithinBase(dn))
        // Look for a record whose key matches the current DN.
        if (cursor.positionToKey(toKey(dn)))
        {
          // Look for a record whose key matches the current DN.
          if (cursor.positionToKey(toKey(dn)))
          {
            // Construct a set of all the labeled URIs in the referral.
            final Pair<DN, List<String>> dnAndUris = decode(cursor.getValue());
            Collection<String> labeledURIs = dnAndUris.getSecond();
            throwReferralException(targetDN, dn, labeledURIs, searchScope);
          }
          // Construct a set of all the labeled URIs in the referral.
          final Pair<DN, List<String>> dnAndUris = decode(cursor.getValue());
          Collection<String> labeledURIs = dnAndUris.getSecond();
          throwReferralException(targetDN, dn, labeledURIs, searchScope);
        }
      }
      finally
      {
        cursor.close();
      }
    }
    catch (StorageRuntimeException e)
    {
@@ -589,39 +573,31 @@
    ByteStringBuilder suffix = beforeKey(baseDN);
    ByteStringBuilder end = afterKey(baseDN);
    try
    try (Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
    {
      final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
      try
      // Initialize the cursor very close to the starting value then
      // step forward until we pass the ending value.
      boolean success = cursor.positionToKey(suffix);
      while (success && cursor.getKey().compareTo(end) < 0)
      {
        // Initialize the cursor very close to the starting value then
        // step forward until we pass the ending value.
        boolean success = cursor.positionToKey(suffix);
        while (success && cursor.getKey().compareTo(end) < 0)
        // We have found a subordinate referral.
        // Make sure the referral is within scope.
        if (searchOp.getScope() == SearchScope.SINGLE_LEVEL
            && DnKeyFormat.findDNKeyParent(cursor.getKey()) != baseDN.length())
        {
          // We have found a subordinate referral.
          // Make sure the referral is within scope.
          if (searchOp.getScope() == SearchScope.SINGLE_LEVEL
              && DnKeyFormat.findDNKeyParent(cursor.getKey()) != baseDN.length())
          {
            continue;
          }
          // Construct a list of all the URIs in the referral.
          final Pair<DN, List<String>> dnAndUris = decode(cursor.getValue());
          final DN dn = dnAndUris.getFirst();
          final Collection<String> labeledURIs = dnAndUris.getSecond();
          SearchResultReference reference = toSearchResultReference(dn, labeledURIs, searchOp.getScope());
          if (!searchOp.returnReference(dn, reference))
          {
            return false;
          }
          success = cursor.next();
          continue;
        }
      }
      finally
      {
        cursor.close();
        // Construct a list of all the URIs in the referral.
        final Pair<DN, List<String>> dnAndUris = decode(cursor.getValue());
        final DN dn = dnAndUris.getFirst();
        final Collection<String> labeledURIs = dnAndUris.getSecond();
        SearchResultReference reference = toSearchResultReference(dn, labeledURIs, searchOp.getScope());
        if (!searchOp.returnReference(dn, reference))
        {
          return false;
        }
        success = cursor.next();
      }
    }
    catch (StorageRuntimeException e)
@@ -634,7 +610,7 @@
  private SearchResultReference toSearchResultReference(DN dn, Collection<String> labeledURIs, SearchScope scope)
  {
    ArrayList<String> URIList = new ArrayList<String>(labeledURIs.size());
    ArrayList<String> URIList = new ArrayList<>(labeledURIs.size());
    for (String labeledURI : labeledURIs)
    {
      // Remove the label part of the labeled URI if there is a label.
@@ -685,8 +661,16 @@
    return new SearchResultReference(URIList);
  }
  private ByteString toKey(DN dn)
  ByteString toKey(DN dn)
  {
    return DnKeyFormat.dnToDNKey(dn, prefixRDNComponents);
  }
  ByteSequence toValue(DN dn, Entry entry)
  {
    // FIXME JNR This is not very efficient:
    // getReferralsURL() converts from bytestring into string
    // and the code down below then does the reverse
    return encode(dn, entry.getReferralURLs());
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/IndexBuffer.java
@@ -26,11 +26,11 @@
 */
package org.opends.server.backends.pluggable;
import static org.opends.server.backends.pluggable.EntryIDSet.newDefinedSet;
import static org.opends.server.backends.pluggable.EntryIDSet.*;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -42,10 +42,12 @@
/**
 * A buffered index is used to buffer multiple reads or writes to the
 * same index key into a single read or write.
 * <p>
 * It can only be used to buffer multiple reads and writes under
 * the same transaction. The transaction may be null if it is known
 * that there are no other concurrent updates to the index.
 */
@SuppressWarnings("javadoc")
class IndexBuffer
{
  private final EntryContainer entryContainer;
@@ -54,12 +56,10 @@
   * The buffered records stored as a map from the record key to the
   * buffered value for that key for each index.
   */
  private final LinkedHashMap<Index, TreeMap<ByteString, BufferedIndexValues>> bufferedIndexes =
      new LinkedHashMap<Index, TreeMap<ByteString, BufferedIndexValues>>();
  private final LinkedHashMap<Index, TreeMap<ByteString, BufferedIndexValues>> bufferedIndexes = new LinkedHashMap<>();
  /** The buffered records stored as a set of buffered VLV values for each index. */
  private final LinkedHashMap<VLVIndex, BufferedVLVIndexValues> bufferedVLVIndexes =
      new LinkedHashMap<VLVIndex, BufferedVLVIndexValues>();
  private final LinkedHashMap<VLVIndex, BufferedVLVIndexValues> bufferedVLVIndexes = new LinkedHashMap<>();
  /**
   * A simple class representing a pair of added and deleted indexed IDs. Initially both addedIDs
@@ -112,7 +112,7 @@
      {
        if (addedSortKeys == null)
        {
          addedSortKeys = new TreeSet<ByteString>();
          addedSortKeys = new TreeSet<>();
        }
        addedSortKeys.add(sortKey);
      }
@@ -124,7 +124,7 @@
      {
        if (deletedSortKeys == null)
        {
          deletedSortKeys = new TreeSet<ByteString>();
          deletedSortKeys = new TreeSet<>();
        }
        deletedSortKeys.add(sortKey);
      }
@@ -159,19 +159,9 @@
  private BufferedIndexValues createOrGetBufferedIndexValues(Index index, ByteString keyBytes)
  {
    BufferedIndexValues values = null;
    Map<ByteString, BufferedIndexValues> bufferedOperations = createOrGetBufferedOperations(index);
    TreeMap<ByteString, BufferedIndexValues> bufferedOperations = bufferedIndexes.get(index);
    if (bufferedOperations == null)
    {
      bufferedOperations = new TreeMap<ByteString, BufferedIndexValues>();
      bufferedIndexes.put(index, bufferedOperations);
    }
    else
    {
      values = bufferedOperations.get(keyBytes);
    }
    BufferedIndexValues values = bufferedOperations.get(keyBytes);
    if (values == null)
    {
      values = new BufferedIndexValues();
@@ -180,6 +170,17 @@
    return values;
  }
  private Map<ByteString, BufferedIndexValues> createOrGetBufferedOperations(Index index)
  {
    TreeMap<ByteString, BufferedIndexValues> bufferedOperations = bufferedIndexes.get(index);
    if (bufferedOperations == null)
    {
      bufferedOperations = new TreeMap<>();
      bufferedIndexes.put(index, bufferedOperations);
    }
    return bufferedOperations;
  }
  /**
   * Flush the buffered index changes to storage.
   *
@@ -190,9 +191,8 @@
  void flush(WriteableTransaction txn) throws StorageRuntimeException, DirectoryException
  {
    /*
     * FIXME: this seems like a surprising way to update the indexes. Why not
     * store the buffered changes in a TreeMap in order to have a predictable
     * iteration order?
     * FIXME: this seems like a surprising way to update the indexes. Why not store the buffered
     * changes in a TreeMap in order to have a predictable iteration order?
     */
    for (AttributeIndex attributeIndex : entryContainer.getAttributeIndexes())
    {
@@ -232,20 +232,17 @@
    createOrGetBufferedIndexValues(index, key).deleteEntryID(entryID);
  }
  private void flushIndex(Index index, WriteableTransaction txn,
      Map<ByteString, BufferedIndexValues> bufferedValues)
  private void flushIndex(Index index, WriteableTransaction txn, Map<ByteString, BufferedIndexValues> bufferedValues)
  {
    if (bufferedValues != null)
    {
      final Iterator<Map.Entry<ByteString, BufferedIndexValues>> it = bufferedValues.entrySet().iterator();
      while (it.hasNext())
      for (Entry<ByteString, BufferedIndexValues> entry : bufferedValues.entrySet())
      {
        final Map.Entry<ByteString, BufferedIndexValues> entry = it.next();
        final ByteString key = entry.getKey();
        final BufferedIndexValues values = entry.getValue();
        index.update(txn, key, values.deletedEntryIDs, values.addedEntryIDs);
        it.remove();
      }
      bufferedValues.clear();
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeStorageImporter.java
@@ -66,7 +66,6 @@
import org.opends.server.backends.pluggable.AttributeIndex.MatchingRuleIndex;
import org.opends.server.backends.pluggable.ImportLDIFReader.EntryInformation;
import org.opends.server.backends.pluggable.OnDiskMergeBufferImporter.DNCache;
import org.opends.server.backends.pluggable.OnDiskMergeBufferImporter.IndexKey;
import org.opends.server.backends.pluggable.spi.Cursor;
import org.opends.server.backends.pluggable.spi.Importer;
import org.opends.server.backends.pluggable.spi.ReadOperation;
@@ -993,7 +992,7 @@
        processDN2ID(suffix, entry.getName(), entryID);
      }
      processDN2URI(suffix, entry);
      processIndexes(suffix, entry, entryID, false);
      processIndexes(suffix, entry, entryID);
      processVLVIndexes(suffix, entry, entryID);
      // FIXME JNR run a dedicated thread to do the puts ordered by entryID
      // suffix.getID2Entry().put(importer, entryID, entry);
@@ -1026,22 +1025,39 @@
      return true;
    }
    void processIndexes(Suffix suffix, Entry entry, EntryID entryID, boolean allIndexes)
    void processDN2ID(Suffix suffix, DN dn, EntryID entryID)
    {
      DN2ID dn2id = suffix.getDN2ID();
      importer.put(dn2id.getName(), dn2id.toKey(dn), entryID.toByteString());
    }
    private void processDN2URI(Suffix suffix, Entry entry)
    {
      DN2URI dn2uri = suffix.getDN2URI();
      DN entryDN = entry.getName();
      ByteSequence value = dn2uri.toValue(entryDN, entry);
      if (value != null)
      {
        importer.put(dn2uri.getName(), dn2uri.toKey(entryDN), value);
      }
    }
    void processIndexes(Suffix suffix, Entry entry, EntryID entryID)
        throws StorageRuntimeException, InterruptedException
    {
      final ByteString value = entryID.toByteString();
      for (Map.Entry<AttributeType, AttributeIndex> mapEntry : suffix.getAttrIndexMap().entrySet())
      {
        AttributeType attrType = mapEntry.getKey();
        AttributeIndex attrIndex = mapEntry.getValue();
        if (allIndexes || entry.hasAttribute(attrType))
        final AttributeType attrType = mapEntry.getKey();
        final AttributeIndex attrIndex = mapEntry.getValue();
        if (entry.hasAttribute(attrType))
        {
          for (Map.Entry<String, MatchingRuleIndex> mapEntry2 : attrIndex.getNameToIndexes().entrySet())
          for (MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
          {
            String indexID = mapEntry2.getKey();
            MatchingRuleIndex index = mapEntry2.getValue();
            IndexKey indexKey = new IndexKey(attrType, indexID, index.getIndexEntryLimit());
            processAttribute(index, entry, entryID, indexKey);
            for (ByteString key : index.indexEntry(entry))
            {
              importer.put(index.getName(), key, value);
            }
          }
        }
      }
@@ -1050,38 +1066,11 @@
    void processVLVIndexes(Suffix suffix, Entry entry, EntryID entryID)
        throws DirectoryException
    {
      final EntryContainer entryContainer = suffix.getEntryContainer();
      final IndexBuffer buffer = new IndexBuffer(entryContainer);
      for (VLVIndex vlvIdx : entryContainer.getVLVIndexes())
      for (VLVIndex vlvIndex : suffix.getEntryContainer().getVLVIndexes())
      {
        vlvIdx.addEntry(buffer, entryID, entry);
        ByteString key = vlvIndex.toKey(entry, entryID);
        importer.put(vlvIndex.getName(), key, ByteString.empty());
      }
      // buffer.flush(txn); // TODO JNR do something about it
    }
    void processAttribute(MatchingRuleIndex index, Entry entry, EntryID entryID, IndexKey indexKey)
        throws StorageRuntimeException, InterruptedException
    {
      for (ByteString key : index.indexEntry(entry))
      {
        processKey(index, key, entryID, indexKey);
      }
    }
    final int processKey(Tree tree, ByteString key, EntryID entryID, IndexKey indexKey) throws InterruptedException
    {
      // TODO JNR implement
      return -1;
    }
    void processDN2ID(Suffix suffix, DN dn, EntryID entryID)
    {
      // TODO JNR implement
    }
    private void processDN2URI(Suffix suffix, Entry entry)
    {
      // TODO JNR implement
    }
  }
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VLVIndex.java
@@ -91,7 +91,6 @@
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /** The VLV vlvIndex configuration. */
  private BackendVLVIndexCfg config;
@@ -349,10 +348,15 @@
  {
    if (shouldInclude(entry))
    {
      buffer.put(this, encodeVLVKey(entry, entryID.longValue()));
      buffer.put(this, toKey(entry, entryID));
    }
  }
  ByteString toKey(final Entry entry, final EntryID entryID)
  {
    return encodeVLVKey(entry, entryID.longValue());
  }
  private boolean shouldInclude(final Entry entry) throws DirectoryException
  {
    return entry.getName().matchesBaseAndScope(baseDN, scope) && filter.matchesEntry(entry);
@@ -415,7 +419,7 @@
  {
    if (shouldInclude(entry))
    {
      buffer.remove(this, encodeVLVKey(entry, entryID.longValue()));
      buffer.remove(this, toKey(entry, entryID));
    }
  }
@@ -489,16 +493,11 @@
  private EntryIDSet evaluateNonVLVRequest(final ReadableTransaction txn, final StringBuilder debugBuilder)
  {
    final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
    try
    try (Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
    {
      final long[] selectedIDs = readRange(cursor, count.get(), debugBuilder);
      return newDefinedSet(selectedIDs);
    }
    finally
    {
      cursor.close();
    }
  }
  /**
@@ -516,10 +515,9 @@
    final ByteString assertion = vlvRequest.getGreaterThanOrEqualAssertion();
    final ByteSequence encodedTargetAssertion =
        encodeTargetAssertion(sortOrder, assertion, searchOperation, currentCount);
    final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
    try
    try (Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
    {
      final LinkedList<Long> selectedIDs = new LinkedList<Long>();
      final LinkedList<Long> selectedIDs = new LinkedList<>();
      int targetPosition = 0;
      // Don't waste cycles looking for an assertion that does not match anything.
@@ -572,10 +570,6 @@
          LDAPResultCode.SUCCESS));
      return newDefinedSet(toPrimitiveLongArray(selectedIDs));
    }
    finally
    {
      cursor.close();
    }
  }
  private long[] toPrimitiveLongArray(final List<Long> entryIDs)
@@ -589,9 +583,7 @@
    return result;
  }
  /**
   * Normalize the assertion using the primary key's ordering matching rule.
   */
  /** Normalize the assertion using the primary key's ordering matching rule. */
  static ByteSequence encodeTargetAssertion(final SortOrder sortOrder, final ByteString assertion,
      final SearchOperation searchOperation, final int resultSetSize) throws DirectoryException
  {
@@ -669,8 +661,7 @@
    }
    final int count = 1 + beforeCount + afterCount;
    final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
    try
    try (Cursor<ByteString, ByteString> cursor = txn.openCursor(getName()))
    {
      final long[] selectedIDs;
      if (cursor.positionToIndex(startPos))
@@ -684,10 +675,6 @@
      searchOperation.addResponseControl(new VLVResponseControl(targetOffset, currentCount, LDAPResultCode.SUCCESS));
      return newDefinedSet(selectedIDs);
    }
    finally
    {
      cursor.close();
    }
  }
  private long[] readRange(final Cursor<ByteString, ByteString> cursor, final int count,
@@ -746,7 +733,7 @@
  {
    if (shouldInclude(entry))
    {
      final ByteString key = encodeVLVKey(entry, entryID.longValue());
      final ByteString key = toKey(entry, entryID);
      return txn.read(getName(), key) != null;
    }
    return false;
@@ -760,7 +747,7 @@
    return builder.toByteString();
  }
  ByteString encodeVLVKey(final Entry entry, final long entryID)
  private ByteString encodeVLVKey(final Entry entry, final long entryID)
  {
    return encodeVLVKey(sortOrder, entry, entryID);
  }
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/VerifyJob.java
@@ -29,6 +29,7 @@
import static org.opends.messages.BackendMessages.*;
import static org.opends.server.backends.pluggable.DnKeyFormat.*;
import static org.opends.server.backends.pluggable.VLVIndex.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.AbstractSet;
import java.util.ArrayList;
@@ -89,12 +90,8 @@
  /** The maximum number of references per record. */
  private long maxEntryPerValue;
  /**
   * This map is used to gather some statistics about values that have
   * exceeded the entry limit.
   */
  private IdentityHashMap<Index, HashMap<ByteString, Long>> entryLimitMap =
       new IdentityHashMap<Index, HashMap<ByteString, Long>>();
  /** This map is used to gather some statistics about values that have exceeded the entry limit. */
  private IdentityHashMap<Index, HashMap<ByteString, Long>> entryLimitMap = new IdentityHashMap<>();
  /** Indicates whether dn2id is to be verified. */
  private boolean verifyDN2ID;
@@ -109,13 +106,14 @@
  private ID2Count id2childrenCount;
  /** A list of the attribute indexes to be verified. */
  private final ArrayList<AttributeIndex> attrIndexList = new ArrayList<AttributeIndex>();
  private final ArrayList<AttributeIndex> attrIndexList = new ArrayList<>();
  /** A list of the VLV indexes to be verified. */
  private final ArrayList<VLVIndex> vlvIndexList = new ArrayList<VLVIndex>();
  private final ArrayList<VLVIndex> vlvIndexList = new ArrayList<>();
  /**
   * Construct a VerifyJob.
   *
   * @param rootContainer The root container.
   * @param verifyConfig The verify configuration.
   */
  VerifyJob(RootContainer rootContainer, VerifyConfig verifyConfig)
@@ -132,8 +130,7 @@
   * @throws StorageRuntimeException If an error occurs in the storage.
   * @throws DirectoryException If an error occurs while verifying the backend.
   */
  long verifyBackend() throws StorageRuntimeException,
      DirectoryException
  long verifyBackend() throws StorageRuntimeException, DirectoryException
  {
    try
    {
@@ -158,8 +155,7 @@
  private long verifyBackend0(ReadableTransaction txn) throws StorageRuntimeException, DirectoryException
  {
    EntryContainer entryContainer =
        rootContainer.getEntryContainer(verifyConfig.getBaseDN());
    EntryContainer entryContainer = rootContainer.getEntryContainer(verifyConfig.getBaseDN());
    entryContainer.sharedLock.lock();
    try
@@ -205,8 +201,7 @@
              throw new StorageRuntimeException(ERR_VLV_INDEX_NOT_CONFIGURED.get(lowerName).toString());
            }
            VLVIndex vlvIndex =
                entryContainer.getVLVIndex(lowerName.substring(4));
            VLVIndex vlvIndex = entryContainer.getVLVIndex(lowerName.substring(4));
            if(vlvIndex == null)
            {
              throw new StorageRuntimeException(ERR_VLV_INDEX_NOT_CONFIGURED.get(lowerName.substring(4)).toString());
@@ -231,7 +226,7 @@
        }
      }
      entryLimitMap = new IdentityHashMap<Index, HashMap<ByteString, Long>>(attrIndexList.size());
      entryLimitMap = new IdentityHashMap<>(attrIndexList.size());
      // We will be updating these files independently of the indexes
      // so we need direct access to them rather than going through
@@ -426,7 +421,7 @@
    {
      iterateID2ChildrenCount(txn);
    }
    else if (attrIndexList.size() > 0)
    else if (!attrIndexList.isEmpty())
    {
      AttributeIndex attrIndex = attrIndexList.get(0);
      for (MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
@@ -434,7 +429,7 @@
        iterateAttrIndex(txn, index);
      }
    }
    else if (vlvIndexList.size() > 0)
    else if (!vlvIndexList.isEmpty())
    {
      iterateVLVIndex(txn, vlvIndexList.get(0), true);
    }
@@ -541,7 +536,7 @@
    EntryID currentEntryID = new EntryID(-1);
    while(cursor.next()) {
      if (cursor.getKey().equals(currentEntryID)) {
        /** Sharded cursor may return the same EntryID multiple times */
        // Sharded cursor may return the same EntryID multiple times
        continue;
      }
      currentEntryID = cursor.getKey();
@@ -565,7 +560,7 @@
    HashMap<ByteString,Long> hashMap = entryLimitMap.get(index);
    if (hashMap == null)
    {
      hashMap = new HashMap<ByteString, Long>();
      hashMap = new HashMap<>();
      entryLimitMap.put(index, hashMap);
    }
    Long counter = hashMap.get(key);
@@ -648,7 +643,7 @@
          continue;
        }
        ByteString expectedKey = vlvIndex.encodeVLVKey(entry, id.longValue());
        ByteString expectedKey = vlvIndex.toKey(entry, id);
        if (expectedKey.compareTo(key) != 0)
        {
          errorCount++;
@@ -658,7 +653,6 @@
                id, keyDump(vlvIndex.toString(), expectedKey));
          }
        }
      }
    }
  }
@@ -707,10 +701,7 @@
          {
            if (prevID != null && id.equals(prevID) && logger.isTraceEnabled())
            {
              if (logger.isTraceEnabled())
              {
                logger.trace("Duplicate reference to ID %d%n%s", id, keyDump(index.toString(), key));
              }
              logger.trace("Duplicate reference to ID %d%n%s", id, keyDump(index.toString(), key));
            }
            prevID = id;
@@ -889,11 +880,8 @@
  private static String keyDump(String indexName, ByteSequence key)
  {
    StringBuilder buffer = new StringBuilder(128);
    buffer.append("Index: ");
    buffer.append(indexName);
    buffer.append(ServerConstants.EOL);
    buffer.append("Key:");
    buffer.append(ServerConstants.EOL);
    buffer.append("Index: ").append(indexName).append(ServerConstants.EOL);
    buffer.append("Key:").append(ServerConstants.EOL);
    StaticUtils.byteArrayToHexPlusAscii(buffer, key.toByteArray(), 6);
    return buffer.toString();
  }
@@ -949,9 +937,7 @@
    }
  }
  /**
   * Check that an attribute index is complete for a given attribute.
   */
  /** Check that an attribute index is complete for a given attribute. */
  private void verifyAttribute(ReadableTransaction txn, EntryID entryID, Entry entry, AttributeIndex attrIndex)
  {
    for (MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
@@ -1019,9 +1005,7 @@
    return dn.getParentDNInSuffix();
  }
  /**
   * This class maintain the number of children for a given dn
   */
  /** This class maintain the number of children for a given dn. */
  private static final class ChildrenCount {
    private final ByteString baseDN;
    private final EntryID entryID;
@@ -1038,23 +1022,12 @@
  {
    /** The total number of records to process. */
    private long totalCount;
    /**
     * The number of records that had been processed at the time of the
     * previous progress report.
     */
    /** The number of records that had been processed at the time of the previous progress report. */
    private long previousCount;
    /** The time in milliseconds of the previous progress report. */
    private long previousTime;
    /**
     * The number of bytes in a megabyte.
     * Note that 1024*1024 bytes may eventually become known as a mebibyte(MiB).
     */
    private static final int bytesPerMegabyte = 1024*1024;
    /**
     * Create a new verify progress task.
     * @param indexIterator boolean, indicates if the task is iterating
     * through indexes or the entries.
@@ -1124,7 +1097,7 @@
      try
      {
        Runtime runtime = Runtime.getRuntime();
        long freeMemory = runtime.freeMemory() / bytesPerMegabyte;
        long freeMemory = runtime.freeMemory() / MB;
        // FIXME JNR compute the cache miss rate
        float cacheMissRate = 0;
@@ -1136,7 +1109,6 @@
        logger.traceException(e);
      }
      previousCount = latestCount;
      previousTime = latestTime;
    }