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

boli
22.56.2009 4942a89087d560696123c5de7d87ee99c2210c9d
Fix for issue 4117: Reordered the access to dn2id such that all moved DNs are removed sequentially then added back. Also fixed a case where an error caused by a corrupted index does not result in the index being put into the untrusted state.
2 files modified
230 ■■■■ changed files
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java 185 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/Index.java 45 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -2527,9 +2527,13 @@
        }
      }
      MovedEntry head = new MovedEntry(null, null, false);
      MovedEntry current = head;
      // Move or rename the apex entry.
      renameApexEntry(txn, buffer, oldSuperiorDN, newSuperiorDN, oldApexID,
          newApexID, oldApexEntry, entry,isApexEntryMoved, modifyDNOperation);
      removeApexEntry(txn, buffer, oldSuperiorDN, oldApexID,
          newApexID, oldApexEntry, entry,isApexEntryMoved, modifyDNOperation,
          current);
      current = current.next;
      /*
       * We will iterate forwards through a range of the dn2id keys to
@@ -2606,9 +2610,10 @@
          }
          // Move this entry.
          renameSubordinateEntry(txn, buffer, oldSuperiorDN, newSuperiorDN,
          removeSubordinateEntry(txn, buffer, oldSuperiorDN,
              oldID, newID, oldEntry, newDN, isApexEntryMoved,
              modifyDNOperation);
              modifyDNOperation, current);
          current = current.next;
          if(modifyDNOperation != null)
          {
@@ -2624,6 +2629,17 @@
        cursor.close();
      }
      // Set current to the first moved entry and null out the head. This will
      // allow processed moved entries to be GCed.
      current = head.next;
      head = null;
      while(current != null)
      {
        addRenamedEntry(txn, buffer, current.entryID, current.entry,
                        isApexEntryMoved, current.renumbered,
                        modifyDNOperation);
        current = current.next;
      }
      buffer.flush(txn);
      if(modifyDNOperation != null)
@@ -2665,27 +2681,84 @@
    }
  }
  private void renameApexEntry(Transaction txn, IndexBuffer buffer,
      DN oldSuperiorDN, DN newSuperiorDN,
      EntryID oldID, EntryID newID,
      Entry oldEntry, Entry newEntry,
  /**
   * Represents an renamed entry that was deleted from JE but yet to be added
   * back.
   */
  private static class MovedEntry
  {
    EntryID entryID;
    Entry entry;
    MovedEntry next;
    boolean renumbered;
    private MovedEntry(EntryID entryID, Entry entry, boolean renumbered)
    {
      this.entryID = entryID;
      this.entry = entry;
      this.renumbered = renumbered;
    }
  }
  private void addRenamedEntry(Transaction txn, IndexBuffer buffer,
                           EntryID newID,
                           Entry newEntry,
      boolean isApexEntryMoved,
                           boolean renumbered,
      ModifyDNOperation modifyDNOperation)
  throws DirectoryException, DatabaseException
  {
    DN oldDN = oldEntry.getDN();
    DN newDN = newEntry.getDN();
    // Remove the old DN from dn2id.
    dn2id.remove(txn, oldDN);
    // Put the new DN in dn2id.
    if (!dn2id.insert(txn, newDN, newID))
    if (!dn2id.insert(txn, newEntry.getDN(), newID))
    {
      Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString());
      Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(
          newEntry.getDN().toString());
      throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS,
          message);
    }
    id2entry.put(txn, newID, newEntry);
    dn2uri.addEntry(txn, newEntry);
    if (renumbered || modifyDNOperation == null)
    {
      // Reindex the entry with the new ID.
      indexInsertEntry(buffer, newEntry, newID);
    }
    // Add the new ID to id2children and id2subtree of new apex parent entry.
    if(isApexEntryMoved)
    {
      EntryID parentID;
      byte[] parentIDKeyBytes;
      boolean isParent = true;
      for (DN dn = getParentWithinBase(newEntry.getDN()); dn != null;
           dn = getParentWithinBase(dn))
      {
        parentID = dn2id.get(txn, dn, LockMode.DEFAULT);
        parentIDKeyBytes =
            JebFormat.entryIDToDatabase(parentID.longValue());
        if(isParent)
        {
          id2children.insertID(buffer, parentIDKeyBytes, newID);
          isParent = false;
        }
        id2subtree.insertID(buffer, parentIDKeyBytes, newID);
      }
    }
  }
  private void removeApexEntry(Transaction txn, IndexBuffer buffer,
      DN oldSuperiorDN,
      EntryID oldID, EntryID newID,
      Entry oldEntry, Entry newEntry,
      boolean isApexEntryMoved,
      ModifyDNOperation modifyDNOperation,
      MovedEntry tail)
  throws DirectoryException, DatabaseException
  {
    DN oldDN = oldEntry.getDN();
    // Remove the old DN from dn2id.
    dn2id.remove(txn, oldDN);
    // Remove old ID from id2entry and put the new entry
    // (old entry with new DN) in id2entry.
@@ -2693,10 +2766,11 @@
    {
      id2entry.remove(txn, oldID);
    }
    id2entry.put(txn, newID, newEntry);
    // Update any referral records.
    dn2uri.replaceEntry(txn, oldEntry, newEntry);
    dn2uri.deleteEntry(txn, oldEntry);
    tail.next = new MovedEntry(newID, newEntry, !newID.equals(oldID));
    // Remove the old ID from id2children and id2subtree of
    // the old apex parent entry.
@@ -2729,7 +2803,6 @@
      // Reindex the entry with the new ID.
      indexRemoveEntry(buffer, oldEntry, oldID);
      indexInsertEntry(buffer, newEntry, newID);
    }
    else
    {
@@ -2738,26 +2811,6 @@
          modifyDNOperation.getModifications());
    }
    // Add the new ID to id2children and id2subtree of new apex parent entry.
    if(newSuperiorDN != null && isApexEntryMoved)
    {
      EntryID parentID;
      byte[] parentIDKeyBytes;
      boolean isParent = true;
      for (DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn))
      {
        parentID = dn2id.get(txn, dn, LockMode.DEFAULT);
        parentIDKeyBytes =
          JebFormat.entryIDToDatabase(parentID.longValue());
        if(isParent)
        {
          id2children.insertID(buffer, parentIDKeyBytes, newID);
          isParent = false;
        }
        id2subtree.insertID(buffer, parentIDKeyBytes, newID);
      }
    }
    // Remove the entry from the entry cache.
    EntryCache<?> entryCache = DirectoryServer.getEntryCache();
    if (entryCache != null)
