From 3f27a7ede5ca9df06137254aa32d41d023ac105d Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Tue, 16 Sep 2014 15:05:25 +0000
Subject: [PATCH] OPENDJ-1444 CR-4537 Remove previous cookie from storage of ChangeNumberIndexDB
---
opends/src/server/org/opends/server/replication/server/changelog/je/ReplicationDB.java | 143 ++++++++++++++++++++++++++++++++++++-----------
1 files changed, 108 insertions(+), 35 deletions(-)
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/je/ReplicationDB.java b/opends/src/server/org/opends/server/replication/server/changelog/je/ReplicationDB.java
index c04ffef..8877fcd 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/je/ReplicationDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/je/ReplicationDB.java
@@ -39,6 +39,7 @@
import org.opends.server.replication.server.ReplicationServerDomain;
import org.opends.server.replication.server.changelog.api.ChangelogException;
import org.opends.server.replication.server.changelog.api.DBCursor;
+import org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy;
import org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy;
import org.opends.server.types.DN;
import org.opends.server.util.StaticUtils;
@@ -50,6 +51,8 @@
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy.*;
+import static org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy.*;
import static org.opends.server.util.StaticUtils.*;
/**
@@ -289,15 +292,18 @@
* @param startCSN
* The CSN from which the cursor must start.If null, start from the
* oldest CSN
+ * @param matchingStrategy
+ * Cursor key matching strategy
* @param positionStrategy
- * indicates at which exact position the cursor must start
+ * Cursor position strategy
* @return The ReplServerDBCursor.
* @throws ChangelogException
* If a database problem happened
*/
- ReplServerDBCursor openReadCursor(CSN startCSN, PositionStrategy positionStrategy) throws ChangelogException
+ ReplServerDBCursor openReadCursor(CSN startCSN, KeyMatchingStrategy matchingStrategy,
+ PositionStrategy positionStrategy) throws ChangelogException
{
- return new ReplServerDBCursor(startCSN, positionStrategy);
+ return new ReplServerDBCursor(startCSN, matchingStrategy, positionStrategy);
}
/**
@@ -447,6 +453,31 @@
return serverId + " " + baseDN.toNormalizedString();
}
+ /** Hold a cursor and an indicator of wether the cursor should be considered as empty. */
+ private static class CursorWithEmptyIndicator
+ {
+ private Cursor cursor;
+ private boolean isEmpty;
+
+ private CursorWithEmptyIndicator(Cursor localCursor, boolean isEmpty)
+ {
+ this.cursor = localCursor;
+ this.isEmpty = isEmpty;
+ }
+
+ /** Creates cursor considered as empty. */
+ static CursorWithEmptyIndicator createEmpty(Cursor cursor)
+ {
+ return new CursorWithEmptyIndicator(cursor, true);
+ }
+
+ /** Creates cursor considered as non-empty. */
+ static CursorWithEmptyIndicator createNonEmpty(Cursor cursor)
+ {
+ return new CursorWithEmptyIndicator(cursor, false);
+ }
+ }
+
/**
* This Class implements a cursor that can be used to browse a
* replicationServer database.
@@ -460,7 +491,7 @@
* <p>
* Will be set non null for a write cursor
*/
- private final Cursor cursor;
+ private Cursor cursor;
private final DatabaseEntry key;
private final DatabaseEntry data;
/** \@Null for read cursors, \@NotNull for deleting cursors. */
@@ -475,12 +506,16 @@
*
* @param startCSN
* The CSN from which the cursor must start.
+ * @param matchingStrategy
+ * Cursor key matching strategy, which allow to indicates how key
+ * is matched
* @param positionStrategy
* indicates at which exact position the cursor must start
* @throws ChangelogException
* When the startCSN does not exist.
*/
- private ReplServerDBCursor(CSN startCSN, PositionStrategy positionStrategy) throws ChangelogException
+ private ReplServerDBCursor(CSN startCSN, KeyMatchingStrategy matchingStrategy, PositionStrategy positionStrategy)
+ throws ChangelogException
{
key = createReplicationKey(startCSN);
data = new DatabaseEntry();
@@ -491,8 +526,7 @@
// unlock it when throwing an exception.
dbCloseLock.readLock().lock();
- boolean cursorHeld = false;
- Cursor localCursor = null;
+ CursorWithEmptyIndicator maybeEmptyCursor = null;
try
{
// If the DB has been closed then create empty cursor.
@@ -503,35 +537,15 @@
return;
}
- localCursor = db.openCursor(txn, null);
- if (startCSN != null
- && localCursor.getSearchKey(key, data, LockMode.DEFAULT) != SUCCESS)
+ maybeEmptyCursor = generateCursor(startCSN, matchingStrategy, positionStrategy);
+ if (maybeEmptyCursor.isEmpty)
{
- // We could not move the cursor to the expected startCSN
- if (localCursor.getSearchKeyRange(key, data, DEFAULT) != SUCCESS)
- {
- // We could not even move the cursor close to it
- // => return empty cursor
- isClosed = true;
- cursor = null;
- return;
- }
-
- if (positionStrategy == PositionStrategy.AFTER_MATCHING_KEY)
- {
- // We can move close to the startCSN.
- // Let's create a cursor from that point.
- key.setData(null);
- if (localCursor.getPrev(key, data, LockMode.DEFAULT) != SUCCESS)
- {
- localCursor.close();
- localCursor = db.openCursor(txn, null);
- }
- }
+ isClosed = true;
+ cursor = null;
+ return;
}
- cursor = localCursor;
- cursorHeld = cursor != null;
+ cursor = maybeEmptyCursor.cursor;
if (key.getData() != null)
{
computeCurrentRecord();
@@ -543,13 +557,72 @@
}
finally
{
- if (!cursorHeld)
+ if (maybeEmptyCursor != null && maybeEmptyCursor.isEmpty)
{
- closeAndReleaseReadLock(localCursor);
+ closeAndReleaseReadLock(maybeEmptyCursor.cursor);
}
}
}
+ /** Generate a possibly empty cursor with the provided start CSN and strategies. */
+ private CursorWithEmptyIndicator generateCursor(CSN startCSN, KeyMatchingStrategy matchingStrategy,
+ PositionStrategy positionStrategy)
+ {
+ Cursor cursor = db.openCursor(txn, null);
+ boolean isCsnFound = startCSN == null || cursor.getSearchKey(key, data, LockMode.DEFAULT) == SUCCESS;
+ if (!isCsnFound)
+ {
+ if (matchingStrategy == EQUAL_TO_KEY)
+ {
+ return CursorWithEmptyIndicator.createEmpty(cursor);
+ }
+
+ boolean isGreaterCsnFound = cursor.getSearchKeyRange(key, data, DEFAULT) == SUCCESS;
+ if (isGreaterCsnFound)
+ {
+ if (matchingStrategy == GREATER_THAN_OR_EQUAL_TO_KEY && positionStrategy == AFTER_MATCHING_KEY)
+ {
+ // Move backward so that the first call to next() points to this greater csn
+ key.setData(null);
+ if (cursor.getPrev(key, data, LockMode.DEFAULT) != SUCCESS)
+ {
+ // Edge case: we're at the beginning of the database
+ cursor.close();
+ cursor = db.openCursor(txn, null);
+ }
+ }
+ else if (matchingStrategy == LESS_THAN_OR_EQUAL_TO_KEY)
+ {
+ // Move backward to point on the lower csn
+ key.setData(null);
+ if (cursor.getPrev(key, data, LockMode.DEFAULT) != SUCCESS)
+ {
+ // Edge case: we're at the beginning of the log, there is no lower csn
+ return CursorWithEmptyIndicator.createEmpty(cursor);
+ }
+ }
+ }
+ else
+ {
+ if (matchingStrategy == GREATER_THAN_OR_EQUAL_TO_KEY)
+ {
+ // There is no greater csn
+ return CursorWithEmptyIndicator.createEmpty(cursor);
+ }
+ // LESS_THAN_OR_EQUAL_TO_KEY case : the lower csn is the highest csn available
+ key.setData(null);
+ boolean isLastKeyFound = cursor.getLast(key, data, LockMode.DEFAULT) == SUCCESS;
+ if (!isLastKeyFound)
+ {
+ // Edge case: empty database
+ cursor.close();
+ cursor = db.openCursor(txn, null);
+ }
+ }
+ }
+ return CursorWithEmptyIndicator.createNonEmpty(cursor);
+ }
+
private ReplServerDBCursor() throws ChangelogException
{
key = new DatabaseEntry();
--
Gitblit v1.10.0