summaryrefslogtreecommitdiff
path: root/src/mongo/db/logical_clock_test.cpp
diff options
context:
space:
mode:
authorJack Mulrow <jack.mulrow@mongodb.com>2017-06-08 19:06:02 -0400
committerJack Mulrow <jack.mulrow@mongodb.com>2017-06-16 14:11:33 -0400
commit1dfd505d9fd3547a963fe748c34690c0d5c9add4 (patch)
tree9e700fcf85f1a4b52ca92d963acc5b416adc542f /src/mongo/db/logical_clock_test.cpp
parent73390210633a157f87221d561ce6cad1497225f9 (diff)
downloadmongo-1dfd505d9fd3547a963fe748c34690c0d5c9add4.tar.gz
SERVER-28459 Prevent the max value from being reached in the logical clock
Diffstat (limited to 'src/mongo/db/logical_clock_test.cpp')
-rw-r--r--src/mongo/db/logical_clock_test.cpp115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/mongo/db/logical_clock_test.cpp b/src/mongo/db/logical_clock_test.cpp
index 8e7a1da3547..2130b5e9944 100644
--- a/src/mongo/db/logical_clock_test.cpp
+++ b/src/mongo/db/logical_clock_test.cpp
@@ -49,6 +49,10 @@ std::string kDummyNamespaceString = "test.foo";
using LogicalClockTest = LogicalClockTestFixture;
+LogicalTime buildLogicalTime(unsigned secs, unsigned inc) {
+ return LogicalTime(Timestamp(secs, inc));
+}
+
// Check that the initial time does not change during logicalClock creation.
TEST_F(LogicalClockTest, roundtrip) {
Timestamp tX(1);
@@ -215,5 +219,116 @@ TEST_F(LogicalClockTest, WallClockSetTooFarInFuture) {
ASSERT_TRUE(getClock()->getClusterTime() == nextTime);
}
+// Verify the behavior of advancing cluster time around the max allowed values.
+TEST_F(LogicalClockTest, ReserveTicksBehaviorAroundMaxTime) {
+ unsigned maxVal = LogicalClock::kMaxSignedInt;
+
+ // Verify clock can be advanced near the max values.
+
+ // Can always advance to the max value for the inc field.
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal - 1));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal - 1, maxVal));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal - 5));
+ getClock()->reserveTicks(5);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal - 1, maxVal));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(0, maxVal - 1));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(0, maxVal));
+
+ // Can overflow inc into seconds to reach max seconds value.
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, 1));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal - 5));
+ getClock()->reserveTicks(10);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, 10));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, 1));
+ getClock()->reserveTicks(maxVal);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal));
+
+ // Can advance inc field when seconds field is at the max value.
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, 1));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, 2));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, 1));
+ getClock()->reserveTicks(100);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, 101));
+
+ // Can advance to the max value for both the inc and seconds fields.
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 1));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 5));
+ getClock()->reserveTicks(5);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal));
+
+ // Verify scenarios where the clock cannot be advanced.
+
+ // Can't overflow inc into seconds when seconds field is at the max value.
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal));
+ ASSERT_THROWS(getClock()->reserveTicks(1), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal));
+ ASSERT_THROWS(getClock()->reserveTicks(5), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 1));
+ ASSERT_THROWS(getClock()->reserveTicks(2), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal - 1));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 11));
+ ASSERT_THROWS(getClock()->reserveTicks(12), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, maxVal - 11));
+}
+
+// Verify behavior of advancing cluster time when the wall clock is near the max allowed value.
+TEST_F(LogicalClockTest, ReserveTicksBehaviorWhenWallClockNearMaxTime) {
+ unsigned maxVal = LogicalClock::kMaxSignedInt;
+
+ // Can be set to the max possible time by catching up to the wall clock.
+ setMockClockSourceTime(Date_t::fromDurationSinceEpoch(Seconds(maxVal)));
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(1, 1));
+ getClock()->reserveTicks(1);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(maxVal, 1));
+
+ // Should fail when wall clock would advance cluster time beyond the max allowed time.
+ setMockClockSourceTime(Date_t::max());
+
+ resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(1, 1));
+ ASSERT_THROWS(getClock()->reserveTicks(1), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == buildLogicalTime(1, 1));
+}
+
+// Verify the clock rejects logical times greater than the max allowed time.
+TEST_F(LogicalClockTest, RejectsLogicalTimesGreaterThanMaxTime) {
+ unsigned maxVal = LogicalClock::kMaxSignedInt;
+
+ // A logical time can be greater than the maximum value allowed because the signed integer
+ // maximum is used for legacy compatibility, but these fields are stored as unsigned integers.
+ auto beyondMaxTime = buildLogicalTime(maxVal + 1, maxVal + 1);
+
+ // The clock can't be initialized to a time greater than the max possible.
+ resetClock();
+ ASSERT_THROWS(getClock()->setClusterTimeFromTrustedSource(beyondMaxTime), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == LogicalTime());
+
+ // The time can't be advanced through metadata to a time greater than the max possible.
+ // Advance the wall clock close enough to the new value so the rate check is passed.
+ auto almostMaxSecs =
+ Seconds(maxVal) - LogicalClock::kMaxAcceptableLogicalClockDriftSecs + Seconds(10);
+ setMockClockSourceTime(Date_t::fromDurationSinceEpoch(almostMaxSecs));
+ ASSERT_THROWS(getClock()->advanceClusterTime(beyondMaxTime), std::exception);
+ ASSERT_TRUE(getClock()->getClusterTime() == LogicalTime());
+}
+
} // unnamed namespace
} // namespace mongo