diff options
Diffstat (limited to 'src/mongo/db/repl')
-rw-r--r-- | src/mongo/db/repl/roll_back_local_operations.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/repl/roll_back_local_operations.h | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/roll_back_local_operations_test.cpp | 35 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_impl.cpp | 43 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_impl.h | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/rollback_impl_test.cpp | 36 |
6 files changed, 114 insertions, 29 deletions
diff --git a/src/mongo/db/repl/roll_back_local_operations.cpp b/src/mongo/db/repl/roll_back_local_operations.cpp index 6e63671dc1f..a950a162b32 100644 --- a/src/mongo/db/repl/roll_back_local_operations.cpp +++ b/src/mongo/db/repl/roll_back_local_operations.cpp @@ -89,11 +89,15 @@ RollBackLocalOperations::RollBackLocalOperations(const OplogInterface& localOplo } RollBackLocalOperations::RollbackCommonPoint::RollbackCommonPoint(BSONObj oplogBSON, - RecordId recordId) + RecordId recordId, + BSONObj nextOplogBSON) : _recordId(std::move(recordId)) { auto oplogEntry = uassertStatusOK(repl::OplogEntry::parse(oplogBSON)); _opTime = oplogEntry.getOpTime(); _wallClockTime = oplogEntry.getWallClockTime(); + // nextOplogEntry holds the oplog entry immediately after the common point. + auto nextOplogEntry = uassertStatusOK(repl::OplogEntry::parse(nextOplogBSON)); + _firstWallClockTimeAfterCommonPoint = nextOplogEntry.getWallClockTime(); } StatusWith<RollBackLocalOperations::RollbackCommonPoint> RollBackLocalOperations::onRemoteOperation( @@ -106,6 +110,10 @@ StatusWith<RollBackLocalOperations::RollbackCommonPoint> RollBackLocalOperations _localOplogValue = result.getValue(); } + // As we iterate through the oplog in reverse, opAfterCurrentEntry holds the oplog entry + // immediately after the entry stored in _localOplogValue. + BSONObj opAfterCurrentEntry = _localOplogValue.first; + while (getTimestamp(_localOplogValue) > getTimestamp(operation)) { _scanned++; LOG(2) << "Local oplog entry to roll back: " << redact(_localOplogValue.first); @@ -126,13 +134,16 @@ StatusWith<RollBackLocalOperations::RollbackCommonPoint> RollBackLocalOperations << getTimestamp(_localOplogValue).toString() << "}"); } + opAfterCurrentEntry = _localOplogValue.first; _localOplogValue = result.getValue(); } if (getTimestamp(_localOplogValue) == getTimestamp(operation)) { _scanned++; + if (getHash(_localOplogValue) == getHash(operation)) { - return RollbackCommonPoint(_localOplogValue.first, _localOplogValue.second); + return RollbackCommonPoint( + _localOplogValue.first, _localOplogValue.second, opAfterCurrentEntry); } LOG(2) << "Local oplog entry to roll back: " << redact(_localOplogValue.first); @@ -194,8 +205,8 @@ StatusWith<RollBackLocalOperations::RollbackCommonPoint> syncRollBackLocalOperat RollBackLocalOperations finder(localOplog, rollbackOperation); Timestamp theirTime; while (remoteResult.isOK()) { - theirTime = remoteResult.getValue().first["ts"].timestamp(); BSONObj theirObj = remoteResult.getValue().first; + theirTime = theirObj["ts"].timestamp(); auto result = finder.onRemoteOperation(theirObj); if (result.isOK()) { return result.getValue(); diff --git a/src/mongo/db/repl/roll_back_local_operations.h b/src/mongo/db/repl/roll_back_local_operations.h index 95452596f67..36052c945ed 100644 --- a/src/mongo/db/repl/roll_back_local_operations.h +++ b/src/mongo/db/repl/roll_back_local_operations.h @@ -59,7 +59,7 @@ public: class RollbackCommonPoint { public: - RollbackCommonPoint(BSONObj oplogBSON, RecordId recordId); + RollbackCommonPoint(BSONObj oplogBSON, RecordId recordId, BSONObj nextOplogBSON); RecordId getRecordId() const { return _recordId; @@ -73,10 +73,16 @@ public: return _wallClockTime; } + boost::optional<Date_t> getFirstOpWallClockTimeAfterCommonPoint() { + return _firstWallClockTimeAfterCommonPoint; + } + private: RecordId _recordId; OpTime _opTime; boost::optional<Date_t> _wallClockTime; + // The wall clock time of the first operation after the common point if it exists. + boost::optional<Date_t> _firstWallClockTimeAfterCommonPoint; }; /** diff --git a/src/mongo/db/repl/roll_back_local_operations_test.cpp b/src/mongo/db/repl/roll_back_local_operations_test.cpp index e49315be62e..3fdd12ae0bb 100644 --- a/src/mongo/db/repl/roll_back_local_operations_test.cpp +++ b/src/mongo/db/repl/roll_back_local_operations_test.cpp @@ -59,10 +59,34 @@ BSONObj makeOp(long long seconds, long long hash) { << uuid); } +BSONObj makeOpWithWallClockTime(long count, + long long hash, + long wallClockMillis, + long long term = 1LL) { + auto uuid = unittest::assertGet(UUID::parse("b4c66a44-c1ca-4d86-8d25-12e82fa2de5b")); + return BSON("ts" << Timestamp(count, count) << "h" << hash << "t" << term << "op" + << "n" + << "o" + << BSONObj() + << "ns" + << "roll_back_local_operations.test" + << "ui" + << uuid + << "wall" + << Date_t::fromMillisSinceEpoch(wallClockMillis)); +}; + int recordId = 0; OplogInterfaceMock::Operation makeOpAndRecordId(long long seconds, long long hash) { return std::make_pair(makeOp(seconds, hash), RecordId(++recordId)); } +OplogInterfaceMock::Operation makeOpWithWallClockTimeAndRecordId(long long seconds, + long long hash, + long wallClockMillis, + long long term = 1LL) { + return std::make_pair(makeOpWithWallClockTime(seconds, hash, wallClockMillis), + RecordId(++recordId)); +} TEST(RollBackLocalOperationsTest, InvalidLocalOplogIterator) { class InvalidOplogInterface : public OplogInterface { @@ -261,10 +285,13 @@ TEST(SyncRollBackLocalOperationsTest, RemoteOplogMissing) { } TEST(SyncRollBackLocalOperationsTest, RollbackTwoOperations) { - auto commonOperation = makeOpAndRecordId(1, 1); + auto commonOperation = makeOpWithWallClockTimeAndRecordId(1, 1, 1 * 5000); + auto firstOpAfterCommonPoint = + makeOpWithWallClockTimeAndRecordId(2, 2, 2 * 60 * 60 * 24 * 1000); OplogInterfaceMock::Operations localOperations({ - makeOpAndRecordId(3, 1), makeOpAndRecordId(2, 1), commonOperation, + makeOpAndRecordId(3, 3), firstOpAfterCommonPoint, commonOperation, }); + auto i = localOperations.cbegin(); auto result = syncRollBackLocalOperations(OplogInterfaceMock(localOperations), OplogInterfaceMock({commonOperation}), @@ -279,6 +306,10 @@ TEST(SyncRollBackLocalOperationsTest, RollbackTwoOperations) { ASSERT_EQUALS(commonOperation.second, result.getValue().getRecordId()); ASSERT_FALSE(i == localOperations.cend()); ASSERT_BSONOBJ_EQ(commonOperation.first, i->first); + auto firstOplogEntryAfterCommonPoint = + uassertStatusOK(OplogEntry::parse(firstOpAfterCommonPoint.first)); + ASSERT_EQUALS(*result.getValue().getFirstOpWallClockTimeAfterCommonPoint(), + *firstOplogEntryAfterCommonPoint.getWallClockTime()); i++; ASSERT_TRUE(i == localOperations.cend()); } diff --git a/src/mongo/db/repl/rollback_impl.cpp b/src/mongo/db/repl/rollback_impl.cpp index dca6748e931..f9cc08a0350 100644 --- a/src/mongo/db/repl/rollback_impl.cpp +++ b/src/mongo/db/repl/rollback_impl.cpp @@ -692,21 +692,26 @@ Status RollbackImpl::_checkAgainstTimeLimit( _rollbackStats.lastLocalOptime = topOfOplog.getOpTime(); auto topOfOplogWallOpt = topOfOplog.getWallClockTime(); - auto commonPointWallOpt = commonPoint.getWallClockTime(); + // We check the difference between the top of the oplog and the first oplog entry after the + // common point when computing the rollback time limit. + auto firstOpWallClockTimeAfterCommonPointOpt = + commonPoint.getFirstOpWallClockTimeAfterCommonPoint(); - // Only compute the difference if both the top of the oplog and the common point - // have wall clock times. - if (commonPointWallOpt && topOfOplogWallOpt) { + + // Only compute the difference if both the top of the oplog and the first oplog entry after the + // common point have wall clock times. + if (firstOpWallClockTimeAfterCommonPointOpt && topOfOplogWallOpt) { auto topOfOplogWallTime = topOfOplogWallOpt.get(); - auto commonPointWallTime = commonPointWallOpt.get(); + auto firstOpWallClockTimeAfterCommonPoint = firstOpWallClockTimeAfterCommonPointOpt.get(); - if (topOfOplogWallTime >= commonPointWallTime) { + if (topOfOplogWallTime >= firstOpWallClockTimeAfterCommonPoint) { - unsigned long long diff = - durationCount<Seconds>(Milliseconds(topOfOplogWallTime - commonPointWallTime)); + unsigned long long diff = durationCount<Seconds>( + Milliseconds(topOfOplogWallTime - firstOpWallClockTimeAfterCommonPoint)); _rollbackStats.lastLocalWallClockTime = topOfOplogWallTime; - _rollbackStats.commonPointWallClockTime = commonPointWallTime; + _rollbackStats.firstOpWallClockTimeAfterCommonPoint = + firstOpWallClockTimeAfterCommonPoint; auto timeLimit = static_cast<unsigned long long>(rollbackTimeLimitSecs.loadRelaxed()); @@ -719,9 +724,11 @@ Status RollbackImpl::_checkAgainstTimeLimit( } } else { - warning() << "Wall clock times on oplog entries not monotonically increasing. This " - "might indicate a backward clock skew. Time at common point: " - << commonPointWallTime << ". Time at top of oplog: " << topOfOplogWallTime; + warning() + << "Wall clock times on oplog entries not monotonically increasing. This " + "might indicate a backward clock skew. Time at first oplog after common point: " + << firstOpWallClockTimeAfterCommonPoint + << ". Time at top of oplog: " << topOfOplogWallTime; } } @@ -922,14 +929,18 @@ void RollbackImpl::_summarizeRollback(OperationContext* opCtx) const { if (_rollbackStats.commonPoint) { log() << "\tcommon point optime: " << *_rollbackStats.commonPoint; } - if (_rollbackStats.lastLocalWallClockTime && _rollbackStats.commonPointWallClockTime) { + if (_rollbackStats.lastLocalWallClockTime && + _rollbackStats.firstOpWallClockTimeAfterCommonPoint) { auto lastWall = *_rollbackStats.lastLocalWallClockTime; - auto commonWall = *_rollbackStats.commonPointWallClockTime; - unsigned long long diff = durationCount<Seconds>(Milliseconds(lastWall - commonWall)); + auto firstOpWallClockTimeAfterCommonPoint = + *_rollbackStats.firstOpWallClockTimeAfterCommonPoint; + unsigned long long diff = + durationCount<Seconds>(Milliseconds(lastWall - firstOpWallClockTimeAfterCommonPoint)); log() << "\tlast wall clock time on the branch of history rolled back: " << lastWall; - log() << "\tcommon point wall clock time: " << commonWall; + log() << "\twall clock time of the first operation after the common point: " + << firstOpWallClockTimeAfterCommonPoint; log() << "\tdifference in wall clock times: " << diff << " second(s)"; } if (_rollbackStats.truncateTimestamp) { diff --git a/src/mongo/db/repl/rollback_impl.h b/src/mongo/db/repl/rollback_impl.h index 445b1c17a06..2619c98fb7c 100644 --- a/src/mongo/db/repl/rollback_impl.h +++ b/src/mongo/db/repl/rollback_impl.h @@ -102,9 +102,9 @@ struct RollbackStats { boost::optional<Date_t> lastLocalWallClockTime; /** - * The wall clock time at the common point, if known. + * The wall clock time of the first operation after the common point, if known. */ - boost::optional<Date_t> commonPointWallClockTime; + boost::optional<Date_t> firstOpWallClockTimeAfterCommonPoint; }; /** diff --git a/src/mongo/db/repl/rollback_impl_test.cpp b/src/mongo/db/repl/rollback_impl_test.cpp index c888ee07949..3f9f920755f 100644 --- a/src/mongo/db/repl/rollback_impl_test.cpp +++ b/src/mongo/db/repl/rollback_impl_test.cpp @@ -280,6 +280,8 @@ protected: UUID kGenericUUID = unittest::assertGet(UUID::parse(kGenericUUIDStr)); + const long kOneDayInMillis = 60 * 60 * 24 * 1000; + private: long _counter = 100; }; @@ -461,11 +463,31 @@ TEST_F(RollbackImplTest, RollbackReturnsNoMatchingDocumentWhenNoCommonPoint) { } TEST_F(RollbackImplTest, RollbackSucceedsIfRollbackPeriodIsWithinTimeLimit) { - - // The default limit is 1 day, so we make the difference be just under a day. auto commonPoint = makeOpAndRecordId(makeOpWithWallClockTime(1, 1, 5 * 1000)); - auto topOfOplog = makeOpAndRecordId(makeOpWithWallClockTime(2, 2, 60 * 60 * 24 * 1000)); + // We use the difference of wall clock times between the top of the oplog and the first op after + // the common point to calculate the rollback time limit. + auto firstOpAfterCommonPoint = + makeOpAndRecordId(makeOpWithWallClockTime(2, 2, 2 * kOneDayInMillis)); + // The default limit is 1 day, so we make the difference be just under a day. + auto topOfOplog = makeOpAndRecordId(makeOpWithWallClockTime(3, 3, kOneDayInMillis)); + _remoteOplog->setOperations({commonPoint}); + ASSERT_OK(_insertOplogEntry(commonPoint.first)); + ASSERT_OK(_insertOplogEntry(firstOpAfterCommonPoint.first)); + ASSERT_OK(_insertOplogEntry(topOfOplog.first)); + _storageInterface->setStableTimestamp(nullptr, Timestamp(1, 1)); + + // Run rollback. + ASSERT_OK(_rollback->runRollback(_opCtx.get())); +} + +TEST_F(RollbackImplTest, RollbackSucceedsIfTopOfOplogIsFirstOpAfterCommonPoint) { + + auto commonPoint = makeOpAndRecordId(makeOpWithWallClockTime(1, 1, 5 * 1000)); + // The default limit is 1 day, so we make the difference be 2 days. The rollback should still + // succeed since we calculate the difference of wall clock times between the top of the oplog + // and the first op after the common point which are both the same operation in this case. + auto topOfOplog = makeOpAndRecordId(makeOpWithWallClockTime(3, 3, 2 * kOneDayInMillis)); _remoteOplog->setOperations({commonPoint}); ASSERT_OK(_insertOplogEntry(commonPoint.first)); ASSERT_OK(_insertOplogEntry(topOfOplog.first)); @@ -478,12 +500,16 @@ TEST_F(RollbackImplTest, RollbackSucceedsIfRollbackPeriodIsWithinTimeLimit) { TEST_F(RollbackImplTest, RollbackFailsIfRollbackPeriodIsTooLong) { - // The default limit is 1 day, so we make the difference be 2 days. auto commonPoint = makeOpAndRecordId(makeOpWithWallClockTime(1, 1, 5 * 1000)); - auto topOfOplog = makeOpAndRecordId(makeOpWithWallClockTime(2, 2, 2 * 60 * 60 * 24 * 1000)); + auto opAfterCommonPoint = makeOpAndRecordId(makeOpWithWallClockTime(2, 2, 5 * 1000)); + // We calculate the roll back time limit by comparing the difference between the top of the + // oplog and the first oplog entry after the commit point. The default limit is 1 day, so we + // make the difference be 2 days. + auto topOfOplog = makeOpAndRecordId(makeOpWithWallClockTime(3, 3, 2 * 60 * 60 * 24 * 1000)); _remoteOplog->setOperations({commonPoint}); ASSERT_OK(_insertOplogEntry(commonPoint.first)); + ASSERT_OK(_insertOplogEntry(opAfterCommonPoint.first)); ASSERT_OK(_insertOplogEntry(topOfOplog.first)); _storageInterface->setStableTimestamp(nullptr, Timestamp(1, 1)); |