summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/noPassthrough/index_version_v2.js17
-rw-r--r--src/mongo/db/catalog/database_impl.cpp6
-rw-r--r--src/mongo/db/catalog/index_catalog.h8
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.cpp5
-rw-r--r--src/mongo/db/catalog/index_catalog_impl.h3
-rw-r--r--src/mongo/db/catalog/index_key_validate.cpp14
-rw-r--r--src/mongo/db/catalog/index_spec_validate_test.cpp21
-rw-r--r--src/mongo/db/commands/drop_indexes.cpp5
-rw-r--r--src/mongo/db/index/btree_key_generator.cpp1
-rw-r--r--src/mongo/db/index/index_descriptor.cpp18
-rw-r--r--src/mongo/db/index/index_descriptor.h6
-rw-r--r--src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp1
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp558
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_index.h95
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp8
15 files changed, 356 insertions, 410 deletions
diff --git a/jstests/noPassthrough/index_version_v2.js b/jstests/noPassthrough/index_version_v2.js
index edb176dad6f..b3fe65841d3 100644
--- a/jstests/noPassthrough/index_version_v2.js
+++ b/jstests/noPassthrough/index_version_v2.js
@@ -4,9 +4,6 @@
* Additionally, this file tests that index version v=2 is required to create an index with a
* collation and that index version v=2 is required to index decimal data on storage engines using
* the KeyString format.
- *
- * Also, index version v=3 is required to prohibit duplicates in unique index at secondary. Enhance
- * the tests for index version v=3.
*/
(function() {
"use strict";
@@ -121,17 +118,7 @@
testDB.dropDatabase();
- // MongoDB4.0 onwards index version v=3 would be supported. Test that an index created with v=3
- // succeeds.
- assert.commandWorked(testDB.index_version.createIndex({withV3: 1}, {v: 3}));
-
- //
- // Index version v=4
- //
-
- testDB.dropDatabase();
-
- // Test that attempting to create an index with v=4 returns an error.
- assert.commandFailed(testDB.index_version.createIndex({withV4: 1}, {v: 4}));
+ // Test that attempting to create an index with v=3 returns an error.
+ assert.commandFailed(testDB.index_version.createIndex({withV3: 1}, {v: 3}));
MongoRunner.stopMongod(conn);
})();
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 12ab0dfedc0..a3727707381 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -815,13 +815,9 @@ Collection* DatabaseImpl::createCollection(OperationContext* opCtx,
optionsWithUUID.autoIndexId == CollectionOptions::DEFAULT) {
// createCollection() may be called before the in-memory fCV parameter is
// initialized, so use the unsafe fCV getter here.
- const auto featureCompatibilityVersion =
- serverGlobalParams.featureCompatibility.getVersionUnsafe();
IndexCatalog* ic = collection->getIndexCatalog();
fullIdIndexSpec = uassertStatusOK(ic->createIndexOnEmptyCollection(
- opCtx,
- !idIndex.isEmpty() ? idIndex
- : ic->getDefaultIdIndexSpec(featureCompatibilityVersion)));
+ opCtx, !idIndex.isEmpty() ? idIndex : ic->getDefaultIdIndexSpec()));
} else {
// autoIndexId: false is only allowed on unreplicated collections.
uassert(50001,
diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h
index 1e56e83cd9c..5e5b17281e2 100644
--- a/src/mongo/db/catalog/index_catalog.h
+++ b/src/mongo/db/catalog/index_catalog.h
@@ -161,8 +161,7 @@ public:
virtual bool haveIdIndex(OperationContext* opCtx) const = 0;
- virtual BSONObj getDefaultIdIndexSpec(ServerGlobalParams::FeatureCompatibility::Version
- featureCompatibilityVersion) const = 0;
+ virtual BSONObj getDefaultIdIndexSpec() const = 0;
virtual IndexDescriptor* findIdIndex(OperationContext* opCtx) const = 0;
@@ -312,9 +311,8 @@ public:
/**
* Returns the spec for the id index to create by default for this collection.
*/
- inline BSONObj getDefaultIdIndexSpec(
- const ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion) const {
- return this->_impl().getDefaultIdIndexSpec(featureCompatibilityVersion);
+ inline BSONObj getDefaultIdIndexSpec() const {
+ return this->_impl().getDefaultIdIndexSpec();
}
inline IndexDescriptor* findIdIndex(OperationContext* const opCtx) const {
diff --git a/src/mongo/db/catalog/index_catalog_impl.cpp b/src/mongo/db/catalog/index_catalog_impl.cpp
index 9a090e94109..1e1cc649afe 100644
--- a/src/mongo/db/catalog/index_catalog_impl.cpp
+++ b/src/mongo/db/catalog/index_catalog_impl.cpp
@@ -870,11 +870,10 @@ Status IndexCatalogImpl::_doesSpecConflictWithExisting(OperationContext* opCtx,
return Status::OK();
}
-BSONObj IndexCatalogImpl::getDefaultIdIndexSpec(
- ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion) const {
+BSONObj IndexCatalogImpl::getDefaultIdIndexSpec() const {
dassert(_idObj["_id"].type() == NumberInt);
- const auto indexVersion = IndexDescriptor::getDefaultIndexVersion(featureCompatibilityVersion);
+ const auto indexVersion = IndexDescriptor::getDefaultIndexVersion();
BSONObjBuilder b;
b.append("v", static_cast<int>(indexVersion));
diff --git a/src/mongo/db/catalog/index_catalog_impl.h b/src/mongo/db/catalog/index_catalog_impl.h
index 52e4568e3eb..cca465c732e 100644
--- a/src/mongo/db/catalog/index_catalog_impl.h
+++ b/src/mongo/db/catalog/index_catalog_impl.h
@@ -84,8 +84,7 @@ public:
/**
* Returns the spec for the id index to create by default for this collection.
*/
- BSONObj getDefaultIdIndexSpec(ServerGlobalParams::FeatureCompatibility::Version
- featureCompatibilityVersion) const override;
+ BSONObj getDefaultIdIndexSpec() const override;
IndexDescriptor* findIdIndex(OperationContext* opCtx) const override;
diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp
index 25f7c43910f..0a027b3c2a5 100644
--- a/src/mongo/db/catalog/index_key_validate.cpp
+++ b/src/mongo/db/catalog/index_key_validate.cpp
@@ -133,8 +133,7 @@ Status validateKeyPattern(const BSONObj& key, IndexDescriptor::IndexVersion inde
break;
}
- case IndexVersion::kV2:
- case IndexVersion::kV2Unique: {
+ case IndexVersion::kV2: {
if (keyElement.isNumber()) {
double value = keyElement.number();
if (std::isnan(value)) {
@@ -220,7 +219,6 @@ StatusWith<BSONObj> validateIndexSpec(
bool hasNamespaceField = false;
bool hasVersionField = false;
bool hasCollationField = false;
- bool isUniqueIndex = false;
auto fieldNamesValidStatus = validateIndexSpecFieldNames(indexSpec);
if (!fieldNamesValidStatus.isOK()) {
@@ -365,13 +363,6 @@ StatusWith<BSONObj> validateIndexSpec(
if (!statusWithMatcher.isOK()) {
return statusWithMatcher.getStatus();
}
- } else if (IndexDescriptor::kUniqueFieldName == indexSpecElemFieldName) {
- // Note: Here we only consider whether or not "unique" field is specified and that its
- // value evaluates to true. "_id" index for instance is unique, but the index spec for
- // it doesn't carry a "unique" field. "isUniqueIndex" being false for "_id" indexes is
- // on purpose, and in future if we were to make "_id" index specs include
- // "unique:true", then we would need to change the logic here to preserve its behavior.
- isUniqueIndex = indexSpecElem.trueValue();
} else {
// We can assume field name is valid at this point. Validation of fieldname is handled
// prior to this in validateIndexSpecFieldNames().
@@ -380,8 +371,7 @@ StatusWith<BSONObj> validateIndexSpec(
}
if (!resolvedIndexVersion) {
- resolvedIndexVersion = IndexDescriptor::getDefaultIndexVersion(
- featureCompatibility.getVersion(), isUniqueIndex);
+ resolvedIndexVersion = IndexDescriptor::getDefaultIndexVersion();
}
if (!hasKeyPatternField) {
diff --git a/src/mongo/db/catalog/index_spec_validate_test.cpp b/src/mongo/db/catalog/index_spec_validate_test.cpp
index 7f6f9beae0b..2b463d0c562 100644
--- a/src/mongo/db/catalog/index_spec_validate_test.cpp
+++ b/src/mongo/db/catalog/index_spec_validate_test.cpp
@@ -299,7 +299,7 @@ TEST(IndexSpecValidateTest, ReturnsAnErrorIfVersionIsUnsupported) {
BSON("key" << BSON("field" << 1) << "name"
<< "indexName"
<< "v"
- << 4
+ << 3
<< "collation"
<< BSON("locale"
<< "en")),
@@ -398,25 +398,6 @@ TEST(IndexSpecValidateTest, AcceptsIndexVersionV1) {
sorted(result.getValue()));
}
-TEST(IndexSpecValidateTest, AcceptsIndexVersionV2Unique) {
- auto result = validateIndexSpec(kDefaultOpCtx,
- BSON("key" << BSON("field" << 1) << "name"
- << "indexName"
- << "v"
- << 3),
- kTestNamespace,
- serverGlobalParams.featureCompatibility);
- ASSERT_OK(result.getStatus());
-
- // We don't care about the order of the fields in the resulting index specification.
- ASSERT_BSONOBJ_EQ(sorted(BSON("key" << BSON("field" << 1) << "name"
- << "indexName"
- << "ns"
- << kTestNamespace.ns()
- << "v"
- << 3)),
- sorted(result.getValue()));
-}
TEST(IndexSpecValidateTest, ReturnsAnErrorIfCollationIsNotAnObject) {
ASSERT_EQ(ErrorCodes::TypeMismatch,
validateIndexSpec(kDefaultOpCtx,
diff --git a/src/mongo/db/commands/drop_indexes.cpp b/src/mongo/db/commands/drop_indexes.cpp
index 11d199c99ec..f6ebf5ec180 100644
--- a/src/mongo/db/commands/drop_indexes.cpp
+++ b/src/mongo/db/commands/drop_indexes.cpp
@@ -142,10 +142,7 @@ public:
// This is necessary to set up CurOp and update the Top stats.
OldClientContext ctx(opCtx, toReIndexNss.ns());
- const auto featureCompatibilityVersion =
- serverGlobalParams.featureCompatibility.getVersion();
- const auto defaultIndexVersion =
- IndexDescriptor::getDefaultIndexVersion(featureCompatibilityVersion);
+ const auto defaultIndexVersion = IndexDescriptor::getDefaultIndexVersion();
vector<BSONObj> all;
{
diff --git a/src/mongo/db/index/btree_key_generator.cpp b/src/mongo/db/index/btree_key_generator.cpp
index 9e24016e3e7..d1737ddf314 100644
--- a/src/mongo/db/index/btree_key_generator.cpp
+++ b/src/mongo/db/index/btree_key_generator.cpp
@@ -77,7 +77,6 @@ std::unique_ptr<BtreeKeyGenerator> BtreeKeyGenerator::make(IndexVersion indexVer
return stdx::make_unique<BtreeKeyGeneratorV0>(fieldNames, fixed, isSparse);
case IndexVersion::kV1:
case IndexVersion::kV2:
- case IndexVersion::kV2Unique:
return stdx::make_unique<BtreeKeyGeneratorV1>(fieldNames, fixed, isSparse, collator);
}
return nullptr;
diff --git a/src/mongo/db/index/index_descriptor.cpp b/src/mongo/db/index/index_descriptor.cpp
index ca9ea36ece0..71fe92d0e47 100644
--- a/src/mongo/db/index/index_descriptor.cpp
+++ b/src/mongo/db/index/index_descriptor.cpp
@@ -101,7 +101,6 @@ bool IndexDescriptor::isIndexVersionSupported(IndexVersion indexVersion) {
case IndexVersion::kV0:
case IndexVersion::kV1:
case IndexVersion::kV2:
- case IndexVersion::kV2Unique:
return true;
}
return false;
@@ -120,7 +119,6 @@ Status IndexDescriptor::isIndexVersionAllowedForCreation(
break;
case IndexVersion::kV1:
case IndexVersion::kV2:
- case IndexVersion::kV2Unique:
return Status::OK();
}
return {ErrorCodes::CannotCreateIndex,
@@ -129,21 +127,7 @@ Status IndexDescriptor::isIndexVersionAllowedForCreation(
<< static_cast<int>(indexVersion)};
}
-IndexVersion IndexDescriptor::getDefaultIndexVersion(
- ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion,
- bool isUniqueIndex) {
- // The gating variable would allow creation of V2 format unique index when set to true.
- // Note: Here "isUniqueIndex" only considers whether or not "unique" field is specified
- // and that its value evaluates to true. "_id" index for instance is unique, but the
- // index spec for it doesn't carry a "unique" field. "isUniqueIndex" being false for
- // "_id" indexes is on purpose, and in future if we were to make "_id" index specs
- // include "unique:true", then we would need to change the logic here to preserve its
- // behavior.
- const bool useV2UniqueIndexFormat = false;
- if (useV2UniqueIndexFormat && isUniqueIndex) {
- return IndexVersion::kV2Unique;
- }
-
+IndexVersion IndexDescriptor::getDefaultIndexVersion() {
return IndexVersion::kV2;
}
diff --git a/src/mongo/db/index/index_descriptor.h b/src/mongo/db/index/index_descriptor.h
index f1135ca3f91..b62cbeb9e38 100644
--- a/src/mongo/db/index/index_descriptor.h
+++ b/src/mongo/db/index/index_descriptor.h
@@ -56,7 +56,7 @@ class OperationContext;
*/
class IndexDescriptor {
public:
- enum class IndexVersion { kV0 = 0, kV1 = 1, kV2 = 2, kV2Unique = 3 };
+ enum class IndexVersion { kV0 = 0, kV1 = 1, kV2 = 2 };
static constexpr IndexVersion kLatestIndexVersion = IndexVersion::kV2;
static constexpr StringData k2dIndexBitsFieldName = "bits"_sd;
@@ -132,9 +132,7 @@ public:
/**
* Returns the index version to use if it isn't specified in the index specification.
*/
- static IndexVersion getDefaultIndexVersion(
- ServerGlobalParams::FeatureCompatibility::Version featureCompatibilityVersion,
- bool isUniqueIndex = false);
+ static IndexVersion getDefaultIndexVersion();
//
// Information about the key pattern.
diff --git a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
index 11943d5479e..14a3e57503b 100644
--- a/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
+++ b/src/mongo/db/storage/mmap_v1/btree/btree_interface.cpp
@@ -428,7 +428,6 @@ SortedDataInterface* getMMAPV1Interface(HeadManager* headManager,
headManager, recordStore, cursorRegistry, ordering, indexName, isUnique);
case IndexVersion::kV1:
case IndexVersion::kV2:
- case IndexVersion::kV2Unique:
return new BtreeInterfaceImpl<BtreeLayoutV1>(
headManager, recordStore, cursorRegistry, ordering, indexName, isUnique);
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
index c98617bdc78..14d495386a3 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp
@@ -84,12 +84,6 @@ static const int TempKeyMaxSize = 1024; // this goes away with SERVER-3372
static const WiredTigerItem emptyItem(NULL, 0);
-// Keystring format 7 was used in 3.3.6 - 3.3.8 development releases.
-static const int kKeyStringV0Version = 6;
-static const int kKeyStringV1Version = 8;
-static const int kMinimumIndexVersion = kKeyStringV0Version;
-static const int kMaximumIndexVersion = kKeyStringV1Version;
-
// This is the size constituted by CType byte and the kEnd byte in a Keystring object.
constexpr std::size_t kCTypeAndKEndSize = 2;
@@ -123,6 +117,17 @@ Status checkKeySize(const BSONObj& key) {
}
} // namespace
+
+// Keystring format 7 was used in 3.3.6 - 3.3.8 development releases. 4.2 onwards, unique indexes
+// can be either format version 9 or 10. On upgrading to 4.2, an existing format 6 unique index
+// will upgrade to format 9 and an existing format 8 unique index will upgrade to format 10.
+const int kDataFormatV1KeyStringV0IndexVersionV1 = 6;
+const int kDataFormatV2KeyStringV1IndexVersionV2 = 8;
+const int kDataFormatV3KeyStringV0UniqueIndexVersionV1 = 9;
+const int kDataFormatV4KeyStringV1UniqueIndexVersionV2 = 10;
+const int kMinimumIndexVersion = kDataFormatV1KeyStringV0IndexVersionV1;
+const int kMaximumIndexVersion = kDataFormatV4KeyStringV1UniqueIndexVersionV2;
+
Status WiredTigerIndex::dupKeyError(const BSONObj& key) {
StringBuilder sb;
sb << "E11000 duplicate key error";
@@ -162,6 +167,35 @@ StatusWith<std::string> WiredTigerIndex::parseIndexOptions(const BSONObj& option
}
// static
+std::string WiredTigerIndex::generateAppMetadataString(const IndexDescriptor& desc) {
+ StringBuilder ss;
+
+ int keyStringVersion;
+
+ // This gating variable controls the creation between timestamp safe and timestamp unsafe
+ // unique indexes. The gating condition will be enhanced to check for FCV 4.2 by SERVER-32825
+ // and the gating variable will be removed when FCV 4.2 becomes available.
+ bool createNewStyleUniqueIdx = false;
+
+ if (createNewStyleUniqueIdx && desc.unique() && !desc.isIdIndex()) {
+ keyStringVersion = desc.version() >= IndexDescriptor::IndexVersion::kV2
+ ? kDataFormatV4KeyStringV1UniqueIndexVersionV2
+ : kDataFormatV3KeyStringV0UniqueIndexVersionV1;
+ } else {
+ keyStringVersion = desc.version() >= IndexDescriptor::IndexVersion::kV2
+ ? kDataFormatV2KeyStringV1IndexVersionV2
+ : kDataFormatV1KeyStringV0IndexVersionV1;
+ }
+
+ // Index metadata
+ ss << ",app_metadata=("
+ << "formatVersion=" << keyStringVersion << ',' << "infoObj=" << desc.infoObj().jsonString()
+ << "),";
+
+ return (ss.str());
+}
+
+// static
StatusWith<std::string> WiredTigerIndex::generateCreateString(const std::string& engineName,
const std::string& sysIndexConfig,
const std::string& collIndexConfig,
@@ -212,15 +246,8 @@ StatusWith<std::string> WiredTigerIndex::generateCreateString(const std::string&
}
ss << ",value_format=u";
- // Index versions greater than 2 use KeyString version 1.
- const int keyStringVersion = desc.version() >= IndexDescriptor::IndexVersion::kV2
- ? kKeyStringV1Version
- : kKeyStringV0Version;
-
// Index metadata
- ss << ",app_metadata=("
- << "formatVersion=" << keyStringVersion << ',' << "infoObj=" << desc.infoObj().jsonString()
- << "),";
+ ss << generateAppMetadataString(desc);
bool replicatedWrites = getGlobalReplSettings().usingReplSets() ||
getGlobalReplSettings().getShouldRecoverFromOplogAsStandalone();
@@ -254,7 +281,8 @@ WiredTigerIndex::WiredTigerIndex(OperationContext* ctx,
_tableId(WiredTigerSession::genTableId()),
_collectionNamespace(desc->parentNS()),
_indexName(desc->indexName()),
- _prefix(prefix) {
+ _prefix(prefix),
+ _isIdIndex(desc->isIdIndex()) {
auto version = WiredTigerUtil::checkApplicationMetadataFormatVersion(
ctx, uri, kMinimumIndexVersion, kMaximumIndexVersion);
if (!version.isOK()) {
@@ -269,8 +297,14 @@ WiredTigerIndex::WiredTigerIndex(OperationContext* ctx,
<< " instructions on how to handle this error.");
fassertFailedWithStatusNoTrace(28579, indexVersionStatus);
}
- _keyStringVersion =
- version.getValue() == kKeyStringV1Version ? KeyString::Version::V1 : KeyString::Version::V0;
+ _dataFormatVersion = version.getValue();
+
+ // Index data format 6 and 9 correspond to KeyString version V0 and data format 8 and 10
+ // correspond to KeyString version V1
+ _keyStringVersion = (_dataFormatVersion == kDataFormatV2KeyStringV1IndexVersionV2 ||
+ _dataFormatVersion == kDataFormatV4KeyStringV1UniqueIndexVersionV2)
+ ? KeyString::Version::V1
+ : KeyString::Version::V0;
if (!isReadOnly) {
bool replicatedWrites = getGlobalReplSettings().usingReplSets() ||
@@ -399,11 +433,6 @@ Status WiredTigerIndex::dupKeyCheck(OperationContext* opCtx,
invariant(!hasFieldNames(key));
invariant(unique());
- if (isV2FormatUniqueIndex()) {
- // This is stubbed; specific implementation would come later.
- return Status::OK();
- }
-
WiredTigerCursor curwrap(_uri, _tableId, false, opCtx);
WT_CURSOR* c = curwrap.get();
@@ -511,63 +540,6 @@ bool WiredTigerIndex::isDup(OperationContext* opCtx,
return true;
}
-bool WiredTigerIndexUniqueV2::isDup(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id) {
- KeyString prefixKey(keyStringVersion(), key, _ordering);
- WiredTigerItem prefixKeyItem(prefixKey.getBuffer(), prefixKey.getSize());
- setKey(c, prefixKeyItem.Get());
-
- // An index entry key is KeyString of the prefix key + RecordId. To prevent duplicate prefix
- // key, search a record matching the prefix key.
- int cmp;
- auto searchStatus =
- wiredTigerPrepareConflictRetry(opCtx, [&] { return c->search_near(c, &cmp); });
-
- if (searchStatus == WT_NOTFOUND)
- return false;
- invariantWTOK(searchStatus);
-
- if (cmp == 0)
- return true;
-
- WT_ITEM item;
- // Obtain the key from the record returned by search near.
- invariantWTOK(c->get_key(c, &item));
- KeyString foundKey(keyStringVersion());
- foundKey.resetFromBuffer(item.data, item.size);
- if (std::memcmp(prefixKey.getBuffer(),
- foundKey.getBuffer(),
- std::min(prefixKey.getSize(), foundKey.getSize())) == 0)
- return true;
-
- // We got the smaller key adjacent to prefix key, check the next key too.
- int ret;
- if (cmp < 0) {
- ret = wiredTigerPrepareConflictRetry(opCtx, [&] { return c->next(c); });
- } else {
- // We got the larger key adjacent to prefix key, check the previous key too.
- ret = wiredTigerPrepareConflictRetry(opCtx, [&] { return c->prev(c); });
- }
-
- if (ret == 0) {
- invariantWTOK(c->get_key(c, &item));
- foundKey.resetFromBuffer(item.data, item.size);
- return (std::memcmp(prefixKey.getBuffer(),
- foundKey.getBuffer(),
- std::min(prefixKey.getSize(), foundKey.getSize())) == 0);
- }
-
- // Make sure that next or prev did not fail due to any other error but not found. In case of
- // another error, we are not good to move forward.
- if (ret == WT_NOTFOUND)
- return false;
- fassertFailedWithStatus(40685, wtRCToStatus(ret));
-
- MONGO_UNREACHABLE;
-}
-
Status WiredTigerIndex::initAsEmpty(OperationContext* opCtx) {
// No-op
return Status::OK();
@@ -651,7 +623,7 @@ public:
StandardBulkBuilder(WiredTigerIndex* idx, OperationContext* opCtx, KVPrefix prefix)
: BulkBuilder(idx, opCtx, prefix), _idx(idx) {}
- Status addKey(const BSONObj& key, const RecordId& id) {
+ Status addKey(const BSONObj& key, const RecordId& id) override {
{
const Status s = checkKeySize(key);
if (!s.isOK())
@@ -675,7 +647,7 @@ public:
return Status::OK();
}
- void commit(bool mayInterrupt) {
+ void commit(bool mayInterrupt) override {
// TODO do we still need this?
// this is bizarre, but required as part of the contract
WriteUnitOfWork uow(_opCtx);
@@ -705,16 +677,73 @@ public:
_dupsAllowed(dupsAllowed),
_keyString(idx->keyStringVersion()) {}
- Status addKey(const BSONObj& newKey, const RecordId& id) {
+ Status addKey(const BSONObj& newKey, const RecordId& id) override {
+ if (_idx->isTimestampSafeUniqueIdx()) {
+ return addKeyTimestampSafe(newKey, id);
+ }
+ return addKeyTimestampUnsafe(newKey, id);
+ }
+
+ void commit(bool mayInterrupt) override {
+ WriteUnitOfWork uow(_opCtx);
+ if (!_records.empty()) {
+ // This handles inserting the last unique key.
+ doInsert();
+ }
+ uow.commit();
+ }
+
+private:
+ Status addKeyTimestampSafe(const BSONObj& newKey, const RecordId& id) {
+ {
+ const Status s = checkKeySize(newKey);
+ if (!s.isOK())
+ return s;
+ }
+
+ // Do a duplicate check
+ const int cmp = newKey.woCompare(_previousKey, _ordering);
+ if (cmp == 0) {
+ // Duplicate found!
+ if (!_dupsAllowed) {
+ return _idx->dupKeyError(newKey);
+ }
+ } else {
+ // _previousKey.isEmpty() is only true on the first call to addKey().
+ // newKey must be > the last key
+ invariant(_previousKey.isEmpty() || cmp > 0);
+ }
+
+ _keyString.resetToKey(newKey, _idx->ordering(), id);
+
+ // Can't use WiredTigerCursor since we aren't using the cache.
+ WiredTigerItem keyItem(_keyString.getBuffer(), _keyString.getSize());
+ setKey(_cursor, keyItem.Get());
+
+ WiredTigerItem valueItem = _keyString.getTypeBits().isAllZeros()
+ ? emptyItem
+ : WiredTigerItem(_keyString.getTypeBits().getBuffer(),
+ _keyString.getTypeBits().getSize());
+
+ _cursor->set_value(_cursor, valueItem.Get());
+
+ invariantWTOK(_cursor->insert(_cursor));
+
+ _previousKey = newKey.getOwned();
+ return Status::OK();
+ }
+
+ Status addKeyTimestampUnsafe(const BSONObj& newKey, const RecordId& id) {
{
const Status s = checkKeySize(newKey);
if (!s.isOK())
return s;
}
- const int cmp = newKey.woCompare(_key, _ordering);
+ const int cmp = newKey.woCompare(_previousKey, _ordering);
if (cmp != 0) {
- if (!_key.isEmpty()) { // _key.isEmpty() is only true on the first call to addKey().
+ if (!_previousKey.isEmpty()) {
+ // _previousKey.isEmpty() is only true on the first call to addKey().
invariant(cmp > 0); // newKey must be > the last key
// We are done with dups of the last key so we can insert it now.
doInsert();
@@ -728,26 +757,16 @@ public:
// If we get here, we are in the weird mode where dups are allowed on a unique
// index, so add ourselves to the list of duplicate ids. This also replaces the
- // _key which is correct since any dups seen later are likely to be newer.
+ // _previousKey which is correct since any dups seen later are likely to be newer.
}
- _key = newKey.getOwned();
- _keyString.resetToKey(_key, _idx->ordering());
+ _keyString.resetToKey(newKey, _idx->ordering());
_records.push_back(std::make_pair(id, _keyString.getTypeBits()));
+ _previousKey = newKey.getOwned();
return Status::OK();
}
- void commit(bool mayInterrupt) {
- WriteUnitOfWork uow(_opCtx);
- if (!_records.empty()) {
- // This handles inserting the last unique key.
- doInsert();
- }
- uow.commit();
- }
-
-private:
void doInsert() {
invariant(!_records.empty());
@@ -774,66 +793,8 @@ private:
WiredTigerIndex* _idx;
const bool _dupsAllowed;
- BSONObj _key;
KeyString _keyString;
std::vector<std::pair<RecordId, KeyString::TypeBits>> _records;
-};
-
-/**
- * Bulk builds a V2 format unique index.
- *
- * This is similar to `StandardBulkBuilder`, with duplicate check added in.
- */
-class WiredTigerIndex::UniqueV2BulkBuilder : public BulkBuilder {
-public:
- UniqueV2BulkBuilder(WiredTigerIndex* idx,
- OperationContext* opCtx,
- bool dupsAllowed,
- KVPrefix prefix)
- : BulkBuilder(idx, opCtx, prefix), _idx(idx), _dupsAllowed(dupsAllowed) {}
-
- Status addKey(const BSONObj& newKey, const RecordId& id) {
- {
- const Status s = checkKeySize(newKey);
- if (!s.isOK())
- return s;
- }
-
- // Do a duplicate check
- const int cmp = newKey.woCompare(_previousKey, _ordering);
- if (cmp == 0) {
- // Duplicate found!
- if (!_dupsAllowed) {
- return _idx->dupKeyError(newKey);
- }
- } else {
- // _previousKey.isEmpty() is only true on the first call to addKey().
- // newKey must be > the last key
- invariant(_previousKey.isEmpty() || cmp > 0);
- }
-
- KeyString keyString(_idx->keyStringVersion(), newKey, _idx->_ordering, id);
-
- // Can't use WiredTigerCursor since we aren't using the cache.
- WiredTigerItem keyItem(keyString.getBuffer(), keyString.getSize());
- setKey(_cursor, keyItem.Get());
-
- WiredTigerItem valueItem = keyString.getTypeBits().isAllZeros()
- ? emptyItem
- : WiredTigerItem(keyString.getTypeBits().getBuffer(),
- keyString.getTypeBits().getSize());
-
- _cursor->set_value(_cursor, valueItem.Get());
-
- invariantWTOK(_cursor->insert(_cursor));
-
- _previousKey = newKey.getOwned();
- return Status::OK();
- }
-
-private:
- WiredTigerIndex* _idx;
- const bool _dupsAllowed;
BSONObj _previousKey;
};
@@ -965,6 +926,23 @@ public:
}
protected:
+ // Called after _key has been filled in, ie a new key to be processed has been fetched.
+ // Must not throw WriteConflictException, throwing a WriteConflictException will retry the
+ // operation effectively skipping over this key.
+ virtual void updateIdAndTypeBits() {
+ _id = KeyString::decodeRecordIdAtEnd(_key.getBuffer(), _key.getSize());
+
+ WT_CURSOR* c = _cursor->get();
+ WT_ITEM item;
+ // Can't get WT_ROLLBACK and hence won't throw an exception.
+ // Don't expect WT_PREPARE_CONFLICT either.
+ auto ret = c->get_value(c, &item);
+ invariant(ret != WT_ROLLBACK && ret != WT_PREPARE_CONFLICT);
+ invariantWTOK(ret);
+ BufReader br(item.data, item.size);
+ _typeBits.resetFromBuffer(&br);
+ }
+
void setKey(WT_CURSOR* cursor, const WT_ITEM* item) {
if (_prefix == KVPrefix::kNotPrefixed) {
cursor->set_key(cursor, item);
@@ -1134,7 +1112,7 @@ protected:
return;
}
- _updateIdAndTypeBits();
+ updateIdAndTypeBits();
}
OperationContext* _opCtx;
@@ -1161,66 +1139,10 @@ protected:
KVPrefix _prefix;
std::unique_ptr<KeyString> _endPosition;
-
-private:
- // Called after _key has been filled in. Must not throw WriteConflictException.
- void _updateIdAndTypeBits() {
- TRACE_INDEX << "KeyString: [" << _key.toString() << "]";
-
- auto keySize = KeyString::getKeySize(
- _key.getBuffer(), _key.getSize(), _idx.ordering(), _key.getTypeBits());
-
- // An index can have both old and new format index keys after a rolling upgrade. Detect
- // correct index key format by checking key's size. Old format keys just had the index key
- // while new format key has index key + Record id.
- // When KeyString contains just the key, the RecordId is in value.
- if (_key.getSize() == keySize + kCTypeAndKEndSize) {
- _updateIdAndTypeBitsFromValue();
- } else {
- // The RecordId is in the key at the end.
- _updateIdFromKeyAndTypeBitsFromValue();
- }
- }
-
- void _updateIdAndTypeBitsFromValue() {
- // We assume that cursors can only ever see unique indexes in their "pristine" state,
- // where no duplicates are possible. The cases where dups are allowed should hold
- // sufficient locks to ensure that no cursor ever sees them.
- WT_CURSOR* c = _cursor->get();
- WT_ITEM item;
- invariantWTOK(c->get_value(c, &item));
-
- BufReader br(item.data, item.size);
- _id = KeyString::decodeRecordId(&br);
- _typeBits.resetFromBuffer(&br);
-
- if (!br.atEof()) {
- severe() << "Unique index cursor seeing multiple records for key "
- << redact(curr(kWantKey)->key) << " in index " << _idx.indexName() << " ("
- << _idx.uri() << ") belonging to collection " << _idx.collectionNamespace();
- fassertFailed(28608);
- }
- }
-
- void _updateIdFromKeyAndTypeBitsFromValue() {
- _id = KeyString::decodeRecordIdAtEnd(_key.getBuffer(), _key.getSize());
-
- WT_CURSOR* c = _cursor->get();
- WT_ITEM item;
- invariantWTOK(c->get_value(c, &item));
- BufReader br(item.data, item.size);
- _typeBits.resetFromBuffer(&br);
- }
};
-class WiredTigerIndexStandardCursor final : public WiredTigerIndexCursorBase {
-public:
- WiredTigerIndexStandardCursor(const WiredTigerIndex& idx,
- OperationContext* opCtx,
- bool forward,
- KVPrefix prefix)
- : WiredTigerIndexCursorBase(idx, opCtx, forward, prefix) {}
-};
+// The Standard Cursor doesn't need anything more than the base has.
+using WiredTigerIndexStandardCursor = WiredTigerIndexCursorBase;
class WiredTigerIndexUniqueCursor final : public WiredTigerIndexCursorBase {
public:
@@ -1230,31 +1152,33 @@ public:
KVPrefix prefix)
: WiredTigerIndexCursorBase(idx, opCtx, forward, prefix) {}
- boost::optional<IndexKeyEntry> seekExact(const BSONObj& key, RequestedInfo parts) override {
- _query.resetToKey(stripFieldNames(key), _idx.ordering());
- const WiredTigerItem keyItem(_query.getBuffer(), _query.getSize());
+ // Called after _key has been filled in, ie a new key to be processed has been fetched.
+ // Must not throw WriteConflictException, throwing a WriteConflictException will retry the
+ // operation effectively skipping over this key.
+ void updateIdAndTypeBits() override {
+ TRACE_INDEX << "Unique Index KeyString: [" << _key.toString() << "]";
+
+ // After a rolling upgrade an index can have keys from both timestamp unsafe (old) and
+ // timestamp safe (new) unique indexes. Detect correct index key format by checking key's
+ // size. Old format keys just had the index key while new format key has index key + Record
+ // id. _id indexes remain at the old format. When KeyString contains just the key, the
+ // RecordId is in value.
+ if (_idx.isIdIndex() || !_idx.isTimestampSafeUniqueIdx()) {
+ _updateIdAndTypeBitsFromValue();
+ return;
+ }
- WT_CURSOR* c = _cursor->get();
- setKey(c, keyItem.Get());
+ auto keySize = KeyString::getKeySize(
+ _key.getBuffer(), _key.getSize(), _idx.ordering(), _key.getTypeBits());
- // Using search rather than search_near.
- int ret = wiredTigerPrepareConflictRetry(_opCtx, [&] { return c->search(c); });
- if (ret != WT_NOTFOUND)
- invariantWTOK(ret);
- _cursorAtEof = ret == WT_NOTFOUND;
- updatePosition();
- dassert(_eof || _key.compare(_query) == 0);
- return curr(parts);
+ if (_key.getSize() == keySize + kCTypeAndKEndSize) {
+ _updateIdAndTypeBitsFromValue();
+ } else {
+ // The RecordId is in the key at the end. This implementation is provided by the
+ // base class, let us just invoke that functionality here.
+ WiredTigerIndexCursorBase::updateIdAndTypeBits();
+ }
}
-};
-
-class WiredTigerIndexUniqueV2Cursor final : public WiredTigerIndexCursorBase {
-public:
- WiredTigerIndexUniqueV2Cursor(const WiredTigerIndex& idx,
- OperationContext* opCtx,
- bool forward,
- KVPrefix prefix)
- : WiredTigerIndexCursorBase(idx, opCtx, forward, prefix) {}
boost::optional<IndexKeyEntry> seekExact(const BSONObj& key, RequestedInfo parts) override {
_query.resetToKey(stripFieldNames(key), _idx.ordering());
@@ -1272,6 +1196,34 @@ public:
dassert(_eof || _key.compare(_query) == 0);
return curr(parts);
}
+
+private:
+ // Called after _key has been filled in, ie a new key to be processed has been fetched.
+ // Must not throw WriteConflictException, throwing a WriteConflictException will retry the
+ // operation effectively skipping over this key.
+ void _updateIdAndTypeBitsFromValue() {
+ // We assume that cursors can only ever see unique indexes in their "pristine" state,
+ // where no duplicates are possible. The cases where dups are allowed should hold
+ // sufficient locks to ensure that no cursor ever sees them.
+ WT_CURSOR* c = _cursor->get();
+ WT_ITEM item;
+ // Can't get WT_ROLLBACK and hence won't throw an exception.
+ // Don't expect WT_PREPARE_CONFLICT either.
+ auto ret = c->get_value(c, &item);
+ invariant(ret != WT_ROLLBACK && ret != WT_PREPARE_CONFLICT);
+ invariantWTOK(ret);
+
+ BufReader br(item.data, item.size);
+ _id = KeyString::decodeRecordId(&br);
+ _typeBits.resetFromBuffer(&br);
+
+ if (!br.atEof()) {
+ severe() << "Unique index cursor seeing multiple records for key "
+ << redact(curr(kWantKey)->key) << " in index " << _idx.indexName() << " ("
+ << _idx.uri() << ") belonging to collection " << _idx.collectionNamespace();
+ fassertFailed(28608);
+ }
+ }
};
} // namespace
@@ -1288,29 +1240,76 @@ std::unique_ptr<SortedDataInterface::Cursor> WiredTigerIndexUnique::newCursor(
return stdx::make_unique<WiredTigerIndexUniqueCursor>(*this, opCtx, forward, _prefix);
}
-/**
- * This is duplicate of `WiredTigerIndexUnique` at the moment; might change later.
- */
-WiredTigerIndexUniqueV2::WiredTigerIndexUniqueV2(OperationContext* ctx,
- const std::string& uri,
- const IndexDescriptor* desc,
- KVPrefix prefix,
- bool isReadOnly)
- : WiredTigerIndex(ctx, uri, desc, prefix, isReadOnly), _partial(desc->isPartial()) {}
-
-std::unique_ptr<SortedDataInterface::Cursor> WiredTigerIndexUniqueV2::newCursor(
- OperationContext* opCtx, bool forward) const {
- return stdx::make_unique<WiredTigerIndexUniqueV2Cursor>(*this, opCtx, forward, _prefix);
-}
-
SortedDataBuilderInterface* WiredTigerIndexUnique::getBulkBuilder(OperationContext* opCtx,
bool dupsAllowed) {
return new UniqueBulkBuilder(this, opCtx, dupsAllowed, _prefix);
}
-SortedDataBuilderInterface* WiredTigerIndexUniqueV2::getBulkBuilder(OperationContext* opCtx,
- bool dupsAllowed) {
- return new UniqueV2BulkBuilder(this, opCtx, dupsAllowed, _prefix);
+bool WiredTigerIndexUnique::isTimestampSafeUniqueIdx() const {
+ if (_dataFormatVersion == kDataFormatV1KeyStringV0IndexVersionV1 ||
+ _dataFormatVersion == kDataFormatV2KeyStringV1IndexVersionV2) {
+ return false;
+ }
+ return true;
+}
+
+bool WiredTigerIndexUnique::isDup(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id) {
+ if (!isTimestampSafeUniqueIdx()) {
+ // The parent class provides a functionality that works fine, just use that.
+ return WiredTigerIndex::isDup(opCtx, c, key, id);
+ }
+
+ // This procedure to determine duplicates is exclusive for timestamp safe unique indexes.
+ KeyString prefixKey(keyStringVersion(), key, _ordering);
+ WiredTigerItem prefixKeyItem(prefixKey.getBuffer(), prefixKey.getSize());
+ setKey(c, prefixKeyItem.Get());
+
+ // An index entry key is KeyString of the prefix key + RecordId. To prevent duplicate prefix
+ // key, search a record matching the prefix key.
+ int cmp;
+ auto searchStatus =
+ wiredTigerPrepareConflictRetry(opCtx, [&] { return c->search_near(c, &cmp); });
+
+ if (searchStatus == WT_NOTFOUND)
+ return false;
+ invariantWTOK(searchStatus);
+
+ if (cmp == 0)
+ return true;
+
+ WT_ITEM item;
+ // Obtain the key from the record returned by search near.
+ invariantWTOK(c->get_key(c, &item));
+ if (std::memcmp(prefixKey.getBuffer(), item.data, std::min(prefixKey.getSize(), item.size)) ==
+ 0)
+ return true;
+
+ int ret;
+ if (cmp < 0) {
+ // We got the smaller key adjacent to prefix key, check the next key too.
+ ret = wiredTigerPrepareConflictRetry(opCtx, [&] { return c->next(c); });
+ } else {
+ // We got the larger key adjacent to prefix key, check the previous key too.
+ ret = wiredTigerPrepareConflictRetry(opCtx, [&] { return c->prev(c); });
+ }
+
+ if (ret == 0) {
+ invariantWTOK(c->get_key(c, &item));
+ return (std::memcmp(prefixKey.getBuffer(),
+ item.data,
+ std::min(prefixKey.getSize(), item.size)) == 0);
+ }
+
+ // Make sure that next or prev did not fail due to any other error but not found. In case of
+ // another error, we are not good to move forward.
+ if (ret == WT_NOTFOUND)
+ return false;
+ fassertFailedWithStatus(40685, wtRCToStatus(ret));
+
+ MONGO_UNREACHABLE;
}
Status WiredTigerIndexUnique::_insert(OperationContext* opCtx,
@@ -1318,6 +1317,17 @@ Status WiredTigerIndexUnique::_insert(OperationContext* opCtx,
const BSONObj& key,
const RecordId& id,
bool dupsAllowed) {
+ if (isTimestampSafeUniqueIdx()) {
+ return _insertTimestampSafe(opCtx, c, key, id, dupsAllowed);
+ }
+ return _insertTimestampUnsafe(opCtx, c, key, id, dupsAllowed);
+}
+
+Status WiredTigerIndexUnique::_insertTimestampUnsafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed) {
const KeyString data(keyStringVersion(), key, _ordering);
WiredTigerItem keyItem(data.getBuffer(), data.getSize());
@@ -1378,12 +1388,12 @@ Status WiredTigerIndexUnique::_insert(OperationContext* opCtx,
return wtRCToStatus(c->update(c));
}
-Status WiredTigerIndexUniqueV2::_insert(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id,
- bool dupsAllowed) {
- TRACE_INDEX << "V2Unique key: " << key << " id: " << id;
+Status WiredTigerIndexUnique::_insertTimestampSafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed) {
+ TRACE_INDEX << "Timestamp safe unique idx key: " << key << " id: " << id;
int ret;
@@ -1400,7 +1410,7 @@ Status WiredTigerIndexUniqueV2::_insert(OperationContext* opCtx,
ret = WT_OP_CHECK(c->insert(c));
// An entry with prefix key already exists. This can happen only during rolling upgrade when
- // both old and new format index key could be present.
+ // both timestamp unsafe and timestamp safe index format keys could be present.
if (ret == WT_DUPLICATE_KEY) {
return dupKeyError(key);
}
@@ -1447,6 +1457,17 @@ void WiredTigerIndexUnique::_unindex(OperationContext* opCtx,
const BSONObj& key,
const RecordId& id,
bool dupsAllowed) {
+ if (isTimestampSafeUniqueIdx()) {
+ return _unindexTimestampSafe(opCtx, c, key, id, dupsAllowed);
+ }
+ return _unindexTimestampUnsafe(opCtx, c, key, id, dupsAllowed);
+}
+
+void WiredTigerIndexUnique::_unindexTimestampUnsafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed) {
KeyString data(keyStringVersion(), key, _ordering);
WiredTigerItem keyItem(data.getBuffer(), data.getSize());
setKey(c, keyItem.Get());
@@ -1548,11 +1569,11 @@ void WiredTigerIndexUnique::_unindex(OperationContext* opCtx,
invariantWTOK(c->update(c));
}
-void WiredTigerIndexUniqueV2::_unindex(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id,
- bool dupsAllowed) {
+void WiredTigerIndexUnique::_unindexTimestampSafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed) {
KeyString data(keyStringVersion(), key, _ordering, id);
WiredTigerItem item(data.getBuffer(), data.getSize());
setKey(c, item.Get());
@@ -1562,7 +1583,10 @@ void WiredTigerIndexUniqueV2::_unindex(OperationContext* opCtx,
return;
}
- // WT_NOTFOUND is possible if index key is in old format. Retry removal of key using old format.
+ // After a rolling upgrade an index can have keys from both timestamp unsafe (old) and
+ // timestamp safe (new) unique indexes. Old format keys just had the index key while new
+ // format key has index key + Record id. WT_NOTFOUND is possible if index key is in old format.
+ // Retry removal of key using old format.
KeyString oldFormatKey(keyStringVersion(), key, _ordering);
WiredTigerItem keyItem(oldFormatKey.getBuffer(), oldFormatKey.getSize());
setKey(c, keyItem.Get());
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
index 9202d59e39e..94ed0a371e0 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.h
@@ -54,6 +54,13 @@ public:
static StatusWith<std::string> parseIndexOptions(const BSONObj& options);
/**
+ * Creates the "app_metadata" string for the index from the index descriptor, to be stored
+ * in WiredTiger's metadata. The output string is of the form:
+ * ",app_metadata=(...)," and can be appended to the config strings for WiredTiger's API calls.
+ */
+ static std::string generateAppMetadataString(const IndexDescriptor& desc);
+
+ /**
* Creates a configuration string suitable for 'config' parameter in WT_SESSION::create().
* Configuration string is constructed from:
* built-in defaults
@@ -141,13 +148,13 @@ public:
return _indexName;
}
- virtual bool unique() const = 0;
-
- // Returns true if V2 unique index format is supported.
- virtual bool isV2FormatUniqueIndex() const {
- return false;
+ bool isIdIndex() const {
+ return _isIdIndex;
}
+ virtual bool unique() const = 0;
+ virtual bool isTimestampSafeUniqueIdx() const = 0;
+
Status dupKeyError(const BSONObj& key);
protected:
@@ -168,19 +175,20 @@ protected:
class BulkBuilder;
class StandardBulkBuilder;
class UniqueBulkBuilder;
- class UniqueV2BulkBuilder;
const Ordering _ordering;
- // The keystring version is effectively const after the WiredTigerIndex instance is constructed.
+ // The keystring and data format version are effectively const after the WiredTigerIndex
+ // instance is constructed.
KeyString::Version _keyStringVersion;
+ int _dataFormatVersion;
std::string _uri;
uint64_t _tableId;
std::string _collectionNamespace;
std::string _indexName;
KVPrefix _prefix;
+ bool _isIdIndex;
};
-
class WiredTigerIndexUnique : public WiredTigerIndex {
public:
WiredTigerIndexUnique(OperationContext* ctx,
@@ -198,42 +206,12 @@ public:
return true;
}
- Status _insert(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id,
- bool dupsAllowed) override;
-
- void _unindex(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id,
- bool dupsAllowed) override;
-
-private:
- bool _partial;
-};
+ bool isTimestampSafeUniqueIdx() const override;
-class WiredTigerIndexUniqueV2 : public WiredTigerIndex {
-public:
- WiredTigerIndexUniqueV2(OperationContext* ctx,
- const std::string& uri,
- const IndexDescriptor* desc,
- KVPrefix prefix,
- bool readOnly = false);
-
- std::unique_ptr<SortedDataInterface::Cursor> newCursor(OperationContext* opCtx,
- bool forward) const override;
-
- SortedDataBuilderInterface* getBulkBuilder(OperationContext* opCtx, bool dupsAllowed) override;
-
- bool unique() const override {
- return true;
- }
-
- bool isV2FormatUniqueIndex() const override {
- return true;
- }
+ bool isDup(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id) override;
Status _insert(OperationContext* opCtx,
WT_CURSOR* c,
@@ -241,16 +219,35 @@ public:
const RecordId& id,
bool dupsAllowed) override;
+ Status _insertTimestampUnsafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed);
+
+ Status _insertTimestampSafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed);
+
void _unindex(OperationContext* opCtx,
WT_CURSOR* c,
const BSONObj& key,
const RecordId& id,
bool dupsAllowed) override;
- bool isDup(OperationContext* opCtx,
- WT_CURSOR* c,
- const BSONObj& key,
- const RecordId& id) override;
+ void _unindexTimestampUnsafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed);
+
+ void _unindexTimestampSafe(OperationContext* opCtx,
+ WT_CURSOR* c,
+ const BSONObj& key,
+ const RecordId& id,
+ bool dupsAllowed);
private:
bool _partial;
@@ -273,6 +270,10 @@ public:
return false;
}
+ bool isTimestampSafeUniqueIdx() const override {
+ return false;
+ }
+
Status _insert(OperationContext* opCtx,
WT_CURSOR* c,
const BSONObj& key,
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp
index f58fe7cd92e..bf7db8530ec 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp
@@ -832,13 +832,7 @@ SortedDataInterface* WiredTigerKVEngine::getGroupedSortedDataInterface(Operation
const IndexDescriptor* desc,
KVPrefix prefix) {
if (desc->unique()) {
- // MongoDB 4.0 onwards new index version `kV2Unique` would be supported. By default unique
- // index would be created with index version `kV2`. New format unique index would be created
- // only if `IndexVersion` is `kV2Unique` and for non _id indexes.
- if (desc->version() == IndexDescriptor::IndexVersion::kV2Unique && !desc->isIdIndex())
- return new WiredTigerIndexUniqueV2(opCtx, _uri(ident), desc, prefix, _readOnly);
- else
- return new WiredTigerIndexUnique(opCtx, _uri(ident), desc, prefix, _readOnly);
+ return new WiredTigerIndexUnique(opCtx, _uri(ident), desc, prefix, _readOnly);
}
return new WiredTigerIndexStandard(opCtx, _uri(ident), desc, prefix, _readOnly);