diff options
Diffstat (limited to 'src/mongo/db')
43 files changed, 1289 insertions, 640 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 5ec20fc2915..04b218c1af6 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -400,7 +400,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/util/caching', '$BUILD_DIR/mongo/util/concurrency/thread_pool', - 'logical_clock', + 'vector_clock', ], ) @@ -1015,7 +1015,7 @@ env.Library( LIBDEPS=[ '$BUILD_DIR/mongo/db/catalog/collection', 'rw_concern_d', - 'logical_clock', + 'vector_clock', ], LIBDEPS_PRIVATE=[ 'index_builds_coordinator_interface', @@ -1218,7 +1218,7 @@ env.Library( LIBDEPS=[ '$BUILD_DIR/mongo/crypto/sha1_block', 'keys_collection_document', - 'logical_clock', + 'vector_clock', ], ) @@ -1504,9 +1504,9 @@ env.Library( 'keys_collection_client_direct.cpp', ], LIBDEPS=[ - 'logical_clock', 'keys_collection_document', 'logical_time', + '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client', '$BUILD_DIR/mongo/s/client/rs_local_client', '$BUILD_DIR/mongo/s/client/shard_interface', ], @@ -1518,7 +1518,6 @@ env.Library( 'keys_collection_client_sharded.cpp', ], LIBDEPS=[ - 'logical_clock', 'keys_collection_document', 'logical_time', '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client', @@ -1526,45 +1525,31 @@ env.Library( ) env.Library( - target='keys_collection_manager', + target='vector_clock', source=[ - env.Idlc('keys_collection_manager.idl')[0], - 'keys_collection_manager.cpp', - 'keys_collection_cache.cpp', 'key_generator.cpp', - ], - LIBDEPS=[ - 'logical_clock', - 'keys_collection_document', - 'logical_time', - 'keys_collection_client_sharded', - 'repl/repl_coordinator_interface', - '$BUILD_DIR/mongo/transport/transport_layer_common', - ], -) - -env.Library( - target='logical_clock', - source=[ + 'keys_collection_cache.cpp', + 'keys_collection_manager.cpp', 'logical_clock.cpp', + 'logical_time_validator.cpp', + 'vector_clock.cpp', + env.Idlc('keys_collection_manager.idl')[0], env.Idlc('logical_clock.idl')[0], ], LIBDEPS=[ + 'auth/authprivilege', 'global_settings', + 'keys_collection_client_sharded', + 'keys_collection_document', 'logical_time', + 'repl/repl_coordinator_interface', 'service_context', - 'vector_clock_mutable', - ], -) - -env.Library( - target='vector_clock', - source=[ - 'vector_clock.cpp', + 'signed_logical_time', + 'time_proof_service', + '$BUILD_DIR/mongo/transport/transport_layer_common', ], - LIBDEPS=[ - 'logical_time', - 'service_context', + LIBDEPS_PRIVATE=[ + 'server_options_core', ], ) @@ -1579,7 +1564,7 @@ env.Library( ) env.Library( - target='vector_clock_d', + target='vector_clock_mongod', source=[ 'vector_clock_mongod.cpp', ], @@ -1603,28 +1588,13 @@ env.Library( ) env.Library( - target='logical_time_validator', - source=[ - 'logical_time_validator.cpp', - ], - LIBDEPS=[ - 'keys_collection_manager', - 'service_context', - 'signed_logical_time', - 'time_proof_service', - '$BUILD_DIR/mongo/db/auth/auth', - '$BUILD_DIR/mongo/db/auth/authprivilege', - ], -) - -env.Library( target= 'logical_time_metadata_hook', source= [ 'logical_time_metadata_hook.cpp', ], LIBDEPS= [ - 'logical_time_validator', - 'signed_logical_time', + 'logical_time', + 'vector_clock', '$BUILD_DIR/mongo/rpc/metadata', ], ) @@ -1646,8 +1616,8 @@ env.Library( 'logical_clock_test_fixture.cpp', ], LIBDEPS= [ - 'logical_clock', 'signed_logical_time', + 'vector_clock', '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/sharding_mongod_test_fixture', '$BUILD_DIR/mongo/util/clock_source_mock' @@ -1906,9 +1876,8 @@ envWithAsio.CppUnitTest( 'dbmessage', 'index_build_entry_helpers', 'index_builds_coordinator_mongod', + 'keys_collection_client_direct', 'keys_collection_document', - 'keys_collection_manager', - 'logical_clock', 'logical_clock_test_fixture', 'logical_session_cache', 'logical_session_cache_impl', @@ -1946,6 +1915,7 @@ envWithAsio.CppUnitTest( 'time_proof_service', 'transaction', 'update_index_data', + 'vector_clock', 'write_concern_options', 'write_ops', ], @@ -1966,8 +1936,7 @@ envWithAsio.CppUnitTest( '$BUILD_DIR/mongo/s/catalog/dist_lock_manager_mock', '$BUILD_DIR/mongo/s/config_server_test_fixture', 'auth/authmocks', - 'keys_collection_manager', - 'logical_time_validator', + 'vector_clock', ], ) diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 6aae6707afe..ac856017c24 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -187,9 +187,9 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/concurrency/write_conflict_exception', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', '$BUILD_DIR/mongo/db/server_options_core', + '$BUILD_DIR/mongo/db/vector_clock', ] ) @@ -207,8 +207,8 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/index/index_build_interceptor', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', + '$BUILD_DIR/mongo/db/vector_clock', ] ) @@ -366,11 +366,11 @@ env.Library( '$BUILD_DIR/mongo/db/commands/server_status_core', '$BUILD_DIR/mongo/db/index/index_build_interceptor', '$BUILD_DIR/mongo/db/index/index_access_methods', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/repl/repl_settings', '$BUILD_DIR/mongo/db/storage/storage_engine_common', '$BUILD_DIR/mongo/db/storage/storage_debug_util', '$BUILD_DIR/mongo/db/transaction', + '$BUILD_DIR/mongo/db/vector_clock', 'index_build_block', 'throttle_cursor', 'validate_idl', diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript index eaa3ebaaf40..84f2727d177 100644 --- a/src/mongo/db/index/SConscript +++ b/src/mongo/db/index/SConscript @@ -139,7 +139,7 @@ serveronlyEnv.Library( ], LIBDEPS_PRIVATE=[ 'skipped_record_tracker', - '$BUILD_DIR/mongo/db/logical_clock', + '$BUILD_DIR/mongo/db/vector_clock', '$BUILD_DIR/mongo/idl/server_parameter', ], ) diff --git a/src/mongo/db/key_generator_update_test.cpp b/src/mongo/db/key_generator_update_test.cpp index 885d7dfe25b..511c9e493fc 100644 --- a/src/mongo/db/key_generator_update_test.cpp +++ b/src/mongo/db/key_generator_update_test.cpp @@ -37,7 +37,7 @@ #include "mongo/db/key_generator.h" #include "mongo/db/keys_collection_client_sharded.h" #include "mongo/db/keys_collection_document.h" -#include "mongo/db/logical_clock.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/s/catalog/dist_lock_manager_mock.h" #include "mongo/s/config_server_test_fixture.h" #include "mongo/unittest/unittest.h" @@ -79,7 +79,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreate2KeysFromEmpty) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); const LogicalTime currentTime(LogicalTime(Timestamp(100, 2))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); @@ -105,7 +106,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldPropagateWriteError) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); const LogicalTime currentTime(LogicalTime(Timestamp(100, 2))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); FailPointEnableBlock failWriteBlock("failCollectionInserts"); @@ -116,8 +118,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldPropagateWriteError) { TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfOnlyOneKeyExists) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); - LogicalClock::get(operationContext()) - ->setClusterTimeFromTrustedSource(LogicalTime(Timestamp(100, 2))); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(100, 2))); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); @@ -135,7 +137,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfOnlyOneKeyExists) { ASSERT_EQ(Timestamp(105, 0), key1.getExpiresAt().asTimestamp()); } - auto currentTime = LogicalClock::get(operationContext())->getClusterTime(); + auto currentTime = + VectorClock::get(operationContext())->getTime()[VectorClock::Component::ClusterTime]; auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); @@ -163,8 +166,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfOnlyOneKeyExists) { TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfNoValidKeyAfterCurrent) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); - LogicalClock::get(operationContext()) - ->setClusterTimeFromTrustedSource(LogicalTime(Timestamp(108, 2))); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(108, 2))); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); @@ -192,7 +195,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfNoValidKeyAfterCurrent) { ASSERT_EQ(Timestamp(110, 0), key2.getExpiresAt().asTimestamp()); } - auto currentTime = LogicalClock::get(operationContext())->getClusterTime(); + auto currentTime = + VectorClock::get(operationContext())->getTime()[VectorClock::Component::ClusterTime]; auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); @@ -246,8 +250,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfNoValidKeyAfterCurrent) { TEST_F(KeyGeneratorUpdateTest, ShouldCreate2KeysIfAllKeysAreExpired) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); - LogicalClock::get(operationContext()) - ->setClusterTimeFromTrustedSource(LogicalTime(Timestamp(120, 2))); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(120, 2))); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); @@ -275,7 +279,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreate2KeysIfAllKeysAreExpired) { ASSERT_EQ(Timestamp(110, 0), key2.getExpiresAt().asTimestamp()); } - auto currentTime = LogicalClock::get(operationContext())->getClusterTime(); + auto currentTime = + VectorClock::get(operationContext())->getTime()[VectorClock::Component::ClusterTime]; auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); @@ -344,7 +349,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldNotCreateNewKeyIfThereAre2UnexpiredKeys) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); const LogicalTime currentTime(LogicalTime(Timestamp(100, 2))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); @@ -403,7 +409,8 @@ TEST_F(KeyGeneratorUpdateTest, ShouldNotCreateKeysWithDisableKeyGenerationFailPo KeyGenerator generator("dummy", catalogClient(), Seconds(5)); const LogicalTime currentTime(LogicalTime(Timestamp(100, 0))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); { FailPointEnableBlock failKeyGenerationBlock("disableKeyGeneration"); diff --git a/src/mongo/db/keys_collection_manager_sharding_test.cpp b/src/mongo/db/keys_collection_manager_sharding_test.cpp index f6d4fc53a97..dd6e7b52f06 100644 --- a/src/mongo/db/keys_collection_manager_sharding_test.cpp +++ b/src/mongo/db/keys_collection_manager_sharding_test.cpp @@ -37,7 +37,7 @@ #include "mongo/db/keys_collection_client_sharded.h" #include "mongo/db/keys_collection_document.h" #include "mongo/db/keys_collection_manager.h" -#include "mongo/db/logical_clock.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/s/catalog/dist_lock_manager_mock.h" #include "mongo/s/config_server_test_fixture.h" #include "mongo/unittest/unittest.h" @@ -261,7 +261,8 @@ TEST_F(KeysManagerShardedTest, ShouldCreateKeysIfKeyGeneratorEnabled) { keyManager()->startMonitoring(getServiceContext()); const LogicalTime currentTime(LogicalTime(Timestamp(100, 0))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); keyManager()->enableKeyGenerator(operationContext(), true); keyManager()->refreshNow(operationContext()); @@ -277,7 +278,8 @@ TEST_F(KeysManagerShardedTest, EnableModeFlipFlopStressTest) { keyManager()->startMonitoring(getServiceContext()); const LogicalTime currentTime(LogicalTime(Timestamp(100, 0))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); bool doEnable = true; @@ -303,7 +305,8 @@ TEST_F(KeysManagerShardedTest, ShouldStillBeAbleToUpdateCacheEvenIfItCantCreateK // Set the time to be very ahead so the updater will be forced to create new keys. const LogicalTime fakeTime(Timestamp(20000, 0)); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(fakeTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, fakeTime); FailPointEnableBlock failWriteBlock("failCollectionInserts"); @@ -325,7 +328,8 @@ TEST_F(KeysManagerShardedTest, ShouldStillBeAbleToUpdateCacheEvenIfItCantCreateK TEST_F(KeysManagerShardedTest, ShouldNotCreateKeysWithDisableKeyGenerationFailPoint) { const LogicalTime currentTime(Timestamp(100, 0)); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); { FailPointEnableBlock failKeyGenerationBlock("disableKeyGeneration"); @@ -346,7 +350,8 @@ TEST_F(KeysManagerShardedTest, ShouldNotCreateKeysWithDisableKeyGenerationFailPo TEST_F(KeysManagerShardedTest, HasSeenKeysIsFalseUntilKeysAreFound) { const LogicalTime currentTime(Timestamp(100, 0)); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); ASSERT_EQ(false, keyManager()->hasSeenKeys()); diff --git a/src/mongo/db/logical_clock.cpp b/src/mongo/db/logical_clock.cpp index 3cb7dc1b192..1f99fde99d9 100644 --- a/src/mongo/db/logical_clock.cpp +++ b/src/mongo/db/logical_clock.cpp @@ -32,25 +32,19 @@ #include "mongo/platform/basic.h" #include "mongo/db/logical_clock.h" -#include "mongo/db/logical_clock_gen.h" #include "mongo/base/status.h" #include "mongo/db/global_settings.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/db/time_proof_service.h" -#include "mongo/db/vector_clock_mutable.h" +#include "mongo/db/vector_clock.h" #include "mongo/logv2/log.h" namespace mongo { namespace { const auto getLogicalClock = ServiceContext::declareDecoration<std::unique_ptr<LogicalClock>>(); - -bool lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks) { - return time.asTimestamp().getSecs() <= LogicalClock::kMaxSignedInt && - time.asTimestamp().getInc() <= (LogicalClock::kMaxSignedInt - nTicks); -} } // namespace LogicalTime LogicalClock::getClusterTimeForReplicaSet(ServiceContext* svcCtx) { @@ -81,116 +75,7 @@ void LogicalClock::set(ServiceContext* service, std::unique_ptr<LogicalClock> cl LogicalClock::LogicalClock(ServiceContext* service) : _service(service) {} LogicalTime LogicalClock::getClusterTime() { - stdx::lock_guard<Latch> lock(_mutex); - return _clusterTime; -} - -Status LogicalClock::advanceClusterTime(const LogicalTime newTime) { - stdx::lock_guard<Latch> lock(_mutex); - - auto rateLimitStatus = _passesRateLimiter_inlock(newTime); - if (!rateLimitStatus.isOK()) { - return rateLimitStatus; - } - - if (newTime > _clusterTime) { - _clusterTime = newTime; - } - - return Status::OK(); -} - -LogicalTime LogicalClock::reserveTicks(uint64_t nTicks) { - (void)VectorClockMutable::get(_service)->tick(VectorClock::Component::ClusterTime, nTicks); - - invariant(nTicks > 0 && nTicks <= kMaxSignedInt); - - stdx::lock_guard<Latch> lock(_mutex); - - LogicalTime clusterTime = _clusterTime; - - const unsigned wallClockSecs = - durationCount<Seconds>(_service->getFastClockSource()->now().toDurationSinceEpoch()); - unsigned clusterTimeSecs = clusterTime.asTimestamp().getSecs(); - - // Synchronize clusterTime with wall clock time, if clusterTime was behind in seconds. - if (clusterTimeSecs < wallClockSecs) { - clusterTime = LogicalTime(Timestamp(wallClockSecs, 0)); - } - // If reserving 'nTicks' would force the cluster timestamp's increment field to exceed (2^31-1), - // overflow by moving to the next second. We use the signed integer maximum as an overflow point - // in order to preserve compatibility with potentially signed or unsigned integral Timestamp - // increment types. It is also unlikely to apply more than 2^31 oplog entries in the span of one - // second. - else if (clusterTime.asTimestamp().getInc() > (kMaxSignedInt - nTicks)) { - - LOGV2(20709, - "Exceeded maximum allowable increment value within one second. Moving clusterTime " - "forward to the next second."); - - // Move time forward to the next second - clusterTime = LogicalTime(Timestamp(clusterTime.asTimestamp().getSecs() + 1, 0)); - } - - uassert(40482, - "cluster time cannot be advanced beyond its maximum value", - lessThanOrEqualToMaxPossibleTime(clusterTime, nTicks)); - - // Save the next cluster time. - clusterTime.addTicks(1); - _clusterTime = clusterTime; - - // Add the rest of the requested ticks if needed. - if (nTicks > 1) { - _clusterTime.addTicks(nTicks - 1); - } - - return clusterTime; -} - -void LogicalClock::setClusterTimeFromTrustedSource(LogicalTime newTime) { - stdx::lock_guard<Latch> lock(_mutex); - // Rate limit checks are skipped here so a server with no activity for longer than - // maxAcceptableLogicalClockDriftSecs seconds can still have its cluster time initialized. - - uassert(40483, - "cluster time cannot be advanced beyond its maximum value", - lessThanOrEqualToMaxPossibleTime(newTime, 0)); - - if (newTime > _clusterTime) { - _clusterTime = newTime; - } -} - -Status LogicalClock::_passesRateLimiter_inlock(LogicalTime newTime) { - const unsigned wallClockSecs = - durationCount<Seconds>(_service->getFastClockSource()->now().toDurationSinceEpoch()); - auto maxAcceptableDriftSecs = static_cast<const unsigned>(gMaxAcceptableLogicalClockDriftSecs); - auto newTimeSecs = newTime.asTimestamp().getSecs(); - - // Both values are unsigned, so compare them first to avoid wrap-around. - if ((newTimeSecs > wallClockSecs) && (newTimeSecs - wallClockSecs) > maxAcceptableDriftSecs) { - return Status(ErrorCodes::ClusterTimeFailsRateLimiter, - str::stream() << "New cluster time, " << newTimeSecs - << ", is too far from this node's wall clock time, " - << wallClockSecs << "."); - } - - uassert(40484, - "cluster time cannot be advanced beyond its maximum value", - lessThanOrEqualToMaxPossibleTime(newTime, 0)); - - return Status::OK(); -} - -bool LogicalClock::isEnabled() const { - stdx::lock_guard<Latch> lock(_mutex); - return _isEnabled; -} - -void LogicalClock::disable() { - stdx::lock_guard<Latch> lock(_mutex); - _isEnabled = false; + return VectorClock::get(_service)->getTime()[VectorClock::Component::ClusterTime]; } } // namespace mongo diff --git a/src/mongo/db/logical_clock.h b/src/mongo/db/logical_clock.h index ec4bddc518a..059a9dcdfd6 100644 --- a/src/mongo/db/logical_clock.h +++ b/src/mongo/db/logical_clock.h @@ -36,9 +36,12 @@ namespace mongo { class ServiceContext; class OperationContext; + /** - * LogicalClock maintain the clusterTime for a clusterNode. Every cluster node in a replica set has - * an instance of the LogicalClock installed as a ServiceContext decoration. + * LogicalClock provides a legacy interface to the cluster time, which is now provided by the + * VectorClock class. + * + * TODO SERVER-48433: Remove this legacy LogicalClock interface. */ class LogicalClock { public: @@ -47,8 +50,6 @@ public: static LogicalClock* get(OperationContext* ctx); static void set(ServiceContext* service, std::unique_ptr<LogicalClock> logicalClock); - static const uint32_t kMaxSignedInt = ((1U << 31) - 1); - /** * Returns the current cluster time if this is a replica set node, otherwise returns a null * logical time. @@ -62,55 +63,12 @@ public: LogicalClock(ServiceContext*); /** - * The method sets current time to newTime if the newTime > current time and it passes the rate - * check. - * - * Returns an error if the newTime does not pass the rate check. - */ - Status advanceClusterTime(const LogicalTime newTime); - - /** * Returns the current clusterTime. */ LogicalTime getClusterTime(); - /** - * Returns the next clusterTime value and provides a guarantee that any future call to - * reserveTicks() will return a value at least 'nTicks' ticks in the future from the current - * clusterTime. - */ - LogicalTime reserveTicks(uint64_t nTicks); - - /** - * Resets current time to newTime. Should only be used for initializing this clock from an - * oplog timestamp. - */ - void setClusterTimeFromTrustedSource(LogicalTime newTime); - - /** - * Returns true if the clock is enabled and can be used. Defaults to true. - */ - bool isEnabled() const; - - /** - * Disables the logical clock. A disabled clock won't process logical times and can't be - * re-enabled. - */ - void disable(); - private: - /** - * Rate limiter for advancing cluster time. Rejects newTime if its seconds value is more than - * kMaxAcceptableLogicalClockDriftSecs seconds ahead of this node's wall clock. - */ - Status _passesRateLimiter_inlock(LogicalTime newTime); - ServiceContext* const _service; - - // The mutex protects _clusterTime and _isEnabled. - mutable Mutex _mutex = MONGO_MAKE_LATCH("LogicalClock::_mutex"); - LogicalTime _clusterTime; - bool _isEnabled{true}; }; } // namespace mongo diff --git a/src/mongo/db/logical_clock.idl b/src/mongo/db/logical_clock.idl index 18eb2e2b2ca..84a33d1cb3e 100644 --- a/src/mongo/db/logical_clock.idl +++ b/src/mongo/db/logical_clock.idl @@ -29,13 +29,14 @@ global: cpp_namespace: "mongo" +# TODO SERVER-48435: Rename this file to vector_clock.idl (but keep the parameter name the same). server_parameters: maxAcceptableLogicalClockDriftSecs: - description: "The value defines the maximum difference, in seconds, between the current clusterTime and a new clusterTime. Default value is 1 year." + description: "The value defines the maximum time, in seconds, that a new logical time (such as $clusterTime) may be ahead of the node's current wallclock time. Default value is 1 year." set_at: [ startup ] cpp_vartype: long long cpp_varname: gMaxAcceptableLogicalClockDriftSecs default: expr: (365 * 24 * 60 * 60) # one year validator: - gt: 0
\ No newline at end of file + gt: 0 diff --git a/src/mongo/db/logical_clock_test.cpp b/src/mongo/db/logical_clock_test.cpp index fdb49a7390b..248d6609cc5 100644 --- a/src/mongo/db/logical_clock_test.cpp +++ b/src/mongo/db/logical_clock_test.cpp @@ -41,6 +41,7 @@ #include "mongo/db/logical_clock_test_fixture.h" #include "mongo/db/logical_time.h" #include "mongo/db/repl/replication_coordinator_mock.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/unittest/unittest.h" #include "mongo/util/clock_source_mock.h" @@ -51,6 +52,9 @@ const NamespaceString kDummyNamespaceString("test", "foo"); using LogicalClockTest = LogicalClockTestFixture; +constexpr auto ClusterTime = VectorClock::Component::ClusterTime; +constexpr unsigned maxVal = std::numeric_limits<int32_t>::max(); + LogicalTime buildLogicalTime(unsigned secs, unsigned inc) { return LogicalTime(Timestamp(secs, inc)); } @@ -60,7 +64,7 @@ TEST_F(LogicalClockTest, roundtrip) { Timestamp tX(1); auto time = LogicalTime(tX); - getClock()->setClusterTimeFromTrustedSource(time); + VectorClockMutable::get(getServiceContext())->tickTo(ClusterTime, time); auto storedTime(getClock()->getClusterTime()); ASSERT_TRUE(storedTime == time); @@ -71,37 +75,37 @@ TEST_F(LogicalClockTest, reserveTicks) { // Set clock to a non-zero time, so we can verify wall clock synchronization. setMockClockSourceTime(Date_t::fromMillisSinceEpoch(10 * 1000)); - auto t1 = getClock()->reserveTicks(1); + auto t1 = VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); auto t2(getClock()->getClusterTime()); ASSERT_TRUE(t1 == t2); // Make sure we synchronized with the wall clock. ASSERT_TRUE(t2.asTimestamp().getSecs() == 10); - auto t3 = getClock()->reserveTicks(1); + auto t3 = VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); t1.addTicks(1); ASSERT_TRUE(t3 == t1); - t3 = getClock()->reserveTicks(100); + t3 = VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 100); t1.addTicks(1); ASSERT_TRUE(t3 == t1); - t3 = getClock()->reserveTicks(1); + t3 = VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); t1.addTicks(100); ASSERT_TRUE(t3 == t1); // Ensure overflow to a new second. auto initTimeSecs = getClock()->getClusterTime().asTimestamp().getSecs(); - getClock()->reserveTicks((1U << 31) - 1); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, (1U << 31) - 1); auto newTimeSecs = getClock()->getClusterTime().asTimestamp().getSecs(); ASSERT_TRUE(newTimeSecs == initTimeSecs + 1); } // Verify the advanceClusterTime functionality. TEST_F(LogicalClockTest, advanceClusterTime) { - auto t1 = getClock()->reserveTicks(1); + auto t1 = VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); t1.addTicks(100); - ASSERT_OK(getClock()->advanceClusterTime(t1)); + advanceClusterTime(t1); ASSERT_TRUE(t1 == getClock()->getClusterTime()); } @@ -116,7 +120,8 @@ TEST_F(LogicalClockTest, RateLimiterRejectsLogicalTimesTooFarAhead) { 1); LogicalTime t1(tooFarAheadTimestamp); - ASSERT_EQ(ErrorCodes::ClusterTimeFailsRateLimiter, getClock()->advanceClusterTime(t1)); + ASSERT_THROWS_CODE( + advanceClusterTime(t1), DBException, ErrorCodes::ClusterTimeFailsRateLimiter); } // Verify cluster time can be initialized to a very old time. @@ -128,7 +133,7 @@ TEST_F(LogicalClockTest, InitFromTrustedSourceCanAcceptVeryOldLogicalTime) { durationCount<Seconds>(getMockClockSourceTime().toDurationSinceEpoch()) - (kMaxAcceptableLogicalClockDriftSecsDefault * 5)); auto veryOldTime = LogicalTime(veryOldTimestamp); - getClock()->setClusterTimeFromTrustedSource(veryOldTime); + VectorClockMutable::get(getServiceContext())->tickTo(ClusterTime, veryOldTime); ASSERT_TRUE(getClock()->getClusterTime() == veryOldTime); } @@ -138,7 +143,7 @@ TEST_F(LogicalClockTest, WritesToOplogAdvanceClusterTime) { Timestamp tX(1, 0); auto initialTime = LogicalTime(tX); - getClock()->setClusterTimeFromTrustedSource(initialTime); + VectorClockMutable::get(getServiceContext())->tickTo(ClusterTime, initialTime); ASSERT_TRUE(getClock()->getClusterTime() == initialTime); getDBClient()->insert(kDummyNamespaceString.ns(), BSON("x" << 1)); @@ -176,7 +181,7 @@ TEST_F(LogicalClockTest, WallClockSetTooFarInPast) { // Verify that maxAcceptableLogicalClockDriftSecs parameter does not need to be increased to // advance cluster time through metadata back to the current time. - ASSERT_OK(getClock()->advanceClusterTime(currentTime)); + advanceClusterTime(currentTime); ASSERT_TRUE(getClock()->getClusterTime() == currentTime); } @@ -210,117 +215,113 @@ TEST_F(LogicalClockTest, WallClockSetTooFarInFuture) { auto nextTime = getClock()->getClusterTime(); nextTime.addTicks(1); // The next lowest cluster time. - ASSERT_EQ(ErrorCodes::ClusterTimeFailsRateLimiter, getClock()->advanceClusterTime(nextTime)); + ASSERT_THROWS_CODE( + advanceClusterTime(nextTime), DBException, ErrorCodes::ClusterTimeFailsRateLimiter); // Set wall clock to the current time + 1 day to simulate increasing the // maxAcceptableLogicalClockDriftSecs parameter, which can only be set at startup, and verify // time can be advanced through metadata again. setMockClockSourceTime(Date_t::fromDurationSinceEpoch(currentSecs + oneDay)); - ASSERT_OK(getClock()->advanceClusterTime(nextTime)); + advanceClusterTime(nextTime); 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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal - 1, maxVal - 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal - 1, maxVal)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal - 5)); - getClock()->reserveTicks(5); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal - 1, maxVal - 5)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 5); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal - 1, maxVal)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(0, maxVal - 1)); - getClock()->reserveTicks(1); + resetClock()->tickTo(ClusterTime, buildLogicalTime(0, maxVal - 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(0, maxVal)); // Can overflow inc into seconds to reach max seconds value. - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal)); - getClock()->reserveTicks(1); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal - 1, maxVal)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, 1)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, maxVal - 5)); - getClock()->reserveTicks(10); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal - 1, maxVal - 5)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 10); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, 10)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal - 1, 1)); - getClock()->reserveTicks(maxVal); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal - 1, 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, maxVal); ASSERT_EQ(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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, 2)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, 1)); - getClock()->reserveTicks(100); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 100); ASSERT_EQ(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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal - 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, maxVal)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 5)); - getClock()->reserveTicks(5); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal - 5)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 5); ASSERT_EQ(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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal)); + ASSERT_THROWS(VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1), DBException); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, maxVal)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal)); - ASSERT_THROWS(getClock()->reserveTicks(5), std::exception); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal)); + ASSERT_THROWS(VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 5), DBException); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, maxVal)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 1)); - ASSERT_THROWS(getClock()->reserveTicks(2), std::exception); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal - 1)); + ASSERT_THROWS(VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 2), DBException); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(maxVal, maxVal - 1)); - resetClock()->setClusterTimeFromTrustedSource(buildLogicalTime(maxVal, maxVal - 11)); - ASSERT_THROWS(getClock()->reserveTicks(12), std::exception); + resetClock()->tickTo(ClusterTime, buildLogicalTime(maxVal, maxVal - 11)); + ASSERT_THROWS(VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 12), DBException); ASSERT_EQ(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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(1, 1)); + VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1); ASSERT_EQ(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); + resetClock()->tickTo(ClusterTime, buildLogicalTime(1, 1)); + ASSERT_THROWS(VectorClockMutable::get(getServiceContext())->tick(ClusterTime, 1), DBException); ASSERT_EQ(getClock()->getClusterTime(), buildLogicalTime(1, 1)); } // Verify the clock rejects cluster times greater than the max allowed time. TEST_F(LogicalClockTest, RejectsLogicalTimesGreaterThanMaxTime) { - unsigned maxVal = LogicalClock::kMaxSignedInt; - // A cluster 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_THROWS(VectorClockMutable::get(getServiceContext())->tickTo(ClusterTime, beyondMaxTime), + DBException); ASSERT_TRUE(getClock()->getClusterTime() == LogicalTime()); // The time can't be advanced through metadata to a time greater than the max possible. @@ -328,7 +329,7 @@ TEST_F(LogicalClockTest, RejectsLogicalTimesGreaterThanMaxTime) { auto almostMaxSecs = Seconds(maxVal) - Seconds(kMaxAcceptableLogicalClockDriftSecsDefault) + Seconds(10); setMockClockSourceTime(Date_t::fromDurationSinceEpoch(almostMaxSecs)); - ASSERT_THROWS(getClock()->advanceClusterTime(beyondMaxTime), std::exception); + ASSERT_THROWS(advanceClusterTime(beyondMaxTime), DBException); ASSERT_TRUE(getClock()->getClusterTime() == LogicalTime()); } diff --git a/src/mongo/db/logical_clock_test_fixture.cpp b/src/mongo/db/logical_clock_test_fixture.cpp index 342f2679522..99d3ee0e7b9 100644 --- a/src/mongo/db/logical_clock_test_fixture.cpp +++ b/src/mongo/db/logical_clock_test_fixture.cpp @@ -40,6 +40,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/signed_logical_time.h" #include "mongo/db/time_proof_service.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/unittest/unittest.h" #include "mongo/util/clock_source_mock.h" @@ -71,14 +72,14 @@ void LogicalClockTestFixture::tearDown() { ShardingMongodTestFixture::tearDown(); } -LogicalClock* LogicalClockTestFixture::resetClock() { +VectorClockMutable* LogicalClockTestFixture::resetClock() { auto service = getServiceContext(); - auto logicalClock = std::make_unique<LogicalClock>(service); - - LogicalClock::set(service, std::move(logicalClock)); - _clock = LogicalClock::get(service); + VectorClock::get(service)->resetVectorClock_forTest(); + return VectorClockMutable::get(service); +} - return _clock; +void LogicalClockTestFixture::advanceClusterTime(LogicalTime newTime) { + VectorClock::get(getServiceContext())->advanceClusterTime_forTest(newTime); } LogicalClock* LogicalClockTestFixture::getClock() const { diff --git a/src/mongo/db/logical_clock_test_fixture.h b/src/mongo/db/logical_clock_test_fixture.h index 593670d5aaf..4b3322d406b 100644 --- a/src/mongo/db/logical_clock_test_fixture.h +++ b/src/mongo/db/logical_clock_test_fixture.h @@ -38,6 +38,7 @@ class ClockSourceMock; class DBDirectClient; class LogicalClock; class LogicalTime; +class VectorClockMutable; /** * A test fixture that installs a LogicalClock instance with a TimeProofService onto a service @@ -58,7 +59,9 @@ protected: void tearDown() override; - LogicalClock* resetClock(); + VectorClockMutable* resetClock(); + + void advanceClusterTime(LogicalTime newTime); LogicalClock* getClock() const; diff --git a/src/mongo/db/logical_time_metadata_hook.cpp b/src/mongo/db/logical_time_metadata_hook.cpp index 36fda90c1ce..11a57a59885 100644 --- a/src/mongo/db/logical_time_metadata_hook.cpp +++ b/src/mongo/db/logical_time_metadata_hook.cpp @@ -33,10 +33,8 @@ #include <memory> -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time_validator.h" #include "mongo/db/operation_time_tracker.h" -#include "mongo/rpc/metadata/logical_time_metadata.h" +#include "mongo/db/vector_clock.h" namespace mongo { @@ -45,48 +43,35 @@ namespace rpc { namespace { const char kOperationTimeFieldName[] = "operationTime"; } + +// TODO SERVER-48434: Rename this class to VectorClockMetadataHook. LogicalTimeMetadataHook::LogicalTimeMetadataHook(ServiceContext* service) : _service(service) {} Status LogicalTimeMetadataHook::writeRequestMetadata(OperationContext* opCtx, BSONObjBuilder* metadataBob) { - auto validator = LogicalTimeValidator::get(_service); - if (!validator || !LogicalClock::get(_service)->isEnabled()) { - return Status::OK(); - } - - auto newTime = LogicalClock::get(_service)->getClusterTime(); - LogicalTimeMetadata metadata(validator->trySignLogicalTime(newTime)); - metadata.writeToMetadata(metadataBob); + VectorClock::get(_service)->gossipOut(opCtx, metadataBob, transport::Session::kInternalClient); return Status::OK(); } Status LogicalTimeMetadataHook::readReplyMetadata(OperationContext* opCtx, StringData replySource, const BSONObj& metadataObj) { - auto parseStatus = LogicalTimeMetadata::readFromMetadata(metadataObj); - if (!parseStatus.isOK()) { - return parseStatus.getStatus(); - } - - auto& signedTime = parseStatus.getValue().getSignedTime(); - - // LogicalTimeMetadata is default constructed if no cluster time metadata was sent, so a - // default constructed SignedLogicalTime should be ignored. - if (signedTime.getTime() == LogicalTime::kUninitialized || - !LogicalClock::get(_service)->isEnabled()) { + if (!VectorClock::get(_service)->isEnabled()) { return Status::OK(); } if (opCtx) { auto timeTracker = OperationTimeTracker::get(opCtx); - auto operationTime = metadataObj[kOperationTimeFieldName]; if (!operationTime.eoo()) { invariant(operationTime.type() == BSONType::bsonTimestamp); timeTracker->updateOperationTime(LogicalTime(operationTime.timestamp())); } } - return LogicalClock::get(_service)->advanceClusterTime(signedTime.getTime()); + + VectorClock::get(_service)->gossipIn( + opCtx, metadataObj, false /* couldBeUnauthorized */, transport::Session::kInternalClient); + return Status::OK(); } } // namespace rpc diff --git a/src/mongo/db/logical_time_validator.cpp b/src/mongo/db/logical_time_validator.cpp index 793772dffb8..3f0e4d9b33b 100644 --- a/src/mongo/db/logical_time_validator.cpp +++ b/src/mongo/db/logical_time_validator.cpp @@ -39,9 +39,9 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/keys_collection_manager.h" -#include "mongo/db/logical_clock.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" +#include "mongo/db/vector_clock.h" #include "mongo/logv2/log.h" #include "mongo/util/assert_util.h" #include "mongo/util/fail_point.h" @@ -130,7 +130,7 @@ SignedLogicalTime LogicalTimeValidator::signLogicalTime(OperationContext* opCtx, auto keyStatusWith = keyManager->getKeyForSigning(nullptr, newTime); auto keyStatus = keyStatusWith.getStatus(); - while (keyStatus == ErrorCodes::KeyNotFound && LogicalClock::get(opCtx)->isEnabled()) { + while (keyStatus == ErrorCodes::KeyNotFound && VectorClock::get(opCtx)->isEnabled()) { keyManager->refreshNow(opCtx); keyStatusWith = keyManager->getKeyForSigning(nullptr, newTime); diff --git a/src/mongo/db/logical_time_validator_test.cpp b/src/mongo/db/logical_time_validator_test.cpp index ce8fea3393e..a5ceeb0c135 100644 --- a/src/mongo/db/logical_time_validator_test.cpp +++ b/src/mongo/db/logical_time_validator_test.cpp @@ -34,12 +34,12 @@ #include "mongo/bson/timestamp.h" #include "mongo/db/keys_collection_client_sharded.h" #include "mongo/db/keys_collection_manager.h" -#include "mongo/db/logical_clock.h" #include "mongo/db/logical_time.h" #include "mongo/db/logical_time_validator.h" #include "mongo/db/server_options.h" #include "mongo/db/signed_logical_time.h" #include "mongo/db/time_proof_service.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/s/catalog/dist_lock_manager_mock.h" #include "mongo/s/config_server_test_fixture.h" #include "mongo/unittest/unittest.h" @@ -64,7 +64,8 @@ protected: Grid::get(operationContext())->catalogClient()); const LogicalTime currentTime(LogicalTime(Timestamp(1, 0))); - LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); + VectorClockMutable::get(operationContext()) + ->tickTo(VectorClock::Component::ClusterTime, currentTime); _keyManager = std::make_shared<KeysCollectionManager>( "dummy", std::move(catalogClient), Seconds(1000)); diff --git a/src/mongo/db/op_msg_fuzzer.cpp b/src/mongo/db/op_msg_fuzzer.cpp index b1c2843e360..94e0cbd7efe 100644 --- a/src/mongo/db/op_msg_fuzzer.cpp +++ b/src/mongo/db/op_msg_fuzzer.cpp @@ -41,6 +41,7 @@ #include "mongo/db/service_context.h" #include "mongo/db/service_entry_point_common.h" #include "mongo/db/service_entry_point_mongod.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/platform/basic.h" #include "mongo/transport/service_entry_point_impl.h" #include "mongo/transport/session.h" @@ -98,8 +99,9 @@ extern "C" int LLVMFuzzerTestOneInput(const char* Data, size_t Size) { mongo::ServiceContext::UniqueOperationContext opCtx = serviceContext->makeOperationContext(client.get()); auto logicalClock = std::make_unique<mongo::LogicalClock>(serviceContext); - logicalClock->setClusterTimeFromTrustedSource(kInMemoryLogicalTime); mongo::LogicalClock::set(serviceContext, std::move(logicalClock)); + VectorClockMutable::get(getServiceContext()) + ->tickTo(VectorClock::Component::ClusterTime, kInMemoryLogicalTime); int new_size = Size + sizeof(int); auto sb = mongo::SharedBuffer::allocate(new_size); diff --git a/src/mongo/db/ops/insert.cpp b/src/mongo/db/ops/insert.cpp index 22dd459e999..69d19f49958 100644 --- a/src/mongo/db/ops/insert.cpp +++ b/src/mongo/db/ops/insert.cpp @@ -34,8 +34,7 @@ #include "mongo/bson/bson_depth.h" #include "mongo/db/commands/feature_compatibility_version_parser.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/db/views/durable_view_catalog.h" #include "mongo/util/str.h" @@ -163,7 +162,8 @@ StatusWith<BSONObj> fixDocumentForInsert(ServiceContext* service, const BSONObj& if (hadId && e.fieldNameStringData() == "_id") { // no-op } else if (e.type() == bsonTimestamp && e.timestampValue() == 0) { - auto nextTime = LogicalClock::get(service)->reserveTicks(1); + auto nextTime = + VectorClockMutable::get(service)->tick(VectorClock::Component::ClusterTime, 1); b.append(e.fieldName(), nextTime.asTimestamp()); } else { b.append(e); diff --git a/src/mongo/db/pipeline/expression_context_test.cpp b/src/mongo/db/pipeline/expression_context_test.cpp index 1135f465bb4..a3dad86199d 100644 --- a/src/mongo/db/pipeline/expression_context_test.cpp +++ b/src/mongo/db/pipeline/expression_context_test.cpp @@ -37,6 +37,7 @@ #include "mongo/db/pipeline/process_interface/stub_mongo_process_interface.h" #include "mongo/db/query/datetime/date_time_support.h" #include "mongo/db/service_context_test_fixture.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/unittest/unittest.h" #define ASSERT_DOES_NOT_THROW(EXPRESSION) \ @@ -56,10 +57,12 @@ using ExpressionContextTest = ServiceContextTest; TEST_F(ExpressionContextTest, ExpressionContextSummonsMissingTimeValues) { auto opCtx = makeOperationContext(); auto logicalClock = std::make_unique<LogicalClock>(opCtx->getServiceContext()); - auto t1 = logicalClock->reserveTicks(1); - t1.addTicks(100); - ASSERT_OK(logicalClock->advanceClusterTime(t1)); LogicalClock::set(opCtx->getServiceContext(), std::move(logicalClock)); + auto t1 = VectorClockMutable::get(opCtx->getServiceContext()) + ->tick(VectorClock::Component::ClusterTime, 1); + t1.addTicks(100); + VectorClockMutable::get(opCtx->getServiceContext()) + ->tickTo(VectorClock::Component::ClusterTime, t1); { const auto expCtx = ExpressionContext{opCtx.get(), {}, // explain diff --git a/src/mongo/db/pipeline/process_interface/SConscript b/src/mongo/db/pipeline/process_interface/SConscript index b24e45be165..8fc9a8e1fb3 100644 --- a/src/mongo/db/pipeline/process_interface/SConscript +++ b/src/mongo/db/pipeline/process_interface/SConscript @@ -109,6 +109,7 @@ env.CppUnitTest( LIBDEPS=[ '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/service_context_test_fixture', + '$BUILD_DIR/mongo/db/vector_clock_mongod', '$BUILD_DIR/mongo/s/catalog_cache_test_fixture', 'mongos_process_interface', 'shardsvr_process_interface', diff --git a/src/mongo/db/read_write_concern_defaults_test.cpp b/src/mongo/db/read_write_concern_defaults_test.cpp index cf9a9a2d9da..6e248abea38 100644 --- a/src/mongo/db/read_write_concern_defaults_test.cpp +++ b/src/mongo/db/read_write_concern_defaults_test.cpp @@ -35,6 +35,7 @@ #include "mongo/db/read_write_concern_defaults_cache_lookup_mock.h" #include "mongo/db/repl/optime.h" #include "mongo/db/service_context_test_fixture.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/util/clock_source_mock.h" namespace mongo { @@ -272,7 +273,7 @@ protected: _lookupMock.setLookupCallReturnValue(std::move(defaults)); auto oldDefaults = _rwcd.getDefault(operationContext()); - getClock()->reserveTicks(1); + VectorClockMutable::get(getServiceContext())->tick(VectorClock::Component::ClusterTime, 1); getMockClockSource()->advance(Milliseconds(1)); return oldDefaults; @@ -471,7 +472,7 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, TestRefreshDefaultsWithDelet ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getUpdateOpTime()); ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getUpdateWallClockTime()); - getClock()->reserveTicks(1); + VectorClockMutable::get(getServiceContext())->tick(VectorClock::Component::ClusterTime, 1); getMockClockSource()->advance(Milliseconds(1)); _lookupMock.setLookupCallReturnValue(RWConcernDefault()); diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index b4900aa5c8e..1f4b55bbdf7 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -23,10 +23,9 @@ env.Library( LIBDEPS_PRIVATE=[ 'optime', 'repl_coordinator_interface', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/logical_time', - '$BUILD_DIR/mongo/db/vector_clock_mutable', '$BUILD_DIR/mongo/db/storage/flow_control', + '$BUILD_DIR/mongo/db/vector_clock_mutable', ], ) @@ -228,7 +227,7 @@ env.Library( '$BUILD_DIR/mongo/db/catalog/database_holder', '$BUILD_DIR/mongo/db/storage/oplog_cap_maintainer_thread', '$BUILD_DIR/mongo/db/storage/storage_control', - '$BUILD_DIR/mongo/db/logical_clock', + '$BUILD_DIR/mongo/db/vector_clock', ], ) @@ -772,6 +771,7 @@ env.Library( 'storage_interface', '$BUILD_DIR/mongo/db/concurrency/lock_manager', '$BUILD_DIR/mongo/db/service_context', + '$BUILD_DIR/mongo/db/replica_set_aware_service', '$BUILD_DIR/mongo/executor/network_interface_mock', ], ) @@ -1173,7 +1173,6 @@ env.Library( '$BUILD_DIR/mongo/db/free_mon/free_mon_mongod', '$BUILD_DIR/mongo/db/kill_sessions_local', '$BUILD_DIR/mongo/db/lasterror', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/logical_time', '$BUILD_DIR/mongo/db/op_observer', '$BUILD_DIR/mongo/db/query_exec', @@ -1184,6 +1183,7 @@ env.Library( '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/db/stats/counters', '$BUILD_DIR/mongo/db/system_index', + '$BUILD_DIR/mongo/db/vector_clock', '$BUILD_DIR/mongo/rpc/client_metadata', '$BUILD_DIR/mongo/util/fail_point', 'bgsync', diff --git a/src/mongo/db/repl/local_oplog_info.cpp b/src/mongo/db/repl/local_oplog_info.cpp index f6573c6a5f8..a36dae6897f 100644 --- a/src/mongo/db/repl/local_oplog_info.cpp +++ b/src/mongo/db/repl/local_oplog_info.cpp @@ -33,12 +33,11 @@ #include "mongo/db/repl/local_oplog_info.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/storage/flow_control.h" #include "mongo/db/storage/record_store.h" #include "mongo/db/storage/recovery_unit.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -96,7 +95,8 @@ void LocalOplogInfo::resetCollection() { void LocalOplogInfo::setNewTimestamp(ServiceContext* service, const Timestamp& newTime) { stdx::lock_guard<Latch> lk(_newOpMutex); - LogicalClock::get(service)->setClusterTimeFromTrustedSource(LogicalTime(newTime)); + VectorClockMutable::get(service)->tickTo(VectorClock::Component::ClusterTime, + LogicalTime(newTime)); } std::vector<OplogSlot> LocalOplogInfo::getNextOpTimes(OperationContext* opCtx, std::size_t count) { @@ -123,7 +123,9 @@ std::vector<OplogSlot> LocalOplogInfo::getNextOpTimes(OperationContext* opCtx, s { stdx::lock_guard<Latch> lk(_newOpMutex); - ts = LogicalClock::get(opCtx)->reserveTicks(count).asTimestamp(); + ts = VectorClockMutable::get(opCtx) + ->tick(VectorClock::Component::ClusterTime, count) + .asTimestamp(); const bool orderedCommit = false; // The local oplog collection pointer must already be established by this point. diff --git a/src/mongo/db/repl/oplog_applier_impl_test_fixture.cpp b/src/mongo/db/repl/oplog_applier_impl_test_fixture.cpp index fcc03c89ee8..45219111f34 100644 --- a/src/mongo/db/repl/oplog_applier_impl_test_fixture.cpp +++ b/src/mongo/db/repl/oplog_applier_impl_test_fixture.cpp @@ -35,7 +35,6 @@ #include "mongo/db/curop.h" #include "mongo/db/db_raii.h" #include "mongo/db/dbdirectclient.h" -#include "mongo/db/logical_clock.h" #include "mongo/db/op_observer_registry.h" #include "mongo/db/query/internal_plans.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" @@ -46,6 +45,7 @@ #include "mongo/db/repl/replication_recovery_mock.h" #include "mongo/db/repl/storage_interface.h" #include "mongo/db/repl/storage_interface_impl.h" +#include "mongo/db/vector_clock_mutable.h" namespace mongo { namespace repl { @@ -131,7 +131,8 @@ void OplogApplierImplTest::setUp() { // This is necessary to generate ghost timestamps for index builds that are not 0, since 0 is an // invalid timestamp. - ASSERT_OK(LogicalClock::get(_opCtx.get())->advanceClusterTime(LogicalTime(Timestamp(1, 0)))); + VectorClockMutable::get(_opCtx.get()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 0))); } void OplogApplierImplTest::tearDown() { diff --git a/src/mongo/db/repl/oplog_fetcher_test.cpp b/src/mongo/db/repl/oplog_fetcher_test.cpp index bb22b7537fe..3987c4a6437 100644 --- a/src/mongo/db/repl/oplog_fetcher_test.cpp +++ b/src/mongo/db/repl/oplog_fetcher_test.cpp @@ -35,10 +35,10 @@ #include "mongo/db/repl/repl_server_parameters_gen.h" #include "mongo/db/repl/task_executor_mock.h" #include "mongo/db/service_context_test_fixture.h" +#include "mongo/db/signed_logical_time.h" #include "mongo/dbtests/mock/mock_dbclient_connection.h" #include "mongo/executor/thread_pool_task_executor_test_fixture.h" #include "mongo/rpc/metadata.h" -#include "mongo/rpc/metadata/logical_time_metadata.h" #include "mongo/rpc/metadata/oplog_query_metadata.h" #include "mongo/rpc/metadata/repl_set_metadata.h" #include "mongo/unittest/death_test.h" @@ -2208,13 +2208,22 @@ TEST_F(OplogFetcherTest, HandleLogicalTimeMetaDataAndAdvanceClusterTime) { auto oldClusterTime = LogicalClock::get(getGlobalServiceContext())->getClusterTime(); auto logicalTime = LogicalTime(Timestamp(123456, 78)); - auto logicalTimeMetadata = - rpc::LogicalTimeMetadata(SignedLogicalTime(logicalTime, TimeProofService::TimeProof(), 0)); + auto signedTime = SignedLogicalTime(logicalTime, TimeProofService::TimeProof(), 0); BSONObjBuilder bob; ASSERT_OK(replSetMetadata.writeToMetadata(&bob)); ASSERT_OK(oqMetadata.writeToMetadata(&bob)); - logicalTimeMetadata.writeToMetadata(&bob); + + BSONObjBuilder subObjBuilder(bob.subobjStart("$clusterTime")); + signedTime.getTime().asTimestamp().append(subObjBuilder.bb(), "clusterTime"); + + BSONObjBuilder signatureObjBuilder(subObjBuilder.subobjStart("signature")); + signedTime.getProof()->appendAsBinData(signatureObjBuilder, "hash"); + signatureObjBuilder.append("keyId", signedTime.getKeyId()); + signatureObjBuilder.doneFast(); + + subObjBuilder.doneFast(); + auto metadataObj = bob.obj(); // Process one batch with the logical time metadata. diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp index 9ff94e5af20..9f0053fb001 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp @@ -40,6 +40,7 @@ #include "mongo/db/client.h" #include "mongo/db/jsobj.h" #include "mongo/db/repl/oplog_buffer_blocking_queue.h" +#include "mongo/db/replica_set_aware_service.h" #include "mongo/util/net/hostandport.h" #include "mongo/util/sequence_util.h" @@ -235,7 +236,9 @@ void ReplicationCoordinatorExternalStateMock::closeConnections() { void ReplicationCoordinatorExternalStateMock::onStepDownHook() {} -void ReplicationCoordinatorExternalStateMock::onBecomeArbiterHook() {} +void ReplicationCoordinatorExternalStateMock::onBecomeArbiterHook() { + ReplicaSetAwareServiceRegistry::get(getGlobalServiceContext()).onBecomeArbiter(); +} void ReplicationCoordinatorExternalStateMock::signalApplierToChooseNewSyncSource() {} diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 943266b43c9..531accbbeb6 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -62,9 +62,6 @@ #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index_builds_coordinator.h" #include "mongo/db/kill_sessions_local.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time.h" -#include "mongo/db/logical_time_validator.h" #include "mongo/db/mongod_options_storage_gen.h" #include "mongo/db/prepare_conflict_tracker.h" #include "mongo/db/repl/check_quorum_for_config_change.h" @@ -91,6 +88,8 @@ #include "mongo/db/replica_set_aware_service.h" #include "mongo/db/server_options.h" #include "mongo/db/storage/storage_options.h" +#include "mongo/db/vector_clock.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/db/write_concern.h" #include "mongo/db/write_concern_options.h" #include "mongo/executor/connection_pool_stats.h" @@ -657,12 +656,6 @@ void ReplicationCoordinatorImpl::_finishLoadLocalConfig( } } else { _externalState->onBecomeArbiterHook(); - // TODO SERVER-47914: move these actions into VectorClockMongoD::onBecomeArbiter(). - // The node is an arbiter hence will not need logical clock for external operations. - LogicalClock::get(getServiceContext())->disable(); - if (auto validator = LogicalTimeValidator::get(getServiceContext())) { - validator->stopKeyManager(); - } } const auto lastOpTime = lastOpTimeAndWallTime.opTime; @@ -1391,7 +1384,7 @@ void ReplicationCoordinatorImpl::_setMyLastAppliedOpTimeAndWallTime( // The last applied opTime should never advance beyond the global timestamp (i.e. the latest // cluster time). Not enforced if the logical clock is disabled, e.g. for arbiters. - dassert(!LogicalClock::get(getServiceContext())->isEnabled() || + dassert(!VectorClock::get(getServiceContext())->isEnabled() || _externalState->getGlobalTimestamp(getServiceContext()) >= opTime.getTimestamp()); _topCoord->setMyLastAppliedOpTimeAndWallTime( @@ -4193,7 +4186,9 @@ void ReplicationCoordinatorImpl::_performPostMemberStateUpdateAction( void ReplicationCoordinatorImpl::_postWonElectionUpdateMemberState(WithLock lk) { invariant(_topCoord->getTerm() != OpTime::kUninitializedTerm); _electionId = OID::fromTerm(_topCoord->getTerm()); - auto ts = LogicalClock::get(getServiceContext())->reserveTicks(1).asTimestamp(); + auto ts = VectorClockMutable::get(getServiceContext()) + ->tick(VectorClock::Component::ClusterTime, 1) + .asTimestamp(); _topCoord->processWinElection(_electionId, ts); const PostMemberStateUpdateAction nextAction = _updateMemberStateFromTopologyCoordinator(lk); diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp index c8d1edc54a3..fc90826da9f 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp @@ -44,8 +44,6 @@ #include "mongo/db/commands/test_commands_enabled.h" #include "mongo/db/index_builds_coordinator.h" #include "mongo/db/kill_sessions_local.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time_validator.h" #include "mongo/db/operation_context.h" #include "mongo/db/repl/heartbeat_response_action.h" #include "mongo/db/repl/repl_server_parameters_gen.h" @@ -690,11 +688,6 @@ void ReplicationCoordinatorImpl::_heartbeatReconfigStore( if (isArbiter) { _externalState->onBecomeArbiterHook(); - // TODO SERVER-47914: move these actions into VectorClockMongoD::onBecomeArbiter(). - LogicalClock::get(getGlobalServiceContext())->disable(); - if (auto validator = LogicalTimeValidator::get(getGlobalServiceContext())) { - validator->stopKeyManager(); - } } if (!isArbiter && isFirstConfig) { diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index 3cb6a57100b..fb555cb1171 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -99,7 +99,7 @@ env.Library( '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/storage/remove_saver', '$BUILD_DIR/mongo/db/transaction', - '$BUILD_DIR/mongo/db/vector_clock_d', + '$BUILD_DIR/mongo/db/vector_clock_mongod', '$BUILD_DIR/mongo/s/client/shard_local', '$BUILD_DIR/mongo/s/query/cluster_aggregate', '$BUILD_DIR/mongo/s/sharding_initialization', @@ -165,6 +165,7 @@ env.Library( '$BUILD_DIR/mongo/db/commands/txn_cmd_request', '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/db/rw_concern_d', + '$BUILD_DIR/mongo/db/vector_clock_mutable', '$BUILD_DIR/mongo/executor/task_executor_pool', '$BUILD_DIR/mongo/s/grid', 'sharding_api_d', @@ -420,6 +421,7 @@ env.CppUnitTest( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/auth/authmocks', + '$BUILD_DIR/mongo/db/keys_collection_client_direct', '$BUILD_DIR/mongo/db/logical_session_cache_impl', '$BUILD_DIR/mongo/db/ops/write_ops_exec', '$BUILD_DIR/mongo/db/query/query_request', diff --git a/src/mongo/db/s/transaction_coordinator.cpp b/src/mongo/db/s/transaction_coordinator.cpp index 885960965be..0a9b900f853 100644 --- a/src/mongo/db/s/transaction_coordinator.cpp +++ b/src/mongo/db/s/transaction_coordinator.cpp @@ -33,10 +33,10 @@ #include "mongo/db/s/transaction_coordinator.h" -#include "mongo/db/logical_clock.h" #include "mongo/db/s/transaction_coordinator_metrics_observer.h" #include "mongo/db/s/wait_for_majority_service.h" #include "mongo/db/server_options.h" +#include "mongo/db/vector_clock_mutable.h" #include "mongo/logv2/log.h" #include "mongo/s/grid.h" #include "mongo/util/fail_point.h" @@ -209,9 +209,9 @@ TransactionCoordinator::TransactionCoordinator(OperationContext* operationContex "txnNumber"_attr = _txnNumber, "commitTimestamp"_attr = *_decision->getCommitTimestamp()); - uassertStatusOK(LogicalClock::get(_serviceContext) - ->advanceClusterTime( - LogicalTime(*_decision->getCommitTimestamp()))); + VectorClockMutable::get(_serviceContext) + ->tickTo(VectorClock::Component::ClusterTime, + LogicalTime(*_decision->getCommitTimestamp())); } }); }) diff --git a/src/mongo/db/s/vector_clock_config_server_test.cpp b/src/mongo/db/s/vector_clock_config_server_test.cpp index 30761daea07..6c62f4834ac 100644 --- a/src/mongo/db/s/vector_clock_config_server_test.cpp +++ b/src/mongo/db/s/vector_clock_config_server_test.cpp @@ -29,22 +29,63 @@ #include "mongo/platform/basic.h" +#include "mongo/db/keys_collection_client_sharded.h" +#include "mongo/db/keys_collection_manager.h" +#include "mongo/db/logical_time_validator.h" #include "mongo/db/vector_clock_mutable.h" #include "mongo/s/config_server_test_fixture.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/clock_source_mock.h" namespace mongo { namespace { -using VectorClockConfigServerTest = ConfigServerTestFixture; +class VectorClockConfigServerTest : public ConfigServerTestFixture { +protected: + void setUp() override { + ConfigServerTestFixture::setUp(); + + auto clockSource = std::make_unique<ClockSourceMock>(); + getServiceContext()->setFastClockSource(std::move(clockSource)); + + auto keysCollectionClient = std::make_unique<KeysCollectionClientSharded>( + Grid::get(operationContext())->catalogClient()); + + VectorClockMutable::get(getServiceContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 0))); + + _keyManager = std::make_shared<KeysCollectionManager>( + "dummy", std::move(keysCollectionClient), Seconds(1000)); + auto validator = std::make_unique<LogicalTimeValidator>(_keyManager); + validator->init(getServiceContext()); + LogicalTimeValidator::set(getServiceContext(), std::move(validator)); + } + + void tearDown() override { + LogicalTimeValidator::get(getServiceContext())->shutDown(); + + ConfigServerTestFixture::tearDown(); + } + + /** + * Forces KeyManager to refresh cache and generate new keys. + */ + void refreshKeyManager() { + _keyManager->refreshNow(operationContext()); + } + +private: + std::shared_ptr<KeysCollectionManager> _keyManager; +}; + TEST_F(VectorClockConfigServerTest, TickClusterTime) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); const auto t0 = vc->getTime(); - ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); const auto t1 = vc->getTime(); @@ -57,6 +98,32 @@ TEST_F(VectorClockConfigServerTest, TickClusterTime) { ASSERT_GT(t2[VectorClock::Component::ClusterTime], r1); } +TEST_F(VectorClockConfigServerTest, TickToClusterTime) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + const auto t0 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); + + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 1))); + const auto t1 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 1)), t1[VectorClock::Component::ClusterTime]); + + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(3, 3))); + const auto t2 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t2[VectorClock::Component::ClusterTime]); + + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(2, 2))); + const auto t3 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t3[VectorClock::Component::ClusterTime]); +} + +DEATH_TEST_F(VectorClockConfigServerTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + vc->tick(VectorClock::Component::ConfigTime, 1); +} + TEST_F(VectorClockConfigServerTest, TickToConfigTime) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); @@ -77,38 +144,122 @@ TEST_F(VectorClockConfigServerTest, TickToConfigTime) { ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t3[VectorClock::Component::ConfigTime]); } -TEST_F(VectorClockConfigServerTest, GossipOutTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: get the gossipOut - // internal and external, and for each check that $clusterTime and $configTime are there, with - // the right format, and right value. +TEST_F(VectorClockConfigServerTest, GossipOutInternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + const auto configTime = LogicalTime(Timestamp(1, 0)); + vc->tickTo(VectorClock::Component::ConfigTime, configTime); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob, transport::Session::kInternalClient); + auto obj = bob.obj(); + + // On config servers, gossip out to internal clients should have $clusterTime and $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_TRUE(obj.hasField("$configTime")); + ASSERT_EQ(obj["$configTime"].timestamp(), configTime.asTimestamp()); } -TEST_F(VectorClockConfigServerTest, GossipInTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: for each of gossipIn - // internal and external, give it BSON in the correct format, and then check that ClusterTime - // has been advanced (or not), ***and that ConfigTime has not***. +TEST_F(VectorClockConfigServerTest, GossipOutExternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); + + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + const auto configTime = LogicalTime(Timestamp(1, 0)); + vc->tickTo(VectorClock::Component::ConfigTime, configTime); - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob); + auto obj = bob.obj(); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); + // On config servers, gossip out to external clients should have $clusterTime, but not + // $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_FALSE(obj.hasField("$configTime")); } -DEATH_TEST_F(VectorClockConfigServerTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") { +TEST_F(VectorClockConfigServerTest, GossipInInternal) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tick(VectorClock::Component::ConfigTime, 1); + + vc->tick(VectorClock::Component::ClusterTime, 1); + const auto configTime = LogicalTime(Timestamp(1, 0)); + vc->tickTo(VectorClock::Component::ConfigTime, configTime); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false, + transport::Session::kInternalClient); + + // On config servers, gossip in from internal clients should update $clusterTime, but not + // $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), + configTime.asTimestamp()); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false, + transport::Session::kInternalClient); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), + configTime.asTimestamp()); } -DEATH_TEST_F(VectorClockConfigServerTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") { +TEST_F(VectorClockConfigServerTest, GossipInExternal) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime()); + + vc->tick(VectorClock::Component::ClusterTime, 1); + const auto configTime = LogicalTime(Timestamp(1, 0)); + vc->tickTo(VectorClock::Component::ConfigTime, configTime); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false); + + // On config servers, gossip in from external clients should update $clusterTime, but not + // $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), + configTime.asTimestamp()); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), + configTime.asTimestamp()); } } // namespace diff --git a/src/mongo/db/s/vector_clock_shard_server_test.cpp b/src/mongo/db/s/vector_clock_shard_server_test.cpp index 594e7d86f59..66bae0d67a0 100644 --- a/src/mongo/db/s/vector_clock_shard_server_test.cpp +++ b/src/mongo/db/s/vector_clock_shard_server_test.cpp @@ -29,22 +29,62 @@ #include "mongo/platform/basic.h" +#include "mongo/db/keys_collection_client_direct.h" +#include "mongo/db/keys_collection_manager.h" +#include "mongo/db/logical_time_validator.h" #include "mongo/db/vector_clock_mutable.h" #include "mongo/s/shard_server_test_fixture.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/clock_source_mock.h" namespace mongo { namespace { -using VectorClockShardServerTest = ShardServerTestFixture; +class VectorClockShardServerTest : public ShardServerTestFixture { +protected: + void setUp() override { + ShardServerTestFixture::setUp(); + + auto clockSource = std::make_unique<ClockSourceMock>(); + getServiceContext()->setFastClockSource(std::move(clockSource)); + + auto keysCollectionClient = std::make_unique<KeysCollectionClientDirect>(); + + VectorClockMutable::get(getServiceContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 0))); + + _keyManager = std::make_shared<KeysCollectionManager>( + "dummy", std::move(keysCollectionClient), Seconds(1000)); + auto validator = std::make_unique<LogicalTimeValidator>(_keyManager); + validator->init(getServiceContext()); + LogicalTimeValidator::set(getServiceContext(), std::move(validator)); + } + + void tearDown() override { + LogicalTimeValidator::get(getServiceContext())->shutDown(); + + ShardServerTestFixture::tearDown(); + } + + /** + * Forces KeyManager to refresh cache and generate new keys. + */ + void refreshKeyManager() { + _keyManager->refreshNow(operationContext()); + } + +private: + std::shared_ptr<KeysCollectionManager> _keyManager; +}; + TEST_F(VectorClockShardServerTest, TickClusterTime) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); const auto t0 = vc->getTime(); - ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); const auto t1 = vc->getTime(); @@ -57,26 +97,24 @@ TEST_F(VectorClockShardServerTest, TickClusterTime) { ASSERT_GT(t2[VectorClock::Component::ClusterTime], r1); } -TEST_F(VectorClockShardServerTest, GossipOutTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: get the gossipOut - // internal and external, and for each check that $clusterTime and $configTime are there, with - // the right format, and right value. - - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); +TEST_F(VectorClockShardServerTest, TickToClusterTime) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); -} + const auto t0 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); -TEST_F(VectorClockShardServerTest, GossipInTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: for each of gossipIn - // internal and external, give it BSON in the correct format, and then check that ClusterTime - // and ConfigTime have been advanced (or not). + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 1))); + const auto t1 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 1)), t1[VectorClock::Component::ClusterTime]); - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(3, 3))); + const auto t2 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t2[VectorClock::Component::ClusterTime]); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(2, 2))); + const auto t3 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t3[VectorClock::Component::ClusterTime]); } DEATH_TEST_F(VectorClockShardServerTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") { @@ -85,16 +123,136 @@ DEATH_TEST_F(VectorClockShardServerTest, CannotTickConfigTime, "Hit a MONGO_UNRE vc->tick(VectorClock::Component::ConfigTime, 1); } -DEATH_TEST_F(VectorClockShardServerTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") { +DEATH_TEST_F(VectorClockShardServerTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime()); + vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime()); } -DEATH_TEST_F(VectorClockShardServerTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") { +TEST_F(VectorClockShardServerTest, GossipOutInternal) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime()); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); + + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob, transport::Session::kInternalClient); + auto obj = bob.obj(); + + // On shard servers, gossip out to internal clients should have $clusterTime and $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_TRUE(obj.hasField("$configTime")); + ASSERT_EQ(obj["$configTime"].timestamp(), Timestamp(0, 0)); +} + +TEST_F(VectorClockShardServerTest, GossipOutExternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); + + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob); + auto obj = bob.obj(); + + // On shard servers, gossip out to external clients should have $clusterTime, but not + // $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_FALSE(obj.hasField("$configTime")); +} + +TEST_F(VectorClockShardServerTest, GossipInInternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + vc->tick(VectorClock::Component::ClusterTime, 1); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false, + transport::Session::kInternalClient); + + // On shard servers, gossip in from internal clients should update $clusterTime and $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(2, 2)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false, + transport::Session::kInternalClient); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(2, 2)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(3, 3) << "signature" << dummySignature) + << "$configTime" << Timestamp(3, 3)), + false, + transport::Session::kInternalClient); + + auto afterTime3 = vc->getTime(); + ASSERT_EQ(afterTime3[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(3, 3)); + ASSERT_EQ(afterTime3[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(3, 3)); +} + +TEST_F(VectorClockShardServerTest, GossipInExternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + vc->tick(VectorClock::Component::ClusterTime, 1); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false); + + // On shard servers, gossip in from external clients should update $clusterTime, but not + // $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(3, 3) << "signature" << dummySignature) + << "$configTime" << Timestamp(3, 3)), + false); + + auto afterTime3 = vc->getTime(); + ASSERT_EQ(afterTime3[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(3, 3)); + ASSERT_EQ(afterTime3[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); } } // namespace diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 8d7c6ce27e8..fa1e80051ac 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -87,7 +87,6 @@ #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/message.h" #include "mongo/rpc/metadata.h" -#include "mongo/rpc/metadata/logical_time_metadata.h" #include "mongo/rpc/metadata/oplog_query_metadata.h" #include "mongo/rpc/metadata/repl_set_metadata.h" #include "mongo/rpc/metadata/tracking_metadata.h" @@ -439,54 +438,20 @@ void appendClusterAndOperationTime(OperationContext* opCtx, LogicalTime startTime) { if (repl::ReplicationCoordinator::get(opCtx)->getReplicationMode() != repl::ReplicationCoordinator::modeReplSet || - !LogicalClock::get(opCtx)->isEnabled()) { - return; - } - - VectorClock::get(opCtx)->gossipOut(metadataBob, opCtx->getClient()->getSessionTags()); - - // Authorized clients always receive operationTime and dummy signed $clusterTime. - if (LogicalTimeValidator::isAuthorizedToAdvanceClock(opCtx)) { - auto operationTime = computeOperationTime(opCtx, startTime); - auto signedTime = SignedLogicalTime( - LogicalClock::get(opCtx)->getClusterTime(), TimeProofService::TimeProof(), 0); - - dassert(signedTime.getTime() >= operationTime); - rpc::LogicalTimeMetadata(signedTime).writeToMetadata(metadataBob); - - LOGV2_DEBUG( - 21957, - 5, - "Appending operationTime to cmd response for authorized client: {operationTime}", - "operationTime"_attr = operationTime); - operationTime.appendAsOperationTime(commandBodyFieldsBob); - - return; - } - - // Servers without validators (e.g. a shard server not yet added to a cluster) do not return - // logical times to unauthorized clients. - auto validator = LogicalTimeValidator::get(opCtx); - if (!validator) { + !VectorClock::get(opCtx)->isEnabled()) { return; } + // The appended operationTime must always be <= the appended $clusterTime, so first compute the + // operationTime. auto operationTime = computeOperationTime(opCtx, startTime); - auto signedTime = validator->trySignLogicalTime(LogicalClock::get(opCtx)->getClusterTime()); - // If there were no keys, do not return $clusterTime or operationTime to unauthorized clients. - if (signedTime.getKeyId() == 0) { - return; - } - - dassert(signedTime.getTime() >= operationTime); - rpc::LogicalTimeMetadata(signedTime).writeToMetadata(metadataBob); + bool clusterTimeWasOutput = VectorClock::get(opCtx)->gossipOut(opCtx, metadataBob); - LOGV2_DEBUG(21958, - 5, - "Appending operationTime to cmd response: {operationTime}", - "operationTime"_attr = operationTime); - operationTime.appendAsOperationTime(commandBodyFieldsBob); + // Ensure that either both operationTime and $clusterTime are output, or neither. + if (clusterTimeWasOutput) { + operationTime.appendAsOperationTime(metadataBob); + } } void appendErrorLabelsAndTopologyVersion(OperationContext* opCtx, diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript index 7d2bc4aba98..740e673bd28 100644 --- a/src/mongo/db/storage/SConscript +++ b/src/mongo/db/storage/SConscript @@ -517,9 +517,9 @@ env.Library( '$BUILD_DIR/mongo/db/storage/storage_options', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/storage/storage_repair_observer', '$BUILD_DIR/mongo/db/catalog/collection_catalog_helper', + '$BUILD_DIR/mongo/db/vector_clock', 'two_phase_index_build_knobs_idl', ], ) diff --git a/src/mongo/db/update/SConscript b/src/mongo/db/update/SConscript index 681e61d0944..4c9c00406c1 100644 --- a/src/mongo/db/update/SConscript +++ b/src/mongo/db/update/SConscript @@ -47,7 +47,6 @@ env.Library( 'update_object_node.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/pipeline/pipeline', '$BUILD_DIR/mongo/db/update_index_data', '$BUILD_DIR/mongo/db/vector_clock_mutable', @@ -101,7 +100,6 @@ env.CppUnitTest( '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/bson/mutable/mutable_bson_test_utils', '$BUILD_DIR/mongo/db/exec/document_value/document_value_test_util', - '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/db/matcher/expressions', '$BUILD_DIR/mongo/db/query/collation/collator_interface_mock', '$BUILD_DIR/mongo/db/query/query_planner', diff --git a/src/mongo/db/update/current_date_node.cpp b/src/mongo/db/update/current_date_node.cpp index 9df15695850..24ce4a95681 100644 --- a/src/mongo/db/update/current_date_node.cpp +++ b/src/mongo/db/update/current_date_node.cpp @@ -31,9 +31,8 @@ #include "mongo/db/update/current_date_node.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time.h" #include "mongo/db/service_context.h" +#include "mongo/db/vector_clock_mutable.h" namespace mongo { @@ -42,12 +41,13 @@ constexpr StringData kType = "$type"_sd; constexpr StringData kDate = "date"_sd; constexpr StringData kTimestamp = "timestamp"_sd; -void setValue(mutablebson::Element* element, bool typeIsDate) { +void setValue(ServiceContext* service, mutablebson::Element* element, bool typeIsDate) { if (typeIsDate) { invariant(element->setValueDate(mongo::jsTime())); } else { - invariant(element->setValueTimestamp( - LogicalClock::get(getGlobalServiceContext())->reserveTicks(1).asTimestamp())); + invariant(element->setValueTimestamp(VectorClockMutable::get(service) + ->tick(VectorClock::Component::ClusterTime, 1) + .asTimestamp())); } } } // namespace @@ -92,17 +92,19 @@ Status CurrentDateNode::init(BSONElement modExpr, " or a $type expression ({$type: 'timestamp/date'})."); } + _service = expCtx->opCtx->getServiceContext(); + return Status::OK(); } ModifierNode::ModifyResult CurrentDateNode::updateExistingElement( mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const { - setValue(element, _typeIsDate); + setValue(_service, element, _typeIsDate); return ModifyResult::kNormalUpdate; } void CurrentDateNode::setValueForNewElement(mutablebson::Element* element) const { - setValue(element, _typeIsDate); + setValue(_service, element, _typeIsDate); } BSONObj CurrentDateNode::operatorValue() const { diff --git a/src/mongo/db/update/current_date_node.h b/src/mongo/db/update/current_date_node.h index a28de18ad36..2c3e4492f86 100644 --- a/src/mongo/db/update/current_date_node.h +++ b/src/mongo/db/update/current_date_node.h @@ -36,6 +36,8 @@ namespace mongo { +class ServiceContext; + /** * Represents the application of a $currentDate to the value at the end of a path. */ @@ -71,6 +73,8 @@ private: // If true, the current date should be expressed as a Date. If false, a Timestamp. bool _typeIsDate; + + ServiceContext* _service; }; } // namespace mongo diff --git a/src/mongo/db/update/object_replace_executor.cpp b/src/mongo/db/update/object_replace_executor.cpp index 31ea35df114..8f0251efc99 100644 --- a/src/mongo/db/update/object_replace_executor.cpp +++ b/src/mongo/db/update/object_replace_executor.cpp @@ -33,10 +33,9 @@ #include "mongo/base/data_view.h" #include "mongo/db/bson/dotted_path_support.h" -#include "mongo/db/logical_clock.h" -#include "mongo/db/logical_time.h" #include "mongo/db/service_context.h" #include "mongo/db/update/storage_validation.h" +#include "mongo/db/vector_clock_mutable.h" namespace mongo { @@ -63,7 +62,9 @@ ObjectReplaceExecutor::ObjectReplaceExecutor(BSONObj replacement) unsigned long long timestamp = timestampView.read<unsigned long long>(); if (timestamp == 0) { ServiceContext* service = getGlobalServiceContext(); - auto ts = LogicalClock::get(service)->reserveTicks(1).asTimestamp(); + auto ts = VectorClockMutable::get(service) + ->tick(VectorClock::Component::ClusterTime, 1) + .asTimestamp(); timestampView.write(tagLittleEndian(ts.asULL())); } } diff --git a/src/mongo/db/vector_clock.cpp b/src/mongo/db/vector_clock.cpp index 5be53064537..24ce442a417 100644 --- a/src/mongo/db/vector_clock.cpp +++ b/src/mongo/db/vector_clock.cpp @@ -31,6 +31,11 @@ #include "mongo/db/vector_clock.h" +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/logical_clock_gen.h" +#include "mongo/db/logical_time_validator.h" + namespace mongo { namespace { @@ -65,8 +70,40 @@ VectorClock::VectorTime VectorClock::getTime() const { return VectorTime(_vectorTime); } +bool VectorClock::_lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks) { + return time.asTimestamp().getSecs() <= kMaxValue && + time.asTimestamp().getInc() <= (kMaxValue - nTicks); +} + +void VectorClock::_ensurePassesRateLimiter(ServiceContext* service, + const LogicalTimeArray& newTime) { + const unsigned wallClockSecs = + durationCount<Seconds>(service->getFastClockSource()->now().toDurationSinceEpoch()); + auto maxAcceptableDriftSecs = static_cast<const unsigned>(gMaxAcceptableLogicalClockDriftSecs); + + for (auto newIt = newTime.begin(); newIt != newTime.end(); ++newIt) { + auto newTimeSecs = newIt->asTimestamp().getSecs(); + auto name = _componentName(Component(newIt - newTime.begin())); + + // Both values are unsigned, so compare them first to avoid wrap-around. + uassert(ErrorCodes::ClusterTimeFailsRateLimiter, + str::stream() << "New " << name << ", " << newTimeSecs + << ", is too far from this node's wall clock time, " << wallClockSecs + << ".", + ((newTimeSecs <= wallClockSecs) || + (newTimeSecs - wallClockSecs) <= maxAcceptableDriftSecs)); + + uassert(40484, + str::stream() << name << " cannot be advanced beyond its maximum value", + _lessThanOrEqualToMaxPossibleTime(*newIt, 0)); + } +} + void VectorClock::_advanceTime(LogicalTimeArray&& newTime) { + _ensurePassesRateLimiter(_service, newTime); + stdx::lock_guard<Latch> lock(_mutex); + auto it = _vectorTime.begin(); auto newIt = newTime.begin(); for (; it != _vectorTime.end() && newIt != newTime.end(); ++it, ++newIt) { @@ -80,14 +117,26 @@ class VectorClock::GossipFormat { public: class Plain; class Signed; + template <class ActualFormat> + class OnlyGossipOutOnNewFCV; static const ComponentArray<std::unique_ptr<GossipFormat>> _formatters; GossipFormat(std::string fieldName) : _fieldName(fieldName) {} virtual ~GossipFormat() = default; - virtual void out(BSONObjBuilder* out, LogicalTime time, Component component) const = 0; - virtual LogicalTime in(const BSONObj& in, Component component) const = 0; + // Returns true if the time was output, false otherwise. + virtual bool out(ServiceContext* service, + OperationContext* opCtx, + bool permitRefresh, + BSONObjBuilder* out, + LogicalTime time, + Component component) const = 0; + virtual LogicalTime in(ServiceContext* service, + OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, + Component component) const = 0; const std::string _fieldName; }; @@ -97,11 +146,21 @@ public: using GossipFormat::GossipFormat; virtual ~Plain() = default; - void out(BSONObjBuilder* out, LogicalTime time, Component component) const override { + bool out(ServiceContext* service, + OperationContext* opCtx, + bool permitRefresh, + BSONObjBuilder* out, + LogicalTime time, + Component component) const override { out->append(_fieldName, time.asTimestamp()); + return true; } - LogicalTime in(const BSONObj& in, Component component) const override { + LogicalTime in(ServiceContext* service, + OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, + Component component) const override { const auto componentElem(in[_fieldName]); if (componentElem.eoo()) { // Nothing to gossip in. @@ -114,74 +173,225 @@ public: } }; +template <class ActualFormat> +class VectorClock::GossipFormat::OnlyGossipOutOnNewFCV : public ActualFormat { +public: + using ActualFormat::ActualFormat; + virtual ~OnlyGossipOutOnNewFCV() = default; + + bool out(ServiceContext* service, + OperationContext* opCtx, + bool permitRefresh, + BSONObjBuilder* out, + LogicalTime time, + Component component) const override { + const auto& fcv = serverGlobalParams.featureCompatibility; + if (fcv.isVersionInitialized() && + fcv.getVersion() == + ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo46) { + return ActualFormat::out(service, opCtx, permitRefresh, out, time, component); + } + return false; + } + + LogicalTime in(ServiceContext* service, + OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, + Component component) const override { + return ActualFormat::in(service, opCtx, in, couldBeUnauthenticated, component); + } +}; + class VectorClock::GossipFormat::Signed : public VectorClock::GossipFormat { public: using GossipFormat::GossipFormat; virtual ~Signed() = default; - void out(BSONObjBuilder* out, LogicalTime time, Component component) const override { - // TODO SERVER-47914: make this do the actual proper signing - BSONObjBuilder bob; - bob.append("time", time.asTimestamp()); - bob.append("signature", 0); - out->append(_fieldName, bob.done()); + bool out(ServiceContext* service, + OperationContext* opCtx, + bool permitRefresh, + BSONObjBuilder* out, + LogicalTime time, + Component component) const override { + SignedLogicalTime signedTime; + + if (opCtx && LogicalTimeValidator::isAuthorizedToAdvanceClock(opCtx)) { + // Authorized clients always receive a dummy-signed $clusterTime (and operationTime). + signedTime = SignedLogicalTime(time, TimeProofService::TimeProof(), 0); + } else { + // Servers without validators (e.g. a shard server not yet added to a cluster) do not + // return logical times to unauthorized clients. + auto validator = LogicalTimeValidator::get(service); + if (!validator) { + return false; + } + + // There are some contexts where refreshing is not permitted. + if (permitRefresh && opCtx) { + signedTime = validator->signLogicalTime(opCtx, time); + } else { + signedTime = validator->trySignLogicalTime(time); + } + + // If there were no keys, do not return $clusterTime (or operationTime) to unauthorized + // clients. + if (signedTime.getKeyId() == 0) { + return false; + } + } + + // TODO SERVER-48432: use IDL to do this serialization. + + BSONObjBuilder subObjBuilder(out->subobjStart(_fieldName)); + signedTime.getTime().asTimestamp().append(subObjBuilder.bb(), kClusterTimeFieldName); + + BSONObjBuilder signatureObjBuilder(subObjBuilder.subobjStart(kSignatureFieldName)); + // Cluster time metadata is only written when the LogicalTimeValidator is set, which + // means the cluster time should always have a proof. + invariant(signedTime.getProof()); + signedTime.getProof()->appendAsBinData(signatureObjBuilder, kSignatureHashFieldName); + signatureObjBuilder.append(kSignatureKeyIdFieldName, signedTime.getKeyId()); + signatureObjBuilder.doneFast(); + + subObjBuilder.doneFast(); + + return true; } - LogicalTime in(const BSONObj& in, Component component) const override { - // TODO SERVER-47914: make this do the actual proper signing - const auto componentElem(in[_fieldName]); - if (componentElem.eoo()) { + LogicalTime in(ServiceContext* service, + OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, + Component component) const override { + // TODO SERVER-48432: use IDL to do this deserialization. + + const auto& metadataElem = in.getField(_fieldName); + if (metadataElem.eoo()) { // Nothing to gossip in. return LogicalTime(); } - uassert(ErrorCodes::BadValue, - str::stream() << _fieldName << " is not a sub-object", - componentElem.isABSONObj()); - const auto subobj = componentElem.embeddedObject(); - const auto timeElem(subobj["time"]); - uassert(ErrorCodes::FailedToParse, "No time found", !timeElem.eoo()); - uassert(ErrorCodes::BadValue, - str::stream() << "time is not a Timestamp", - timeElem.type() == bsonTimestamp); - return LogicalTime(timeElem.timestamp()); + + const auto& obj = metadataElem.Obj(); + + Timestamp ts; + uassertStatusOK(bsonExtractTimestampField(obj, kClusterTimeFieldName, &ts)); + + BSONElement signatureElem; + uassertStatusOK(bsonExtractTypedField(obj, kSignatureFieldName, Object, &signatureElem)); + + const auto& signatureObj = signatureElem.Obj(); + + // Extract BinData type signature hash and construct a SHA1Block instance from it. + BSONElement hashElem; + uassertStatusOK( + bsonExtractTypedField(signatureObj, kSignatureHashFieldName, BinData, &hashElem)); + + int hashLength = 0; + auto rawBinSignature = hashElem.binData(hashLength); + BSONBinData proofBinData(rawBinSignature, hashLength, hashElem.binDataType()); + auto proofStatus = SHA1Block::fromBinData(proofBinData); + uassertStatusOK(proofStatus); + + long long keyId; + uassertStatusOK(bsonExtractIntegerField(signatureObj, kSignatureKeyIdFieldName, &keyId)); + + auto signedTime = + SignedLogicalTime(LogicalTime(ts), std::move(proofStatus.getValue()), keyId); + + if (!opCtx) { + // If there's no opCtx then this must be coming from a reply, which must be internal, + // and so doesn't require validation. + return signedTime.getTime(); + } + + // Validate the signature. + if (couldBeUnauthenticated && AuthorizationManager::get(service)->isAuthEnabled() && + (!signedTime.getProof() || *signedTime.getProof() == TimeProofService::TimeProof())) { + + AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); + // The client is not authenticated and is not using localhost auth bypass. Do not + // gossip. + if (authSession && !authSession->isAuthenticated() && + !authSession->isUsingLocalhostBypass()) { + return {}; + } + } + + auto logicalTimeValidator = LogicalTimeValidator::get(service); + if (!LogicalTimeValidator::isAuthorizedToAdvanceClock(opCtx)) { + if (!logicalTimeValidator) { + uasserted(ErrorCodes::CannotVerifyAndSignLogicalTime, + "Cannot accept logicalTime: " + signedTime.getTime().toString() + + ". May not be a part of a sharded cluster"); + } else { + uassertStatusOK(logicalTimeValidator->validate(opCtx, signedTime)); + } + } + + return signedTime.getTime(); } + +private: + static constexpr char kClusterTimeFieldName[] = "clusterTime"; + static constexpr char kSignatureFieldName[] = "signature"; + static constexpr char kSignatureHashFieldName[] = "hash"; + static constexpr char kSignatureKeyIdFieldName[] = "keyId"; }; -// TODO SERVER-47914: update $clusterTimeNew to $clusterTime once LogicalClock is migrated into -// VectorClock. const VectorClock::ComponentArray<std::unique_ptr<VectorClock::GossipFormat>> VectorClock::GossipFormat::_formatters{ - std::make_unique<VectorClock::GossipFormat::Signed>("$clusterTimeNew"), - std::make_unique<VectorClock::GossipFormat::Plain>("$configTime")}; - -void VectorClock::gossipOut(BSONObjBuilder* outMessage, - const transport::Session::TagMask clientSessionTags) const { + std::make_unique<VectorClock::GossipFormat::Signed>(VectorClock::kClusterTimeFieldName), + std::make_unique< + VectorClock::GossipFormat::OnlyGossipOutOnNewFCV<VectorClock::GossipFormat::Plain>>( + VectorClock::kConfigTimeFieldName)}; + +bool VectorClock::gossipOut(OperationContext* opCtx, + BSONObjBuilder* outMessage, + const transport::Session::TagMask defaultClientSessionTags) const { + auto clientSessionTags = defaultClientSessionTags; + if (opCtx && opCtx->getClient()) { + clientSessionTags = opCtx->getClient()->getSessionTags(); + } + VectorTime now = getTime(); if (clientSessionTags & transport::Session::kInternalClient) { - _gossipOutInternal(outMessage); + return _gossipOutInternal(opCtx, outMessage, now._time); } else { - _gossipOutExternal(outMessage); + return _gossipOutExternal(opCtx, outMessage, now._time); } } -void VectorClock::gossipIn(const BSONObj& inMessage, - const transport::Session::TagMask clientSessionTags) { +void VectorClock::gossipIn(OperationContext* opCtx, + const BSONObj& inMessage, + bool couldBeUnauthenticated, + const transport::Session::TagMask defaultClientSessionTags) { + auto clientSessionTags = defaultClientSessionTags; + if (opCtx && opCtx->getClient()) { + clientSessionTags = opCtx->getClient()->getSessionTags(); + } if (clientSessionTags & transport::Session::kInternalClient) { - _advanceTime(_gossipInInternal(inMessage)); + _advanceTime(_gossipInInternal(opCtx, inMessage, couldBeUnauthenticated)); } else { - _advanceTime(_gossipInExternal(inMessage)); + _advanceTime(_gossipInExternal(opCtx, inMessage, couldBeUnauthenticated)); } } -void VectorClock::_gossipOutComponent(BSONObjBuilder* out, - VectorTime time, +bool VectorClock::_gossipOutComponent(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time, Component component) const { - GossipFormat::_formatters[component]->out(out, time[component], component); + bool wasOutput = GossipFormat::_formatters[component]->out( + _service, opCtx, _permitRefreshDuringGossipOut(), out, time[component], component); + return (component == Component::ClusterTime) ? wasOutput : false; } -void VectorClock::_gossipInComponent(const BSONObj& in, +void VectorClock::_gossipInComponent(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, LogicalTimeArray* newTime, Component component) { - (*newTime)[component] = GossipFormat::_formatters[component]->in(in, component); + (*newTime)[component] = GossipFormat::_formatters[component]->in( + _service, opCtx, in, couldBeUnauthenticated, component); } std::string VectorClock::_componentName(Component component) { @@ -193,9 +403,24 @@ bool VectorClock::isEnabled() const { return _isEnabled; } -void VectorClock::disable() { +void VectorClock::_disable() { stdx::lock_guard<Latch> lock(_mutex); _isEnabled = false; } +void VectorClock::resetVectorClock_forTest() { + stdx::lock_guard<Latch> lock(_mutex); + auto it = _vectorTime.begin(); + for (; it != _vectorTime.end(); ++it) { + *it = LogicalTime(); + } + _isEnabled = true; +} + +void VectorClock::advanceClusterTime_forTest(LogicalTime newClusterTime) { + LogicalTimeArray newTime; + newTime[Component::ClusterTime] = newClusterTime; + _advanceTime(std::move(newTime)); +} + } // namespace mongo diff --git a/src/mongo/db/vector_clock.h b/src/mongo/db/vector_clock.h index b7381652e9c..db2a74e6140 100644 --- a/src/mongo/db/vector_clock.h +++ b/src/mongo/db/vector_clock.h @@ -93,40 +93,154 @@ public: const LogicalTimeArray _time; }; + static constexpr char kClusterTimeFieldName[] = "$clusterTime"; + static constexpr char kConfigTimeFieldName[] = "$configTime"; + // Decorate ServiceContext with VectorClock* which points to the actual vector clock // implementation. static VectorClock* get(ServiceContext* service); static VectorClock* get(OperationContext* ctx); - static void registerVectorClockOnServiceContext(ServiceContext* service, VectorClock* vectorClock); + /** + * Returns an instantaneous snapshot of the current time of all components. + */ VectorTime getTime() const; - // Gossipping - void gossipOut(BSONObjBuilder* outMessage, - const transport::Session::TagMask clientSessionTags) const; - void gossipIn(const BSONObj& inMessage, const transport::Session::TagMask clientSessionTags); + /** + * Adds the necessary fields to outMessage to gossip the current time to another node, taking + * into account if the gossiping is to an internal or external client (based on the session + * tags). Returns true if the ClusterTime was output into outMessage, or false otherwise. + */ + bool gossipOut(OperationContext* opCtx, + BSONObjBuilder* outMessage, + const transport::Session::TagMask defaultClientSessionTags = 0) const; + /** + * Read the necessary fields from inMessage in order to update the current time, based on this + * message received from another node, taking into account if the gossiping is from an internal + * or external client (based on the session tags). + */ + void gossipIn(OperationContext* opCtx, + const BSONObj& inMessage, + bool couldBeUnauthenticated, + const transport::Session::TagMask defaultClientSessionTags = 0); + /** + * Returns true if the clock is enabled and can be used. Defaults to true. + */ bool isEnabled() const; - void disable(); + + void resetVectorClock_forTest(); + void advanceClusterTime_forTest(LogicalTime newClusterTime); protected: VectorClock(); virtual ~VectorClock(); + /** + * The maximum permissible value for each part of a LogicalTime's Timestamp (ie. "secs" and + * "inc"). + */ + static constexpr uint32_t kMaxValue = std::numeric_limits<int32_t>::max(); + + /** + * The "name" of the given component, for user-facing error messages. The name used is the + * field name used when gossiping. + */ static std::string _componentName(Component component); - // Internal Gossipping API - virtual void _gossipOutInternal(BSONObjBuilder* out) const = 0; - virtual void _gossipOutExternal(BSONObjBuilder* out) const = 0; - virtual LogicalTimeArray _gossipInInternal(const BSONObj& in) = 0; - virtual LogicalTimeArray _gossipInExternal(const BSONObj& in) = 0; + /** + * Disables the clock. A disabled clock won't process logical times and can't be re-enabled. + */ + void _disable(); + + /** + * "Rate limiter" for advancing logical times. Rejects newTime if any of its Components have a + * seconds value that's more than gMaxAcceptableLogicalClockDriftSecs ahead of this node's wall + * clock. + */ + static void _ensurePassesRateLimiter(ServiceContext* service, const LogicalTimeArray& newTime); + + /** + * Used to ensure that gossiped or ticked times never overflow the maximum possible LogicalTime. + */ + static bool _lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks); + + + /** + * Adds the necessary fields to outMessage to gossip the given time to a node internal to the + * cluster. Returns true if the ClusterTime was output into outMessage, or false otherwise. + */ + virtual bool _gossipOutInternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const = 0; + + /** + * As for _gossipOutInternal, except for an outMessage to be sent to a client external to the + * cluster, eg. a driver or user client. + */ + virtual bool _gossipOutExternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const = 0; + + /** + * Reads the necessary fields from the BSONObj, which has come from a node internal to the + * cluster, and returns an array of LogicalTimes for each component present in the BSONObj. + * + * This array is suitable for passing to _advanceTime(), in order to monotonically increase + * any Component times that are larger than the current time. Since the times in + * LogicalTimeArray are default constructed (ie. to Timestamp(0, 0)), any fields not present + * in the input BSONObj won't be advanced. + * + * The couldBeUnauthenticated parameter is used to indicate if the source of the input BSONObj + * is an incoming request for a command that can be run by an unauthenticated client. + */ + virtual LogicalTimeArray _gossipInInternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) = 0; + + /** + * As for _gossipInInternal, except for an input BSONObj from a client external to the cluster, + * eg. a driver or user client. + */ + virtual LogicalTimeArray _gossipInExternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) = 0; + + /** + * Whether or not it's permissable to refresh external state (eg. updating gossip signing keys) + * during gossip out. + */ + virtual bool _permitRefreshDuringGossipOut() const = 0; + + /** + * Called by sub-classes in order to actually output a Component time to the output + * BSONObjBuilder, using the appropriate field name and representation for that Component. + * + * Returns true if the component is ClusterTime and it was output, or false otherwise. + */ + bool _gossipOutComponent(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time, + Component component) const; - void _gossipOutComponent(BSONObjBuilder* out, VectorTime time, Component component) const; - void _gossipInComponent(const BSONObj& in, LogicalTimeArray* newTime, Component component); + /** + * Called by sub-classes in order to actually input a Component time into the given + * LogicalTimeArray from the given BSONObj, using the appropriate field name and representation + * for that Component. + */ + void _gossipInComponent(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated, + LogicalTimeArray* newTime, + Component component); - // Used to atomically advance the time of several components (eg. during gossip-in). + /** + * For each component in the LogicalTimeArray, sets the current time to newTime if the newTime > + * current time and it passes the rate check. If any component fails the rate check, then this + * function uasserts on the first such component (without setting any current times). + */ void _advanceTime(LogicalTimeArray&& newTime); ServiceContext* _service{nullptr}; diff --git a/src/mongo/db/vector_clock_mongod.cpp b/src/mongo/db/vector_clock_mongod.cpp index 4f5248aed29..3a270314da0 100644 --- a/src/mongo/db/vector_clock_mongod.cpp +++ b/src/mongo/db/vector_clock_mongod.cpp @@ -54,26 +54,25 @@ public: void tickTo(Component component, LogicalTime newTime) override; protected: - void _gossipOutInternal(BSONObjBuilder* out) const override; - void _gossipOutExternal(BSONObjBuilder* out) const override; - LogicalTimeArray _gossipInInternal(const BSONObj& in) override; - LogicalTimeArray _gossipInExternal(const BSONObj& in) override; + bool _gossipOutInternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const override; + bool _gossipOutExternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const override; + LogicalTimeArray _gossipInInternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) override; + LogicalTimeArray _gossipInExternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) override; + bool _permitRefreshDuringGossipOut() const override; private: - enum class ReplState { - Unset, - StepUpBegin, - StepUpComplete, - StepDown, - Arbiter, - }; - - void onStepUpBegin(OperationContext* opCtx) override; - void onStepUpComplete(OperationContext* opCtx) override; - void onStepDown() override; + void onStepUpBegin(OperationContext* opCtx) override {} + void onStepUpComplete(OperationContext* opCtx) override {} + void onStepDown() override {} void onBecomeArbiter() override; - - ReplState _replState{ReplState::Unset}; }; const auto vectorClockMongoDDecoration = ServiceContext::declareDecoration<VectorClockMongoD>(); @@ -98,62 +97,54 @@ VectorClockMongoD::VectorClockMongoD() = default; VectorClockMongoD::~VectorClockMongoD() = default; -void VectorClockMongoD::onStepUpBegin(OperationContext* opCtx) { - _replState = ReplState::StepUpBegin; -} - -void VectorClockMongoD::onStepUpComplete(OperationContext* opCtx) { - _replState = ReplState::StepUpComplete; -} - -void VectorClockMongoD::onStepDown() { - _replState = ReplState::StepDown; -} - void VectorClockMongoD::onBecomeArbiter() { - _replState = ReplState::Arbiter; - // The node has become an arbiter, hence will not need logical clock for external operations. - disable(); + _disable(); + if (auto validator = LogicalTimeValidator::get(_service)) { + validator->stopKeyManager(); + } } -void VectorClockMongoD::_gossipOutInternal(BSONObjBuilder* out) const { - VectorTime now = getTime(); - // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has - // been migrated into VectorClock. - // _gossipOutComponent(out, now, Component::ClusterTime); +bool VectorClockMongoD::_gossipOutInternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const { + bool wasClusterTimeOutput = _gossipOutComponent(opCtx, out, time, Component::ClusterTime); if (serverGlobalParams.clusterRole == ClusterRole::ShardServer || serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { - _gossipOutComponent(out, now, Component::ConfigTime); + _gossipOutComponent(opCtx, out, time, Component::ConfigTime); } + return wasClusterTimeOutput; } -void VectorClockMongoD::_gossipOutExternal(BSONObjBuilder* out) const { - // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has - // been migrated into VectorClock. - // VectorTime now = getTime(); - // _gossipOutComponent(out, now, Component::ClusterTime); +bool VectorClockMongoD::_gossipOutExternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const { + return _gossipOutComponent(opCtx, out, time, Component::ClusterTime); } -VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInInternal(const BSONObj& in) { +VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInInternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) { LogicalTimeArray newTime; - // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has - // been migrated into VectorClock. - // _gossipInComponent(in, &newTime, Component::ClusterTime); + _gossipInComponent(opCtx, in, couldBeUnauthenticated, &newTime, Component::ClusterTime); if (serverGlobalParams.clusterRole == ClusterRole::ShardServer) { - _gossipInComponent(in, &newTime, Component::ConfigTime); + _gossipInComponent(opCtx, in, couldBeUnauthenticated, &newTime, Component::ConfigTime); } return newTime; } -VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInExternal(const BSONObj& in) { +VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInExternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) { LogicalTimeArray newTime; - // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has - // been migrated into VectorClock. - // _gossipInComponent(in, &newTime, Component::ClusterTime); + _gossipInComponent(opCtx, in, couldBeUnauthenticated, &newTime, Component::ClusterTime); return newTime; } +bool VectorClockMongoD::_permitRefreshDuringGossipOut() const { + return false; +} + LogicalTime VectorClockMongoD::tick(Component component, uint64_t nTicks) { if (component == Component::ClusterTime) { // Although conceptually ClusterTime can only be ticked when a mongod is able to take writes @@ -181,6 +172,13 @@ void VectorClockMongoD::tickTo(Component component, LogicalTime newTime) { return; } + if (component == Component::ClusterTime) { + // The ClusterTime is allowed to tickTo in certain very limited and trusted cases (eg. + // initializing based on oplog timestamps), so we have to allow it here. + _advanceComponentTimeTo(component, std::move(newTime)); + return; + } + // tickTo is not permitted in other circumstances. MONGO_UNREACHABLE; } diff --git a/src/mongo/db/vector_clock_mongod_test.cpp b/src/mongo/db/vector_clock_mongod_test.cpp index 1a443ca68ba..cbb34b41ccd 100644 --- a/src/mongo/db/vector_clock_mongod_test.cpp +++ b/src/mongo/db/vector_clock_mongod_test.cpp @@ -29,22 +29,67 @@ #include "mongo/platform/basic.h" -#include "mongo/db/service_context_test_fixture.h" +#include "mongo/db/keys_collection_client_direct.h" +#include "mongo/db/keys_collection_manager.h" +#include "mongo/db/logical_time_validator.h" #include "mongo/db/vector_clock_mutable.h" +#include "mongo/s/sharding_mongod_test_fixture.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/clock_source_mock.h" namespace mongo { namespace { -using VectorClockMongoDTest = ServiceContextTest; +/** + * Even though these tests are to exercise logic for plain replica set members, it uses + * ShardingMongodTestFixture as a convenient way to get the necessary support infrastructure (such + * as a TaskExecutor with pool), while still being neither "config server" nor "shard server". + */ +class VectorClockMongoDTest : public ShardingMongodTestFixture { +protected: + void setUp() override { + ShardingMongodTestFixture::setUp(); + + auto clockSource = std::make_unique<ClockSourceMock>(); + getServiceContext()->setFastClockSource(std::move(clockSource)); + + auto keysCollectionClient = std::make_unique<KeysCollectionClientDirect>(); + + VectorClockMutable::get(getServiceContext()) + ->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 0))); + + _keyManager = std::make_shared<KeysCollectionManager>( + "dummy", std::move(keysCollectionClient), Seconds(1000)); + auto validator = std::make_unique<LogicalTimeValidator>(_keyManager); + validator->init(getServiceContext()); + LogicalTimeValidator::set(getServiceContext(), std::move(validator)); + } + + void tearDown() override { + LogicalTimeValidator::get(getServiceContext())->shutDown(); + + ShardingMongodTestFixture::tearDown(); + } + + /** + * Forces KeyManager to refresh cache and generate new keys. + */ + void refreshKeyManager() { + _keyManager->refreshNow(operationContext()); + } + +private: + std::shared_ptr<KeysCollectionManager> _keyManager; +}; + TEST_F(VectorClockMongoDTest, TickClusterTime) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); const auto t0 = vc->getTime(); - ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); const auto t1 = vc->getTime(); @@ -57,26 +102,24 @@ TEST_F(VectorClockMongoDTest, TickClusterTime) { ASSERT_GT(t2[VectorClock::Component::ClusterTime], r1); } -TEST_F(VectorClockMongoDTest, GossipOutTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: get the gossipOut - // internal and external, and for each check that $clusterTime is there, with the right format, - // and right value, and not configTime. - - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); +TEST_F(VectorClockMongoDTest, TickToClusterTime) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); -} + const auto t0 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 0)), t0[VectorClock::Component::ClusterTime]); -TEST_F(VectorClockMongoDTest, GossipInTest) { - // TODO SERVER-47914: after ClusterTime gossiping has been re-enabled: for each of gossipIn - // internal and external, give it BSON in the correct format, and then check that ClusterTime - // has been advanced (or not), and that ConfigTime has not. + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(1, 1))); + const auto t1 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(1, 1)), t1[VectorClock::Component::ClusterTime]); - // auto sc = getGlobalServiceContext(); - // auto vc = VectorClockMutable::get(sc); + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(3, 3))); + const auto t2 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t2[VectorClock::Component::ClusterTime]); - // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1); + vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime(Timestamp(2, 2))); + const auto t3 = vc->getTime(); + ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t3[VectorClock::Component::ClusterTime]); } DEATH_TEST_F(VectorClockMongoDTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") { @@ -85,16 +128,137 @@ DEATH_TEST_F(VectorClockMongoDTest, CannotTickConfigTime, "Hit a MONGO_UNREACHAB vc->tick(VectorClock::Component::ConfigTime, 1); } -DEATH_TEST_F(VectorClockMongoDTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") { +DEATH_TEST_F(VectorClockMongoDTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime()); + vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime()); } -DEATH_TEST_F(VectorClockMongoDTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") { +TEST_F(VectorClockMongoDTest, GossipOutInternal) { auto sc = getGlobalServiceContext(); auto vc = VectorClockMutable::get(sc); - vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime()); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); + + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob, transport::Session::kInternalClient); + auto obj = bob.obj(); + + // On plain replset servers, gossip out to internal clients should have $clusterTime, but not + // $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_FALSE(obj.hasField("$configTime")); +} + +TEST_F(VectorClockMongoDTest, GossipOutExternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + LogicalTimeValidator::get(getServiceContext())->enableKeyGenerator(operationContext(), true); + refreshKeyManager(); + + const auto clusterTime = vc->tick(VectorClock::Component::ClusterTime, 1); + + BSONObjBuilder bob; + vc->gossipOut(nullptr, &bob); + auto obj = bob.obj(); + + // On plain replset servers, gossip out to external clients should have $clusterTime, but not + // $configTime. + ASSERT_TRUE(obj.hasField("$clusterTime")); + ASSERT_EQ(obj["$clusterTime"].Obj()["clusterTime"].timestamp(), clusterTime.asTimestamp()); + ASSERT_FALSE(obj.hasField("$configTime")); +} + +TEST_F(VectorClockMongoDTest, GossipInInternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + vc->tick(VectorClock::Component::ClusterTime, 1); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false, + transport::Session::kInternalClient); + + // On plain replset servers, gossip in from internal clients should update $clusterTime, but not + // $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false, + transport::Session::kInternalClient); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(3, 3) << "signature" << dummySignature) + << "$configTime" << Timestamp(3, 3)), + false, + transport::Session::kInternalClient); + + auto afterTime3 = vc->getTime(); + ASSERT_EQ(afterTime3[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(3, 3)); + ASSERT_EQ(afterTime3[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); +} + +TEST_F(VectorClockMongoDTest, GossipInExternal) { + auto sc = getGlobalServiceContext(); + auto vc = VectorClockMutable::get(sc); + + vc->tick(VectorClock::Component::ClusterTime, 1); + + auto dummySignature = + BSON("hash" << BSONBinData("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1", 20, BinDataGeneral) + << "keyId" << 0); + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(2, 2) << "signature" << dummySignature) + << "$configTime" << Timestamp(2, 2)), + false); + + // On plain replset servers, gossip in from external clients should update $clusterTime, but not + // $configTime. + auto afterTime = vc->getTime(); + ASSERT_EQ(afterTime[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(1, 1) << "signature" << dummySignature) + << "$configTime" << Timestamp(1, 1)), + false); + + auto afterTime2 = vc->getTime(); + ASSERT_EQ(afterTime2[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(2, 2)); + ASSERT_EQ(afterTime2[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); + + vc->gossipIn(nullptr, + BSON("$clusterTime" + << BSON("clusterTime" << Timestamp(3, 3) << "signature" << dummySignature) + << "$configTime" << Timestamp(3, 3)), + false); + + auto afterTime3 = vc->getTime(); + ASSERT_EQ(afterTime3[VectorClock::Component::ClusterTime].asTimestamp(), Timestamp(3, 3)); + ASSERT_EQ(afterTime3[VectorClock::Component::ConfigTime].asTimestamp(), Timestamp(0, 0)); } } // namespace diff --git a/src/mongo/db/vector_clock_mutable.cpp b/src/mongo/db/vector_clock_mutable.cpp index 6e18fbd028a..5bf3fda8d9e 100644 --- a/src/mongo/db/vector_clock_mutable.cpp +++ b/src/mongo/db/vector_clock_mutable.cpp @@ -29,8 +29,6 @@ #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault -#include <limits> - #include "mongo/platform/basic.h" #include "mongo/db/vector_clock_mutable.h" @@ -65,13 +63,8 @@ void VectorClockMutable::registerVectorClockOnServiceContext( clock = std::move(vectorClockMutable); } -bool VectorClockMutable::_lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks) { - return time.asTimestamp().getSecs() <= std::numeric_limits<uint32_t>::max() && - time.asTimestamp().getInc() <= (std::numeric_limits<uint32_t>::max() - nTicks); -} - LogicalTime VectorClockMutable::_advanceComponentTimeByTicks(Component component, uint64_t nTicks) { - invariant(nTicks > 0 && nTicks <= std::numeric_limits<uint32_t>::max()); + invariant(nTicks > 0 && nTicks <= kMaxValue); stdx::lock_guard<Latch> lock(_mutex); @@ -90,10 +83,9 @@ LogicalTime VectorClockMutable::_advanceComponentTimeByTicks(Component component // in order to preserve compatibility with potentially signed or unsigned integral Timestamp // increment types. It is also unlikely to tick a clock by more than 2^31 in the span of one // second. - else if (time.asTimestamp().getInc() > (std::numeric_limits<uint32_t>::max() - nTicks)) { + else if (time.asTimestamp().getInc() > (kMaxValue - nTicks)) { - // TODO SERVER-47914: update this log id back to 20709. - LOGV2(4620000 /*20709*/, + LOGV2(20709, "Exceeded maximum allowable increment value within one second. Moving time forward " "to the next second.", "vectorClockComponent"_attr = _componentName(component)); @@ -102,10 +94,9 @@ LogicalTime VectorClockMutable::_advanceComponentTimeByTicks(Component component time = LogicalTime(Timestamp(time.asTimestamp().getSecs() + 1, 0)); } - // TODO SERVER-47914: update this uassert id back to 40482. - uassert(4620001 /*40482*/, + uassert(40482, str::stream() << _componentName(component) - << " cannot be advanced beyond the maximum cluster time value", + << " cannot be advanced beyond the maximum logical time value", _lessThanOrEqualToMaxPossibleTime(time, nTicks)); // Save the next time. @@ -122,6 +113,14 @@ LogicalTime VectorClockMutable::_advanceComponentTimeByTicks(Component component void VectorClockMutable::_advanceComponentTimeTo(Component component, LogicalTime&& newTime) { stdx::lock_guard<Latch> lock(_mutex); + + // Rate limit checks are skipped here so a server with no activity for longer than + // maxAcceptableLogicalClockDriftSecs seconds can still have its cluster time initialized. + uassert(40483, + str::stream() << _componentName(component) + << " cannot be advanced beyond the maximum logical time value", + _lessThanOrEqualToMaxPossibleTime(newTime, 0)); + if (newTime > _vectorTime[component]) { _vectorTime[component] = std::move(newTime); } diff --git a/src/mongo/db/vector_clock_mutable.h b/src/mongo/db/vector_clock_mutable.h index 7e99aa32f2a..f8239b1fc4b 100644 --- a/src/mongo/db/vector_clock_mutable.h +++ b/src/mongo/db/vector_clock_mutable.h @@ -44,22 +44,41 @@ public: // clock implementation. static VectorClockMutable* get(ServiceContext* service); static VectorClockMutable* get(OperationContext* ctx); - static void registerVectorClockOnServiceContext(ServiceContext* service, VectorClockMutable* vectorClockMutable); - // Ticking + /** + * Returns the next time value for this Component, and provides a guarantee that any future call + * to tick() (for this Component) will return a value at least 'nTicks' ticks in the future from + * the current time. + */ virtual LogicalTime tick(Component component, uint64_t nTicks) = 0; + + /** + * Authoritatively ticks the current time of the Component to newTime. + * + * For ClusterTime, this should only be used for initializing from a trusted source, eg. from an + * oplog timestamp. + */ virtual void tickTo(Component component, LogicalTime newTime) = 0; protected: - static bool _lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks); - VectorClockMutable(); virtual ~VectorClockMutable(); - // Internal Ticking API + /** + * Called by sub-classes in order to actually tick a Component time, once they have determined + * that doing so is permissible. + * + * Returns as per tick(), ie. returns the next time value, and guarantees that future calls will + * return at least nTicks later. + */ LogicalTime _advanceComponentTimeByTicks(Component component, uint64_t nTicks); + + /** + * Called by sub-classes in order to actually tickTo a Component time, once they have determined + * that doing so is permissible. + */ void _advanceComponentTimeTo(Component component, LogicalTime&& newTime); }; diff --git a/src/mongo/db/vector_clock_trivial.cpp b/src/mongo/db/vector_clock_trivial.cpp index 88f00a512ea..96cde5788de 100644 --- a/src/mongo/db/vector_clock_trivial.cpp +++ b/src/mongo/db/vector_clock_trivial.cpp @@ -49,10 +49,19 @@ public: void tickTo(Component component, LogicalTime newTime) override; protected: - void _gossipOutInternal(BSONObjBuilder* out) const override; - void _gossipOutExternal(BSONObjBuilder* out) const override; - LogicalTimeArray _gossipInInternal(const BSONObj& in) override; - LogicalTimeArray _gossipInExternal(const BSONObj& in) override; + bool _gossipOutInternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const override; + bool _gossipOutExternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const override; + LogicalTimeArray _gossipInInternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) override; + LogicalTimeArray _gossipInExternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) override; + bool _permitRefreshDuringGossipOut() const override; }; const auto vectorClockTrivialDecoration = ServiceContext::declareDecoration<VectorClockTrivial>(); @@ -70,24 +79,38 @@ VectorClockTrivial::VectorClockTrivial() = default; VectorClockTrivial::~VectorClockTrivial() = default; -void VectorClockTrivial::_gossipOutInternal(BSONObjBuilder* out) const { +bool VectorClockTrivial::_gossipOutInternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const { // Clocks are not gossipped in trivial (non-distributed) environments. + return false; } -void VectorClockTrivial::_gossipOutExternal(BSONObjBuilder* out) const { +bool VectorClockTrivial::_gossipOutExternal(OperationContext* opCtx, + BSONObjBuilder* out, + const LogicalTimeArray& time) const { // Clocks are not gossipped in trivial (non-distributed) environments. + return false; } -VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInInternal(const BSONObj& in) { +VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInInternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) { // Clocks are not gossipped in trivial (non-distributed) environments. return {}; } -VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInExternal(const BSONObj& in) { +VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInExternal(OperationContext* opCtx, + const BSONObj& in, + bool couldBeUnauthenticated) { // Clocks are not gossipped in trivial (non-distributed) environments. return {}; } +bool VectorClockTrivial::_permitRefreshDuringGossipOut() const { + return false; +} + LogicalTime VectorClockTrivial::tick(Component component, uint64_t nTicks) { return _advanceComponentTimeByTicks(component, nTicks); } |