@@ -2766,12 +2819,13 @@
    }
  }
  private void renameSubordinateEntry(Transaction txn, IndexBuffer buffer,
      DN oldSuperiorDN, DN newSuperiorDN,
  private void removeSubordinateEntry(Transaction txn, IndexBuffer buffer,
      DN oldSuperiorDN,
      EntryID oldID, EntryID newID,
      Entry oldEntry, DN newDN,
      boolean isApexEntryMoved,
      ModifyDNOperation modifyDNOperation)
      ModifyDNOperation modifyDNOperation,
      MovedEntry tail)
  throws DirectoryException, DatabaseException
  {
    DN oldDN = oldEntry.getDN();
@@ -2824,24 +2878,17 @@
    // Remove the old DN from dn2id.
    dn2id.remove(txn, oldDN);
    // Put the new DN in dn2id.
    if (!dn2id.insert(txn, newDN, newID))
    {
      Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString());
      throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS,
          message);
    }
    // Remove old ID from id2entry and put the new entry
    // (old entry with new DN) in id2entry.
    if (!newID.equals(oldID))
    {
      id2entry.remove(txn, oldID);
    }
    id2entry.put(txn, newID, newEntry);
    // Update any referral records.
    dn2uri.replaceEntry(txn, oldEntry, newEntry);
    dn2uri.deleteEntry(txn, oldEntry);
    tail.next = new MovedEntry(newID, newEntry, !newID.equals(oldID));
    if(isApexEntryMoved)
    {
@@ -2863,28 +2910,8 @@
      id2children.delete(buffer, oldIDKeyBytes);
      id2subtree.delete(buffer, oldIDKeyBytes);
      // Add new ID to the id2c and id2s of our new parent and
      // new ID to id2s up the tree.
      EntryID newParentID;
      byte[] parentIDKeyBytes;
      boolean isParent = true;
      for (DN superiorDN = newDN; superiorDN != null;
      superiorDN = getParentWithinBase(superiorDN))
      {
        newParentID = dn2id.get(txn, superiorDN, LockMode.DEFAULT);
        parentIDKeyBytes =
          JebFormat.entryIDToDatabase(newParentID.longValue());
        if(isParent)
        {
          id2children.insertID(buffer, parentIDKeyBytes, newID);
          isParent = false;
        }
        id2subtree.insertID(buffer, parentIDKeyBytes, newID);
      }
      // Reindex the entry with the new ID.
      indexRemoveEntry(buffer, oldEntry, oldID);
      indexInsertEntry(buffer, newEntry, newID);
    }
    else
    {
@@ -2893,18 +2920,6 @@
      {
        indexModifications(buffer, oldEntry, newEntry, oldID, modifications);
      }
      if(isApexEntryMoved)
      {
        // Add the new ID to the id2s of new apex superior entries.
        for(DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn))
        {
          EntryID parentID = dn2id.get(txn, dn, LockMode.DEFAULT);
          byte[] parentIDKeyBytes =
            JebFormat.entryIDToDatabase(parentID.longValue());
          id2subtree.insertID(buffer, parentIDKeyBytes, newID);
        }
      }
    }
    // Remove the entry from the entry cache.
