1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
32
33 #include "mongo/platform/basic.h"
34
35 #include "mongo/db/commands/feature_compatibility_version.h"
36
37 #include "mongo/base/status.h"
38 #include "mongo/db/dbdirectclient.h"
39 #include "mongo/db/operation_context.h"
40 #include "mongo/db/repl/optime.h"
41 #include "mongo/db/repl/storage_interface.h"
42 #include "mongo/db/server_parameters.h"
43 #include "mongo/db/service_context.h"
44 #include "mongo/db/storage/storage_engine.h"
45 #include "mongo/db/wire_version.h"
46 #include "mongo/db/write_concern_options.h"
47 #include "mongo/rpc/get_status_from_command_result.h"
48 #include "mongo/transport/service_entry_point.h"
49 #include "mongo/util/log.h"
50
51 namespace mongo {
52
53 using repl::UnreplicatedWritesBlock;
54
55 constexpr StringData FeatureCompatibilityVersion::kCollection;
56 constexpr StringData FeatureCompatibilityVersion::kCommandName;
57 constexpr StringData FeatureCompatibilityVersion::kDatabase;
58 constexpr StringData FeatureCompatibilityVersion::kParameterName;
59 constexpr StringData FeatureCompatibilityVersion::kVersionField;
60 constexpr StringData FeatureCompatibilityVersion::kTargetVersionField;
61
62 Lock::ResourceMutex FeatureCompatibilityVersion::fcvLock("featureCompatibilityVersionLock");
63
parse(const BSONObj & featureCompatibilityVersionDoc)64 StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibilityVersion::parse(
65 const BSONObj& featureCompatibilityVersionDoc) {
66 ServerGlobalParams::FeatureCompatibility::Version version =
67 ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior;
68 std::string versionString;
69 std::string targetVersionString;
70
71 for (auto&& elem : featureCompatibilityVersionDoc) {
72 auto fieldName = elem.fieldNameStringData();
73 if (fieldName == "_id") {
74 continue;
75 } else if (fieldName == FeatureCompatibilityVersion::kVersionField ||
76 fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
77 if (elem.type() != BSONType::String) {
78 return Status(ErrorCodes::TypeMismatch,
79 str::stream() << fieldName
80 << " must be of type String, but was of type "
81 << typeName(elem.type())
82 << ". Contents of "
83 << FeatureCompatibilityVersion::kParameterName
84 << " document in "
85 << FeatureCompatibilityVersion::kCollection
86 << ": "
87 << featureCompatibilityVersionDoc
88 << ". See "
89 << feature_compatibility_version::kDochubLink
90 << ".");
91 }
92
93 if (elem.String() != FeatureCompatibilityVersionCommandParser::kVersion36 &&
94 elem.String() != FeatureCompatibilityVersionCommandParser::kVersion34) {
95 return Status(ErrorCodes::BadValue,
96 str::stream() << "Invalid value for " << fieldName << ", found "
97 << elem.String()
98 << ", expected '"
99 << FeatureCompatibilityVersionCommandParser::kVersion36
100 << "' or '"
101 << FeatureCompatibilityVersionCommandParser::kVersion34
102 << "'. Contents of "
103 << FeatureCompatibilityVersion::kParameterName
104 << " document in "
105 << FeatureCompatibilityVersion::kCollection
106 << ": "
107 << featureCompatibilityVersionDoc
108 << ". See "
109 << feature_compatibility_version::kDochubLink
110 << ".");
111 }
112
113 if (fieldName == FeatureCompatibilityVersion::kVersionField) {
114 versionString = elem.String();
115 } else if (fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
116 targetVersionString = elem.String();
117 }
118 } else {
119 return Status(ErrorCodes::BadValue,
120 str::stream() << "Unrecognized field '" << fieldName << "'. Contents of "
121 << FeatureCompatibilityVersion::kParameterName
122 << " document in "
123 << FeatureCompatibilityVersion::kCollection
124 << ": "
125 << featureCompatibilityVersionDoc
126 << ". See "
127 << feature_compatibility_version::kDochubLink
128 << ".");
129 }
130 }
131
132 if (versionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
133 if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion36) {
134 version = ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36;
135 } else if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
136 version = ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34;
137 } else {
138 version = ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34;
139 }
140
141 } else if (versionString == FeatureCompatibilityVersionCommandParser::kVersion36) {
142 if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion36 ||
143 targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
144 return Status(ErrorCodes::BadValue,
145 str::stream() << "Invalid state for "
146 << FeatureCompatibilityVersion::kParameterName
147 << " document in "
148 << FeatureCompatibilityVersion::kCollection
149 << ": "
150 << featureCompatibilityVersionDoc
151 << ". See "
152 << feature_compatibility_version::kDochubLink
153 << ".");
154 } else {
155 version = ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36;
156 }
157 } else {
158 return Status(ErrorCodes::BadValue,
159 str::stream() << "Missing required field '"
160 << FeatureCompatibilityVersion::kVersionField
161 << "''. Contents of "
162 << FeatureCompatibilityVersion::kParameterName
163 << " document in "
164 << FeatureCompatibilityVersion::kCollection
165 << ": "
166 << featureCompatibilityVersionDoc
167 << ". See "
168 << feature_compatibility_version::kDochubLink
169 << ".");
170 }
171
172 return version;
173 }
174
setTargetUpgrade(OperationContext * opCtx)175 void FeatureCompatibilityVersion::setTargetUpgrade(OperationContext* opCtx) {
176 // Sets both 'version' and 'targetVersion' fields.
177 _runUpdateCommand(opCtx, [](auto updateMods) {
178 updateMods.append(FeatureCompatibilityVersion::kVersionField,
179 FeatureCompatibilityVersionCommandParser::kVersion34);
180 updateMods.append(FeatureCompatibilityVersion::kTargetVersionField,
181 FeatureCompatibilityVersionCommandParser::kVersion36);
182 });
183 }
184
setTargetDowngrade(OperationContext * opCtx)185 void FeatureCompatibilityVersion::setTargetDowngrade(OperationContext* opCtx) {
186 // Sets both 'version' and 'targetVersion' fields.
187 _runUpdateCommand(opCtx, [](auto updateMods) {
188 updateMods.append(FeatureCompatibilityVersion::kVersionField,
189 FeatureCompatibilityVersionCommandParser::kVersion34);
190 updateMods.append(FeatureCompatibilityVersion::kTargetVersionField,
191 FeatureCompatibilityVersionCommandParser::kVersion34);
192 });
193 }
194
unsetTargetUpgradeOrDowngrade(OperationContext * opCtx,StringData version)195 void FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(OperationContext* opCtx,
196 StringData version) {
197 _validateVersion(version);
198
199 // Updates 'version' field, while also unsetting the 'targetVersion' field.
200 _runUpdateCommand(opCtx, [version](auto updateMods) {
201 updateMods.append(FeatureCompatibilityVersion::kVersionField, version);
202 });
203 }
204
setIfCleanStartup(OperationContext * opCtx,repl::StorageInterface * storageInterface)205 void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
206 repl::StorageInterface* storageInterface) {
207 if (!isCleanStartUp())
208 return;
209
210 // If the server was not started with --shardsvr, the default featureCompatibilityVersion on
211 // clean startup is the upgrade version. If it was started with --shardsvr, the default
212 // featureCompatibilityVersion is the downgrade version, so that it can be safely added to a
213 // downgrade version cluster. The config server will run setFeatureCompatibilityVersion as part
214 // of addShard.
215 const bool storeUpgradeVersion = serverGlobalParams.clusterRole != ClusterRole::ShardServer;
216
217 UnreplicatedWritesBlock unreplicatedWritesBlock(opCtx);
218 NamespaceString nss(FeatureCompatibilityVersion::kCollection);
219
220 {
221 CollectionOptions options;
222 options.uuid = CollectionUUID::gen();
223
224 // Only for 3.4 shard servers, we create admin.system.version without UUID on clean startup.
225 // The mention of kFullyDowngradedTo34 below is to ensure this will not compile in 3.8.
226 if (!storeUpgradeVersion &&
227 serverGlobalParams.featureCompatibility.getVersion() ==
228 ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34)
229 options.uuid.reset();
230
231 uassertStatusOK(storageInterface->createCollection(opCtx, nss, options));
232 }
233
234 // We then insert the featureCompatibilityVersion document into the "admin.system.version"
235 // collection. The server parameter will be updated on commit by the op observer.
236 uassertStatusOK(storageInterface->insertDocument(
237 opCtx,
238 nss,
239 repl::TimestampedBSONObj{
240 BSON("_id" << FeatureCompatibilityVersion::kParameterName
241 << FeatureCompatibilityVersion::kVersionField
242 << (storeUpgradeVersion
243 ? FeatureCompatibilityVersionCommandParser::kVersion36
244 : FeatureCompatibilityVersionCommandParser::kVersion34)),
245 Timestamp()},
246 repl::OpTime::kUninitializedTerm)); // No timestamp or term because this write is not
247 // replicated.
248 }
249
isCleanStartUp()250 bool FeatureCompatibilityVersion::isCleanStartUp() {
251 std::vector<std::string> dbNames;
252 StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine();
253 storageEngine->listDatabases(&dbNames);
254
255 for (auto&& dbName : dbNames) {
256 if (dbName != "local") {
257 return false;
258 }
259 }
260 return true;
261 }
262
263 namespace {
264
265 // If a node finishes rollback while in fcv=3.6, but while in RECOVERING before reaching
266 // minValid, sees that its sync source started a downgrade to fcv=3.4, it may be be unable to
267 // become consistent. This is because an fcv=3.6 node will have used the UUID algorithm for
268 // rollback, which treats a missing UUID as a collection drop. We fassert before becoming
269 // SECONDARY in an inconsistent state.
270 //
271 // We only care about the start of downgrade, because if we see an oplog entry marking the end
272 // of downgrade, then either we also saw a downgrade start op, or we did our rollback in fcv=3.4
273 // with the no-UUID algorithm. Similarly, we do not care about upgrade, because either
274 // it will also have a downgrade op, or we did our rollback in fcv=3.4. The no-UUID rollback
275 // algorithm will be as safe as it was in 3.4 regardless of if the sync source has UUIDs or not.
uassertDuringRollbackOnDowngradeOp(OperationContext * opCtx,ServerGlobalParams::FeatureCompatibility::Version newVersion,std::string msg)276 void uassertDuringRollbackOnDowngradeOp(
277 OperationContext* opCtx,
278 ServerGlobalParams::FeatureCompatibility::Version newVersion,
279 std::string msg) {
280 if ((newVersion != ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34) ||
281 (serverGlobalParams.featureCompatibility.getVersion() !=
282 ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36)) {
283 // This is only the start of a downgrade operation if we're currently upgraded to 3.6.
284 return;
285 }
286
287 auto replCoord = repl::ReplicationCoordinator::get(opCtx);
288 uassert(ErrorCodes::OplogOperationUnsupported,
289 msg,
290 replCoord->getReplicationMode() != repl::ReplicationCoordinator::modeReplSet ||
291 replCoord->isInPrimaryOrSecondaryState());
292 }
293
294 // If we are finishing an upgrade, all collections should have UUIDs. We check to make sure
295 // that the feature compatibility version collection has a UUID.
uassertDuringInvalidUpgradeOp(OperationContext * opCtx,ServerGlobalParams::FeatureCompatibility::Version version)296 void uassertDuringInvalidUpgradeOp(OperationContext* opCtx,
297 ServerGlobalParams::FeatureCompatibility::Version version) {
298 auto replCoord = repl::ReplicationCoordinator::get(opCtx);
299 if (replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeMasterSlave) {
300 // TODO (SERVER-31593) master-slave should uassert here, but cannot due to a bug.
301 return;
302 }
303
304 if (version != ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36) {
305 // Only check this assertion if we're completing an upgrade to 3.6.
306 return;
307 }
308
309 auto fcvUUID = repl::StorageInterface::get(opCtx)->getCollectionUUID(
310 opCtx, NamespaceString(FeatureCompatibilityVersion::kCollection));
311 uassert(ErrorCodes::IllegalOperation,
312 str::stream() << "Error checking for UUID in feature compatibility version collection: "
313 << fcvUUID.getStatus().toString(),
314 fcvUUID.isOK());
315 uassert(ErrorCodes::IllegalOperation,
316 str::stream() << "Tried to complete upgrade, but "
317 << FeatureCompatibilityVersion::kCollection
318 << " did not have a UUID.",
319 fcvUUID.getValue());
320 }
321
322 } // namespace
323
onInsertOrUpdate(OperationContext * opCtx,const BSONObj & doc)324 void FeatureCompatibilityVersion::onInsertOrUpdate(OperationContext* opCtx, const BSONObj& doc) {
325 auto idElement = doc["_id"];
326 if (idElement.type() != BSONType::String ||
327 idElement.String() != FeatureCompatibilityVersion::kParameterName) {
328 return;
329 }
330 auto newVersion = uassertStatusOK(FeatureCompatibilityVersion::parse(doc));
331
332 uassertDuringRollbackOnDowngradeOp(opCtx,
333 newVersion,
334 str::stream()
335 << "Must be in primary or secondary state to "
336 "downgrade feature compatibility version document: "
337 << redact(doc));
338 uassertDuringInvalidUpgradeOp(opCtx, newVersion);
339
340 // To avoid extra log messages when the targetVersion is set/unset, only log when the version
341 // changes.
342 auto oldVersion = serverGlobalParams.featureCompatibility.getVersion();
343 if (oldVersion != newVersion) {
344 log() << "setting featureCompatibilityVersion to " << toString(newVersion);
345 }
346
347 // On commit, update the server parameters, and close any incoming connections with a wire
348 // version that is below the minimum.
349 opCtx->recoveryUnit()->onCommit([opCtx, newVersion]() {
350 serverGlobalParams.featureCompatibility.setVersion(newVersion);
351 updateMinWireVersion();
352
353 // Close all incoming connections from internal clients with binary versions lower than
354 // ours. It would be desirable to close all outgoing connections to servers with lower
355 // binary version, but it is not currently possible.
356 if (newVersion != ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34) {
357 opCtx->getServiceContext()->getServiceEntryPoint()->endAllSessions(
358 transport::Session::kLatestVersionInternalClientKeepOpen |
359 transport::Session::kExternalClientKeepOpen);
360 }
361 });
362 }
363
onDelete(OperationContext * opCtx,const BSONObj & doc)364 void FeatureCompatibilityVersion::onDelete(OperationContext* opCtx, const BSONObj& doc) {
365 auto idElement = doc["_id"];
366 if (idElement.type() != BSONType::String ||
367 idElement.String() != FeatureCompatibilityVersion::kParameterName) {
368 return;
369 }
370
371 uassertDuringRollbackOnDowngradeOp(
372 opCtx,
373 ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34,
374 "Must be in primary or secondary state to delete feature compatibility version document");
375
376 log() << "setting featureCompatibilityVersion to "
377 << FeatureCompatibilityVersionCommandParser::kVersion34;
378 opCtx->recoveryUnit()->onCommit([]() {
379 serverGlobalParams.featureCompatibility.setVersion(
380 ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34);
381 updateMinWireVersion();
382 });
383 }
384
onDropCollection(OperationContext * opCtx)385 void FeatureCompatibilityVersion::onDropCollection(OperationContext* opCtx) {
386
387 uassertDuringRollbackOnDowngradeOp(
388 opCtx,
389 ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34,
390 str::stream() << "Must be in primary or secondary state to drop "
391 << FeatureCompatibilityVersion::kCollection
392 << " collection");
393
394 log() << "setting featureCompatibilityVersion to "
395 << FeatureCompatibilityVersionCommandParser::kVersion34;
396 opCtx->recoveryUnit()->onCommit([]() {
397 serverGlobalParams.featureCompatibility.setVersion(
398 ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34);
399 updateMinWireVersion();
400 });
401 }
402
updateMinWireVersion()403 void FeatureCompatibilityVersion::updateMinWireVersion() {
404 WireSpec& spec = WireSpec::instance();
405
406 switch (serverGlobalParams.featureCompatibility.getVersion()) {
407 case ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36:
408 case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36:
409 case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34:
410 spec.incomingInternalClient.minWireVersion = LATEST_WIRE_VERSION;
411 spec.outgoing.minWireVersion = LATEST_WIRE_VERSION;
412 return;
413 case ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34:
414 // It would be preferable to set 'incomingInternalClient.minWireVersion' and
415 // 'outgoing.minWireVersion' to LATEST_WIRE_VERSION - 1, but this is not possible due to
416 // a bug in 3.4, where if the receiving node says it supports wire version range
417 // [COMMANDS_ACCEPT_WRITE_CONCERN, SUPPORTS_OP_MSG], the initiating node will think it
418 // only supports OP_QUERY.
419 spec.incomingInternalClient.minWireVersion = RELEASE_2_4_AND_BEFORE;
420 spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE;
421 return;
422 case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior:
423 // getVersion() does not return this value.
424 MONGO_UNREACHABLE;
425 }
426 }
427
_validateVersion(StringData version)428 void FeatureCompatibilityVersion::_validateVersion(StringData version) {
429 uassert(40284,
430 str::stream() << "featureCompatibilityVersion must be '"
431 << FeatureCompatibilityVersionCommandParser::kVersion36
432 << "' or '"
433 << FeatureCompatibilityVersionCommandParser::kVersion34
434 << "'. See "
435 << feature_compatibility_version::kDochubLink
436 << ".",
437 version == FeatureCompatibilityVersionCommandParser::kVersion36 ||
438 version == FeatureCompatibilityVersionCommandParser::kVersion34);
439 }
440
_runUpdateCommand(OperationContext * opCtx,UpdateBuilder builder)441 void FeatureCompatibilityVersion::_runUpdateCommand(OperationContext* opCtx,
442 UpdateBuilder builder) {
443 DBDirectClient client(opCtx);
444 NamespaceString nss(FeatureCompatibilityVersion::kCollection);
445
446 BSONObjBuilder updateCmd;
447 updateCmd.append("update", nss.coll());
448 {
449 BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
450 {
451 BSONObjBuilder updateSpec(updates.subobjStart());
452 {
453 BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
454 queryFilter.append("_id", FeatureCompatibilityVersion::kParameterName);
455 }
456 {
457 BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
458 builder(std::move(updateMods));
459 }
460 updateSpec.appendBool("upsert", true);
461 }
462 }
463 updateCmd.append(WriteConcernOptions::kWriteConcernField, WriteConcernOptions::Majority);
464
465 // Update the featureCompatibilityVersion document stored in the "admin.system.version"
466 // collection.
467 BSONObj updateResult;
468 client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult);
469 uassertStatusOK(getStatusFromWriteCommandReply(updateResult));
470 }
471 /**
472 * Read-only server parameter for featureCompatibilityVersion.
473 */
474 class FeatureCompatibilityVersionParameter : public ServerParameter {
475 public:
FeatureCompatibilityVersionParameter()476 FeatureCompatibilityVersionParameter()
477 : ServerParameter(ServerParameterSet::getGlobal(),
478 FeatureCompatibilityVersion::kParameterName.toString(),
479 false, // allowedToChangeAtStartup
480 false // allowedToChangeAtRuntime
481 ) {}
482
append(OperationContext * opCtx,BSONObjBuilder & b,const std::string & name)483 virtual void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) {
484 BSONObjBuilder featureCompatibilityVersionBuilder(b.subobjStart(name));
485 switch (serverGlobalParams.featureCompatibility.getVersion()) {
486 case ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36:
487 featureCompatibilityVersionBuilder.append(
488 FeatureCompatibilityVersion::kVersionField,
489 FeatureCompatibilityVersionCommandParser::kVersion36);
490 return;
491 case ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34:
492 featureCompatibilityVersionBuilder.append(
493 FeatureCompatibilityVersion::kVersionField,
494 FeatureCompatibilityVersionCommandParser::kVersion34);
495 return;
496 case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36:
497 featureCompatibilityVersionBuilder.append(
498 FeatureCompatibilityVersion::kVersionField,
499 FeatureCompatibilityVersionCommandParser::kVersion34);
500 featureCompatibilityVersionBuilder.append(
501 FeatureCompatibilityVersion::kTargetVersionField,
502 FeatureCompatibilityVersionCommandParser::kVersion36);
503 return;
504 case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34:
505 featureCompatibilityVersionBuilder.append(
506 FeatureCompatibilityVersion::kVersionField,
507 FeatureCompatibilityVersionCommandParser::kVersion34);
508 featureCompatibilityVersionBuilder.append(
509 FeatureCompatibilityVersion::kTargetVersionField,
510 FeatureCompatibilityVersionCommandParser::kVersion34);
511 return;
512 case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior:
513 // getVersion() does not return this value.
514 MONGO_UNREACHABLE;
515 }
516 }
517
set(const BSONElement & newValueElement)518 virtual Status set(const BSONElement& newValueElement) {
519 return Status(ErrorCodes::IllegalOperation,
520 str::stream() << FeatureCompatibilityVersion::kParameterName
521 << " cannot be set via setParameter. See "
522 << feature_compatibility_version::kDochubLink
523 << ".");
524 }
525
setFromString(const std::string & str)526 virtual Status setFromString(const std::string& str) {
527 return Status(ErrorCodes::IllegalOperation,
528 str::stream() << FeatureCompatibilityVersion::kParameterName
529 << " cannot be set via setParameter. See "
530 << feature_compatibility_version::kDochubLink
531 << ".");
532 }
533 } featureCompatibilityVersionParameter;
534
535 MONGO_EXPORT_STARTUP_SERVER_PARAMETER(internalValidateFeaturesAsMaster, bool, true);
536
537 } // namespace mongo
538