From 641e89ef0e15c9edde69f3b8cf82c7dd5f68687a Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <ylecaillez@forgerock.com>
Date: Wed, 30 Sep 2015 14:28:07 +0000
Subject: [PATCH] OPENDJ-2016: New on disk merge import strategy based on storage engine.

---
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java |  149 ++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 108 insertions(+), 41 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java
index 4db360b..4f64dd1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java
@@ -29,10 +29,16 @@
 import static org.opends.server.backends.pluggable.CursorTransformer.*;
 import static org.opends.server.backends.pluggable.DnKeyFormat.*;
 
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
 import org.forgerock.opendj.ldap.ByteSequence;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ByteStringBuilder;
+import org.forgerock.opendj.ldap.Functions;
 import org.forgerock.util.Function;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.opends.server.backends.pluggable.OnDiskMergeImporter.SequentialCursorDecorator;
 import org.opends.server.backends.pluggable.spi.Cursor;
 import org.opends.server.backends.pluggable.spi.ReadableTransaction;
 import org.opends.server.backends.pluggable.spi.SequentialCursor;
@@ -41,7 +47,6 @@
 import org.opends.server.backends.pluggable.spi.UpdateFunction;
 import org.opends.server.backends.pluggable.spi.WriteableTransaction;
 import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
 
 /**
  * This class represents the dn2id index, which has one record
@@ -51,25 +56,18 @@
 @SuppressWarnings("javadoc")
 class DN2ID extends AbstractTree
 {
-  private static final Function<ByteString, Void, DirectoryException> TO_VOID_KEY =
-      new Function<ByteString, Void, DirectoryException>()
-      {
-        @Override
-        public Void apply(ByteString value) throws DirectoryException
-        {
-          return null;
-        }
-      };
+  private static final Function<ByteString, Void, NeverThrowsException> TO_VOID_KEY = Functions.returns(null);
 
-  private static final CursorTransformer.ValueTransformer<ByteString, ByteString, EntryID, Exception> TO_ENTRY_ID =
-      new CursorTransformer.ValueTransformer<ByteString, ByteString, EntryID, Exception>()
-      {
-        @Override
-        public EntryID transform(ByteString key, ByteString value) throws Exception
-        {
-          return new EntryID(value);
-        }
-      };
+  private static final CursorTransformer.ValueTransformer<ByteString, ByteString, EntryID, NeverThrowsException>
+     TO_ENTRY_ID =
+          new CursorTransformer.ValueTransformer<ByteString, ByteString, EntryID, NeverThrowsException>()
+          {
+            @Override
+            public EntryID transform(ByteString key, ByteString value)
+            {
+              return new EntryID(value);
+            }
+          };
 
   private final DN baseDN;
 
@@ -154,6 +152,12 @@
     return value != null ? new EntryID(value) : null;
   }
 
+  <V> SequentialCursor<ByteString, ByteString> openCursor(SequentialCursor<ByteString, ByteString> dn2IdCursor,
+      TreeVisitor<V> treeVisitor)
+  {
+    return new TreeVisitorCursor<>(dn2IdCursor, treeVisitor);
+  }
+
   Cursor<Void, EntryID> openCursor(ReadableTransaction txn, DN dn)
   {
     return transformKeysAndValues(openCursor0(txn, dn), TO_VOID_KEY, TO_ENTRY_ID);
@@ -167,11 +171,11 @@
 
   SequentialCursor<Void, EntryID> openChildrenCursor(ReadableTransaction txn, DN dn)
   {
-    return new ChildrenCursor(openCursor0(txn, dn));
+    return transformKeysAndValues(new ChildrenCursor(openCursor0(txn, dn)), TO_VOID_KEY, TO_ENTRY_ID);
   }
 
   SequentialCursor<Void, EntryID> openSubordinatesCursor(ReadableTransaction txn, DN dn) {
-    return new SubtreeCursor(openCursor0(txn, dn));
+    return transformKeysAndValues(new SubtreeCursor(openCursor0(txn, dn)), TO_VOID_KEY, TO_ENTRY_ID);
   }
 
   /**
@@ -209,7 +213,9 @@
    * Decorator overriding the next() behavior to iterate through children of the entry pointed by the given cursor at
    * creation.
    */
