From 9f0904fda87bfcf921deeccdbaeafe834fbad696 Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Fri, 24 Apr 2015 14:30:47 +0000
Subject: [PATCH] OPENDJ-1725: Persistit: very long recovery and many discarded txns after addrate test
---
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DN2ID.java | 201 ++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 185 insertions(+), 16 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 917c324..0daa986 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
@@ -27,13 +27,20 @@
package org.opends.server.backends.pluggable;
import static org.opends.server.backends.pluggable.JebFormat.*;
+import static org.opends.server.backends.pluggable.CursorTransformer.*;
+import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ByteStringBuilder;
+import org.forgerock.util.promise.Function;
+import org.opends.server.backends.pluggable.spi.Cursor;
import org.opends.server.backends.pluggable.spi.ReadableTransaction;
+import org.opends.server.backends.pluggable.spi.SequentialCursor;
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
/**
* This class represents the DN database, or dn2id, which has one record
@@ -42,19 +49,40 @@
*/
class DN2ID extends AbstractDatabaseContainer
{
- private final int prefixRDNComponents;
+ 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 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 final DN baseDN;
+
/**
* Create a DN2ID instance for the DN database in a given entryContainer.
*
* @param treeName The name of the DN database.
- * @param entryContainer The entryContainer of the DN database.
+ * @param baseDN The base DN of the database.
* @throws StorageRuntimeException If an error occurs in the database.
*/
- DN2ID(TreeName treeName, EntryContainer entryContainer) throws StorageRuntimeException
+ DN2ID(TreeName treeName, DN baseDN) throws StorageRuntimeException
{
super(treeName);
- this.prefixRDNComponents = entryContainer.getBaseDN().size();
+ this.baseDN = baseDN;
}
/**
@@ -65,11 +93,13 @@
* @throws StorageRuntimeException If an error occurred while attempting to insert
* the new record.
*/
- void put(WriteableTransaction txn, DN dn, EntryID id) throws StorageRuntimeException
+ void put(final WriteableTransaction txn, DN dn, final EntryID id) throws StorageRuntimeException
{
- ByteString key = dnToDNKey(dn, prefixRDNComponents);
- ByteString value = id.toByteString();
- txn.put(getName(), key, value);
+ txn.put(getName(), dnToKey(dn), id.toByteString());
+ }
+
+ private ByteString dnToKey(DN dn) {
+ return dnToDNKey(dn, baseDN.size());
}
/**
@@ -82,9 +112,7 @@
*/
boolean remove(WriteableTransaction txn, DN dn) throws StorageRuntimeException
{
- ByteString key = dnToDNKey(dn, prefixRDNComponents);
-
- return txn.delete(getName(), key);
+ return txn.delete(getName(), dnToKey(dn));
}
/**
@@ -96,12 +124,153 @@
*/
EntryID get(ReadableTransaction txn, DN dn) throws StorageRuntimeException
{
- ByteString key = dnToDNKey(dn, prefixRDNComponents);
- ByteString value = txn.read(getName(), key);
- if (value != null)
+ final ByteString value = txn.read(getName(), dnToKey(dn));
+ return value != null ? new EntryID(value) : null;
+ }
+
+ Cursor<Void, EntryID> openCursor(ReadableTransaction txn, DN dn)
+ {
+ return transformKeysAndValues(openCursor0(txn, dn), TO_VOID_KEY, TO_ENTRY_ID);
+ }
+
+ private Cursor<ByteString, ByteString> openCursor0(ReadableTransaction txn, DN dn) {
+ final Cursor<ByteString, ByteString> cursor = txn.openCursor(getName());
+ cursor.positionToKey(dnToKey(dn));
+ return cursor;
+ }
+
+ SequentialCursor<Void, EntryID> openChildrenCursor(ReadableTransaction txn, DN dn)
+ {
+ return new ChildrenCursor(openCursor0(txn, dn));
+ }
+
+ SequentialCursor<Void, EntryID> openSubordinatesCursor(ReadableTransaction txn, DN dn) {
+ return new SubtreeCursor(openCursor0(txn, dn));
+ }
+
+
+ /**
+ * Check if two DN have a parent-child relationship.
+ *
+ * @param parent
+ * The potential parent
+ * @param child
+ * The potential child of parent
+ * @return true if child is a direct children of parent, false otherwise.
+ */
+ static boolean isChild(ByteSequence parent, ByteSequence child)
+ {
+ if (!child.startsWith(parent))
{
- return new EntryID(value);
+ return false;
}
- return null;
+ // Immediate children should only have one RDN separator past the parent length
+ for (int i = child.length(); i >= parent.length(); i--)
+ {
+ if (child.byteAt(i) == DN.NORMALIZED_RDN_SEPARATOR && i != parent.length())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 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 final ByteStringBuilder builder;
+ private final ByteString parentDN;
+ private boolean cursorOnParent;
+
+ ChildrenCursor(Cursor<ByteString, ByteString> delegate)
+ {
+ super(delegate);
+ builder = new ByteStringBuilder(128);
+ parentDN = delegate.isDefined() ? delegate.getKey() : null;
+ cursorOnParent = true;
+ }
+
+ @Override
+ public boolean next()
+ {
+ if (cursorOnParent) {
+ /** Go to the first children */
+ delegate.next();
+ cursorOnParent = false;
+ } else {
+ /** Go to the next sibling */
+ delegate.positionToKeyOrNext(nextSibling());
+ }
+ return isDefined() && delegate.getKey().startsWith(parentDN);
+ }
+
+ private ByteStringBuilder nextSibling()
+ {
+ return builder.clear().append(delegate.getKey()).append((byte) 0x1);
+ }
+ }
+
+ /**
+ * 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 final ByteString baseDN;
+
+ SubtreeCursor(Cursor<ByteString, ByteString> delegate)
+ {
+ super(delegate);
+ baseDN = delegate.isDefined() ? delegate.getKey() : null;
+ }
+
+ @Override
+ public boolean next()
+ {
+ return delegate.next() && delegate.getKey().startsWith(baseDN);
+ }
+ }
+
+ /**
+ * 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;
+
+ SequentialCursorForwarding(Cursor<ByteString, ByteString> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean isDefined()
+ {
+ return delegate.isDefined();
+ }
+
+ @Override
+ public boolean next()
+ {
+ return delegate.next();
+ }
+
+ @Override
+ public Void getKey()
+ {
+ return null;
+ }
+
+ @Override
+ public EntryID getValue()
+ {
+ return new EntryID(delegate.getValue());
+ }
+
+ @Override
+ public void close()
+ {
+ delegate.close();
+ }
}
}
--
Gitblit v1.10.0