mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
02.47.2014 d9b412170996aa617f6303104b7fc5ea0892de0a
OPENDJ-1090 ECL changenumbers get reset after a purge and server restart


Never trim the newest (last) record in the ChangeNumberIndexDB from production code.
This last record contains the last published change number, which is the one that must be read on startup and used to compute the following monotonically increasing change numbers.


JEChangeNumberIndexDB.java:
Improved and fixed javadocs.
In ctor, factorized code doing twice the same thing.
In addRecord(), updated the newestChangeNumber after a newest record is committed.
In clear(DN, AtomicBoolean), updated the oldestChangeNumber + do not trim the newest record from the DB.

JEChangeNumberIndexDBTest.java:
Updated testClear() to assert the newest record is never trimmed by production code.
2 files modified
56 ■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDB.java 41 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/je/JEChangeNumberIndexDBTest.java 15 ●●●● patch | view | raw | blame | history
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();
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);