diff options
13 files changed, 81 insertions, 1 deletions
diff --git a/jstests/replsets/linearizable_read_concern.js b/jstests/replsets/linearizable_read_concern.js index 4cbef17b568..f7f7d3b9228 100644 --- a/jstests/replsets/linearizable_read_concern.js +++ b/jstests/replsets/linearizable_read_concern.js @@ -31,9 +31,39 @@ load('jstests/libs/write_concern_util.js'); ErrorCodes.InterruptedDueToReplStateChange); }; + // Linearizable read concern is an opt-in feature. Replica sets started with the startup + // parameter --setParameter enableLinearizableReadConcern=true should error on receiving a + // linearizable read command. + { + jsTestLog('Testing that linearizable read is disabled by default'); + let replTest = new ReplSetTest({nodes: 1}); + replTest.startSet(); + replTest.initiate(); + let primary = replTest.getPrimary(); + + // Linearizable read concern is a startup-only server parameter. + assert.commandFailed( + primary.adminCommand({setParameter: 1, enableLinearizableReadConcern: true})); + assert.commandFailed( + primary.adminCommand({setParameter: 1, enableLinearizableReadConcern: false})); + + let coll = primary.getDB('test').foo; + assert.writeOK(coll.insert({x: 1})); + assert.commandFailedWithCode( + coll.runCommand( + {'find': coll.getName(), readConcern: {level: "linearizable"}, maxTimeMS: 60000}), + ErrorCodes.LinearizableReadConcernNotEnabled); + replTest.stopSet(); + } + var num_nodes = 3; var name = 'linearizable_read_concern'; - var replTest = new ReplSetTest({name: name, nodes: num_nodes, useBridge: true}); + var replTest = new ReplSetTest({ + name: name, + nodes: num_nodes, + nodeOptions: {setParameter: 'enableLinearizableReadConcern=true'}, + useBridge: true, + }); var config = replTest.getReplSetConfig(); // Increased election timeout to avoid having the primary step down while we are diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index cb524d53263..c6a0e7d014b 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -194,6 +194,7 @@ error_code("FailPointEnabled", 192) error_code("NoShardingEnabled", 193) error_code("BalancerInterrupted", 194) error_code("ViewPipelineMaxSizeExceeded", 195) +error_code("LinearizableReadConcernNotEnabled", 196) # Non-sequential error codes (for compatibility only) error_code("SocketException", 9001) diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index 432c630166d..a25288efa8e 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -1403,6 +1403,22 @@ bool Command::run(OperationContext* txn, return result; } + if (readConcernArgsStatus.getValue().getLevel() == + repl::ReadConcernLevel::kLinearizableReadConcern) { + auto replCoord = repl::ReplicationCoordinator::get(txn); + if (!replCoord->isLinearizableReadConcernEnabled()) { + Status status(ErrorCodes::LinearizableReadConcernNotEnabled, + "Linearizable read concern requested, but server was not started with " + "--setParameter enableLinearizableReadConcern=true"); + inPlaceReplyBob.resetToEmpty(); + auto result = appendCommandStatus(inPlaceReplyBob, status); + inPlaceReplyBob.doneFast(); + replyBuilder->setMetadata(rpc::makeEmptyMetadata()); + return result; + } + } + + Status rcStatus = waitForReadConcern(txn, readConcernArgsStatus.getValue()); if (!rcStatus.isOK()) { if (rcStatus == ErrorCodes::ExceededTimeLimit) { diff --git a/src/mongo/db/repl/replication_coordinator.h b/src/mongo/db/repl/replication_coordinator.h index 019add3e0cf..e2a6ff4e76b 100644 --- a/src/mongo/db/repl/replication_coordinator.h +++ b/src/mongo/db/repl/replication_coordinator.h @@ -794,6 +794,11 @@ public: virtual Status stepUpIfEligible() = 0; + /** + * Returns true if linearizable read concern is supported. + */ + virtual bool isLinearizableReadConcernEnabled() const = 0; + protected: ReplicationCoordinator(); }; diff --git a/src/mongo/db/repl/replication_coordinator_external_state.h b/src/mongo/db/repl/replication_coordinator_external_state.h index 73e99635c36..fbb8d365dbb 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state.h +++ b/src/mongo/db/repl/replication_coordinator_external_state.h @@ -355,6 +355,11 @@ public: * Stops periodic noop writes to oplog. */ virtual void stopNoopWriter() = 0; + + /** + * Returns true if linearizable read concern is supported. + */ + virtual bool isLinearizableReadConcernEnabled() const = 0; }; } // namespace repl diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp index 21708c3ebff..b3d4b4ffbb4 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp @@ -118,6 +118,8 @@ const char kBlockingQueueOplogBufferName[] = "inMemoryBlockingQueue"; // repl::SnapshotThread introduces. MONGO_EXPORT_STARTUP_SERVER_PARAMETER(enableReplSnapshotThread, bool, false); +MONGO_EXPORT_STARTUP_SERVER_PARAMETER(enableLinearizableReadConcern, bool, false); + MONGO_EXPORT_STARTUP_SERVER_PARAMETER(use3dot2InitialSync, bool, false); // Set this to specify whether to use a collection to buffer the oplog on the destination server @@ -899,6 +901,10 @@ std::size_t ReplicationCoordinatorExternalStateImpl::getOplogFetcherMaxFetcherRe return oplogFetcherMaxFetcherRestarts; } +bool ReplicationCoordinatorExternalStateImpl::isLinearizableReadConcernEnabled() const { + return enableLinearizableReadConcern; +} + JournalListener::Token ReplicationCoordinatorExternalStateImpl::getToken() { return repl::getGlobalReplicationCoordinator()->getMyLastAppliedOpTime(); } diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.h b/src/mongo/db/repl/replication_coordinator_external_state_impl.h index 372a1661e37..9c173a97823 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.h +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.h @@ -113,6 +113,7 @@ public: OperationContext* txn) const override; virtual bool shouldUseDataReplicatorInitialSync() const override; virtual std::size_t getOplogFetcherMaxFetcherRestarts() const override; + virtual bool isLinearizableReadConcernEnabled() const override; // Methods from JournalListener. virtual JournalListener::Token getToken(); diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp index 700d1608880..087e8d84ab0 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp @@ -294,6 +294,10 @@ std::size_t ReplicationCoordinatorExternalStateMock::getOplogFetcherMaxFetcherRe return 0; } +bool ReplicationCoordinatorExternalStateMock::isLinearizableReadConcernEnabled() const { + return false; +} + void ReplicationCoordinatorExternalStateMock::setIsReadCommittedEnabled(bool val) { _isReadCommittedSupported = val; } diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.h b/src/mongo/db/repl/replication_coordinator_external_state_mock.h index 35ff01d5585..fcc6eea1d4b 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_mock.h +++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.h @@ -107,6 +107,7 @@ public: OperationContext* txn) const override; virtual bool shouldUseDataReplicatorInitialSync() const override; virtual std::size_t getOplogFetcherMaxFetcherRestarts() const override; + virtual bool isLinearizableReadConcernEnabled() const override; /** * Adds "host" to the list of hosts that this mock will match when responding to "isSelf" diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 81c8cef4821..481c4f86c7a 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -3878,6 +3878,9 @@ void ReplicationCoordinatorImpl::setIndexPrefetchConfig( _indexPrefetchConfig = cfg; } +bool ReplicationCoordinatorImpl::isLinearizableReadConcernEnabled() const { + return _externalState->isLinearizableReadConcernEnabled(); +} } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/replication_coordinator_impl.h b/src/mongo/db/repl/replication_coordinator_impl.h index df76c251c0b..d66dd431279 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.h +++ b/src/mongo/db/repl/replication_coordinator_impl.h @@ -339,6 +339,8 @@ public: virtual Status stepUpIfEligible() override; + virtual bool isLinearizableReadConcernEnabled() const override; + // ================== Test support API =================== /** diff --git a/src/mongo/db/repl/replication_coordinator_mock.cpp b/src/mongo/db/repl/replication_coordinator_mock.cpp index 03878cc300c..3b1aa2e8960 100644 --- a/src/mongo/db/repl/replication_coordinator_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_mock.cpp @@ -467,5 +467,9 @@ Status ReplicationCoordinatorMock::stepUpIfEligible() { return Status::OK(); } +bool ReplicationCoordinatorMock::isLinearizableReadConcernEnabled() const { + return false; +} + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/replication_coordinator_mock.h b/src/mongo/db/repl/replication_coordinator_mock.h index 4daf24d894e..96dd2486e98 100644 --- a/src/mongo/db/repl/replication_coordinator_mock.h +++ b/src/mongo/db/repl/replication_coordinator_mock.h @@ -262,6 +262,8 @@ public: virtual Status stepUpIfEligible() override; + virtual bool isLinearizableReadConcernEnabled() const override; + /** * Sets the return value for calls to getConfig. */ |