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

Jean-Noel Rouvignac
23.42.2013 62a518f7d2b432b072de8fdbb97976fb257953cb
OPENDJ-1262 NPE in ChangeNumberIndex during server startup


The problem was due to incorrect code factorization around cursor creation:
When creating the cursors, the startAfterCSN to use in different depending on where we come from:
- during ChangeNumberIndexer initialization, the cursors are already created from the previous cookie, so the provided CSN is the one to use
- on publishUpdateMsg(), we want the cursor to start exactly with the provided CSN, so we need to create the cursor with the immediately preceding CSN.


ChangeNumberIndexer.java:
In ensureCursorExists(), added boolean parameter startFromPrecedingCSN.
Added lots of comments.
1 files modified
21 ■■■■■ changed files
opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/changelog/je/ChangeNumberIndexer.java
@@ -296,6 +296,8 @@
      // restore the mediumConsistencyRUV from DB
      mediumConsistencyRUV.update(
          new MultiDomainServerState(newestRecord.getPreviousCookie()));
      // Do not update with the newestRecord CSN
      // as it will be used for a sanity check later in the same method
    }
    // initialize the DB cursor and the last seen updates
@@ -313,7 +315,7 @@
      for (Integer serverId : entry.getValue())
      {
        final CSN csn = mediumConsistencyRUV.getCSN(baseDN, serverId);
        ensureCursorExists(baseDN, serverId, csn);
        ensureCursorExists(baseDN, serverId, csn, false);
      }
      ServerState latestKnownState = domainDB.getDomainNewestCSNs(baseDN);
@@ -325,12 +327,15 @@
    {
      // restore the "previousCookie" state before shutdown
      final UpdateMsg record = nextChangeForInsertDBCursor.getRecord();
      // sanity check: ensure that when initializing the cursors at the previous
      // cookie, the next change we find is the newest record in the CNIndexDB
      if (!record.getCSN().equals(newestRecord.getCSN()))
      {
        throw new ChangelogException(
            ERR_CHANGE_NUMBER_INDEXER_INCONSISTENT_CSN_READ.get(newestRecord
                .getCSN().toStringUI(), record.getCSN().toStringUI()));
      }
      // Now we can update the mediumConsistencyRUV
      mediumConsistencyRUV.update(newestRecord.getBaseDN(), record.getCSN());
      nextChangeForInsertDBCursor.next();
    }
@@ -357,8 +362,8 @@
    nextChangeForInsertDBCursor = result;
  }
  private boolean ensureCursorExists(DN baseDN, Integer serverId, CSN csn)
      throws ChangelogException
  private boolean ensureCursorExists(DN baseDN, Integer serverId, CSN csn,
      boolean startFromPrecedingCSN) throws ChangelogException
  {
    Map<Integer, DBCursor<UpdateMsg>> map = allCursors.get(baseDN);
    if (map == null)
@@ -370,9 +375,11 @@
    if (cursor == null)
    {
      final ReplicationDomainDB domainDB = changelogDB.getReplicationDomainDB();
      // use an older CSN because getCursorFrom() starts after the given CSN
      final CSN anOlderCSN = getPrecedingCSN(csn);
      cursor = domainDB.getCursorFrom(baseDN, serverId, anOlderCSN);
      // start from preceding CSN for publishUpdateMsg(),
      // or from the actual CSN when initializing from the previous cookie
      final CSN startAfterCSN =
          startFromPrecedingCSN ? getPrecedingCSN(csn) : csn;
      cursor = domainDB.getCursorFrom(baseDN, serverId, startAfterCSN);
      map.put(serverId, cursor);
      return false;
    }
@@ -595,7 +602,7 @@
        final Entry<Pair<DN, Integer>, CSN> entry = iter.next();
        final DN baseDN = entry.getKey().getFirst();
        final CSN csn = entry.getValue();
        if (!ensureCursorExists(baseDN, csn.getServerId(), csn))
        if (!ensureCursorExists(baseDN, csn.getServerId(), csn, true))
        {
          newCursorAdded = true;
        }