diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2020-02-03 09:11:19 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-02-14 10:54:32 +0000 |
commit | b6ef7212c4f1c263e9d997b606c9127601e023e3 (patch) | |
tree | 0516c4d3e89e5b4ee7788379804ba46b7340ac43 | |
parent | a7a4c477283d6cdddb5d16de6b976b71a7afaffe (diff) | |
download | mongo-b6ef7212c4f1c263e9d997b606c9127601e023e3.tar.gz |
SERVER-45120 Move 'localSetTime' out of the RWConcernDefault IDL
20 files changed, 373 insertions, 274 deletions
diff --git a/jstests/libs/read_write_concern_defaults_propagation.js b/jstests/libs/read_write_concern_defaults_propagation_common.js index c4b0cc31ba7..7fe8b2a364f 100644 --- a/jstests/libs/read_write_concern_defaults_propagation.js +++ b/jstests/libs/read_write_concern_defaults_propagation_common.js @@ -3,13 +3,13 @@ var ReadWriteConcernDefaultsPropagation = (function() { const kDefaultReadConcernField = "defaultReadConcern"; const kDefaultWriteConcernField = "defaultWriteConcern"; - const kEpochField = "epoch"; - const kSetTimeField = "setTime"; - const kLocalSetTimeField = "localSetTime"; + const kUpdateOpTimeField = "updateOpTime"; + const kUpdateWallClockTimeField = "updateWallClockTime"; + const kLocalUpdateWallClockTimeField = "localUpdateWallClockTime"; const kDefaultRWCFields = [kDefaultReadConcernField, kDefaultWriteConcernField]; - const kExtraSetFields = [kEpochField, kSetTimeField]; - const kExtraLocalFields = [kLocalSetTimeField]; + const kExtraSetFields = [kUpdateOpTimeField, kUpdateWallClockTimeField]; + const kExtraLocalFields = [kLocalUpdateWallClockTimeField]; const kExtraFields = [...kExtraSetFields, ...kExtraLocalFields]; const kSetFields = [...kDefaultRWCFields, ...kExtraSetFields]; @@ -70,8 +70,8 @@ var ReadWriteConcernDefaultsPropagation = (function() { assert.hasFields( newDefaultsRes, kExtraFields, "missing field in result of setDefaultRWConcern"); - // Check that epoch has increased. Since everything is running on one host, we can also - // check that each of setTime and localSetTime have increased. + // Check that updateOpTime has increased. Since everything is running on one host, we can + // also check that each of kUpdateOpTime and localUpdateWallClockTime have increased. kExtraFields.forEach(field => { if (field in initialSetConnDefaults) { assert.gt(newDefaultsRes[field], @@ -101,10 +101,11 @@ var ReadWriteConcernDefaultsPropagation = (function() { } } - // localSetTime reflects which the conn updated its cache. Since all the conns - // (including setConn) are running on a single host, we can check that this is - // later than when setDefaultRWConcern was run. - if (!(connDefault[kLocalSetTimeField] >= connDefault[kSetTimeField])) { + // localUpdateWallClockTime reflects which the conn updated its cache. Since + // all the conns (including setConn) are running on a single host, we can check + // that this is later than when setDefaultRWConcern was run. + if (!(connDefault[kLocalUpdateWallClockTimeField] >= + connDefault[kUpdateWallClockTimeField])) { return false; } } @@ -141,8 +142,8 @@ var ReadWriteConcernDefaultsPropagation = (function() { const defaultsRes = assert.commandWorked( checkConn.adminCommand({getDefaultRWConcern: 1, inMemory: true})); - // Note localSetTime is generated by the in-memory cache, so it will be present - // even if there are no defaults + // Note localUpdateWallClockTime is generated by the in-memory cache, so it will be + // present even if there are no defaults const unexpectedFields = kDefaultRWCFields.concat(kExtraSetFields); return unexpectedFields.every(field => !defaultsRes.hasOwnProperty(field)); }), diff --git a/jstests/noPassthrough/read_write_concern_defaults_metrics.js b/jstests/noPassthrough/read_write_concern_defaults_metrics.js index 47328ac3d64..2137fe44ddd 100644 --- a/jstests/noPassthrough/read_write_concern_defaults_metrics.js +++ b/jstests/noPassthrough/read_write_concern_defaults_metrics.js @@ -35,14 +35,15 @@ function verifyServerStatus(conn, if (expectNoDefaultsDocument) { // When defaults have never been set (or the defaults document was deleted) the response - // should only contain localSetTime. - assert.hasFields(defaultsRes, ["localSetTime"]); - assert.eq(undefined, defaultsRes.setTime, tojson(defaultsRes)); - assert.eq(undefined, defaultsRes.epoch, tojson(defaultsRes)); + // should only contain localUpdateWallClockTime. + assert.hasFields(defaultsRes, ["localUpdateWallClockTime"]); + assert.eq(undefined, defaultsRes.updateWallClockTime, tojson(defaultsRes)); + assert.eq(undefined, defaultsRes.updateOpTime, tojson(defaultsRes)); } else { // These fields are always set once a read or write concern has been set at least once and // the defaults document is still present. - assert.hasFields(defaultsRes, ["epoch", "setTime", "localSetTime"]); + assert.hasFields(defaultsRes, + ["updateOpTime", "updateWallClockTime", "localUpdateWallClockTime"]); } } @@ -83,7 +84,7 @@ function testServerStatus(conn) { // Wait for the cache to discover the defaults were deleted. Note the cache is invalidated // on delete on a mongod, so this is only to handle the mongos case. const res = conn.adminCommand({getDefaultRWConcern: 1, inMemory: true}); - return !res.hasOwnProperty("epoch"); + return !res.hasOwnProperty("updateOpTime"); }, "mongos failed to pick up deleted default rwc", undefined, 1000, {runHangAnalyzer: false}); verifyServerStatus(conn, {expectNoDefaultsDocument: true}); } diff --git a/jstests/replsets/read_write_concern_defaults_propagation.js b/jstests/replsets/read_write_concern_defaults_propagation.js index 559244c1319..5c96d1a4cf3 100644 --- a/jstests/replsets/read_write_concern_defaults_propagation.js +++ b/jstests/replsets/read_write_concern_defaults_propagation.js @@ -1,12 +1,11 @@ // Tests propagation of RWC defaults across a replica set. // // @tags: [requires_fcv_44] - -load("jstests/libs/read_write_concern_defaults_propagation.js"); - (function() { 'use strict'; +load("jstests/libs/read_write_concern_defaults_propagation_common.js"); + const rst = new ReplSetTest({nodes: 3}); rst.startSet(); rst.initiate(); diff --git a/jstests/sharding/read_write_concern_defaults_commands_api.js b/jstests/sharding/read_write_concern_defaults_commands_api.js index 5a8bf5e5e35..bfb9c8e6bb4 100644 --- a/jstests/sharding/read_write_concern_defaults_commands_api.js +++ b/jstests/sharding/read_write_concern_defaults_commands_api.js @@ -10,7 +10,7 @@ // generated by a getDefaultRWConcern command with inMemory=true. function verifyFields(res, {expectRC, expectWC, isPersistedDocument}) { // These fields are always set once a read or write concern has been set at least once. - let expectedFields = ["epoch", "setTime", "localSetTime"]; + let expectedFields = ["updateOpTime", "updateWallClockTime", "localUpdateWallClockTime"]; let unexpectedFields = ["inMemory"]; if (expectRC) { @@ -25,10 +25,11 @@ function verifyFields(res, {expectRC, expectWC, isPersistedDocument}) { unexpectedFields.push("defaultWriteConcern"); } - // localSetTime is generated by the in-memory cache and is not stored in the persisted document. + // localUpdateWallClockTime is generated by the in-memory cache and is not stored in the + // persisted document. if (isPersistedDocument) { - expectedFields = expectedFields.filter(field => field !== "localSetTime"); - unexpectedFields.push("localSetTime"); + expectedFields = expectedFields.filter(field => field !== "localUpdateWallClockTime"); + unexpectedFields.push("localUpdateWallClockTime"); } assert.hasFields(res, expectedFields); @@ -122,8 +123,8 @@ function verifyDefaultState(conn) { const inMemoryRes = assert.commandWorked(conn.adminCommand({getDefaultRWConcern: 1, inMemory: true})); - // localSetTime is set when a node refreshes its defaults, even if none are found. - const expectedFields = ["localSetTime"]; + // localUpdateWallClockTime is set when a node refreshes its defaults, even if none are found. + const expectedFields = ["localUpdateWallClockTime"]; expectedFields.forEach(field => { assert(res.hasOwnProperty(field), `response did not have field '${field}', res: ${tojson(res)}`); @@ -133,7 +134,8 @@ function verifyDefaultState(conn) { assert.eq(inMemoryRes.inMemory, true, tojson(inMemoryRes)); // No other fields should be returned if neither a default read nor write concern has been set. - const unexpectedFields = ["defaultReadConcern", "defaultWriteConcern", "epoch", "setTime"]; + const unexpectedFields = + ["defaultReadConcern", "defaultWriteConcern", "updateOpTime", "updateWallClockTime"]; unexpectedFields.forEach(field => { assert(!res.hasOwnProperty(field), `response unexpectedly had field '${field}', res: ${tojson(res)}`); diff --git a/jstests/sharding/read_write_concern_defaults_propagation.js b/jstests/sharding/read_write_concern_defaults_propagation.js index 6cc2f1e92d9..4d855a438bb 100644 --- a/jstests/sharding/read_write_concern_defaults_propagation.js +++ b/jstests/sharding/read_write_concern_defaults_propagation.js @@ -1,16 +1,14 @@ // Tests propagation of RWC defaults across a sharded cluster. // // @tags: [requires_fcv_44] - -load("jstests/libs/read_write_concern_defaults_propagation.js"); - (function() { 'use strict'; +load("jstests/libs/read_write_concern_defaults_propagation_common.js"); + var st = new ShardingTest({ shards: 1, mongos: 3, - config: 3, other: { rs: true, rs0: {nodes: 1}, diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 08e9800be1c..780a18011d4 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -374,7 +374,7 @@ env.Library( target='read_write_concern_defaults', source=[ 'read_write_concern_defaults.cpp', - env.Idlc('rw_concern_default.idl')[0], + env.Idlc('read_write_concern_defaults.idl')[0], ], LIBDEPS=[ 'repl/read_concern_args', diff --git a/src/mongo/db/auth/authorization_manager_global.cpp b/src/mongo/db/auth/authorization_manager_global.cpp index e549de2f404..af3342c1c5d 100644 --- a/src/mongo/db/auth/authorization_manager_global.cpp +++ b/src/mongo/db/auth/authorization_manager_global.cpp @@ -38,10 +38,23 @@ #include "mongo/util/assert_util.h" namespace mongo { +namespace { -// This setting is unique in that it is read-only. -// The IDL subststem doesn't actually allow for that, -// so we'll pretend it's startup-settable, then override it here. +ServiceContext::ConstructorActionRegisterer createAuthorizationManager( + "CreateAuthorizationManager", + {"OIDGeneration", "EndStartupOptionStorage"}, + [](ServiceContext* service) { + auto authzManager = AuthorizationManager::create(); + authzManager->setAuthEnabled(serverGlobalParams.authState == + ServerGlobalParams::AuthState::kEnabled); + authzManager->setShouldValidateAuthSchemaOnStartup(gStartupAuthSchemaValidation); + AuthorizationManager::set(service, std::move(authzManager)); + }); + +} // namespace + +// This setting is unique in that it is read-only. The IDL subststem doesn't actually allow for +// that, so we'll pretend it's startup-settable, then override it here. AuthzVersionParameter::AuthzVersionParameter(StringData name, ServerParameterType) : ServerParameter(ServerParameterSet::getGlobal(), name, false, false) {} @@ -58,15 +71,4 @@ Status AuthzVersionParameter::setFromString(const std::string& newValueString) { return {ErrorCodes::InternalError, "set called on unsettable server parameter"}; } -ServiceContext::ConstructorActionRegisterer createAuthorizationManager( - "CreateAuthorizationManager", - {"OIDGeneration", "EndStartupOptionStorage"}, - [](ServiceContext* service) { - auto authzManager = AuthorizationManager::create(); - authzManager->setAuthEnabled(serverGlobalParams.authState == - ServerGlobalParams::AuthState::kEnabled); - authzManager->setShouldValidateAuthSchemaOnStartup(gStartupAuthSchemaValidation); - AuthorizationManager::set(service, std::move(authzManager)); - }); - } // namespace mongo diff --git a/src/mongo/db/commands/read_write_concern_defaults_server_status.cpp b/src/mongo/db/commands/read_write_concern_defaults_server_status.cpp index 6f2cfc6f8b5..16f3d6d0235 100644 --- a/src/mongo/db/commands/read_write_concern_defaults_server_status.cpp +++ b/src/mongo/db/commands/read_write_concern_defaults_server_status.cpp @@ -29,6 +29,7 @@ #include "mongo/platform/basic.h" +#include "mongo/db/commands/rwc_defaults_commands_gen.h" #include "mongo/db/commands/server_status.h" #include "mongo/db/read_write_concern_defaults.h" #include "mongo/db/repl/replication_coordinator.h" @@ -51,7 +52,11 @@ public: return {}; } - return ReadWriteConcernDefaults::get(opCtx).getDefault(opCtx).toBSON(); + auto rwcDefault = ReadWriteConcernDefaults::get(opCtx).getDefault(opCtx); + GetDefaultRWConcernResponse response; + response.setRWConcernDefault(rwcDefault); + response.setLocalUpdateWallClockTime(rwcDefault.localUpdateWallClockTime()); + return response.toBSON(); } } defaultRWConcernServerStatus; diff --git a/src/mongo/db/commands/rwc_defaults_commands.cpp b/src/mongo/db/commands/rwc_defaults_commands.cpp index 2352c1650d7..0cbf5636a09 100644 --- a/src/mongo/db/commands/rwc_defaults_commands.cpp +++ b/src/mongo/db/commands/rwc_defaults_commands.cpp @@ -40,7 +40,6 @@ #include "mongo/db/read_write_concern_defaults.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/repl/replication_coordinator.h" -#include "mongo/db/rw_concern_default_gen.h" #include "mongo/logv2/log.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/util/log.h" @@ -82,6 +81,17 @@ void assertNotStandaloneOrShardServer(OperationContext* opCtx, StringData cmdNam serverGlobalParams.clusterRole != ClusterRole::ShardServer); } +auto makeResponse(const ReadWriteConcernDefaults::RWConcernDefaultAndTime& rwcDefault, + bool inMemory) { + GetDefaultRWConcernResponse response; + response.setRWConcernDefault(rwcDefault); + response.setLocalUpdateWallClockTime(rwcDefault.localUpdateWallClockTime()); + if (inMemory) + response.setInMemory(true); + + return response; +} + class SetDefaultRWConcernCommand : public TypedCommand<SetDefaultRWConcernCommand> { public: AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { @@ -121,7 +131,7 @@ public: // Refresh to populate the cache with the latest defaults. rwcDefaults.refreshIfNecessary(opCtx); - return rwcDefaults.getDefault(opCtx); + return makeResponse(rwcDefaults.getDefault(opCtx), false); } private: @@ -166,15 +176,14 @@ public: assertNotStandaloneOrShardServer(opCtx, GetDefaultRWConcern::kCommandName); auto& rwcDefaults = ReadWriteConcernDefaults::get(opCtx->getServiceContext()); - if (request().getInMemory() && *request().getInMemory()) { - auto rwc = rwcDefaults.getDefault(opCtx); - rwc.setInMemory(true); - return rwc; + const bool inMemory = request().getInMemory().value_or(false); + if (!inMemory) { + // If not asking for the in-memory values, force a refresh to find the most recent + // defaults + rwcDefaults.refreshIfNecessary(opCtx); } - // Force a refresh to find the most recent defaults, then return them. - rwcDefaults.refreshIfNecessary(opCtx); - return rwcDefaults.getDefault(opCtx); + return makeResponse(rwcDefaults.getDefault(opCtx), inMemory); } private: @@ -195,5 +204,6 @@ public: } }; } getDefaultRWConcernCommand; + } // namespace } // namespace mongo diff --git a/src/mongo/db/commands/rwc_defaults_commands.idl b/src/mongo/db/commands/rwc_defaults_commands.idl index ad8efef676f..6d0f8d98c32 100644 --- a/src/mongo/db/commands/rwc_defaults_commands.idl +++ b/src/mongo/db/commands/rwc_defaults_commands.idl @@ -32,11 +32,31 @@ global: imports: - "mongo/idl/basic_types.idl" - "mongo/db/repl/read_concern_args.idl" + - "mongo/db/read_write_concern_defaults.idl" - "mongo/db/write_concern_options.idl" +structs: + GetDefaultRWConcernResponse: + description: "Describes the response for the getDefaultRWConcern command" + strict: false + chained_structs: + RWConcernDefault: RWConcernDefault + fields: + inMemory: + description: "Whether the value came from the in-memory cache or it reflects the + defaults which were persisted as of the time of invocation." + optional: true + type: bool + localUpdateWallClockTime: + description: "The wallclock time of when the default read or write concern was + applied to the cache of the node, which returned the response. This + value is only informational and must not be used for any recency + comparisons." + type: date + commands: setDefaultRWConcern: - description: "set the current read/write concern defaults (cluster-wide)" + description: "Set the current read/write concern defaults (cluster-wide)" namespace: ignored fields: defaultReadConcern: @@ -49,7 +69,7 @@ commands: optional: true getDefaultRWConcern: - description: "get the current read/write concern defaults being applied by this node" + description: "Get the current read/write concern defaults being applied by this node" namespace: ignored fields: inMemory: diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp index 26de717a90a..22f880514e1 100644 --- a/src/mongo/db/op_observer_impl.cpp +++ b/src/mongo/db/op_observer_impl.cpp @@ -53,7 +53,6 @@ #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/oplog_entry_gen.h" #include "mongo/db/repl/replication_coordinator.h" -#include "mongo/db/rw_concern_default_gen.h" #include "mongo/db/s/collection_sharding_state.h" #include "mongo/db/server_options.h" #include "mongo/db/session_catalog_mongod.h" diff --git a/src/mongo/db/op_observer_impl_test.cpp b/src/mongo/db/op_observer_impl_test.cpp index 06b18144353..79270c9b5cd 100644 --- a/src/mongo/db/op_observer_impl_test.cpp +++ b/src/mongo/db/op_observer_impl_test.cpp @@ -2306,25 +2306,25 @@ TEST_F(OpObserverTest, OnRollbackInvalidatesDefaultRWConcernCache) { // Put initial defaults in the cache. { RWConcernDefault origDefaults; - origDefaults.setEpoch(Timestamp(10, 20)); - origDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + origDefaults.setUpdateOpTime(Timestamp(10, 20)); + origDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(origDefaults)); } auto origCachedDefaults = rwcDefaults.getDefault(opCtx.get()); - ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getUpdateWallClockTime()); // Change the mock's defaults, but don't invalidate the cache yet. The cache should still return // the original defaults. { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(50, 20)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults.setUpdateOpTime(Timestamp(50, 20)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto cachedDefaults = rwcDefaults.getDefault(opCtx.get()); - ASSERT_EQ(Timestamp(10, 20), *cachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *cachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(10, 20), *cachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *cachedDefaults.getUpdateWallClockTime()); } // Rollback to a timestamp should invalidate the cache and getting the defaults should now @@ -2335,8 +2335,8 @@ TEST_F(OpObserverTest, OnRollbackInvalidatesDefaultRWConcernCache) { opObserver.onReplicationRollback(opCtx.get(), rbInfo); } auto newCachedDefaults = rwcDefaults.getDefault(opCtx.get()); - ASSERT_EQ(Timestamp(50, 20), *newCachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(5678), *newCachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(50, 20), *newCachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(5678), *newCachedDefaults.getUpdateWallClockTime()); } } // namespace diff --git a/src/mongo/db/read_write_concern_defaults.cpp b/src/mongo/db/read_write_concern_defaults.cpp index 4f4113e1db8..020d094cd72 100644 --- a/src/mongo/db/read_write_concern_defaults.cpp +++ b/src/mongo/db/read_write_concern_defaults.cpp @@ -43,38 +43,6 @@ namespace { static constexpr auto kReadConcernLevelsDisallowedAsDefault = { repl::ReadConcernLevel::kSnapshotReadConcern, repl::ReadConcernLevel::kLinearizableReadConcern}; -/** - * Used to invalidate the cache on updates to the persisted defaults document. - */ -class OnUpdateCommitHandler final : public RecoveryUnit::Change { -public: - // rwcDefaults must outlive instantiations of this class. - OnUpdateCommitHandler(ServiceContext* service, - ReadWriteConcernDefaults* rwcDefaults, - const boost::optional<BSONObj>& newDefaultsDoc) - : _service(service), _rwcDefaults(rwcDefaults) { - // Note this will throw if the document can't be parsed. In the case of a delete, there will - // be no new defaults document and the RWConcern will be default constructed, which matches - // the behavior when lookup discovers a non-existent defaults document. - _rwc = newDefaultsDoc - ? RWConcernDefault::parse(IDLParserErrorContext("RWDefaultsWriteObserver"), - *newDefaultsDoc) - : RWConcernDefault(); - } - - void commit(boost::optional<Timestamp> timestamp) final { - _rwc.setLocalSetTime(_service->getFastClockSource()->now()); - _rwcDefaults->setDefault(std::move(_rwc)); - } - - void rollback() final {} - -private: - ServiceContext* _service; - ReadWriteConcernDefaults* _rwcDefaults; - RWConcernDefault _rwc; -}; - const auto getReadWriteConcernDefaults = ServiceContext::declareDecoration<std::unique_ptr<ReadWriteConcernDefaults>>(); @@ -134,10 +102,10 @@ RWConcernDefault ReadWriteConcernDefaults::generateNewConcerns( checkSuitabilityAsDefault(*wc); rwc.setDefaultWriteConcern(wc); } - auto epoch = LogicalClock::get(opCtx->getServiceContext())->getClusterTime().asTimestamp(); - rwc.setEpoch(epoch); - auto now = opCtx->getServiceContext()->getFastClockSource()->now(); - rwc.setSetTime(now); + + auto* const serviceContext = opCtx->getServiceContext(); + rwc.setUpdateOpTime(LogicalClock::get(serviceContext)->getClusterTime().asTimestamp()); + rwc.setUpdateWallClockTime(serviceContext->getFastClockSource()->now()); auto current = _getDefault(opCtx); if (!rc && current) { @@ -158,16 +126,28 @@ void ReadWriteConcernDefaults::observeDirectWriteToConfigSettings(OperationConte return; } - opCtx->recoveryUnit()->registerChange( - std::make_unique<OnUpdateCommitHandler>(opCtx->getServiceContext(), this, newDoc)); + // Note this will throw if the document can't be parsed. In the case of a delete, there will be + // no new defaults document and the RWConcern will be default constructed, which matches the + // behavior when lookup discovers a non-existent defaults document. + auto newDefaultsDoc = newDoc + ? RWConcernDefault::parse(IDLParserErrorContext("RWDefaultsWriteObserver"), + newDoc->getOwned()) + : RWConcernDefault(); + + opCtx->recoveryUnit()->onCommit([this, opCtx, newDefaultsDoc = std::move(newDefaultsDoc)]( + boost::optional<Timestamp> unusedCommitTime) mutable { + setDefault(opCtx, std::move(newDefaultsDoc)); + }); } void ReadWriteConcernDefaults::invalidate() { _defaults.invalidate(Type::kReadWriteConcernEntry); } -void ReadWriteConcernDefaults::setDefault(RWConcernDefault&& rwc) { - _defaults.insertOrAssignAndGet(Type::kReadWriteConcernEntry, std::move(rwc)); +void ReadWriteConcernDefaults::setDefault(OperationContext* opCtx, RWConcernDefault&& rwc) { + _defaults.insertOrAssignAndGet(Type::kReadWriteConcernEntry, + std::move(rwc), + opCtx->getServiceContext()->getFastClockSource()->now()); } void ReadWriteConcernDefaults::refreshIfNecessary(OperationContext* opCtx) { @@ -177,18 +157,19 @@ void ReadWriteConcernDefaults::refreshIfNecessary(OperationContext* opCtx) { } auto currentDefaultsHandle = _defaults.acquire(opCtx, Type::kReadWriteConcernEntry); - if (!currentDefaultsHandle || !possibleNewDefaults->getEpoch() || - (possibleNewDefaults->getEpoch() > currentDefaultsHandle->getEpoch())) { + if (!currentDefaultsHandle || !possibleNewDefaults->getUpdateOpTime() || + (possibleNewDefaults->getUpdateOpTime() > currentDefaultsHandle->getUpdateOpTime())) { // Use the new defaults if they have a higher epoch, if there are no defaults in the cache, // or if the found defaults have no epoch, meaning there are no defaults in config.settings. LOGV2(20997, - "refreshed RWC defaults to {possibleNewDefaults}", - "possibleNewDefaults"_attr = possibleNewDefaults->toBSON()); - setDefault(std::move(*possibleNewDefaults)); + "refreshed RWC defaults to {newDefaults}", + "newDefaults"_attr = possibleNewDefaults->toBSON()); + setDefault(opCtx, std::move(*possibleNewDefaults)); } } -boost::optional<RWConcernDefault> ReadWriteConcernDefaults::_getDefault(OperationContext* opCtx) { +boost::optional<ReadWriteConcernDefaults::RWConcernDefaultAndTime> +ReadWriteConcernDefaults::_getDefault(OperationContext* opCtx) { auto defaultsHandle = _defaults.acquire(opCtx, Type::kReadWriteConcernEntry); if (defaultsHandle) { // Since CWRWC is ok with continuing to use a value well after it has been invalidated @@ -196,7 +177,7 @@ boost::optional<RWConcernDefault> ReadWriteConcernDefaults::_getDefault(Operatio // defaultsValue.isValid() here, and we don't need to return the Handle, since callers don't // need to check defaultsValue.isValid() later, either. Just dereference it to get the // underlying contents. - return *defaultsHandle; + return RWConcernDefaultAndTime(*defaultsHandle, defaultsHandle.updateWallClockTime()); } return boost::none; } @@ -213,10 +194,6 @@ ReadWriteConcernDefaults::getDefaultWriteConcern(OperationContext* opCtx) { return current.getDefaultWriteConcern(); } -RWConcernDefault ReadWriteConcernDefaults::getDefault(OperationContext* opCtx) { - return _getDefault(opCtx).value_or(RWConcernDefault()); -} - ReadWriteConcernDefaults& ReadWriteConcernDefaults::get(ServiceContext* service) { return *getReadWriteConcernDefaults(service); } @@ -242,11 +219,7 @@ ReadWriteConcernDefaults::Cache::Cache(LookupFn lookupFn) boost::optional<RWConcernDefault> ReadWriteConcernDefaults::Cache::lookup( OperationContext* opCtx, const ReadWriteConcernDefaults::Type& key) { invariant(key == Type::kReadWriteConcernEntry); - auto newDefaults = _lookupFn(opCtx, key); - if (newDefaults) { - newDefaults->setLocalSetTime(opCtx->getServiceContext()->getFastClockSource()->now()); - } - return newDefaults; + return _lookupFn(opCtx, key); } } // namespace mongo diff --git a/src/mongo/db/read_write_concern_defaults.h b/src/mongo/db/read_write_concern_defaults.h index 1f10af61999..d493fdf2cc3 100644 --- a/src/mongo/db/read_write_concern_defaults.h +++ b/src/mongo/db/read_write_concern_defaults.h @@ -32,8 +32,8 @@ #include <map> #include "mongo/db/operation_context.h" +#include "mongo/db/read_write_concern_defaults_gen.h" #include "mongo/db/repl/read_concern_args.h" -#include "mongo/db/rw_concern_default_gen.h" #include "mongo/db/service_context.h" #include "mongo/db/write_concern_options.h" #include "mongo/platform/mutex.h" @@ -75,7 +75,28 @@ public: boost::optional<ReadConcern> getDefaultReadConcern(OperationContext* opCtx); boost::optional<WriteConcern> getDefaultWriteConcern(OperationContext* opCtx); - RWConcernDefault getDefault(OperationContext* opCtx); + class RWConcernDefaultAndTime : public RWConcernDefault { + public: + RWConcernDefaultAndTime() = default; + RWConcernDefaultAndTime(RWConcernDefault rwcd, Date_t localUpdateWallClockTime) + : RWConcernDefault(std::move(rwcd)), + _localUpdateWallClockTime(localUpdateWallClockTime) {} + + Date_t localUpdateWallClockTime() const { + return _localUpdateWallClockTime; + } + + private: + Date_t _localUpdateWallClockTime; + }; + + /** + * Returns the current set of read/write concern defaults along with the wallclock time when + * they were cached (for diagnostic purposes). + */ + RWConcernDefaultAndTime getDefault(OperationContext* opCtx) { + return _getDefault(opCtx).value_or(RWConcernDefaultAndTime()); + } /** * Returns true if the RC level is permissible to use as a default, and false if it cannot be a @@ -128,12 +149,12 @@ public: /** * Sets the given read write concern as the defaults in the cache. */ - void setDefault(RWConcernDefault&& rwc); + void setDefault(OperationContext* opCtx, RWConcernDefault&& rwc); private: enum class Type { kReadWriteConcernEntry }; - boost::optional<RWConcernDefault> _getDefault(OperationContext* opCtx); + boost::optional<RWConcernDefaultAndTime> _getDefault(OperationContext* opCtx); class Cache : public ReadThroughCache<Type, RWConcernDefault> { Cache(const Cache&) = delete; diff --git a/src/mongo/db/rw_concern_default.idl b/src/mongo/db/read_write_concern_defaults.idl index 5ff0b997b2a..bdf67c6fca5 100644 --- a/src/mongo/db/rw_concern_default.idl +++ b/src/mongo/db/read_write_concern_defaults.idl @@ -28,13 +28,11 @@ global: cpp_namespace: "mongo" - imports: - "mongo/idl/basic_types.idl" - "mongo/db/repl/read_concern_args.idl" - "mongo/db/write_concern_options.idl" - structs: RWConcernDefault: description: "Represents a set of read/write concern defaults, and associated metadata" @@ -48,19 +46,15 @@ structs: description: "The default write concern" type: WriteConcern optional: true - epoch: - description: "The epoch (unique generation id) of when the default read or write concern was last set" + updateOpTime: + description: "The optime of when the default read or write concern was last set. On + replica sets it advances with the primary's optime and on clusters it + advances with the config server primary's optime." type: timestamp optional: true - setTime: - description: "The wallclock time when the default read or write concern was last set by an administrator" + updateWallClockTime: + description: "The wallclock time when the default read or write concern was last set + by an administrator. This value is only informational and must not be + used for any recency comparisons." type: date optional: true - localSetTime: - description: "The wallclock time when this node updated its understanding of the default read or write concern" - type: date - optional: true - inMemory: - description: "Whether these defaults were from the inMemory cache" - type: bool - optional: true diff --git a/src/mongo/db/read_write_concern_defaults_test.cpp b/src/mongo/db/read_write_concern_defaults_test.cpp index 5b726994d1f..69c37853665 100644 --- a/src/mongo/db/read_write_concern_defaults_test.cpp +++ b/src/mongo/db/read_write_concern_defaults_test.cpp @@ -64,9 +64,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithAbsentDefaults) { auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT(!defaults.getEpoch()); - ASSERT(!defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT(!defaults.getUpdateOpTime()); + ASSERT(!defaults.getUpdateWallClockTime()); + ASSERT_EQ(Date_t(), defaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithDefaultsNeverSet) { @@ -75,23 +75,23 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithDefaultsNeverSet) { auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT(!defaults.getEpoch()); - ASSERT(!defaults.getSetTime()); - ASSERT(defaults.getLocalSetTime()); + ASSERT(!defaults.getUpdateOpTime()); + ASSERT(!defaults.getUpdateWallClockTime()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithUnsetDefaults) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetDefaults) { @@ -101,17 +101,17 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithSetDefaults) { WriteConcernOptions wc; wc.wNumNodes = 4; newDefaults.setDefaultWriteConcern(wc); - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); ASSERT(defaults.getDefaultReadConcern()->getLevel() == repl::ReadConcernLevel::kLocalReadConcern); ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultLookupFailure) { @@ -121,16 +121,16 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultLookupFailure) { TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallLookup) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); RWConcernDefault newDefaults2; newDefaults2.setDefaultReadConcern( @@ -138,30 +138,30 @@ TEST_F(ReadWriteConcernDefaultsTest, TestGetDefaultWithoutInvalidateDoesNotCallL WriteConcernOptions wc; wc.wNumNodes = 4; newDefaults2.setDefaultWriteConcern(wc); - newDefaults2.setEpoch(Timestamp(3, 4)); - newDefaults2.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults2.setUpdateOpTime(Timestamp(3, 4)); + newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults2)); auto defaults2 = _rwcd.getDefault(_opCtx); ASSERT(!defaults2.getDefaultReadConcern()); ASSERT(!defaults2.getDefaultWriteConcern()); - ASSERT_EQ(Timestamp(1, 2), *defaults2.getEpoch()); - ASSERT_EQ(1234, defaults2.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults2.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults2.getUpdateOpTime()); + ASSERT_EQ(1234, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults2.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); RWConcernDefault newDefaults2; newDefaults2.setDefaultReadConcern( @@ -169,8 +169,8 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) { WriteConcernOptions wc; wc.wNumNodes = 4; newDefaults2.setDefaultWriteConcern(wc); - newDefaults2.setEpoch(Timestamp(3, 4)); - newDefaults2.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults2.setUpdateOpTime(Timestamp(3, 4)); + newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults2)); _rwcd.invalidate(); @@ -178,9 +178,9 @@ TEST_F(ReadWriteConcernDefaultsTest, TestInvalidate) { ASSERT(defaults2.getDefaultReadConcern()->getLevel() == repl::ReadConcernLevel::kLocalReadConcern); ASSERT_EQ(4, defaults2.getDefaultWriteConcern()->wNumNodes); - ASSERT_EQ(Timestamp(3, 4), *defaults2.getEpoch()); - ASSERT_EQ(5678, defaults2.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults2.getLocalSetTime()); + ASSERT_EQ(Timestamp(3, 4), *defaults2.getUpdateOpTime()); + ASSERT_EQ(5678, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults2.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithEmptyCacheAndAbsentDefaults) { @@ -189,67 +189,67 @@ TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithEmptyCacheAndAbsentD auto defaults = _rwcd.getDefault(_opCtx); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT(!defaults.getEpoch()); - ASSERT(!defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT(!defaults.getUpdateOpTime()); + ASSERT(!defaults.getUpdateWallClockTime()); + ASSERT_EQ(Date_t(), defaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithEmptyCacheAndSetDefaults) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); _rwcd.refreshIfNecessary(_opCtx); auto defaults = _rwcd.getDefault(_opCtx); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); - ASSERT(defaults.getLocalSetTime()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); + ASSERT_GT(defaults.localUpdateWallClockTime(), Date_t()); } TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithHigherEpoch) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(1, 2)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(1, 2)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); - ASSERT_EQ(Timestamp(1, 2), *defaults.getEpoch()); - ASSERT_EQ(1234, defaults.getSetTime()->toMillisSinceEpoch()); + ASSERT_EQ(Timestamp(1, 2), *defaults.getUpdateOpTime()); + ASSERT_EQ(1234, defaults.getUpdateWallClockTime()->toMillisSinceEpoch()); RWConcernDefault newDefaults2; - newDefaults2.setEpoch(Timestamp(3, 4)); - newDefaults2.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults2.setUpdateOpTime(Timestamp(3, 4)); + newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults2)); _rwcd.refreshIfNecessary(_opCtx); auto defaults2 = _rwcd.getDefault(_opCtx); - ASSERT_EQ(Timestamp(3, 4), *defaults2.getEpoch()); - ASSERT_EQ(5678, defaults2.getSetTime()->toMillisSinceEpoch()); + ASSERT_EQ(Timestamp(3, 4), *defaults2.getUpdateOpTime()); + ASSERT_EQ(5678, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch()); } TEST_F(ReadWriteConcernDefaultsTest, TestRefreshDefaultsWithLowerEpoch) { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(10, 20)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + newDefaults.setUpdateOpTime(Timestamp(10, 20)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto defaults = _rwcd.getDefault(_opCtx); - ASSERT(defaults.getEpoch()); - ASSERT(defaults.getSetTime()); + ASSERT(defaults.getUpdateOpTime()); + ASSERT(defaults.getUpdateWallClockTime()); RWConcernDefault newDefaults2; - newDefaults2.setEpoch(Timestamp(5, 6)); - newDefaults2.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults2.setUpdateOpTime(Timestamp(5, 6)); + newDefaults2.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults2)); _rwcd.refreshIfNecessary(_opCtx); auto defaults2 = _rwcd.getDefault(_opCtx); - ASSERT_EQ(Timestamp(10, 20), *defaults2.getEpoch()); - ASSERT_EQ(1234, defaults2.getSetTime()->toMillisSinceEpoch()); + ASSERT_EQ(Timestamp(10, 20), *defaults2.getUpdateOpTime()); + ASSERT_EQ(1234, defaults2.getUpdateWallClockTime()->toMillisSinceEpoch()); } /** @@ -271,9 +271,8 @@ protected: ASSERT(defaults.getDefaultReadConcern()->getLevel() == repl::ReadConcernLevel::kLocalReadConcern); ASSERT_EQ(4, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT(defaults.getEpoch()); - ASSERT(defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT(defaults.getUpdateOpTime()); + ASSERT(defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); auto oldDefaults = _rwcd.getDefault(operationContext()); @@ -361,14 +360,13 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT(defaults.getDefaultReadConcern()->getLevel() == repl::ReadConcernLevel::kMajorityReadConcern); ASSERT_EQ(5, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, @@ -382,14 +380,13 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, repl::ReadConcernLevel::kMajorityReadConcern); ASSERT_EQ(oldDefaults.getDefaultWriteConcern()->wNumNodes, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, @@ -402,14 +399,13 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT(oldDefaults.getDefaultReadConcern()->getLevel() == defaults.getDefaultReadConcern()->getLevel()); ASSERT_EQ(5, defaults.getDefaultWriteConcern()->wNumNodes); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, @@ -419,14 +415,13 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, operationContext(), repl::ReadConcernArgs(), WriteConcernOptions()); ASSERT(!defaults.getDefaultReadConcern()); ASSERT(!defaults.getDefaultWriteConcern()); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, @@ -441,14 +436,13 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT_EQ(1, defaults.getDefaultWriteConcern()->wNumNodes); ASSERT_EQ(0, defaults.getDefaultWriteConcern()->wTimeout); ASSERT(WriteConcernOptions::SyncMode::JOURNAL == defaults.getDefaultWriteConcern()->syncMode); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, @@ -463,25 +457,24 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, ASSERT_EQ(1, defaults.getDefaultWriteConcern()->wNumNodes); ASSERT_EQ(12345, defaults.getDefaultWriteConcern()->wTimeout); ASSERT(WriteConcernOptions::SyncMode::UNSET == defaults.getDefaultWriteConcern()->syncMode); - ASSERT_LT(*oldDefaults.getEpoch(), *defaults.getEpoch()); - ASSERT_LT(*oldDefaults.getSetTime(), *defaults.getSetTime()); - ASSERT(!defaults.getLocalSetTime()); + ASSERT_LT(*oldDefaults.getUpdateOpTime(), *defaults.getUpdateOpTime()); + ASSERT_LT(*oldDefaults.getUpdateWallClockTime(), *defaults.getUpdateWallClockTime()); _lookupMock.setLookupCallReturnValue(std::move(defaults)); _rwcd.refreshIfNecessary(operationContext()); auto newDefaults = _rwcd.getDefault(operationContext()); - ASSERT_LT(*oldDefaults.getLocalSetTime(), *newDefaults.getLocalSetTime()); + ASSERT_LT(oldDefaults.localUpdateWallClockTime(), newDefaults.localUpdateWallClockTime()); } TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, TestRefreshDefaultsWithDeletedDefaults) { RWConcernDefault origDefaults; - origDefaults.setEpoch(Timestamp(10, 20)); - origDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + origDefaults.setUpdateOpTime(Timestamp(10, 20)); + origDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(origDefaults)); auto origCachedDefaults = _rwcd.getDefault(operationContext()); - ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getUpdateWallClockTime()); getClock()->reserveTicks(1); getMockClockSource()->advance(Milliseconds(1)); @@ -492,9 +485,10 @@ TEST_F(ReadWriteConcernDefaultsTestWithClusterTime, TestRefreshDefaultsWithDelet // The cache should now contain default constructed defaults. auto newCachedDefaults = _rwcd.getDefault(operationContext()); - ASSERT(!newCachedDefaults.getEpoch()); - ASSERT(!newCachedDefaults.getSetTime()); - ASSERT_LT(*origCachedDefaults.getLocalSetTime(), *newCachedDefaults.getLocalSetTime()); + ASSERT(!newCachedDefaults.getUpdateOpTime()); + ASSERT(!newCachedDefaults.getUpdateWallClockTime()); + ASSERT_LT(origCachedDefaults.localUpdateWallClockTime(), + newCachedDefaults.localUpdateWallClockTime()); } } // namespace diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index ac288a13df4..b9a78ef8ed3 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -63,9 +63,9 @@ #include "mongo/unittest/unittest.h" #include "mongo/util/net/hostandport.h" +namespace mongo { namespace { -using namespace mongo; using namespace mongo::repl; using namespace mongo::repl::rollback_internal; @@ -2918,25 +2918,25 @@ TEST_F(RSRollbackTest, RollbackInvalidatesDefaultRWConcernCache) { // Put initial defaults in the cache. { RWConcernDefault origDefaults; - origDefaults.setEpoch(Timestamp(10, 20)); - origDefaults.setSetTime(Date_t::fromMillisSinceEpoch(1234)); + origDefaults.setUpdateOpTime(Timestamp(10, 20)); + origDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(1234)); _lookupMock.setLookupCallReturnValue(std::move(origDefaults)); } auto origCachedDefaults = rwcDefaults.getDefault(_opCtx.get()); - ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(10, 20), *origCachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *origCachedDefaults.getUpdateWallClockTime()); // Change the mock's defaults, but don't invalidate the cache yet. The cache should still return // the original defaults. { RWConcernDefault newDefaults; - newDefaults.setEpoch(Timestamp(50, 20)); - newDefaults.setSetTime(Date_t::fromMillisSinceEpoch(5678)); + newDefaults.setUpdateOpTime(Timestamp(50, 20)); + newDefaults.setUpdateWallClockTime(Date_t::fromMillisSinceEpoch(5678)); _lookupMock.setLookupCallReturnValue(std::move(newDefaults)); auto cachedDefaults = rwcDefaults.getDefault(_opCtx.get()); - ASSERT_EQ(Timestamp(10, 20), *cachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *cachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(10, 20), *cachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(1234), *cachedDefaults.getUpdateWallClockTime()); } // Rollback via refetch should invalidate the cache and getting the defaults should now return @@ -2949,8 +2949,9 @@ TEST_F(RSRollbackTest, RollbackInvalidatesDefaultRWConcernCache) { _testRollbackDelete(_opCtx.get(), _coordinator, _replicationProcess.get(), coll->uuid(), doc); auto newCachedDefaults = rwcDefaults.getDefault(_opCtx.get()); - ASSERT_EQ(Timestamp(50, 20), *newCachedDefaults.getEpoch()); - ASSERT_EQ(Date_t::fromMillisSinceEpoch(5678), *newCachedDefaults.getSetTime()); + ASSERT_EQ(Timestamp(50, 20), *newCachedDefaults.getUpdateOpTime()); + ASSERT_EQ(Date_t::fromMillisSinceEpoch(5678), *newCachedDefaults.getUpdateWallClockTime()); } } // namespace +} // namespace mongo
\ No newline at end of file diff --git a/src/mongo/s/commands/cluster_rwc_defaults_commands.cpp b/src/mongo/s/commands/cluster_rwc_defaults_commands.cpp index 3587c6cda59..44ff531a3a4 100644 --- a/src/mongo/s/commands/cluster_rwc_defaults_commands.cpp +++ b/src/mongo/s/commands/cluster_rwc_defaults_commands.cpp @@ -36,7 +36,6 @@ #include "mongo/db/commands/rwc_defaults_commands_gen.h" #include "mongo/db/namespace_string.h" #include "mongo/db/read_write_concern_defaults.h" -#include "mongo/db/rw_concern_default_gen.h" #include "mongo/s/cluster_commands_helpers.h" #include "mongo/s/grid.h" #include "mongo/util/log.h" @@ -72,7 +71,7 @@ public: // Quickly pick up the new defaults by setting them in the cache. auto newDefaults = RWConcernDefault::parse( IDLParserErrorContext("ClusterSetDefaultRWConcern"), cmdResponse.response); - ReadWriteConcernDefaults::get(opCtx).setDefault(std::move(newDefaults)); + ReadWriteConcernDefaults::get(opCtx).setDefault(opCtx, std::move(newDefaults)); CommandHelpers::filterCommandReplyForPassthrough(cmdResponse.response, &result); return true; @@ -113,20 +112,23 @@ class ClusterGetDefaultRWConcernCommand final : public TypedCommand<ClusterGetDefaultRWConcernCommand> { public: using Request = GetDefaultRWConcern; - using Response = RWConcernDefault; class Invocation final : public InvocationBase { public: using InvocationBase::InvocationBase; - Response typedRun(OperationContext* opCtx) { - if (request().getInMemory() && *request().getInMemory()) { - auto rwc = - ReadWriteConcernDefaults::get(opCtx->getServiceContext()).getDefault(opCtx); - rwc.setInMemory(true); - return rwc; + GetDefaultRWConcernResponse typedRun(OperationContext* opCtx) { + auto& rwcDefaults = ReadWriteConcernDefaults::get(opCtx->getServiceContext()); + if (request().getInMemory().value_or(false)) { + const auto rwcDefault = rwcDefaults.getDefault(opCtx); + GetDefaultRWConcernResponse response; + response.setRWConcernDefault(rwcDefault); + response.setLocalUpdateWallClockTime(rwcDefault.localUpdateWallClockTime()); + response.setInMemory(true); + return response; } + // If not asking for the in-memory defaults, fetch them from the config server GetDefaultRWConcern configsvrRequest; configsvrRequest.setDbName(request().getDbName()); @@ -140,8 +142,8 @@ public: uassertStatusOK(cmdResponse.commandStatus); - return Response::parse(IDLParserErrorContext("ClusterGetDefaultRWConcernResponse"), - cmdResponse.response); + return GetDefaultRWConcernResponse::parse( + IDLParserErrorContext("ClusterGetDefaultRWConcernResponse"), cmdResponse.response); } private: diff --git a/src/mongo/s/commands/s_read_write_concern_defaults_server_status.cpp b/src/mongo/s/commands/s_read_write_concern_defaults_server_status.cpp index c7d9382bb43..4a012777bdc 100644 --- a/src/mongo/s/commands/s_read_write_concern_defaults_server_status.cpp +++ b/src/mongo/s/commands/s_read_write_concern_defaults_server_status.cpp @@ -29,6 +29,7 @@ #include "mongo/platform/basic.h" +#include "mongo/db/commands/rwc_defaults_commands_gen.h" #include "mongo/db/commands/server_status.h" #include "mongo/db/read_write_concern_defaults.h" @@ -45,7 +46,11 @@ public: BSONObj generateSection(OperationContext* opCtx, const BSONElement& configElement) const override { - return ReadWriteConcernDefaults::get(opCtx).getDefault(opCtx).toBSON(); + auto rwcDefault = ReadWriteConcernDefaults::get(opCtx).getDefault(opCtx); + GetDefaultRWConcernResponse response; + response.setRWConcernDefault(rwcDefault); + response.setLocalUpdateWallClockTime(rwcDefault.localUpdateWallClockTime()); + return response.toBSON(); } } defaultRWConcernServerStatus; diff --git a/src/mongo/util/read_through_cache.h b/src/mongo/util/read_through_cache.h index 83790bd9605..f13cd486e13 100644 --- a/src/mongo/util/read_through_cache.h +++ b/src/mongo/util/read_through_cache.h @@ -32,14 +32,13 @@ #include <boost/optional.hpp> #include "mongo/bson/oid.h" +#include "mongo/db/operation_context.h" #include "mongo/platform/mutex.h" #include "mongo/stdx/condition_variable.h" #include "mongo/util/invalidating_lru_cache.h" namespace mongo { -class OperationContext; - /** * Serves as a container of the non-templatised parts of the ReadThroughCache class below. */ @@ -214,12 +213,82 @@ protected: */ template <typename Key, typename Value> class ReadThroughCache : public ReadThroughCacheBase { + /** + * Data structure wrapping and expanding on the values stored in the cache. + */ + struct StoredValue { + Value value; + + // Contains the wallclock time of when the value was fetched from the backing storage. This + // value is not precise and should only be used for diagnostics purposes (i.e., it cannot be + // relied on to perform any recency comparisons for example). + Date_t updateWallClockTime; + }; + using Cache = InvalidatingLRUCache<Key, StoredValue>; + public: - using Cache = InvalidatingLRUCache<Key, Value>; - using ValueHandle = typename Cache::ValueHandle; using LookupFn = std::function<boost::optional<Value>(OperationContext*, const Key&)>; /** + * Common type for values returned from the cache. + */ + class ValueHandle { + public: + // The two constructors below are present in order to offset the fact that the cache doesn't + // support pinning items. Their only usage must be in the authorization mananager for the + // internal authentication user. + ValueHandle(Value&& value) : _valueHandle({std::move(value), Date_t::min()}) {} + ValueHandle() = default; + + operator bool() const { + return bool(_valueHandle); + } + + bool isValid() const { + return _valueHandle.isValid(); + } + + Value* get() { + return &_valueHandle->value; + } + + const Value* get() const { + return &_valueHandle->value; + } + + Value& operator*() { + return *get(); + } + + const Value& operator*() const { + return *get(); + } + + Value* operator->() { + return get(); + } + + const Value* operator->() const { + return get(); + } + + /** + * See the comments for `StoredValue::updateWallClockTime` above. + */ + Date_t updateWallClockTime() const { + return _valueHandle->updateWallClockTime; + } + + private: + friend class ReadThroughCache; + + ValueHandle(typename Cache::ValueHandle&& valueHandle) + : _valueHandle(std::move(valueHandle)) {} + + typename Cache::ValueHandle _valueHandle; + }; + + /** * If 'key' is found in the cache, returns a ValidHandle, otherwise invokes the blocking * 'lookup' method below to fetch the 'key' from the backing store. If the key is not found in * the backing store, returns a ValueHandle which defaults to not-set (it's bool operator is @@ -233,7 +302,7 @@ public: while (true) { auto cachedValue = _cache.get(key); if (cachedValue) - return cachedValue; + return ValueHandle(std::move(cachedValue)); // Otherwise make sure we have the locks we need and check whether and wait on another // thread is fetching into the cache @@ -244,7 +313,7 @@ public: } if (cachedValue) - return cachedValue; + return ValueHandle(std::move(cachedValue)); // If there's still no value in the cache, then we need to go and get it. Take the slow // path. @@ -252,14 +321,16 @@ public: auto value = lookup(opCtx, key); if (!value) - return cachedValue; + return ValueHandle(); // All this does is re-acquire the _cacheWriteMutex if we don't hold it already - a // caller may also call endFetchPhase() after this returns. guard.endFetchPhase(); if (guard.isSameCacheGeneration()) - return _cache.insertOrAssignAndGet(key, std::move(*value)); + return ValueHandle(_cache.insertOrAssignAndGet( + key, + {std::move(*value), opCtx->getServiceContext()->getFastClockSource()->now()})); // If the cache generation changed while this thread was in fetch mode, the data // associated with the value may now be invalid, so we will throw out the fetched value @@ -270,10 +341,10 @@ public: /** * Invalidates the given 'key' and immediately replaces it with a new value. */ - ValueHandle insertOrAssignAndGet(const Key& key, Value&& newValue) { + ValueHandle insertOrAssignAndGet(const Key& key, Value&& newValue, Date_t updateWallClockTime) { CacheGuard guard(this); _updateCacheGeneration(guard); - return _cache.insertOrAssignAndGet(key, std::move(newValue)); + return _cache.insertOrAssignAndGet(key, {std::move(newValue), updateWallClockTime}); } /** @@ -290,8 +361,9 @@ public: void invalidateIf(const Pred& predicate) { CacheGuard guard(this); _updateCacheGeneration(guard); - _cache.invalidateIf( - [&](const Key& key, const Value* value) { return predicate(key, value); }); + _cache.invalidateIf([&](const Key& key, const StoredValue* storedValue) { + return predicate(key, &storedValue->value); + }); } void invalidateAll() { |