From 907de1c96699460cd5be04ad579d14567c90639f Mon Sep 17 00:00:00 2001 From: Jack Mulrow Date: Mon, 7 Dec 2020 15:41:45 +0000 Subject: SERVER-52654 HMAC keys monitoring thread should never sleep longer than 20 days (cherry picked from commit e804031ae4ea69c2cfbfcca47202fcc468d826b2) --- src/mongo/db/keys_collection_manager_sharding.cpp | 16 +++--- .../db/keys_collection_manager_sharding_test.cpp | 62 ++++++++++++++++++---- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/mongo/db/keys_collection_manager_sharding.cpp b/src/mongo/db/keys_collection_manager_sharding.cpp index ccb9c56f75d..3d3061873f4 100644 --- a/src/mongo/db/keys_collection_manager_sharding.cpp +++ b/src/mongo/db/keys_collection_manager_sharding.cpp @@ -54,7 +54,11 @@ namespace { Milliseconds kDefaultRefreshWaitTime(30 * 1000); Milliseconds kRefreshIntervalIfErrored(200); -Milliseconds kMaxRefreshWaitTime(10 * 60 * 1000); +Milliseconds kMaxRefreshWaitTimeIfErrored(10 * 60 * 1000); +// Never wait more than the number of milliseconds in 20 days to avoid sleeping for a number greater +// than can fit in a signed 32 bit integer. +// 20 days = 1000 * 60 * 60 * 24 * 20 = 1,728,000,000 vs signed integer max of 2,147,483,648. +Milliseconds kMaxRefreshWaitTimeOnSuccess(Hours(24) * 20); // Prevents the refresher thread from waiting longer than the given number of milliseconds, even on // a successful refresh. @@ -78,11 +82,7 @@ Milliseconds howMuchSleepNeedFor(const LogicalTime& currentTime, Milliseconds millisBeforeExpire = Milliseconds(expiredSecs) - Milliseconds(currentSecs); - if (interval <= millisBeforeExpire) { - return interval; - } - - return millisBeforeExpire; + return std::min({millisBeforeExpire, interval, kMaxRefreshWaitTimeOnSuccess}); } } // namespace keys_collection_manager_util @@ -256,8 +256,8 @@ void KeysCollectionManagerSharding::PeriodicRunner::_doPeriodicRefresh( } else { errorCount += 1; nextWakeup = Milliseconds(kRefreshIntervalIfErrored.count() * errorCount); - if (nextWakeup > kMaxRefreshWaitTime) { - nextWakeup = kMaxRefreshWaitTime; + if (nextWakeup > kMaxRefreshWaitTimeIfErrored) { + nextWakeup = kMaxRefreshWaitTimeIfErrored; } } } else { diff --git a/src/mongo/db/keys_collection_manager_sharding_test.cpp b/src/mongo/db/keys_collection_manager_sharding_test.cpp index 8bde5344ea6..812dd7e1ded 100644 --- a/src/mongo/db/keys_collection_manager_sharding_test.cpp +++ b/src/mongo/db/keys_collection_manager_sharding_test.cpp @@ -400,26 +400,66 @@ LogicalTime addSeconds(const LogicalTime& logicalTime, const Seconds& seconds) { return LogicalTime(Timestamp(asTimestamp.getSecs() + seconds.count(), asTimestamp.getInc())); } -TEST(KeysCollectionManagerUtilTest, HowMuchSleepNeededForCalculationDoesNotOverflow) { +TEST(KeysCollectionManagerUtilTest, HowMuchSleepNeedForWithDefaultKeysRotationIntervalIs20Days) { auto secondsSinceEpoch = durationCount(Date_t::now().toDurationSinceEpoch()); auto defaultKeysIntervalSeconds = Seconds(KeysRotationIntervalSec); - // Mock inputs that would have caused an overflow without the changes from SERVER-48709. - // "currentTime" is the current logical time in the LogicalClock, which will typically be close - // to a timestamp constructed from the number of seconds since the unix epoch. "latestExpiredAt" - // is the highest expiration logical time of any key, which will at most be currentTime + - // (default key rotation interval * 2) because two valid keys are kept at a time. "interval" is - // the duration a key is valid for, which defaults to 90 days = 7,776,000 seconds. auto currentTime = LogicalTime(Timestamp(secondsSinceEpoch, 0)); auto latestExpiredAt = addSeconds(currentTime, defaultKeysIntervalSeconds * 2); - auto interval = Milliseconds(defaultKeysIntervalSeconds); + auto defaultInterval = Milliseconds(defaultKeysIntervalSeconds); + + auto nextWakeupMillis = keys_collection_manager_util::howMuchSleepNeedFor( + currentTime, latestExpiredAt, defaultInterval); + ASSERT_EQ(nextWakeupMillis, Hours(24) * 20); +} + +TEST(KeysCollectionManagerUtilTest, HowMuchSleepNeedForIsNeverLongerThan20Days) { + auto secondsSinceEpoch = durationCount(Date_t::now().toDurationSinceEpoch()); + auto keysRotationInterval = Seconds(Hours(24) * 50); + + auto currentTime = LogicalTime(Timestamp(secondsSinceEpoch, 0)); + auto latestExpiredAt = addSeconds(currentTime, keysRotationInterval * 2); + auto interval = Milliseconds(keysRotationInterval); + + auto nextWakeupMillis = + keys_collection_manager_util::howMuchSleepNeedFor(currentTime, latestExpiredAt, interval); + ASSERT_EQ(nextWakeupMillis, Hours(24) * 20); +} + +TEST(KeysCollectionManagerUtilTest, HowMuchSleepNeedForIsNeverHigherThanRotationInterval) { + auto secondsSinceEpoch = durationCount(Date_t::now().toDurationSinceEpoch()); + auto keysRotationInterval = Seconds(Hours(24) * 5); + + auto currentTime = LogicalTime(Timestamp(secondsSinceEpoch, 0)); + auto latestExpiredAt = addSeconds(currentTime, keysRotationInterval * 2); + auto interval = Milliseconds(keysRotationInterval); - // Despite the default rotation interval seconds * 1000 not fitting in a 32 bit unsigned - // integer (7,776,000,000 vs. 4,294,967,295), the calculation should not overflow, and the next - // wakeup should correctly be the default interval. auto nextWakeupMillis = keys_collection_manager_util::howMuchSleepNeedFor(currentTime, latestExpiredAt, interval); ASSERT_EQ(nextWakeupMillis, interval); } +LogicalTime subtractSeconds(const LogicalTime& logicalTime, const Seconds& seconds) { + auto asTimestamp = logicalTime.asTimestamp(); + return LogicalTime(Timestamp(asTimestamp.getSecs() - seconds.count(), asTimestamp.getInc())); +} + +TEST(KeysCollectionManagerUtilTest, HowMuchSleepNeedForAfterNotFindingKeys) { + // Default refresh interval if keys could not be found. + const Milliseconds kRefreshIntervalIfErrored(200); + + auto secondsSinceEpoch = durationCount(Date_t::now().toDurationSinceEpoch()); + auto keysRotationInterval = Milliseconds(5000); + + // The latest found key expired before the current time, which means no new keys were found + // despite the previous refresh succeeding. + auto currentTime = LogicalTime(Timestamp(secondsSinceEpoch, 0)); + auto latestExpiredAt = subtractSeconds(currentTime, Seconds(1)); + auto interval = Milliseconds(keysRotationInterval); + + auto nextWakeupMillis = + keys_collection_manager_util::howMuchSleepNeedFor(currentTime, latestExpiredAt, interval); + ASSERT_EQ(nextWakeupMillis, kRefreshIntervalIfErrored); +} + } // namespace mongo -- cgit v1.2.1