opends/src/server/org/opends/server/backends/jeb/Index.java
@@ -499,21 +499,6 @@
    OperationStatus status;
    DatabaseEntry data = new DatabaseEntry();
    // Handle cases where nothing is changed early to avoid
    // DB access.
    if(deletedIDs != null && deletedIDs.size() == 0 &&
        (addedIDs == null || addedIDs.size() == 0))
    {
      return;
    }
    if(addedIDs != null && addedIDs.size() == 0 &&
        (deletedIDs == null || deletedIDs.size() == 0))
    {
      return;
    }
    if(deletedIDs == null && addedIDs == null)
    {
      status = delete(txn, key);
@@ -532,6 +517,14 @@
      return;
    }
    // Handle cases where nothing is changed early to avoid
    // DB access.
    if((deletedIDs == null || deletedIDs.size() == 0) &&
        (addedIDs == null || addedIDs.size() == 0))
    {
      return;
    }
    if(maintainCount)
    {
      for(int i = 0; i < phantomWriteRetires; i++)
@@ -565,9 +558,7 @@
      }
      else
      {
        if(rebuildRunning || trusted)
        {
          if(deletedIDs != null)
        if(deletedIDs != null && !rebuildRunning && trusted)
          {
            if(debugEnabled())
            {
@@ -576,7 +567,13 @@
              TRACER.debugError("The expected key does not exist in the " +
                  "index %s.\nKey:%s", name, builder.toString());
            }
          setTrusted(txn, false);
          }
        if((rebuildRunning || trusted) && addedIDs != null &&
            addedIDs.size() > 0)
        {
          data.setData(addedIDs.toDatabase());
          status = insert(txn, key, data);
@@ -682,9 +679,7 @@
    }
    else
    {
      if(rebuildRunning || trusted)
      {
        if(deletedIDs != null)
      if(deletedIDs != null && !rebuildRunning && trusted)
        {
          if(debugEnabled())
          {
@@ -693,16 +688,18 @@
            TRACER.debugError("The expected key does not exist in the " +
                "index %s.\nKey:%s", name, builder.toString());
          }
        setTrusted(txn, false);
        }
      if((rebuildRunning || trusted) && addedIDs != null && addedIDs.size() > 0)
      {
        data.setData(addedIDs.toDatabase());
        return insert(txn, key, data);
      }
      else
      {
        return OperationStatus.SUCCESS;
      }
    }
  }
  /**
   * Add an remove entry ID operation into a index buffer.