From d9b412170996aa617f6303104b7fc5ea0892de0a Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 02 Jan 2014 15:47:14 +0000
Subject: [PATCH] OPENDJ-1090 ECL changenumbers get reset after a purge and server restart
---
opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java | 41 +++++++++++++++++++++++++++++++----------
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java | 15 +++++++++++++--
2 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java b/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java
index 166accf..8ae8161 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2009-2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2013 ForgeRock AS
+ * Portions Copyright 2011-2014 ForgeRock AS
*/
package org.opends.server.replication.server.changelog.je;
@@ -71,17 +71,25 @@
private static int NO_KEY = 0;
private DraftCNDB db;
- /**
- * FIXME Is this field that useful? {@link #getOldestChangeNumber()} does not
- * even use it!
- */
+ /** FIXME What is this field used for? */
private volatile long oldestChangeNumber = NO_KEY;
/**
- * FIXME Is this field that useful? {@link #getNewestChangeNumber()} does not
- * even use it!
+ * The newest changenumber stored in the DB. It is used to avoid trimming the
+ * record with the newest changenumber. The newest record in the changenumber
+ * index DB is used to persist the {@link #lastGeneratedChangeNumber} which is
+ * then retrieved on server startup.
*/
private volatile long newestChangeNumber = NO_KEY;
- /** The last generated value for the change number. */
+ /**
+ * The last generated value for the change number. It is kept separate from
+ * the {@link #newestChangeNumber} because there is an opportunity for a race
+ * condition between:
+ * <ol>
+ * <li>this atomic long being incremented for a new record ('recordB')</li>
+ * <li>the current newest record ('recordA') being trimmed from the DB</li>
+ * <li>'recordB' failing to be inserted in the DB</li>
+ * </ol>
+ */
private final AtomicLong lastGeneratedChangeNumber;
private DbMonitorProvider dbMonitor = new DbMonitorProvider();
private final AtomicBoolean shutdown = new AtomicBoolean(false);
@@ -124,10 +132,10 @@
final ChangeNumberIndexRecord oldestRecord = db.readFirstRecord();
final ChangeNumberIndexRecord newestRecord = db.readLastRecord();
oldestChangeNumber = getChangeNumber(oldestRecord);
- newestChangeNumber = getChangeNumber(newestRecord);
+ final long newestCN = getChangeNumber(newestRecord);
+ newestChangeNumber = newestCN;
// initialization of the lastGeneratedChangeNumber from the DB content
// if DB is empty => last record does not exist => default to 0
- long newestCN = (newestRecord != null) ? newestRecord.getChangeNumber() : 0;
lastGeneratedChangeNumber = new AtomicLong(newestCN);
// Monitoring registration
@@ -165,6 +173,7 @@
new ChangeNumberIndexRecord(changeNumber, record.getPreviousCookie(),
record.getBaseDN(), record.getCSN());
db.addRecord(newRecord);
+ newestChangeNumber = changeNumber;
if (debugEnabled())
TRACER.debugInfo("In JEChangeNumberIndexDB.add, added: " + newRecord);
@@ -383,6 +392,18 @@
}
final ChangeNumberIndexRecord record = cursor.currentRecord();
+ if (record.getChangeNumber() != oldestChangeNumber)
+ {
+ oldestChangeNumber = record.getChangeNumber();
+ }
+ if (record.getChangeNumber() == newestChangeNumber)
+ {
+ // do not trim the newest record to avoid having the last generated
+ // changenumber dropping back to 0 if the server restarts
+ cursor.close();
+ return;
+ }
+
if (baseDNToClear != null && baseDNToClear.equals(record.getBaseDN()))
{
cursor.delete();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java
index 72f04ae..e1446e4 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2009-2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2013 ForgeRock AS
+ * Portions Copyright 2011-2014 ForgeRock AS
*/
package org.opends.server.replication.server.changelog.je;
@@ -219,9 +219,20 @@
cursor = cnIndexDB.getCursorFrom(cn3);
assertCursorReadsInOrder(cursor, cn3);
- cnIndexDB.clear();
+ // check only the last record is left
+ cnIndexDB.clear(null);
+ final ChangeNumberIndexRecord oldest = cnIndexDB.getOldestRecord();
+ final ChangeNumberIndexRecord newest = cnIndexDB.getNewestRecord();
+ assertEquals(oldest.getChangeNumber(), 3);
+ assertEquals(oldest.getChangeNumber(), newest.getChangeNumber());
+ assertEquals(oldest.getPreviousCookie(), newest.getPreviousCookie());
+ assertEquals(oldest.getBaseDN(), newest.getBaseDN());
+ assertEquals(oldest.getCSN(), newest.getCSN());
+ assertEquals(cnIndexDB.count(), 1);
+ assertFalse(cnIndexDB.isEmpty());
// Check the db is cleared.
+ cnIndexDB.clear();
assertNull(cnIndexDB.getOldestRecord());
assertNull(cnIndexDB.getNewestRecord());
assertEquals(cnIndexDB.count(), 0);
--
Gitblit v1.10.0