From b88a555f5a584c355442ee8db6d218c9fe95fa36 Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <ylecaillez@forgerock.com>
Date: Tue, 17 Nov 2015 10:16:04 +0000
Subject: [PATCH] OPENDJ-2393: Possible index corruption

---
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java |   43 ++++++++++++++++++++++++++++++++++++-------
 1 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
index 9ff764e..b4028c1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -161,7 +161,7 @@
   /** The entry tree maps an entry ID (8 bytes) to a complete encoded entry. */
   private ID2Entry id2entry;
   /** Store the number of children for each entry. */
-  private final ID2Count id2childrenCount;
+  private final ID2ChildrenCount id2childrenCount;
   /** The referral tree maps a normalized DN string to labeled URIs. */
   private final DN2URI dn2uri;
   /** The state tree maps a config DN to config entries. */
@@ -416,7 +416,7 @@
     this.storage = storage;
     this.rootContainer = rootContainer;
     this.treePrefix = baseDN.toNormalizedUrlSafeString();
-    this.id2childrenCount = new ID2Count(getIndexName(ID2CHILDREN_COUNT_TREE_NAME));
+    this.id2childrenCount = new ID2ChildrenCount(getIndexName(ID2CHILDREN_COUNT_TREE_NAME));
     this.dn2id = new DN2ID(getIndexName(DN2ID_TREE_NAME), baseDN);
     this.dn2uri = new DN2URI(getIndexName(REFERRAL_TREE_NAME), this);
     this.state = new State(getIndexName(STATE_TREE_NAME));
@@ -565,7 +565,7 @@
    *
    * @return The children tree.
    */
-  ID2Count getID2ChildrenCount()
+  ID2ChildrenCount getID2ChildrenCount()
   {
     return id2childrenCount;
   }
@@ -1458,6 +1458,7 @@
         @Override
         public void run(WriteableTransaction txn) throws Exception
         {
+          // No need to call indexBuffer.reset() since IndexBuffer content will be the same for each retry attempt.
           try
           {
             // Check whether the entry already exists.
@@ -1515,6 +1516,7 @@
     }
     catch (Exception e)
     {
+      writeTrustState(indexBuffer);
       throwAllowedExceptionTypes(e, DirectoryException.class, CanceledOperationException.class);
     }
 
@@ -1525,6 +1527,28 @@
     }
   }
 
+  private void writeTrustState(final IndexBuffer indexBuffer)
+  {
+    // Transaction modifying the index has been rolled back.
+    // Ensure that the index trusted state is persisted.
+    try
+    {
+      storage.write(new WriteOperation()
+      {
+        @Override
+        public void run(WriteableTransaction txn) throws Exception
+        {
+          indexBuffer.writeTrustState(txn);
+        }
+      });
+    }
+    catch (Exception e)
+    {
+      // Cannot throw because this method is used in a catch block and we do not want to hide the real exception.
+      logger.traceException(e);
+    }
+  }
+
   void importEntry(WriteableTransaction txn, EntryID entryID, Entry entry) throws DirectoryException,
       StorageRuntimeException
   {
@@ -1555,6 +1579,7 @@
   void deleteEntry(final DN entryDN, final DeleteOperation deleteOperation)
           throws DirectoryException, StorageRuntimeException, CanceledOperationException
   {
+    final IndexBuffer indexBuffer = new IndexBuffer();
     try
     {
       storage.write(new WriteOperation()
@@ -1562,6 +1587,7 @@
         @Override
         public void run(WriteableTransaction txn) throws Exception
         {
+          indexBuffer.reset();
           try
           {
             // Check for referral entries above the target entry.
@@ -1629,7 +1655,6 @@
 
             // Now update id2entry, dn2uri, and id2childrenCount in key order.
             id2childrenCount.updateCount(txn, parentID, -1);
-            final IndexBuffer indexBuffer = new IndexBuffer();
             final EntryCache<?> entryCache = DirectoryServer.getEntryCache();
             boolean isBaseEntry = true;
             try (final Cursor<EntryID, Entry> cursor = id2entry.openCursor(txn))
@@ -1713,6 +1738,7 @@
     }
     catch (Exception e)
     {
+      writeTrustState(indexBuffer);
       throwAllowedExceptionTypes(e, DirectoryException.class, CanceledOperationException.class);
     }
   }
@@ -1844,6 +1870,7 @@
   void replaceEntry(final Entry oldEntry, final Entry newEntry, final ModifyOperation modifyOperation)
       throws StorageRuntimeException, DirectoryException, CanceledOperationException
   {
+    final IndexBuffer indexBuffer = new IndexBuffer();
     try
     {
       storage.write(new WriteOperation()
@@ -1851,6 +1878,7 @@
         @Override
         public void run(WriteableTransaction txn) throws Exception
         {
+          indexBuffer.reset();
           try
           {
             EntryID entryID = dn2id.get(txn, newEntry.getName());
@@ -1883,9 +1911,7 @@
               dn2uri.replaceEntry(txn, oldEntry, newEntry);
             }
 
-
             // Update the indexes.
-            final IndexBuffer indexBuffer = new IndexBuffer();
             if (modifyOperation != null)
             {
               // In this case we know from the operation what the modifications were.
@@ -1933,6 +1959,7 @@
     }
     catch (Exception e)
     {
+      writeTrustState(indexBuffer);
       throwAllowedExceptionTypes(e, DirectoryException.class, CanceledOperationException.class);
     }
   }
@@ -1960,6 +1987,7 @@
   void renameEntry(final DN oldTargetDN, final Entry newTargetEntry, final ModifyDNOperation modifyDNOperation)
       throws StorageRuntimeException, DirectoryException, CanceledOperationException
   {
+    final IndexBuffer indexBuffer = new IndexBuffer();
     try
     {
       storage.write(new WriteOperation()
@@ -1967,6 +1995,7 @@
         @Override
         public void run(WriteableTransaction txn) throws Exception
         {
+          indexBuffer.reset();
           try
           {
             // Validate the request.
@@ -2027,7 +2056,6 @@
               id2childrenCount.updateCount(txn, oldSuperiorID, -1);
               id2childrenCount.updateCount(txn, newSuperiorID, 1);
             }
-            final IndexBuffer indexBuffer = new IndexBuffer();
             boolean isBaseEntry = true;
             try (final Cursor<EntryID, Entry> cursor = id2entry.openCursor(txn))
             {
@@ -2178,6 +2206,7 @@
     }
     catch (Exception e)
     {
+      writeTrustState(indexBuffer);
       throwAllowedExceptionTypes(e, DirectoryException.class, CanceledOperationException.class);
     }
   }

--
Gitblit v1.10.0