| | |
| | | */ |
| | | package org.opends.server.replication.server; |
| | | |
| | | import static com.sleepycat.je.LockMode.*; |
| | | import static com.sleepycat.je.OperationStatus.*; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | |
| | | */ |
| | | public void addEntries(List<UpdateMsg> changes) |
| | | { |
| | | try |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | | try |
| | | { |
| | | // If the DB has been closed then return immediately. |
| | |
| | | counterCurrValue++; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | dbCloseLock.readLock().unlock(); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | replicationServer.handleUnexpectedDatabaseException(e); |
| | | } |
| | | finally |
| | | { |
| | | dbCloseLock.readLock().unlock(); |
| | | } |
| | | } |
| | | |
| | | private void insertCounterRecordIfNeeded(ChangeNumber changeNumber) |
| | |
| | | */ |
| | | public void shutdown() |
| | | { |
| | | try |
| | | { |
| | | dbCloseLock.writeLock().lock(); |
| | | try |
| | | { |
| | | db.close(); |
| | | db = null; |
| | | } |
| | | finally |
| | | { |
| | | dbCloseLock.writeLock().unlock(); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | MessageBuilder mb = new MessageBuilder(); |
| | |
| | | mb.append(stackTraceToSingleLineString(e)); |
| | | logError(mb.toMessage()); |
| | | } |
| | | finally |
| | | { |
| | | dbCloseLock.writeLock().unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public ChangeNumber readFirstChange() |
| | | { |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | // If the DB has been closed then return immediately. |
| | |
| | | |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | LockMode defaultMode = LockMode.DEFAULT; |
| | | if (cursor.getFirst(key, data, defaultMode) != OperationStatus.SUCCESS) |
| | | if (cursor.getFirst(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // database is empty |
| | | return null; |
| | |
| | | } |
| | | |
| | | // First record is a counter record .. go next |
| | | if (cursor.getNext(key, data, defaultMode) != OperationStatus.SUCCESS) |
| | | if (cursor.getNext(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // DB contains only a counter record |
| | | return null; |
| | |
| | | // it is safe to return this record |
| | | return toChangeNumber(key.getData()); |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | replicationServer.handleUnexpectedDatabaseException(e); |
| | | return null; |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public ChangeNumber readLastChange() |
| | | { |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | // If the DB has been closed then return immediately. |
| | |
| | | |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | LockMode defaultMode = LockMode.DEFAULT; |
| | | if (cursor.getLast(key, data, defaultMode) != OperationStatus.SUCCESS) |
| | | if (cursor.getLast(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // database is empty |
| | | return null; |
| | |
| | | return cn; |
| | | } |
| | | |
| | | if (cursor.getPrev(key, data, defaultMode) != OperationStatus.SUCCESS) |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | /* |
| | | * database only contain a counter record - don't know how much it can |
| | |
| | | // it is safe to return this record |
| | | return toChangeNumber(key.getData()); |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | replicationServer.handleUnexpectedDatabaseException(e); |
| | | return null; |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | return null; |
| | | } |
| | | |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | // If the DB has been closed then return immediately. |
| | |
| | | DatabaseEntry key = createReplicationKey(changeNumber); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | cursor = db.openCursor(null, null); |
| | | if (cursor.getSearchKeyRange(key, data, LockMode.DEFAULT) |
| | | == OperationStatus.SUCCESS) |
| | | if (cursor.getSearchKeyRange(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | // We can move close to the changeNumber. |
| | | // Let's move to the previous change. |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) |
| | | == OperationStatus.SUCCESS) |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return getRegularRecord(cursor, key, data); |
| | | } |
| | |
| | | { |
| | | // We could not move the cursor past to the changeNumber |
| | | // Check if the last change is older than changeNumber |
| | | if (cursor.getLast(key, data, LockMode.DEFAULT) |
| | | == OperationStatus.SUCCESS) |
| | | if (cursor.getLast(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return getRegularRecord(cursor, key, data); |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | replicationServer.handleUnexpectedDatabaseException(e); |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | |
| | | |
| | | // There cannot be 2 counter record next to each other, |
| | | // it is safe to return previous record which must exist |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) == OperationStatus.SUCCESS) |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return toChangeNumber(key.getData()); |
| | | } |
| | |
| | | localCursor = db.openCursor(txn, null); |
| | | if (startingChangeNumber != null) |
| | | { |
| | | if (localCursor.getSearchKey(key, data, LockMode.DEFAULT) != |
| | | OperationStatus.SUCCESS) |
| | | if (localCursor.getSearchKey(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not move the cursor to the expected startingChangeNumber |
| | | if (localCursor.getSearchKeyRange(key, data, LockMode.DEFAULT) != |
| | | OperationStatus.SUCCESS) |
| | | if (localCursor.getSearchKeyRange(key, data, DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not even move the cursor closed to it => failure |
| | | throw new Exception("ChangeNumber not available"); |
| | | } |
| | | else |
| | | { |
| | | |
| | | // We can move close to the startingChangeNumber. |
| | | // Let's create a cursor from that point. |
| | | DatabaseEntry aKey = new DatabaseEntry(); |
| | | DatabaseEntry aData = new DatabaseEntry(); |
| | | if (localCursor.getPrev(aKey, aData, LockMode.DEFAULT) != |
| | | OperationStatus.SUCCESS) |
| | | if (localCursor.getPrev(aKey, aData, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | localCursor.close(); |
| | | localCursor = db.openCursor(txn, null); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | cursor = localCursor; |
| | | } |
| | | catch (Exception e) |
| | |
| | | return null; |
| | | } |
| | | |
| | | OperationStatus status = cursor.getNext(key, data, LockMode.DEFAULT); |
| | | if (status != OperationStatus.SUCCESS) |
| | | if (cursor.getNext(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | return null; |
| | | } |
| | |
| | | { |
| | | try |
| | | { |
| | | OperationStatus status = cursor.getNext(key, data, LockMode.DEFAULT); |
| | | if (status != OperationStatus.SUCCESS) |
| | | if (cursor.getNext(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | return null; |
| | | } |
| | |
| | | */ |
| | | public long count(ChangeNumber start, ChangeNumber stop) |
| | | { |
| | | try |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | | Cursor cursor = null; |
| | | try |
| | | { |
| | | // If the DB has been closed then return immediately. |
| | |
| | | |
| | | // Step 1 : from the start point, traverse db to the next counter record |
| | | // or to the stop point. |
| | | cursor = db.openCursor(null, null); |
| | | findFirstCounterRecordAfterStartPoint(start, stop, cursor, |
| | | counterValues, distanceToCounterRecords); |
| | | cursor.close(); |
| | | findFirstCounterRecordAfterStartPoint(start, stop, counterValues, |
| | | distanceToCounterRecords); |
| | | |
| | | // cases |
| | | if (counterValues[START] == 0) |
| | |
| | | |
| | | // Step 2 : from the stop point, traverse db to the next counter record |
| | | // or to the start point. |
| | | cursor = db.openCursor(null, null); |
| | | if (!findFirstCounterRecordBeforeStopPoint(start, stop, cursor, |
| | | counterValues, distanceToCounterRecords)) |
| | | if (!findFirstCounterRecordBeforeStopPoint(start, stop, counterValues, |
| | | distanceToCounterRecords)) |
| | | { |
| | | // database is empty |
| | | return 0; |
| | | } |
| | | cursor.close(); |
| | | |
| | | // Step 3 : Now consolidates the result |
| | | return computeDistance(counterValues, distanceToCounterRecords); |
| | | } |
| | | finally |
| | | { |
| | | closeAndReleaseReadLock(cursor); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | replicationServer.handleUnexpectedDatabaseException(e); |
| | | } |
| | | finally |
| | | { |
| | | dbCloseLock.readLock().unlock(); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | private void findFirstCounterRecordAfterStartPoint(ChangeNumber start, |
| | | ChangeNumber stop, Cursor cursor, int[] counterValues, |
| | | int[] distanceToCounterRecords) |
| | | ChangeNumber stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | { |
| | | Cursor cursor = db.openCursor(null, null); |
| | | try |
| | | { |
| | | OperationStatus status; |
| | | DatabaseEntry key; |
| | |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | close(cursor); |
| | | } |
| | | } |
| | | |
| | | private boolean findFirstCounterRecordBeforeStopPoint(ChangeNumber start, |
| | | ChangeNumber stop, Cursor cursor, int[] counterValues, |
| | | int[] distanceToCounterRecords) |
| | | ChangeNumber stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | { |
| | | Cursor cursor = db.openCursor(null, null); |
| | | try |
| | | { |
| | | DatabaseEntry key = createReplicationKey(stop); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | |
| | | } |
| | | return true; |
| | | } |
| | | finally |
| | | { |
| | | close(cursor); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * The diagram below shows a visual description of how the distance between |