-  private static final class ChildrenCursor extends SequentialCursorForwarding {
+  private static final class ChildrenCursor extends
+      SequentialCursorDecorator<Cursor<ByteString, ByteString>, ByteString, ByteString>
+  {
     private final ByteStringBuilder builder;
     private final ByteString limit;
     private boolean cursorOnParent;
@@ -236,7 +242,7 @@
       return isDefined() && delegate.getKey().compareTo(limit) < 0;
     }
 
-    private ByteStringBuilder nextSibling()
+    private ByteSequence nextSibling()
     {
       return builder.clear().append(delegate.getKey()).append((byte) 0x1);
     }
@@ -246,7 +252,9 @@
    * Decorator overriding the next() behavior to iterate through subordinates of the entry pointed by the given cursor
    * at creation.
    */
-  private static final class SubtreeCursor extends SequentialCursorForwarding {
+  private static final class SubtreeCursor extends
+      SequentialCursorDecorator<Cursor<ByteString, ByteString>, ByteString, ByteString>
+  {
     private final ByteString limit;
 
     SubtreeCursor(Cursor<ByteString, ByteString> delegate)
@@ -262,15 +270,79 @@
     }
   }
 
-  /**
-   * Decorator allowing to partially overrides methods of a given cursor while keeping the default behavior for other
-   * methods.
-   */
-  private static class SequentialCursorForwarding implements SequentialCursor<Void, EntryID> {
-    final Cursor<ByteString, ByteString> delegate;
+  /** Keep track of information during the visit. */
+  private static final class ParentInfo<V>
+  {
+    private final ByteString parentDN;
+    private final V visitorData;
 
-    SequentialCursorForwarding(Cursor<ByteString, ByteString> delegate) {
+    ParentInfo(ByteString parentDN, V visitorData)
+    {
+      this.parentDN = parentDN;
+      this.visitorData = visitorData;
+    }
+  }
+
+  /** Allows to visit dn2id tree without exposing internal encoding. */
+  static interface TreeVisitor<V>
+  {
+    V beginParent(EntryID parentID);
+
+    void onChild(V parent, EntryID childID);
+
+    void endParent(V parent);
+  }
+
+  /** Perform dn2id cursoring to expose parent and children to the {@link TreeVisitor} */
+  private static final class TreeVisitorCursor<V> implements SequentialCursor<ByteString, ByteString>
+  {
+    private final SequentialCursor<ByteString, ByteString> delegate;
+    private final LinkedList<ParentInfo<V>> parentsInfoStack;
+    private final TreeVisitor<V> visitor;
+
+    TreeVisitorCursor(SequentialCursor<ByteString, ByteString> delegate, TreeVisitor<V> visitor)
+    {
       this.delegate = delegate;
+      this.parentsInfoStack = new LinkedList<>();
+      this.visitor = visitor;
+    }
+
+    @Override
+    public boolean next()
+    {
+      if (delegate.next())
+      {
+        final ByteString dn = delegate.getKey();
+        final EntryID entryID = new EntryID(delegate.getValue());
+        popCompleteParents(dn);
+        notifyChild(entryID);
+        pushNewParent(dn, entryID);
+        return true;
+      }
+      popCompleteParents(DN.NULL_DN.toNormalizedByteString());
+      return false;
+    }
+
+    private void pushNewParent(final ByteString dn, final EntryID entryID)
+    {
+      parentsInfoStack.push(new ParentInfo<>(dn, visitor.beginParent(entryID)));
+    }
+
+    private void notifyChild(final EntryID entryID)
+    {
+      if (!parentsInfoStack.isEmpty())
+      {
+        visitor.onChild(parentsInfoStack.peek().visitorData, entryID);
+      }
+    }
+
+    private void popCompleteParents(ByteString dn)
+    {
+      ParentInfo<V> currentParent;
+      while ((currentParent = parentsInfoStack.peek()) != null && !isChild(currentParent.parentDN, dn))
+      {
+        visitor.endParent(parentsInfoStack.pop().visitorData);
+      }
     }
 
     @Override
@@ -280,21 +352,15 @@
     }
 
     @Override
-    public boolean next()
+    public ByteString getKey() throws NoSuchElementException
     {
-      return delegate.next();
+      return delegate.getKey();
     }
 
     @Override
-    public Void getKey()
+    public ByteString getValue() throws NoSuchElementException
     {
-      return null;
-    }
-
-    @Override
-    public EntryID getValue()
-    {
-      return new EntryID(delegate.getValue());
+      return delegate.getValue();
     }
 
     @Override
@@ -303,4 +369,5 @@
       delegate.close();
     }
   }
+
 }

--
Gitblit v1.10.0