summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Pulo <kevin.pulo@mongodb.com>2020-04-16 14:45:53 +1000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-05-12 13:08:08 +0000
commit94ff51257a099ee6af4a9c41ee61245918227760 (patch)
tree5d5f880fb4708cb65abf9e369f0f022aa6d6d0c4
parent14bb6a661fc7a8b693613203693b8232c6f91944 (diff)
downloadmongo-94ff51257a099ee6af4a9c41ee61245918227760.tar.gz
SERVER-46200 implement basic VectorClock service
-rw-r--r--src/mongo/db/SConscript47
-rw-r--r--src/mongo/db/auth/auth_op_observer.h5
-rw-r--r--src/mongo/db/client.h4
-rw-r--r--src/mongo/db/command_generic_argument.cpp5
-rw-r--r--src/mongo/db/logical_clock.cpp2
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/replica_set_aware_service.h50
-rw-r--r--src/mongo/db/replica_set_aware_service_test.cpp49
-rw-r--r--src/mongo/db/s/SConscript3
-rw-r--r--src/mongo/db/s/balancer/balancer.cpp14
-rw-r--r--src/mongo/db/s/balancer/balancer.h6
-rw-r--r--src/mongo/db/s/config_server_op_observer.cpp6
-rw-r--r--src/mongo/db/s/config_server_op_observer_test.cpp7
-rw-r--r--src/mongo/db/s/vector_clock_config_server_test.cpp115
-rw-r--r--src/mongo/db/s/vector_clock_shard_server_test.cpp101
-rw-r--r--src/mongo/db/service_entry_point_common.cpp3
-rw-r--r--src/mongo/db/update/SConscript2
-rw-r--r--src/mongo/db/vector_clock.cpp201
-rw-r--r--src/mongo/db/vector_clock.h148
-rw-r--r--src/mongo/db/vector_clock_mongod.cpp179
-rw-r--r--src/mongo/db/vector_clock_mongod_test.cpp101
-rw-r--r--src/mongo/db/vector_clock_mutable.cpp130
-rw-r--r--src/mongo/db/vector_clock_mutable.h66
-rw-r--r--src/mongo/db/vector_clock_trivial.cpp100
-rw-r--r--src/mongo/embedded/SConscript1
-rw-r--r--src/mongo/embedded/stitch_support/SConscript1
-rw-r--r--src/mongo/rpc/metadata.cpp3
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/strategy.cpp4
-rw-r--r--src/mongo/s/commands/vector_clock_mongos.cpp103
-rw-r--r--src/mongo/s/grid.cpp7
-rw-r--r--src/mongo/s/grid.h9
32 files changed, 1411 insertions, 63 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 47408b66a57..55595f5999b 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1562,6 +1562,52 @@ env.Library(
'global_settings',
'logical_time',
'service_context',
+ 'vector_clock_mutable',
+ ],
+)
+
+env.Library(
+ target='vector_clock',
+ source=[
+ 'vector_clock.cpp',
+ ],
+ LIBDEPS=[
+ 'logical_time',
+ 'service_context',
+ ],
+)
+
+env.Library(
+ target='vector_clock_mutable',
+ source=[
+ 'vector_clock_mutable.cpp',
+ ],
+ LIBDEPS=[
+ 'vector_clock',
+ ],
+)
+
+env.Library(
+ target='vector_clock_d',
+ source=[
+ 'vector_clock_mongod.cpp',
+ ],
+ LIBDEPS=[
+ 'vector_clock_mutable',
+ ],
+ LIBDEPS_PRIVATE=[
+ 'replica_set_aware_service',
+ 'server_options_core',
+ ],
+)
+
+env.Library(
+ target='vector_clock_trivial',
+ source=[
+ 'vector_clock_trivial.cpp',
+ ],
+ LIBDEPS=[
+ 'vector_clock_mutable',
],
)
@@ -1834,6 +1880,7 @@ envWithAsio.CppUnitTest(
'transaction_participant_retryable_writes_test.cpp',
'transaction_participant_test.cpp',
'update_index_data_test.cpp',
+ 'vector_clock_mongod_test.cpp',
'write_concern_options_test.cpp',
'error_labels_test.cpp',
env.Idlc('commands_test_example.idl')[0],
diff --git a/src/mongo/db/auth/auth_op_observer.h b/src/mongo/db/auth/auth_op_observer.h
index 66ddf72732e..5920146860e 100644
--- a/src/mongo/db/auth/auth_op_observer.h
+++ b/src/mongo/db/auth/auth_op_observer.h
@@ -182,6 +182,11 @@ public:
void onMajorityCommitPointUpdate(ServiceContext* service,
const repl::OpTime& newCommitPoint) final {}
+
+ // Contains the fields of the document that are in the collection's shard key, and "_id".
+ static BSONObj getDocumentKey(OperationContext* opCtx,
+ NamespaceString const& nss,
+ BSONObj const& doc);
};
} // namespace mongo
diff --git a/src/mongo/db/client.h b/src/mongo/db/client.h
index 3ffed27adc2..615902f2c80 100644
--- a/src/mongo/db/client.h
+++ b/src/mongo/db/client.h
@@ -138,6 +138,10 @@ public:
return std::move(_session);
}
+ transport::Session::TagMask getSessionTags() const {
+ return _session ? _session->getTags() : 0;
+ }
+
std::string clientAddress(bool includePort = false) const;
const std::string& desc() const {
return _desc;
diff --git a/src/mongo/db/command_generic_argument.cpp b/src/mongo/db/command_generic_argument.cpp
index 85556c371a6..89c3cd20a87 100644
--- a/src/mongo/db/command_generic_argument.cpp
+++ b/src/mongo/db/command_generic_argument.cpp
@@ -52,7 +52,7 @@ struct SpecialArgRecord {
// If that changes, it should be added. When you add to this list, consider whether you
// should also change the filterCommandRequestForPassthrough() function.
// clang-format off
-static constexpr std::array<SpecialArgRecord, 29> specials{{
+static constexpr std::array<SpecialArgRecord, 30> specials{{
// /-isGeneric
// | /-stripFromRequest
// | | /-stripFromReply
@@ -84,7 +84,8 @@ static constexpr std::array<SpecialArgRecord, 29> specials{{
{"lastCommittedOpTime"_sd, 0, 0, 1},
{"readOnly"_sd, 0, 0, 1},
{"comment"_sd, 1, 0, 0},
- {"maxTimeMSOpOnly"_sd, 1, 0, 0}}};
+ {"maxTimeMSOpOnly"_sd, 1, 0, 0},
+ {"$configTime"_sd, 1, 1, 1}}};
// clang-format on
template <bool SpecialArgRecord::*pmo>
diff --git a/src/mongo/db/logical_clock.cpp b/src/mongo/db/logical_clock.cpp
index 2026667272e..3cb7dc1b192 100644
--- a/src/mongo/db/logical_clock.cpp
+++ b/src/mongo/db/logical_clock.cpp
@@ -39,6 +39,7 @@
#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/logv2/log.h"
namespace mongo {
@@ -100,6 +101,7 @@ Status LogicalClock::advanceClusterTime(const LogicalTime newTime) {
}
LogicalTime LogicalClock::reserveTicks(uint64_t nTicks) {
+ (void)VectorClockMutable::get(_service)->tick(VectorClock::Component::ClusterTime, nTicks);
invariant(nTicks > 0 && nTicks <= kMaxSignedInt);
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index d8acb4fdf42..21dd50fe804 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -25,6 +25,7 @@ env.Library(
'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',
],
)
diff --git a/src/mongo/db/replica_set_aware_service.h b/src/mongo/db/replica_set_aware_service.h
index 0c11a7c2976..4a3d01489a7 100644
--- a/src/mongo/db/replica_set_aware_service.h
+++ b/src/mongo/db/replica_set_aware_service.h
@@ -40,10 +40,14 @@ namespace mongo {
* Using this interface avoids the need to manually hook the various places in
* ReplicationCoordinatorExternalStateImpl where these events occur.
*
- * To define a ReplicaSetAwareService, a class needs to inherit from ReplicaSetAwareService
- * (templated on itself), implement the pure virtual methods in ReplicaSetAwareInterface, and define
- * a static ReplicaSetAwareServiceRegistry::Registerer to declare the name (and optionally
- * pre-requisite services) of the service.
+ * To define a ReplicaSetAwareService, a class needs to:
+ *
+ * 1. Inherit from ReplicaSetAwareService (templated on itself).
+ * 2. Implement the pure virtual methods in ReplicaSetAwareInterface.
+ * 3. Store a singleton object of the class somewhere (ideally as a ServiceContext decoration).
+ * 4. Define a public static `get(ServiceContext*)` function.
+ * 5. Define a static ReplicaSetAwareServiceRegistry::Registerer object to declare the name (and
+ * optionally pre-requisite services) of the service.
*
* If the service should only be active in certain configurations, then the class should override
* shouldRegisterReplicaSetAwareService. For the common cases of services that are only active on
@@ -57,6 +61,8 @@ namespace mongo {
*
* class FooService : public ReplicaSetAwareService<FooService> {
* public:
+ * static FooService* get(ServiceContext* serviceContext);
+ *
* // ...
*
* private:
@@ -79,9 +85,15 @@ namespace mongo {
*
* namespace {
*
- * ReplicaSetAwareServiceRegistry::Registerer<FooService> fooServiceRegisterer("FooService");
+ * const auto _fooDecoration = ServiceContext::declareDecoration<FooService>();
+ *
+ * const ReplicaSetAwareServiceRegistry::Registerer<FooService> _fooServiceRegisterer("FooService");
*
* } // namespace
+ *
+ * FooService* FooService::get(ServiceContext* serviceContext) {
+ * return _fooDecoration(serviceContext);
+ * }
*/
/**
@@ -175,28 +187,9 @@ class ReplicaSetAwareService : private ReplicaSetAwareInterface {
public:
virtual ~ReplicaSetAwareService() = default;
- /**
- * Retrieves the per-serviceContext instance of the ActualService.
- */
- static ActualService* get(ServiceContext* serviceContext) {
- return &_decoration(serviceContext);
- }
-
- static ActualService* get(OperationContext* operationContext) {
- return get(operationContext->getServiceContext());
- }
-
protected:
ReplicaSetAwareService() = default;
- /**
- * Used when services need to get a reference to the serviceContext that they are decorating.
- */
- ServiceContext* getServiceContext() {
- auto* actualService = checked_cast<ActualService*>(this);
- return &_decoration.owner(*actualService);
- }
-
private:
friend ReplicaSetAwareServiceRegistry::Registerer<ActualService>;
@@ -216,17 +209,8 @@ private:
virtual bool shouldRegisterReplicaSetAwareService() const {
return true;
}
-
- // The decoration of the actual service on the ServiceContext.
- // Definition of this static can't be inline, because it isn't constexpr.
- static Decorable<ServiceContext>::Decoration<ActualService> _decoration;
};
-template <class ActualService>
-Decorable<ServiceContext>::Decoration<ActualService>
- ReplicaSetAwareService<ActualService>::_decoration =
- ServiceContext::declareDecoration<ActualService>();
-
/**
* Convenience version of ReplicaSetAwareService that is only active on config servers.
diff --git a/src/mongo/db/replica_set_aware_service_test.cpp b/src/mongo/db/replica_set_aware_service_test.cpp
index 869db7355c0..4d1fddcff8e 100644
--- a/src/mongo/db/replica_set_aware_service_test.cpp
+++ b/src/mongo/db/replica_set_aware_service_test.cpp
@@ -44,15 +44,15 @@ public:
int numCallsOnStepDown{0};
protected:
- virtual void onStepUpBegin(OperationContext* opCtx) {
+ void onStepUpBegin(OperationContext* opCtx) override {
numCallsOnStepUpBegin++;
}
- virtual void onStepUpComplete(OperationContext* opCtx) {
+ void onStepUpComplete(OperationContext* opCtx) override {
numCallsOnStepUpComplete++;
}
- virtual void onStepDown() {
+ void onStepDown() override {
numCallsOnStepDown++;
}
};
@@ -61,34 +61,57 @@ protected:
* Service that's never registered.
*/
class ServiceA : public TestService<ServiceA> {
+public:
+ static ServiceA* get(ServiceContext* serviceContext);
+
private:
- virtual bool shouldRegisterReplicaSetAwareService() const final {
+ bool shouldRegisterReplicaSetAwareService() const final {
return false;
}
};
+const auto getServiceA = ServiceContext::declareDecoration<ServiceA>();
+
ReplicaSetAwareServiceRegistry::Registerer<ServiceA> serviceARegisterer("ServiceA");
+ServiceA* ServiceA::get(ServiceContext* serviceContext) {
+ return &getServiceA(serviceContext);
+}
+
/**
* Service that's always registered.
*/
class ServiceB : public TestService<ServiceB> {
+public:
+ static ServiceB* get(ServiceContext* serviceContext);
+
private:
- virtual bool shouldRegisterReplicaSetAwareService() const final {
+ bool shouldRegisterReplicaSetAwareService() const final {
return true;
}
};
+const auto getServiceB = ServiceContext::declareDecoration<ServiceB>();
+
ReplicaSetAwareServiceRegistry::Registerer<ServiceB> serviceBRegisterer("ServiceB");
+ServiceB* ServiceB::get(ServiceContext* serviceContext) {
+ return &getServiceB(serviceContext);
+}
+
/**
* Service that's always registered, depends on ServiceB.
*/
class ServiceC : public TestService<ServiceC> {
+public:
+ static ServiceC* get(ServiceContext* serviceContext);
+
private:
- virtual bool shouldRegisterReplicaSetAwareService() const final {
+ ServiceContext* getServiceContext();
+
+ bool shouldRegisterReplicaSetAwareService() const final {
return true;
}
@@ -110,8 +133,18 @@ private:
}
};
+const auto getServiceC = ServiceContext::declareDecoration<ServiceC>();
+
ReplicaSetAwareServiceRegistry::Registerer<ServiceC> serviceCRegisterer("ServiceC", {"ServiceB"});
+ServiceC* ServiceC::get(ServiceContext* serviceContext) {
+ return &getServiceC(serviceContext);
+}
+
+ServiceContext* ServiceC::getServiceContext() {
+ return getServiceC.owner(this);
+}
+
using ReplicaSetAwareServiceTest = ServiceContextTest;
@@ -127,9 +160,11 @@ TEST_F(ReplicaSetAwareServiceTest, ReplicaSetAwareService) {
ASSERT_EQ(0, a->numCallsOnStepUpBegin);
ASSERT_EQ(0, a->numCallsOnStepUpComplete);
ASSERT_EQ(0, a->numCallsOnStepDown);
+
ASSERT_EQ(0, b->numCallsOnStepUpBegin);
ASSERT_EQ(0, b->numCallsOnStepUpComplete);
ASSERT_EQ(0, b->numCallsOnStepDown);
+
ASSERT_EQ(0, c->numCallsOnStepUpBegin);
ASSERT_EQ(0, c->numCallsOnStepUpComplete);
ASSERT_EQ(0, c->numCallsOnStepDown);
@@ -144,9 +179,11 @@ TEST_F(ReplicaSetAwareServiceTest, ReplicaSetAwareService) {
ASSERT_EQ(0, a->numCallsOnStepUpBegin);
ASSERT_EQ(0, a->numCallsOnStepUpComplete);
ASSERT_EQ(0, a->numCallsOnStepDown);
+
ASSERT_EQ(3, b->numCallsOnStepUpBegin);
ASSERT_EQ(2, b->numCallsOnStepUpComplete);
ASSERT_EQ(1, b->numCallsOnStepDown);
+
ASSERT_EQ(3, c->numCallsOnStepUpBegin);
ASSERT_EQ(2, c->numCallsOnStepUpComplete);
ASSERT_EQ(1, c->numCallsOnStepDown);
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript
index 66fd73a851d..6ad8bf9f6a8 100644
--- a/src/mongo/db/s/SConscript
+++ b/src/mongo/db/s/SConscript
@@ -100,6 +100,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/s/client/shard_local',
'$BUILD_DIR/mongo/s/query/cluster_aggregate',
'$BUILD_DIR/mongo/s/sharding_initialization',
@@ -359,6 +360,7 @@ env.CppUnitTest(
'sharding_logging_test.cpp',
'start_chunk_clone_request_test.cpp',
'type_shard_identity_test.cpp',
+ 'vector_clock_config_server_test.cpp',
'wait_for_majority_service_test.cpp',
],
LIBDEPS=[
@@ -413,6 +415,7 @@ env.CppUnitTest(
'sharding_initialization_mongod_test.cpp',
'sharding_initialization_op_observer_test.cpp',
'split_vector_test.cpp',
+ 'vector_clock_shard_server_test.cpp',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/auth/authmocks',
diff --git a/src/mongo/db/s/balancer/balancer.cpp b/src/mongo/db/s/balancer/balancer.cpp
index 1e80510985c..74e1a643016 100644
--- a/src/mongo/db/s/balancer/balancer.cpp
+++ b/src/mongo/db/s/balancer/balancer.cpp
@@ -156,17 +156,27 @@ void warnOnMultiVersion(const vector<ClusterStatistics::ShardStatistics>& cluste
"shardVersions"_attr = shardVersions.done());
}
-ReplicaSetAwareServiceRegistry::Registerer<Balancer> balancerRegisterer("Balancer");
+const auto _balancerDecoration = ServiceContext::declareDecoration<Balancer>();
+
+const ReplicaSetAwareServiceRegistry::Registerer<Balancer> _balancerRegisterer("Balancer");
} // namespace
+Balancer* Balancer::get(ServiceContext* serviceContext) {
+ return &_balancerDecoration(serviceContext);
+}
+
+Balancer* Balancer::get(OperationContext* operationContext) {
+ return get(operationContext->getServiceContext());
+}
+
Balancer::Balancer()
: _balancedLastTime(0),
_random(std::random_device{}()),
_clusterStats(std::make_unique<ClusterStatisticsImpl>(_random)),
_chunkSelectionPolicy(
std::make_unique<BalancerChunkSelectionPolicyImpl>(_clusterStats.get(), _random)),
- _migrationManager(getServiceContext()) {}
+ _migrationManager(_balancerDecoration.owner(this)) {}
Balancer::~Balancer() {
diff --git a/src/mongo/db/s/balancer/balancer.h b/src/mongo/db/s/balancer/balancer.h
index fdbbb79d1d5..eef505a014a 100644
--- a/src/mongo/db/s/balancer/balancer.h
+++ b/src/mongo/db/s/balancer/balancer.h
@@ -59,6 +59,12 @@ class Balancer : public ReplicaSetAwareServiceConfigSvr<Balancer> {
Balancer& operator=(const Balancer&) = delete;
public:
+ /**
+ * Provide access to the Balancer decoration on ServiceContext.
+ */
+ static Balancer* get(ServiceContext* serviceContext);
+ static Balancer* get(OperationContext* operationContext);
+
Balancer();
~Balancer();
diff --git a/src/mongo/db/s/config_server_op_observer.cpp b/src/mongo/db/s/config_server_op_observer.cpp
index 13e1d91378e..a25fac1c989 100644
--- a/src/mongo/db/s/config_server_op_observer.cpp
+++ b/src/mongo/db/s/config_server_op_observer.cpp
@@ -34,9 +34,9 @@
#include "mongo/db/s/config_server_op_observer.h"
#include "mongo/db/s/config/sharding_catalog_manager.h"
+#include "mongo/db/vector_clock_mutable.h"
#include "mongo/s/catalog/type_config_version.h"
#include "mongo/s/cluster_identity_loader.h"
-#include "mongo/s/grid.h"
namespace mongo {
@@ -94,8 +94,8 @@ void ConfigServerOpObserver::onReplicationRollback(OperationContext* opCtx,
void ConfigServerOpObserver::onMajorityCommitPointUpdate(ServiceContext* service,
const repl::OpTime& newCommitPoint) {
- // TODO SERVER-46200: tick the VectorClock's ConfigTime.
- Grid::get(service)->advanceConfigOpTimeAuthoritative(newCommitPoint);
+ VectorClockMutable::get(service)->tickTo(VectorClock::Component::ConfigTime,
+ LogicalTime(newCommitPoint.getTimestamp()));
}
} // namespace mongo
diff --git a/src/mongo/db/s/config_server_op_observer_test.cpp b/src/mongo/db/s/config_server_op_observer_test.cpp
index e7383bdab54..d21f30899c3 100644
--- a/src/mongo/db/s/config_server_op_observer_test.cpp
+++ b/src/mongo/db/s/config_server_op_observer_test.cpp
@@ -29,6 +29,7 @@
#include "mongo/db/s/config/sharding_catalog_manager.h"
#include "mongo/db/s/config_server_op_observer.h"
+#include "mongo/db/vector_clock_mutable.h"
#include "mongo/s/cluster_identity_loader.h"
#include "mongo/s/config_server_test_fixture.h"
#include "mongo/unittest/death_test.h"
@@ -102,10 +103,12 @@ TEST_F(ConfigServerOpObserverTest, ConfigOpTimeAdvancedWhenMajorityCommitPointAd
repl::OpTime b(Timestamp(1, 2), 1);
opObserver.onMajorityCommitPointUpdate(getServiceContext(), a);
- // TODO SERVER-46200: Verify that configOpTime is a.
+ const auto aTime = VectorClock::get(getServiceContext())->getTime();
+ ASSERT_EQ(a.getTimestamp(), aTime[VectorClock::Component::ConfigTime].asTimestamp());
opObserver.onMajorityCommitPointUpdate(getServiceContext(), b);
- // TODO SERVER-46200: Verify that configOpTime is b.
+ const auto bTime = VectorClock::get(getServiceContext())->getTime();
+ ASSERT_EQ(b.getTimestamp(), bTime[VectorClock::Component::ConfigTime].asTimestamp());
}
} // namespace
diff --git a/src/mongo/db/s/vector_clock_config_server_test.cpp b/src/mongo/db/s/vector_clock_config_server_test.cpp
new file mode 100644
index 00000000000..30761daea07
--- /dev/null
+++ b/src/mongo/db/s/vector_clock_config_server_test.cpp
@@ -0,0 +1,115 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.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"
+
+namespace mongo {
+namespace {
+
+using VectorClockConfigServerTest = ConfigServerTestFixture;
+
+TEST_F(VectorClockConfigServerTest, TickClusterTime) {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+
+ const auto t0 = vc->getTime();
+ ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]);
+
+ const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+ const auto t1 = vc->getTime();
+ ASSERT_EQ(r1, t1[VectorClock::Component::ClusterTime]);
+ ASSERT_GT(r1, t0[VectorClock::Component::ClusterTime]);
+
+ const auto r2 = vc->tick(VectorClock::Component::ClusterTime, 2);
+ const auto t2 = vc->getTime();
+ ASSERT_GT(r2, r1);
+ ASSERT_GT(t2[VectorClock::Component::ClusterTime], r1);
+}
+
+TEST_F(VectorClockConfigServerTest, TickToConfigTime) {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+
+ const auto t0 = vc->getTime();
+ ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ConfigTime]);
+
+ vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime(Timestamp(1, 1)));
+ const auto t1 = vc->getTime();
+ ASSERT_EQ(LogicalTime(Timestamp(1, 1)), t1[VectorClock::Component::ConfigTime]);
+
+ vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime(Timestamp(3, 3)));
+ const auto t2 = vc->getTime();
+ ASSERT_EQ(LogicalTime(Timestamp(3, 3)), t2[VectorClock::Component::ConfigTime]);
+
+ vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime(Timestamp(2, 2)));
+ const auto t3 = vc->getTime();
+ 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.
+
+ // auto sc = getGlobalServiceContext();
+ // auto vc = VectorClockMutable::get(sc);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+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***.
+
+ // auto sc = getGlobalServiceContext();
+ // auto vc = VectorClockMutable::get(sc);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+DEATH_TEST_F(VectorClockConfigServerTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tick(VectorClock::Component::ConfigTime, 1);
+}
+
+DEATH_TEST_F(VectorClockConfigServerTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime());
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/s/vector_clock_shard_server_test.cpp b/src/mongo/db/s/vector_clock_shard_server_test.cpp
new file mode 100644
index 00000000000..594e7d86f59
--- /dev/null
+++ b/src/mongo/db/s/vector_clock_shard_server_test.cpp
@@ -0,0 +1,101 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.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"
+
+namespace mongo {
+namespace {
+
+using VectorClockShardServerTest = ShardServerTestFixture;
+
+TEST_F(VectorClockShardServerTest, TickClusterTime) {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+
+ const auto t0 = vc->getTime();
+ ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]);
+
+ const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+ const auto t1 = vc->getTime();
+ ASSERT_EQ(r1, t1[VectorClock::Component::ClusterTime]);
+ ASSERT_GT(r1, t0[VectorClock::Component::ClusterTime]);
+
+ const auto r2 = vc->tick(VectorClock::Component::ClusterTime, 2);
+ const auto t2 = vc->getTime();
+ ASSERT_GT(r2, r1);
+ 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);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+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).
+
+ // auto sc = getGlobalServiceContext();
+ // auto vc = VectorClockMutable::get(sc);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+DEATH_TEST_F(VectorClockShardServerTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tick(VectorClock::Component::ConfigTime, 1);
+}
+
+DEATH_TEST_F(VectorClockShardServerTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime());
+}
+
+DEATH_TEST_F(VectorClockShardServerTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime());
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 3ac44b65d25..0a3072214e9 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -82,6 +82,7 @@
#include "mongo/db/stats/top.h"
#include "mongo/db/transaction_participant.h"
#include "mongo/db/transaction_validation.h"
+#include "mongo/db/vector_clock.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/factory.h"
#include "mongo/rpc/get_status_from_command_result.h"
@@ -443,6 +444,8 @@ void appendClusterAndOperationTime(OperationContext* opCtx,
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);
diff --git a/src/mongo/db/update/SConscript b/src/mongo/db/update/SConscript
index 2dc99fd8716..681e61d0944 100644
--- a/src/mongo/db/update/SConscript
+++ b/src/mongo/db/update/SConscript
@@ -50,6 +50,7 @@ env.Library(
'$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',
'update_common',
],
)
@@ -106,6 +107,7 @@ env.CppUnitTest(
'$BUILD_DIR/mongo/db/query/query_planner',
'$BUILD_DIR/mongo/db/query/query_test_service_context',
'$BUILD_DIR/mongo/db/service_context_test_fixture',
+ '$BUILD_DIR/mongo/db/vector_clock_trivial',
'update',
'update_common',
'update_driver',
diff --git a/src/mongo/db/vector_clock.cpp b/src/mongo/db/vector_clock.cpp
new file mode 100644
index 00000000000..5be53064537
--- /dev/null
+++ b/src/mongo/db/vector_clock.cpp
@@ -0,0 +1,201 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/vector_clock.h"
+
+namespace mongo {
+
+namespace {
+
+const auto vectorClockDecoration = ServiceContext::declareDecoration<VectorClock*>();
+
+} // namespace
+
+VectorClock* VectorClock::get(ServiceContext* service) {
+ return vectorClockDecoration(service);
+}
+
+VectorClock* VectorClock::get(OperationContext* ctx) {
+ return get(ctx->getClient()->getServiceContext());
+}
+
+VectorClock::VectorClock() = default;
+
+VectorClock::~VectorClock() = default;
+
+void VectorClock::registerVectorClockOnServiceContext(ServiceContext* service,
+ VectorClock* vectorClock) {
+ invariant(!vectorClock->_service);
+ vectorClock->_service = service;
+ auto& clock = vectorClockDecoration(service);
+ invariant(!clock);
+ clock = std::move(vectorClock);
+}
+
+VectorClock::VectorTime VectorClock::getTime() const {
+ stdx::lock_guard<Latch> lock(_mutex);
+ return VectorTime(_vectorTime);
+}
+
+void VectorClock::_advanceTime(LogicalTimeArray&& newTime) {
+ stdx::lock_guard<Latch> lock(_mutex);
+ auto it = _vectorTime.begin();
+ auto newIt = newTime.begin();
+ for (; it != _vectorTime.end() && newIt != newTime.end(); ++it, ++newIt) {
+ if (*newIt > *it) {
+ *it = std::move(*newIt);
+ }
+ }
+}
+
+class VectorClock::GossipFormat {
+public:
+ class Plain;
+ class Signed;
+
+ 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;
+
+ const std::string _fieldName;
+};
+
+class VectorClock::GossipFormat::Plain : public VectorClock::GossipFormat {
+public:
+ using GossipFormat::GossipFormat;
+ virtual ~Plain() = default;
+
+ void out(BSONObjBuilder* out, LogicalTime time, Component component) const override {
+ out->append(_fieldName, time.asTimestamp());
+ }
+
+ LogicalTime in(const BSONObj& in, Component component) const override {
+ const auto componentElem(in[_fieldName]);
+ if (componentElem.eoo()) {
+ // Nothing to gossip in.
+ return LogicalTime();
+ }
+ uassert(ErrorCodes::BadValue,
+ str::stream() << _fieldName << " is not a Timestamp",
+ componentElem.type() == bsonTimestamp);
+ return LogicalTime(componentElem.timestamp());
+ }
+};
+
+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());
+ }
+
+ 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()) {
+ // 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());
+ }
+};
+
+// 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 {
+ if (clientSessionTags & transport::Session::kInternalClient) {
+ _gossipOutInternal(outMessage);
+ } else {
+ _gossipOutExternal(outMessage);
+ }
+}
+
+void VectorClock::gossipIn(const BSONObj& inMessage,
+ const transport::Session::TagMask clientSessionTags) {
+ if (clientSessionTags & transport::Session::kInternalClient) {
+ _advanceTime(_gossipInInternal(inMessage));
+ } else {
+ _advanceTime(_gossipInExternal(inMessage));
+ }
+}
+
+void VectorClock::_gossipOutComponent(BSONObjBuilder* out,
+ VectorTime time,
+ Component component) const {
+ GossipFormat::_formatters[component]->out(out, time[component], component);
+}
+
+void VectorClock::_gossipInComponent(const BSONObj& in,
+ LogicalTimeArray* newTime,
+ Component component) {
+ (*newTime)[component] = GossipFormat::_formatters[component]->in(in, component);
+}
+
+std::string VectorClock::_componentName(Component component) {
+ return GossipFormat::_formatters[component]->_fieldName;
+}
+
+bool VectorClock::isEnabled() const {
+ stdx::lock_guard<Latch> lock(_mutex);
+ return _isEnabled;
+}
+
+void VectorClock::disable() {
+ stdx::lock_guard<Latch> lock(_mutex);
+ _isEnabled = false;
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock.h b/src/mongo/db/vector_clock.h
new file mode 100644
index 00000000000..b7381652e9c
--- /dev/null
+++ b/src/mongo/db/vector_clock.h
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <array>
+
+#include "mongo/db/logical_time.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/platform/mutex.h"
+#include "mongo/transport/session.h"
+
+namespace mongo {
+
+class VectorClockMutable;
+
+/**
+ * The VectorClock service provides a collection of cluster-wide logical clocks (including the
+ * clusterTime), that are used to provide causal-consistency to various other services.
+ */
+class VectorClock {
+public:
+ enum class Component : uint8_t {
+ ClusterTime = 0,
+ ConfigTime = 1,
+ _kNumComponents = 2,
+ };
+
+private:
+ template <typename T>
+ class ComponentArray
+ : public std::array<T, static_cast<unsigned long>(Component::_kNumComponents)> {
+ public:
+ const T& operator[](Component component) const {
+ invariant(component != Component::_kNumComponents);
+ return std::array<T, static_cast<unsigned long>(Component::_kNumComponents)>::
+ operator[](static_cast<unsigned long>(component));
+ }
+
+ T& operator[](Component component) {
+ invariant(component != Component::_kNumComponents);
+ return std::array<T, static_cast<unsigned long>(Component::_kNumComponents)>::
+ operator[](static_cast<unsigned long>(component));
+ }
+
+ private:
+ const T& operator[](unsigned long i) const;
+ T& operator[](unsigned long i);
+ };
+
+protected:
+ using LogicalTimeArray = ComponentArray<LogicalTime>;
+
+public:
+ class VectorTime {
+ public:
+ LogicalTime operator[](Component component) const {
+ return _time[component];
+ }
+
+ private:
+ friend class VectorClock;
+
+ explicit VectorTime(LogicalTimeArray time) : _time(time) {}
+
+ const LogicalTimeArray _time;
+ };
+
+ // 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);
+
+ VectorTime getTime() const;
+
+ // Gossipping
+ void gossipOut(BSONObjBuilder* outMessage,
+ const transport::Session::TagMask clientSessionTags) const;
+ void gossipIn(const BSONObj& inMessage, const transport::Session::TagMask clientSessionTags);
+
+ bool isEnabled() const;
+ void disable();
+
+protected:
+ VectorClock();
+ virtual ~VectorClock();
+
+ 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;
+
+ void _gossipOutComponent(BSONObjBuilder* out, VectorTime time, Component component) const;
+ void _gossipInComponent(const BSONObj& in, LogicalTimeArray* newTime, Component component);
+
+ // Used to atomically advance the time of several components (eg. during gossip-in).
+ void _advanceTime(LogicalTimeArray&& newTime);
+
+ ServiceContext* _service{nullptr};
+
+ // The mutex protects _vectorTime and _isEnabled.
+ //
+ // Note that ConfigTime is advanced under the ReplicationCoordinator mutex, so to avoid
+ // potential deadlocks the ReplicationCoordator mutex should never be acquired whilst the
+ // VectorClock mutex is held.
+ mutable Mutex _mutex = MONGO_MAKE_LATCH("VectorClock::_mutex");
+
+ LogicalTimeArray _vectorTime;
+ bool _isEnabled{true};
+
+private:
+ class GossipFormat;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock_mongod.cpp b/src/mongo/db/vector_clock_mongod.cpp
new file mode 100644
index 00000000000..69d2eb2e7c1
--- /dev/null
+++ b/src/mongo/db/vector_clock_mongod.cpp
@@ -0,0 +1,179 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/replica_set_aware_service.h"
+#include "mongo/db/vector_clock_mutable.h"
+
+namespace mongo {
+namespace {
+
+/**
+ * Vector clock implementation for mongod.
+ */
+class VectorClockMongoD : public VectorClockMutable,
+ public ReplicaSetAwareService<VectorClockMongoD> {
+ VectorClockMongoD(const VectorClockMongoD&) = delete;
+ VectorClockMongoD& operator=(const VectorClockMongoD&) = delete;
+
+public:
+ static VectorClockMongoD* get(ServiceContext* serviceContext);
+
+ VectorClockMongoD();
+ virtual ~VectorClockMongoD();
+
+ LogicalTime tick(Component component, uint64_t nTicks) override;
+ 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;
+
+private:
+ enum class ReplState {
+ Unset,
+ StepUpBegin,
+ StepUpComplete,
+ StepDown,
+ };
+
+ void onStepUpBegin(OperationContext* opCtx) override;
+ void onStepUpComplete(OperationContext* opCtx) override;
+ void onStepDown() override;
+
+ ReplState _replState{ReplState::Unset};
+};
+
+const auto vectorClockMongoDDecoration = ServiceContext::declareDecoration<VectorClockMongoD>();
+
+const ReplicaSetAwareServiceRegistry::Registerer<VectorClockMongoD> vectorClockMongoDRegisterer(
+ "VectorClockMongoD-ReplicaSetAwareServiceRegistration");
+
+VectorClockMongoD* VectorClockMongoD::get(ServiceContext* serviceContext) {
+ return &vectorClockMongoDDecoration(serviceContext);
+}
+
+ServiceContext::ConstructorActionRegisterer _registerer(
+ "VectorClockMongoD-VectorClockRegistration",
+ {},
+ [](ServiceContext* service) {
+ VectorClockMongoD::registerVectorClockOnServiceContext(
+ service, &vectorClockMongoDDecoration(service));
+ },
+ {});
+
+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::_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);
+ if (serverGlobalParams.clusterRole == ClusterRole::ShardServer ||
+ serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
+ _gossipOutComponent(out, now, Component::ConfigTime);
+ }
+}
+
+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);
+}
+
+VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInInternal(const BSONObj& in) {
+ LogicalTimeArray newTime;
+ // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has
+ // been migrated into VectorClock.
+ // _gossipInComponent(in, &newTime, Component::ClusterTime);
+ if (serverGlobalParams.clusterRole == ClusterRole::ShardServer) {
+ _gossipInComponent(in, &newTime, Component::ConfigTime);
+ }
+ return newTime;
+}
+
+VectorClock::LogicalTimeArray VectorClockMongoD::_gossipInExternal(const BSONObj& in) {
+ LogicalTimeArray newTime;
+ // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has
+ // been migrated into VectorClock.
+ // _gossipInComponent(in, &newTime, Component::ClusterTime);
+ return newTime;
+}
+
+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
+ // (ie. primary, or standalone), this is handled at a higher layer.
+ //
+ // ClusterTime is ticked when replacing zero-valued Timestamps with the current time, which
+ // is usually but not necessarily associated with writes.
+ //
+ // ClusterTime is ticked after winning an election, while persisting the stepUp to the
+ // oplog, which is slightly before the repl state is changed to primary.
+ //
+ // As such, ticking ClusterTime is not restricted here based on repl state.
+
+ return _advanceComponentTimeByTicks(component, nTicks);
+ }
+
+ // tick is not permitted in other circumstances.
+ MONGO_UNREACHABLE;
+}
+
+void VectorClockMongoD::tickTo(Component component, LogicalTime newTime) {
+ if (component == Component::ConfigTime &&
+ serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
+ _advanceComponentTimeTo(component, std::move(newTime));
+ return;
+ }
+
+ // tickTo is not permitted in other circumstances.
+ MONGO_UNREACHABLE;
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock_mongod_test.cpp b/src/mongo/db/vector_clock_mongod_test.cpp
new file mode 100644
index 00000000000..1a443ca68ba
--- /dev/null
+++ b/src/mongo/db/vector_clock_mongod_test.cpp
@@ -0,0 +1,101 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/service_context_test_fixture.h"
+#include "mongo/db/vector_clock_mutable.h"
+#include "mongo/unittest/death_test.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+using VectorClockMongoDTest = ServiceContextTest;
+
+TEST_F(VectorClockMongoDTest, TickClusterTime) {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+
+ const auto t0 = vc->getTime();
+ ASSERT_EQ(LogicalTime(), t0[VectorClock::Component::ClusterTime]);
+
+ const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+ const auto t1 = vc->getTime();
+ ASSERT_EQ(r1, t1[VectorClock::Component::ClusterTime]);
+ ASSERT_GT(r1, t0[VectorClock::Component::ClusterTime]);
+
+ const auto r2 = vc->tick(VectorClock::Component::ClusterTime, 2);
+ const auto t2 = vc->getTime();
+ ASSERT_GT(r2, r1);
+ 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);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+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.
+
+ // auto sc = getGlobalServiceContext();
+ // auto vc = VectorClockMutable::get(sc);
+
+ // const auto r1 = vc->tick(VectorClock::Component::ClusterTime, 1);
+}
+
+DEATH_TEST_F(VectorClockMongoDTest, CannotTickConfigTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tick(VectorClock::Component::ConfigTime, 1);
+}
+
+DEATH_TEST_F(VectorClockMongoDTest, CannotTickToClusterTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tickTo(VectorClock::Component::ClusterTime, LogicalTime());
+}
+
+DEATH_TEST_F(VectorClockMongoDTest, CannotTickToConfigTime, "Hit a MONGO_UNREACHABLE") {
+ auto sc = getGlobalServiceContext();
+ auto vc = VectorClockMutable::get(sc);
+ vc->tickTo(VectorClock::Component::ConfigTime, LogicalTime());
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock_mutable.cpp b/src/mongo/db/vector_clock_mutable.cpp
new file mode 100644
index 00000000000..6e18fbd028a
--- /dev/null
+++ b/src/mongo/db/vector_clock_mutable.cpp
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault
+
+#include <limits>
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/vector_clock_mutable.h"
+
+#include "mongo/logv2/log.h"
+
+namespace mongo {
+
+namespace {
+
+const auto vectorClockMutableDecoration = ServiceContext::declareDecoration<VectorClockMutable*>();
+
+} // namespace
+
+VectorClockMutable* VectorClockMutable::get(ServiceContext* service) {
+ return vectorClockMutableDecoration(service);
+}
+
+VectorClockMutable* VectorClockMutable::get(OperationContext* ctx) {
+ return get(ctx->getClient()->getServiceContext());
+}
+
+VectorClockMutable::VectorClockMutable() = default;
+
+VectorClockMutable::~VectorClockMutable() = default;
+
+void VectorClockMutable::registerVectorClockOnServiceContext(
+ ServiceContext* service, VectorClockMutable* vectorClockMutable) {
+ VectorClock::registerVectorClockOnServiceContext(service, vectorClockMutable);
+ auto& clock = vectorClockMutableDecoration(service);
+ invariant(!clock);
+ 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());
+
+ stdx::lock_guard<Latch> lock(_mutex);
+
+ LogicalTime time = _vectorTime[component];
+
+ const unsigned wallClockSecs =
+ durationCount<Seconds>(_service->getFastClockSource()->now().toDurationSinceEpoch());
+ unsigned timeSecs = time.asTimestamp().getSecs();
+
+ // Synchronize time with wall clock time, if time was behind in seconds.
+ if (timeSecs < wallClockSecs) {
+ time = LogicalTime(Timestamp(wallClockSecs, 0));
+ }
+ // If reserving 'nTicks' would force the time'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 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)) {
+
+ // TODO SERVER-47914: update this log id back to 20709.
+ LOGV2(4620000 /*20709*/,
+ "Exceeded maximum allowable increment value within one second. Moving time forward "
+ "to the next second.",
+ "vectorClockComponent"_attr = _componentName(component));
+
+ // Move time forward to the next second
+ time = LogicalTime(Timestamp(time.asTimestamp().getSecs() + 1, 0));
+ }
+
+ // TODO SERVER-47914: update this uassert id back to 40482.
+ uassert(4620001 /*40482*/,
+ str::stream() << _componentName(component)
+ << " cannot be advanced beyond the maximum cluster time value",
+ _lessThanOrEqualToMaxPossibleTime(time, nTicks));
+
+ // Save the next time.
+ time.addTicks(1);
+ _vectorTime[component] = time;
+
+ // Add the rest of the requested ticks if needed.
+ if (nTicks > 1) {
+ _vectorTime[component].addTicks(nTicks - 1);
+ }
+
+ return time;
+}
+
+void VectorClockMutable::_advanceComponentTimeTo(Component component, LogicalTime&& newTime) {
+ stdx::lock_guard<Latch> lock(_mutex);
+ if (newTime > _vectorTime[component]) {
+ _vectorTime[component] = std::move(newTime);
+ }
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock_mutable.h b/src/mongo/db/vector_clock_mutable.h
new file mode 100644
index 00000000000..7e99aa32f2a
--- /dev/null
+++ b/src/mongo/db/vector_clock_mutable.h
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/vector_clock.h"
+
+namespace mongo {
+
+/**
+ * A vector clock service that additionally permits being advanced authoritatively ("ticking").
+ *
+ * Only linked in contexts where ticking is allowed, ie. mongod, embedded, mongod-based unittests.
+ */
+class VectorClockMutable : public VectorClock {
+public:
+ // Decorate ServiceContext with VectorClockMutable*, that will resolve to the mutable vector
+ // clock implementation.
+ static VectorClockMutable* get(ServiceContext* service);
+ static VectorClockMutable* get(OperationContext* ctx);
+
+ static void registerVectorClockOnServiceContext(ServiceContext* service,
+ VectorClockMutable* vectorClockMutable);
+
+ // Ticking
+ virtual LogicalTime tick(Component component, uint64_t nTicks) = 0;
+ virtual void tickTo(Component component, LogicalTime newTime) = 0;
+
+protected:
+ static bool _lessThanOrEqualToMaxPossibleTime(LogicalTime time, uint64_t nTicks);
+
+ VectorClockMutable();
+ virtual ~VectorClockMutable();
+
+ // Internal Ticking API
+ LogicalTime _advanceComponentTimeByTicks(Component component, uint64_t nTicks);
+ void _advanceComponentTimeTo(Component component, LogicalTime&& newTime);
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/vector_clock_trivial.cpp b/src/mongo/db/vector_clock_trivial.cpp
new file mode 100644
index 00000000000..88f00a512ea
--- /dev/null
+++ b/src/mongo/db/vector_clock_trivial.cpp
@@ -0,0 +1,100 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/vector_clock_mutable.h"
+
+namespace mongo {
+namespace {
+
+/**
+ * Vector clock implementation for non-distributed environments (embedded, some unittests).
+ */
+class VectorClockTrivial : public VectorClockMutable {
+ VectorClockTrivial(const VectorClockTrivial&) = delete;
+ VectorClockTrivial& operator=(const VectorClockTrivial&) = delete;
+
+public:
+ VectorClockTrivial();
+ virtual ~VectorClockTrivial();
+
+ LogicalTime tick(Component component, uint64_t nTicks) override;
+ 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;
+};
+
+const auto vectorClockTrivialDecoration = ServiceContext::declareDecoration<VectorClockTrivial>();
+
+ServiceContext::ConstructorActionRegisterer _registerer(
+ "VectorClockTrivial-VectorClockRegistration",
+ {},
+ [](ServiceContext* service) {
+ VectorClockTrivial::registerVectorClockOnServiceContext(
+ service, &vectorClockTrivialDecoration(service));
+ },
+ {});
+
+VectorClockTrivial::VectorClockTrivial() = default;
+
+VectorClockTrivial::~VectorClockTrivial() = default;
+
+void VectorClockTrivial::_gossipOutInternal(BSONObjBuilder* out) const {
+ // Clocks are not gossipped in trivial (non-distributed) environments.
+}
+
+void VectorClockTrivial::_gossipOutExternal(BSONObjBuilder* out) const {
+ // Clocks are not gossipped in trivial (non-distributed) environments.
+}
+
+VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInInternal(const BSONObj& in) {
+ // Clocks are not gossipped in trivial (non-distributed) environments.
+ return {};
+}
+
+VectorClock::LogicalTimeArray VectorClockTrivial::_gossipInExternal(const BSONObj& in) {
+ // Clocks are not gossipped in trivial (non-distributed) environments.
+ return {};
+}
+
+LogicalTime VectorClockTrivial::tick(Component component, uint64_t nTicks) {
+ return _advanceComponentTimeByTicks(component, nTicks);
+}
+
+void VectorClockTrivial::tickTo(Component component, LogicalTime newTime) {
+ _advanceComponentTimeTo(component, std::move(newTime));
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/embedded/SConscript b/src/mongo/embedded/SConscript
index 6938957ec7d..bcb3a7aaa8e 100644
--- a/src/mongo/embedded/SConscript
+++ b/src/mongo/embedded/SConscript
@@ -122,6 +122,7 @@ env.Library(
'$BUILD_DIR/mongo/db/storage/storage_init_d',
'$BUILD_DIR/mongo/db/storage/storage_options',
'$BUILD_DIR/mongo/db/wire_version',
+ '$BUILD_DIR/mongo/db/vector_clock_trivial',
'$BUILD_DIR/mongo/rpc/client_metadata',
'$BUILD_DIR/mongo/util/latch_analyzer' if get_option('use-diagnostic-latches') == 'on' else [],
'$BUILD_DIR/mongo/util/options_parser/options_parser',
diff --git a/src/mongo/embedded/stitch_support/SConscript b/src/mongo/embedded/stitch_support/SConscript
index 6252a77f889..0cfc0fbdc82 100644
--- a/src/mongo/embedded/stitch_support/SConscript
+++ b/src/mongo/embedded/stitch_support/SConscript
@@ -45,6 +45,7 @@ stitchSupportTargets = stitchSupportEnv.Library(
'$BUILD_DIR/mongo/db/ops/parsed_update',
'$BUILD_DIR/mongo/db/query/collation/collator_factory_icu',
'$BUILD_DIR/mongo/db/query/collation/collator_factory_interface',
+ '$BUILD_DIR/mongo/db/vector_clock_trivial',
],
AIB_COMPONENT='stitch-support',
)
diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp
index ab65025a725..9a546b02572 100644
--- a/src/mongo/rpc/metadata.cpp
+++ b/src/mongo/rpc/metadata.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/logical_clock.h"
#include "mongo/db/logical_time_validator.h"
+#include "mongo/db/vector_clock.h"
#include "mongo/rpc/metadata/client_metadata_ismaster.h"
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/impersonated_user_metadata.h"
@@ -106,6 +107,8 @@ void readRequestMetadata(OperationContext* opCtx, const BSONObj& metadataObj, bo
TrackingMetadata::get(opCtx) =
uassertStatusOK(TrackingMetadata::readFromMetadata(trackingElem));
+ VectorClock::get(opCtx)->gossipIn(metadataObj, opCtx->getClient()->getSessionTags());
+
auto logicalClock = LogicalClock::get(opCtx);
if (logicalClock && logicalClock->isEnabled()) {
auto logicalTimeMetadata =
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 579bacdc54d..f59f09d6aa6 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -96,6 +96,7 @@ env.Library(
'kill_sessions_remote.cpp',
's_read_write_concern_defaults_server_status.cpp',
'strategy.cpp',
+ 'vector_clock_mongos.cpp',
env.Idlc('cluster_multicast.idl')[0],
env.Idlc('kill_sessions_remote.idl')[0],
],
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 6b70018dfd8..d53bec20d32 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -62,6 +62,7 @@
#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/db/stats/counters.h"
#include "mongo/db/transaction_validation.h"
+#include "mongo/db/vector_clock.h"
#include "mongo/db/views/resolved_view.h"
#include "mongo/db/write_concern_options.h"
#include "mongo/logv2/log.h"
@@ -103,6 +104,8 @@ Status processCommandMetadata(OperationContext* opCtx, const BSONObj& cmdObj) {
ReadPreferenceSetting::get(opCtx) =
uassertStatusOK(ReadPreferenceSetting::fromContainingBSON(cmdObj));
+ VectorClock::get(opCtx)->gossipIn(cmdObj, opCtx->getClient()->getSessionTags());
+
auto logicalClock = LogicalClock::get(opCtx);
invariant(logicalClock);
@@ -134,6 +137,7 @@ Status processCommandMetadata(OperationContext* opCtx, const BSONObj& cmdObj) {
* Append required fields to command response.
*/
void appendRequiredFieldsToResponse(OperationContext* opCtx, BSONObjBuilder* responseBuilder) {
+ VectorClock::get(opCtx)->gossipOut(responseBuilder, opCtx->getClient()->getSessionTags());
auto validator = LogicalTimeValidator::get(opCtx);
if (validator->shouldGossipLogicalTime()) {
auto now = LogicalClock::get(opCtx)->getClusterTime();
diff --git a/src/mongo/s/commands/vector_clock_mongos.cpp b/src/mongo/s/commands/vector_clock_mongos.cpp
new file mode 100644
index 00000000000..a8d0b0dbc81
--- /dev/null
+++ b/src/mongo/s/commands/vector_clock_mongos.cpp
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/vector_clock.h"
+
+namespace mongo {
+namespace {
+
+/**
+ * Vector clock implementation for mongos.
+ */
+class VectorClockMongoS : public VectorClock {
+ VectorClockMongoS(const VectorClockMongoS&) = delete;
+ VectorClockMongoS& operator=(const VectorClockMongoS&) = delete;
+
+public:
+ VectorClockMongoS();
+ virtual ~VectorClockMongoS();
+
+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;
+};
+
+const auto vectorClockMongoSDecoration = ServiceContext::declareDecoration<VectorClockMongoS>();
+
+ServiceContext::ConstructorActionRegisterer _registerer(
+ "VectorClockMongoS-VectorClockRegistration",
+ {},
+ [](ServiceContext* service) {
+ VectorClockMongoS::registerVectorClockOnServiceContext(
+ service, &vectorClockMongoSDecoration(service));
+ },
+ {});
+
+VectorClockMongoS::VectorClockMongoS() = default;
+
+VectorClockMongoS::~VectorClockMongoS() = default;
+
+void VectorClockMongoS::_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);
+ _gossipOutComponent(out, now, Component::ConfigTime);
+}
+
+void VectorClockMongoS::_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);
+}
+
+VectorClock::LogicalTimeArray VectorClockMongoS::_gossipInInternal(const BSONObj& in) {
+ 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(in, &newTime, Component::ConfigTime);
+ return newTime;
+}
+
+VectorClock::LogicalTimeArray VectorClockMongoS::_gossipInExternal(const BSONObj& in) {
+ LogicalTimeArray newTime;
+ // TODO SERVER-47914: re-enable gossipping of VectorClock's ClusterTime once LogicalClock has
+ // been migrated into VectorClock.
+ // _gossipInComponent(in, &newTime, Component::ClusterTime);
+ return newTime;
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/s/grid.cpp b/src/mongo/s/grid.cpp
index dbc42a8e20e..9254504dfd9 100644
--- a/src/mongo/s/grid.cpp
+++ b/src/mongo/s/grid.cpp
@@ -124,7 +124,6 @@ repl::OpTime Grid::configOpTime() const {
boost::optional<repl::OpTime> Grid::advanceConfigOpTime(OperationContext* opCtx,
repl::OpTime opTime,
StringData what) {
- invariant(serverGlobalParams.clusterRole != ClusterRole::ConfigServer);
const auto prevOpTime = _advanceConfigOpTime(opTime);
if (prevOpTime && prevOpTime->getTerm() != opTime.getTerm()) {
std::string clientAddr = "(unknown)";
@@ -143,12 +142,8 @@ boost::optional<repl::OpTime> Grid::advanceConfigOpTime(OperationContext* opCtx,
return prevOpTime;
}
-boost::optional<repl::OpTime> Grid::advanceConfigOpTimeAuthoritative(repl::OpTime opTime) {
- invariant(serverGlobalParams.clusterRole == ClusterRole::ConfigServer);
- return _advanceConfigOpTime(opTime);
-}
-
boost::optional<repl::OpTime> Grid::_advanceConfigOpTime(const repl::OpTime& opTime) {
+ invariant(serverGlobalParams.clusterRole != ClusterRole::ConfigServer);
stdx::lock_guard<Latch> lk(_mutex);
if (_configOpTime < opTime) {
repl::OpTime prev = _configOpTime;
diff --git a/src/mongo/s/grid.h b/src/mongo/s/grid.h
index 861fa3f159c..0bd14b9c5cf 100644
--- a/src/mongo/s/grid.h
+++ b/src/mongo/s/grid.h
@@ -163,15 +163,6 @@ public:
StringData what);
/**
- * Called whenever a config server's majority committed optime changes.
- * If the config optime was updated, returns the previous value.
- * NOTE: This is ONLY valid to call on a config server instance.
- */
- // TODO SERVER-46200: remove this once the ConfigServerOpObserver is calling into the
- // VectorClock, rather than here.
- boost::optional<repl::OpTime> advanceConfigOpTimeAuthoritative(repl::OpTime opTime);
-
- /**
* Clears the grid object so that it can be reused between test executions. This will not
* be necessary if grid is hanging off the global ServiceContext and each test gets its
* own service context.