diff options
author | Ian Boros <ian.boros@mongodb.com> | 2019-09-06 16:21:27 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-06 16:21:27 +0000 |
commit | 0d9d438513643d95fba019080f3fc605afdf255d (patch) | |
tree | 3656856aa9835951da4ae1b8ec927a0d86ab4f17 | |
parent | b2d1b22788c295d247482cf2b19631585a10ad19 (diff) | |
download | mongo-0d9d438513643d95fba019080f3fc605afdf255d.tar.gz |
SERVER-41863 make sleep command wait until server clock has advanced
-rw-r--r-- | src/mongo/db/commands/test_commands.cpp | 63 |
1 files changed, 42 insertions, 21 deletions
diff --git a/src/mongo/db/commands/test_commands.cpp b/src/mongo/db/commands/test_commands.cpp index 8f6046e9ffb..10275031ddb 100644 --- a/src/mongo/db/commands/test_commands.cpp +++ b/src/mongo/db/commands/test_commands.cpp @@ -49,6 +49,7 @@ #include "mongo/db/repl/replication_coordinator_global.h" #include "mongo/db/service_context.h" #include "mongo/util/log.h" +#include "mongo/util/scopeguard.h" namespace mongo { @@ -156,39 +157,59 @@ public: const BSONObj& cmdObj, BSONObjBuilder& result) { log() << "test only command sleep invoked"; - long long millis = 0; + Milliseconds msToSleep(0); if (cmdObj["secs"] || cmdObj["millis"]) { if (cmdObj["secs"]) { uassert(34344, "'secs' must be a number.", cmdObj["secs"].isNumber()); - millis += cmdObj["secs"].numberLong() * 1000; + msToSleep += Milliseconds(cmdObj["secs"].numberLong() * 1000); } if (cmdObj["millis"]) { uassert(34345, "'millis' must be a number.", cmdObj["millis"].isNumber()); - millis += cmdObj["millis"].numberLong(); + msToSleep += Milliseconds(cmdObj["millis"].numberLong()); } } else { - millis = 10 * 1000; + msToSleep = Milliseconds(10 * 1000); } - if (!cmdObj["lock"]) { - // Legacy implementation - if (cmdObj.getBoolField("w")) { - _sleepInWriteLock(opCtx, millis); - } else { - _sleepInReadLock(opCtx, millis); - } - } else { - uassert(34346, "Only one of 'w' and 'lock' may be set.", !cmdObj["w"]); - - std::string lock(cmdObj.getStringField("lock")); - if (lock == "none") { - opCtx->sleepFor(Milliseconds(millis)); - } else if (lock == "w") { - _sleepInWriteLock(opCtx, millis); + auto now = opCtx->getServiceContext()->getFastClockSource()->now(); + auto deadline = now + Milliseconds(msToSleep); + + // Note that if the system clock moves _backwards_ (which has been known to happen), this + // could result in a much longer sleep than requested. Since this command is only used for + // testing, we're okay with this imprecision. + while (deadline > now) { + Milliseconds msRemaining = deadline - now; + + // If the clock moves back by an absurd amount then uassert. + Milliseconds threshold(10000); + uassert(31173, + str::stream() << "Clock must have moved backwards by at least " << threshold + << " ms during sleep command", + msRemaining < msToSleep + threshold); + + ON_BLOCK_EXIT( + [&now, opCtx] { now = opCtx->getServiceContext()->getFastClockSource()->now(); }); + + if (!cmdObj["lock"]) { + // Legacy implementation + if (cmdObj.getBoolField("w")) { + _sleepInWriteLock(opCtx, msRemaining.count()); + } else { + _sleepInReadLock(opCtx, msRemaining.count()); + } } else { - uassert(34347, "'lock' must be one of 'r', 'w', 'none'.", lock == "r"); - _sleepInReadLock(opCtx, millis); + uassert(34346, "Only one of 'w' and 'lock' may be set.", !cmdObj["w"]); + + std::string lock(cmdObj.getStringField("lock")); + if (lock == "none") { + opCtx->sleepFor(msRemaining); + } else if (lock == "w") { + _sleepInWriteLock(opCtx, msRemaining.count()); + } else { + uassert(34347, "'lock' must be one of 'r', 'w', 'none'.", lock == "r"); + _sleepInReadLock(opCtx, msRemaining.count()); + } } } |