summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/repl/databases_cloner_test.cpp4
-rw-r--r--src/mongo/db/repl/initial_syncer_test.cpp40
-rw-r--r--src/mongo/db/repl/storage_interface.h10
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp93
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h10
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp228
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h22
7 files changed, 291 insertions, 116 deletions
diff --git a/src/mongo/db/repl/databases_cloner_test.cpp b/src/mongo/db/repl/databases_cloner_test.cpp
index 369b8a27c38..56abaa0c4bd 100644
--- a/src/mongo/db/repl/databases_cloner_test.cpp
+++ b/src/mongo/db/repl/databases_cloner_test.cpp
@@ -146,14 +146,14 @@ protected:
return Status::OK();
};
_storageInterface.insertDocumentFn = [this](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) {
++_storageInterfaceWorkDone.documentsInsertedCount;
return Status::OK();
};
_storageInterface.insertDocumentsFn = [this](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& ops) {
_storageInterfaceWorkDone.insertedOplogEntries = true;
++_storageInterfaceWorkDone.oplogEntriesInserted;
diff --git a/src/mongo/db/repl/initial_syncer_test.cpp b/src/mongo/db/repl/initial_syncer_test.cpp
index 26735134ade..c4c8e26d72c 100644
--- a/src/mongo/db/repl/initial_syncer_test.cpp
+++ b/src/mongo/db/repl/initial_syncer_test.cpp
@@ -261,7 +261,7 @@ protected:
return Status::OK();
};
_storageInterface->insertDocumentFn = [this](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) {
LockGuard lock(_storageInterfaceWorkDoneMutex);
@@ -269,7 +269,7 @@ protected:
return Status::OK();
};
_storageInterface->insertDocumentsFn = [this](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& ops) {
LockGuard lock(_storageInterfaceWorkDoneMutex);
_storageInterfaceWorkDone.insertedOplogEntries = true;
@@ -2396,11 +2396,12 @@ TEST_F(
TimestampedBSONObj insertDocumentDoc;
long long insertDocumentTerm;
_storageInterface->insertDocumentFn =
- [&insertDocumentDoc, &insertDocumentNss, &insertDocumentTerm](OperationContext*,
- const NamespaceString& nss,
- const TimestampedBSONObj& doc,
- long long term) {
- insertDocumentNss = nss;
+ [&insertDocumentDoc, &insertDocumentNss, &insertDocumentTerm](
+ OperationContext*,
+ const NamespaceStringOrUUID& nsOrUUID,
+ const TimestampedBSONObj& doc,
+ long long term) {
+ insertDocumentNss = *nsOrUUID.nss();
insertDocumentDoc = doc;
insertDocumentTerm = term;
return Status(ErrorCodes::OperationFailed, "failed to insert oplog entry");
@@ -2462,19 +2463,18 @@ TEST_F(
NamespaceString insertDocumentNss;
TimestampedBSONObj insertDocumentDoc;
long long insertDocumentTerm;
- _storageInterface->insertDocumentFn = [initialSyncer,
- &insertDocumentDoc,
- &insertDocumentNss,
- &insertDocumentTerm](OperationContext*,
- const NamespaceString& nss,
- const TimestampedBSONObj& doc,
- long long term) {
- insertDocumentNss = nss;
- insertDocumentDoc = doc;
- insertDocumentTerm = term;
- initialSyncer->shutdown().transitional_ignore();
- return Status::OK();
- };
+ _storageInterface->insertDocumentFn =
+ [initialSyncer, &insertDocumentDoc, &insertDocumentNss, &insertDocumentTerm](
+ OperationContext*,
+ const NamespaceStringOrUUID& nsOrUUID,
+ const TimestampedBSONObj& doc,
+ long long term) {
+ insertDocumentNss = *nsOrUUID.nss();
+ insertDocumentDoc = doc;
+ insertDocumentTerm = term;
+ initialSyncer->shutdown().transitional_ignore();
+ return Status::OK();
+ };
_syncSourceSelector->setChooseNewSyncSourceResult_forTest(HostAndPort("localhost", 12345));
ASSERT_OK(initialSyncer->startup(opCtx.get(), maxAttempts));
diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h
index 9f517d96971..be79c228483 100644
--- a/src/mongo/db/repl/storage_interface.h
+++ b/src/mongo/db/repl/storage_interface.h
@@ -125,7 +125,7 @@ public:
* an error is returned.
*/
virtual Status insertDocument(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) = 0;
@@ -135,7 +135,7 @@ public:
* It is an error to call this function with an empty set of documents.
*/
virtual Status insertDocuments(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs) = 0;
/**
@@ -277,7 +277,7 @@ public:
* Not supported on collections with a default collation.
*/
virtual StatusWith<BSONObj> findById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) = 0;
/**
@@ -287,7 +287,7 @@ public:
* Not supported on collections with a default collation.
*/
virtual StatusWith<BSONObj> deleteById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) = 0;
/**
@@ -299,7 +299,7 @@ public:
* applied.
*/
virtual Status upsertById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey,
const BSONObj& update) = 0;
diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp
index d816ee05ea6..58a6eaf21f2 100644
--- a/src/mongo/db/repl/storage_interface_impl.cpp
+++ b/src/mongo/db/repl/storage_interface_impl.cpp
@@ -277,10 +277,10 @@ StorageInterfaceImpl::createCollectionForBulkLoading(
}
Status StorageInterfaceImpl::insertDocument(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) {
- return insertDocuments(opCtx, nss, {InsertStatement(doc.obj, doc.timestamp, term)});
+ return insertDocuments(opCtx, nsOrUUID, {InsertStatement(doc.obj, doc.timestamp, term)});
}
namespace {
@@ -291,30 +291,32 @@ namespace {
*/
template <typename AutoGetCollectionType>
StatusWith<Collection*> getCollection(const AutoGetCollectionType& autoGetCollection,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::string& message) {
if (!autoGetCollection.getDb()) {
+ StringData dbName = nsOrUUID.nss() ? nsOrUUID.nss()->db() : nsOrUUID.dbname();
return {ErrorCodes::NamespaceNotFound,
- str::stream() << "Database [" << nss.db() << "] not found. " << message};
+ str::stream() << "Database [" << dbName << "] not found. " << message};
}
auto collection = autoGetCollection.getCollection();
if (!collection) {
return {ErrorCodes::NamespaceNotFound,
- str::stream() << "Collection [" << nss.ns() << "] not found. " << message};
+ str::stream() << "Collection [" << nsOrUUID.toString() << "] not found. "
+ << message};
}
return collection;
}
Status insertDocumentsSingleBatch(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
std::vector<InsertStatement>::const_iterator begin,
std::vector<InsertStatement>::const_iterator end) {
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
+ AutoGetCollection autoColl(opCtx, nsOrUUID, MODE_IX);
auto collectionResult =
- getCollection(autoColl, nss, "The collection must exist before inserting documents.");
+ getCollection(autoColl, nsOrUUID, "The collection must exist before inserting documents.");
if (!collectionResult.isOK()) {
return collectionResult.getStatus();
}
@@ -334,11 +336,11 @@ Status insertDocumentsSingleBatch(OperationContext* opCtx,
} // namespace
Status StorageInterfaceImpl::insertDocuments(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs) {
if (docs.size() > 1U) {
try {
- if (insertDocumentsSingleBatch(opCtx, nss, docs.cbegin(), docs.cend()).isOK()) {
+ if (insertDocumentsSingleBatch(opCtx, nsOrUUID, docs.cbegin(), docs.cend()).isOK()) {
return Status::OK();
}
} catch (...) {
@@ -349,9 +351,9 @@ Status StorageInterfaceImpl::insertDocuments(OperationContext* opCtx,
// Try to insert the batch one-at-a-time because the batch failed all-at-once inserting.
for (auto it = docs.cbegin(); it != docs.cend(); ++it) {
- auto status =
- writeConflictRetry(opCtx, "StorageInterfaceImpl::insertDocuments", nss.ns(), [&] {
- auto status = insertDocumentsSingleBatch(opCtx, nss, it, it + 1);
+ auto status = writeConflictRetry(
+ opCtx, "StorageInterfaceImpl::insertDocuments", nsOrUUID.toString(), [&] {
+ auto status = insertDocumentsSingleBatch(opCtx, nsOrUUID, it, it + 1);
if (!status.isOK()) {
return status;
}
@@ -555,7 +557,7 @@ DeleteStageParams makeDeleteStageParamsForDeleteDocuments() {
enum class FindDeleteMode { kFind, kDelete };
StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments(
OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
boost::optional<StringData> indexName,
StorageInterface::ScanDirection scanDirection,
const BSONObj& startKey,
@@ -567,15 +569,15 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments(
auto opStr = isFind ? "StorageInterfaceImpl::find" : "StorageInterfaceImpl::delete";
- return writeConflictRetry(opCtx, opStr, nss.ns(), [&] {
+ return writeConflictRetry(opCtx, opStr, nsOrUUID.toString(), [&] {
// We need to explicitly use this in a few places to help the type inference. Use a
// shorthand.
using Result = StatusWith<std::vector<BSONObj>>;
auto collectionAccessMode = isFind ? MODE_IS : MODE_IX;
- AutoGetCollection autoColl(opCtx, nss, collectionAccessMode);
+ AutoGetCollection autoColl(opCtx, nsOrUUID, collectionAccessMode);
auto collectionResult = getCollection(
- autoColl, nss, str::stream() << "Unable to proceed with " << opStr << ".");
+ autoColl, nsOrUUID, str::stream() << "Unable to proceed with " << opStr << ".");
if (!collectionResult.isOK()) {
return Result(collectionResult.getStatus());
}
@@ -598,7 +600,7 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments(
// Use collection scan.
planExecutor = isFind
? InternalPlanner::collectionScan(
- opCtx, nss.ns(), collection, PlanExecutor::NO_YIELD, direction)
+ opCtx, nsOrUUID.toString(), collection, PlanExecutor::NO_YIELD, direction)
: InternalPlanner::deleteWithCollectionScan(
opCtx,
collection,
@@ -614,14 +616,15 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments(
indexCatalog->findIndexByName(opCtx, *indexName, includeUnfinishedIndexes);
if (!indexDescriptor) {
return Result(ErrorCodes::IndexNotFound,
- str::stream() << "Index not found, ns:" << nss.ns() << ", index: "
+ str::stream() << "Index not found, ns:" << nsOrUUID.toString()
+ << ", index: "
<< *indexName);
}
if (indexDescriptor->isPartial()) {
return Result(ErrorCodes::IndexOptionsConflict,
str::stream()
<< "Partial index is not allowed for this operation, ns:"
- << nss.ns()
+ << nsOrUUID.toString()
<< ", index: "
<< *indexName);
}
@@ -674,12 +677,12 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments(
}
StatusWith<BSONObj> _findOrDeleteById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey,
FindDeleteMode mode) {
auto wrappedIdKey = idKey.wrap("");
auto result = _findOrDeleteDocuments(opCtx,
- nss,
+ nsOrUUID,
kIdIndexName,
StorageInterface::ScanDirection::kForward,
wrappedIdKey,
@@ -692,7 +695,9 @@ StatusWith<BSONObj> _findOrDeleteById(OperationContext* opCtx,
}
const auto& docs = result.getValue();
if (docs.empty()) {
- return {ErrorCodes::NoSuchKey, str::stream() << "No document found with _id: " << idKey};
+ return {ErrorCodes::NoSuchKey,
+ str::stream() << "No document found with _id: " << redact(idKey) << " in namespace "
+ << nsOrUUID.toString()};
}
return docs.front();
@@ -764,15 +769,15 @@ StatusWith<BSONObj> StorageInterfaceImpl::findSingleton(OperationContext* opCtx,
}
StatusWith<BSONObj> StorageInterfaceImpl::findById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) {
- return _findOrDeleteById(opCtx, nss, idKey, FindDeleteMode::kFind);
+ return _findOrDeleteById(opCtx, nsOrUUID, idKey, FindDeleteMode::kFind);
}
StatusWith<BSONObj> StorageInterfaceImpl::deleteById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) {
- return _findOrDeleteById(opCtx, nss, idKey, FindDeleteMode::kDelete);
+ return _findOrDeleteById(opCtx, nsOrUUID, idKey, FindDeleteMode::kDelete);
}
namespace {
@@ -843,7 +848,7 @@ Status _updateWithQuery(OperationContext* opCtx,
} // namespace
Status StorageInterfaceImpl::upsertById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey,
const BSONObj& update) {
// Validate and construct an _id query for UpdateResult.
@@ -854,15 +859,24 @@ Status StorageInterfaceImpl::upsertById(OperationContext* opCtx,
}
auto query = queryResult.getValue();
- UpdateRequest request(nss);
- request.setQuery(query);
- request.setUpdates(update);
- request.setUpsert(true);
- invariant(!request.isMulti()); // This follows from using an exact _id query.
- invariant(!request.shouldReturnAnyDocs());
- invariant(PlanExecutor::NO_YIELD == request.getYieldPolicy());
+ return writeConflictRetry(opCtx, "StorageInterfaceImpl::upsertById", nsOrUUID.toString(), [&] {
+ AutoGetCollection autoColl(opCtx, nsOrUUID, MODE_IX);
+ auto collectionResult = getCollection(autoColl, nsOrUUID, "Unable to update document.");
+ if (!collectionResult.isOK()) {
+ return collectionResult.getStatus();
+ }
+ auto collection = collectionResult.getValue();
+
+ // We can create an UpdateRequest now that the collection's namespace has been resolved, in
+ // the event it was specified as a UUID.
+ UpdateRequest request(collection->ns());
+ request.setQuery(query);
+ request.setUpdates(update);
+ request.setUpsert(true);
+ invariant(!request.isMulti()); // This follows from using an exact _id query.
+ invariant(!request.shouldReturnAnyDocs());
+ invariant(PlanExecutor::NO_YIELD == request.getYieldPolicy());
- return writeConflictRetry(opCtx, "StorageInterfaceImpl::upsertById", nss.ns(), [&] {
// ParsedUpdate needs to be inside the write conflict retry loop because it contains
// the UpdateDriver whose state may be modified while we are applying the update.
ParsedUpdate parsedUpdate(opCtx, &request);
@@ -871,13 +885,6 @@ Status StorageInterfaceImpl::upsertById(OperationContext* opCtx,
return parsedUpdateStatus;
}
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
- auto collectionResult = getCollection(autoColl, nss, "Unable to update document.");
- if (!collectionResult.isOK()) {
- return collectionResult.getStatus();
- }
- auto collection = collectionResult.getValue();
-
// We're using the ID hack to perform the update so we have to disallow collections
// without an _id index.
auto descriptor = collection->getIndexCatalog()->findIdIndex(opCtx);
diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h
index 6a9b6c07ee6..761875dd0bf 100644
--- a/src/mongo/db/repl/storage_interface_impl.h
+++ b/src/mongo/db/repl/storage_interface_impl.h
@@ -66,12 +66,12 @@ public:
const std::vector<BSONObj>& secondaryIndexSpecs) override;
Status insertDocument(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) override;
Status insertDocuments(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs) override;
Status dropReplicatedDatabases(OperationContext* opCtx) override;
@@ -127,15 +127,15 @@ public:
const TimestampedBSONObj& update) override;
StatusWith<BSONObj> findById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) override;
StatusWith<BSONObj> deleteById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey) override;
Status upsertById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey,
const BSONObj& update) override;
diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp
index ddacb478aa6..588ae856a2e 100644
--- a/src/mongo/db/repl/storage_interface_impl_test.cpp
+++ b/src/mongo/db/repl/storage_interface_impl_test.cpp
@@ -401,9 +401,12 @@ TEST_F(StorageInterfaceImplTest, SnapshotSupported) {
TEST_F(StorageInterfaceImplTest, InsertDocumentsReturnsOKWhenNoOperationsAreGiven) {
auto opCtx = getOperationContext();
auto nss = makeNamespace(_agent);
- createCollection(opCtx, nss);
+ auto options = generateOptionsWithUuid();
+ createCollection(opCtx, nss, options);
+
StorageInterfaceImpl storage;
ASSERT_OK(storage.insertDocuments(opCtx, nss, {}));
+ ASSERT_OK(storage.insertDocuments(opCtx, {nss.db().toString(), *options.uuid}, {}));
}
TEST_F(StorageInterfaceImplTest,
@@ -412,7 +415,8 @@ TEST_F(StorageInterfaceImplTest,
// fail.
auto opCtx = getOperationContext();
auto nss = makeNamespace(_agent);
- createCollection(opCtx, nss);
+ auto options = generateOptionsWithUuid();
+ createCollection(opCtx, nss, options);
// Non-oplog collection will enforce mandatory _id field requirement on insertion.
StorageInterfaceImpl storage;
@@ -420,6 +424,11 @@ TEST_F(StorageInterfaceImplTest,
auto status = storage.insertDocuments(opCtx, nss, transformInserts({op}));
ASSERT_EQUALS(ErrorCodes::InternalError, status);
ASSERT_STRING_CONTAINS(status.reason(), "Collection::insertDocument got document without _id");
+
+ // Again, but specify the collection with its UUID.
+ ASSERT_EQ(ErrorCodes::InternalError,
+ storage.insertDocuments(
+ opCtx, {nss.db().toString(), *options.uuid}, transformInserts({op})));
}
TEST_F(StorageInterfaceImplTest,
@@ -475,6 +484,30 @@ TEST_F(StorageInterfaceImplTest, InsertDocumentsSavesOperationsReturnsOpTimeOfLa
ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty, iter->next().getStatus());
}
+TEST_F(StorageInterfaceImplTest, InsertDocumentsSavesOperationsWhenCollSpecifiedWithUUID) {
+ // This is exactly like the test InsertDocumentsSavesOperationsReturnsOpTimeOfLastOperation, but
+ // with the UUID specified instead of the namespace string.
+ auto opCtx = getOperationContext();
+ auto nss = makeNamespace(_agent);
+ auto options = createOplogCollectionOptions();
+ createCollection(opCtx, nss, options);
+
+ // Insert operations using storage interface. Ensure optime return is consistent with last
+ // operation inserted.
+ StorageInterfaceImpl storage;
+ auto op1 = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ auto op2 = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ ASSERT_OK(storage.insertDocuments(
+ opCtx, {nss.db().toString(), *options.uuid}, transformInserts({op1, op2})));
+
+ // Check contents of oplog. OplogInterface iterates over oplog collection in reverse.
+ repl::OplogInterfaceLocal oplog(opCtx, nss.ns());
+ auto iter = oplog.makeIterator();
+ ASSERT_BSONOBJ_EQ(op2.obj, unittest::assertGet(iter->next()).first);
+ ASSERT_BSONOBJ_EQ(op1.obj, unittest::assertGet(iter->next()).first);
+ ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty, iter->next().getStatus());
+}
+
TEST_F(StorageInterfaceImplTest,
InsertDocumentsReturnsNamespaceNotFoundIfOplogCollectionDoesNotExist) {
auto op = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
@@ -486,6 +519,16 @@ TEST_F(StorageInterfaceImplTest,
ASSERT_STRING_CONTAINS(status.reason(), "The collection must exist before inserting documents");
}
+TEST_F(StorageInterfaceImplTest, InsertDocumentThrowsNamespaceNotFoundIfOplogUUIDNotInCatalog) {
+ auto op = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ auto opCtx = getOperationContext();
+ StorageInterfaceImpl storage;
+ ASSERT_THROWS_CODE(
+ storage.insertDocuments(opCtx, {"local", UUID::gen()}, transformInserts({op})).ignore(),
+ DBException,
+ ErrorCodes::NamespaceNotFound);
+}
+
TEST_F(StorageInterfaceImplTest, InsertMissingDocWorksOnExistingCappedCollection) {
auto opCtx = getOperationContext();
StorageInterfaceImpl storage;
@@ -500,6 +543,23 @@ TEST_F(StorageInterfaceImplTest, InsertMissingDocWorksOnExistingCappedCollection
ASSERT_TRUE(autoColl.getCollection());
}
+TEST_F(StorageInterfaceImplTest, InsertDocWorksWithExistingCappedCollectionSpecifiedByUUID) {
+ auto opCtx = getOperationContext();
+ auto nss = makeNamespace(_agent);
+ auto options = generateOptionsWithUuid();
+ options.capped = true;
+ options.cappedSize = 1024 * 1024;
+ createCollection(opCtx, nss, options);
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.insertDocument(opCtx,
+ {nss.db().toString(), *options.uuid},
+ {BSON("_id" << 1), Timestamp(1)},
+ OpTime::kUninitializedTerm));
+ AutoGetCollectionForReadCommand autoColl(opCtx, nss);
+ ASSERT_TRUE(autoColl.getCollection());
+}
+
TEST_F(StorageInterfaceImplTest, InsertMissingDocWorksOnExistingCollection) {
auto opCtx = getOperationContext();
StorageInterfaceImpl storage;
@@ -1843,6 +1903,18 @@ TEST_F(StorageInterfaceImplTest, UpdateSingletonUpdatesDocumentWhenCollectionIsN
_assertDocumentsInCollectionEquals(opCtx, nss, {BSON("_id" << 0 << "x" << 1)});
}
+TEST_F(StorageInterfaceImplTest, FindByIdThrowsIfUUIDNotInCatalog) {
+ auto op = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ auto opCtx = getOperationContext();
+ auto obj = BSON("_id"
+ << "kyle");
+ StorageInterfaceImpl storage;
+ ASSERT_THROWS_CODE(
+ storage.findById(opCtx, {"local", UUID::gen()}, obj["_id"]).getStatus().ignore(),
+ DBException,
+ ErrorCodes::NamespaceNotFound);
+}
+
TEST_F(StorageInterfaceImplTest, FindByIdReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) {
auto opCtx = getOperationContext();
StorageInterfaceImpl storage;
@@ -1863,9 +1935,11 @@ TEST_F(StorageInterfaceImplTest, FindByIdReturnsNoSuchKeyWhenCollectionIsEmpty)
TEST_F(StorageInterfaceImplTest, FindByIdReturnsNoSuchKeyWhenDocumentIsNotFound) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto doc1 = BSON("_id" << 0 << "x" << 0);
auto doc2 = BSON("_id" << 1 << "x" << 1);
auto doc3 = BSON("_id" << 2 << "x" << 2);
@@ -1873,14 +1947,20 @@ TEST_F(StorageInterfaceImplTest, FindByIdReturnsNoSuchKeyWhenDocumentIsNotFound)
nss,
{{doc1, Timestamp(0), OpTime::kUninitializedTerm},
{doc3, Timestamp(0), OpTime::kUninitializedTerm}}));
+
ASSERT_EQUALS(ErrorCodes::NoSuchKey, storage.findById(opCtx, nss, doc2["_id"]).getStatus());
+ ASSERT_EQUALS(
+ ErrorCodes::NoSuchKey,
+ storage.findById(opCtx, {nss.db().toString(), *options.uuid}, doc2["_id"]).getStatus());
}
TEST_F(StorageInterfaceImplTest, FindByIdReturnsDocumentWhenDocumentExists) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto doc1 = BSON("_id" << 0 << "x" << 0);
auto doc2 = BSON("_id" << 1 << "x" << 1);
auto doc3 = BSON("_id" << 2 << "x" << 2);
@@ -1889,7 +1969,23 @@ TEST_F(StorageInterfaceImplTest, FindByIdReturnsDocumentWhenDocumentExists) {
{{doc1, Timestamp(0), OpTime::kUninitializedTerm},
{doc2, Timestamp(0), OpTime::kUninitializedTerm},
{doc3, Timestamp(0), OpTime::kUninitializedTerm}}));
+
ASSERT_BSONOBJ_EQ(doc2, unittest::assertGet(storage.findById(opCtx, nss, doc2["_id"])));
+ ASSERT_BSONOBJ_EQ(doc2,
+ unittest::assertGet(storage.findById(
+ opCtx, {nss.db().toString(), *options.uuid}, doc2["_id"])));
+}
+
+TEST_F(StorageInterfaceImplTest, DeleteByIdThrowsIfUUIDNotInCatalog) {
+ auto op = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ auto opCtx = getOperationContext();
+ auto obj = BSON("_id"
+ << "kyle");
+ StorageInterfaceImpl storage;
+ ASSERT_THROWS_CODE(
+ storage.deleteById(opCtx, {"local", UUID::gen()}, obj["_id"]).getStatus().ignore(),
+ DBException,
+ ErrorCodes::NamespaceNotFound);
}
TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) {
@@ -1913,9 +2009,11 @@ TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsNoSuchKeyWhenCollectionIsEmpty
TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsNoSuchKeyWhenDocumentIsNotFound) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto doc1 = BSON("_id" << 0 << "x" << 0);
auto doc2 = BSON("_id" << 1 << "x" << 1);
auto doc3 = BSON("_id" << 2 << "x" << 2);
@@ -1924,14 +2022,19 @@ TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsNoSuchKeyWhenDocumentIsNotFoun
{{doc1, Timestamp(0), OpTime::kUninitializedTerm},
{doc3, Timestamp(0), OpTime::kUninitializedTerm}}));
ASSERT_EQUALS(ErrorCodes::NoSuchKey, storage.deleteById(opCtx, nss, doc2["_id"]).getStatus());
+ ASSERT_EQUALS(
+ ErrorCodes::NoSuchKey,
+ storage.deleteById(opCtx, {nss.db().toString(), *options.uuid}, doc2["_id"]).getStatus());
_assertDocumentsInCollectionEquals(opCtx, nss, {doc1, doc3});
}
TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsDocumentWhenDocumentExists) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto doc1 = BSON("_id" << 0 << "x" << 0);
auto doc2 = BSON("_id" << 1 << "x" << 1);
auto doc3 = BSON("_id" << 2 << "x" << 2);
@@ -1940,8 +2043,23 @@ TEST_F(StorageInterfaceImplTest, DeleteByIdReturnsDocumentWhenDocumentExists) {
{{doc1, Timestamp(0), OpTime::kUninitializedTerm},
{doc2, Timestamp(0), OpTime::kUninitializedTerm},
{doc3, Timestamp(0), OpTime::kUninitializedTerm}}));
+
ASSERT_BSONOBJ_EQ(doc2, unittest::assertGet(storage.deleteById(opCtx, nss, doc2["_id"])));
_assertDocumentsInCollectionEquals(opCtx, nss, {doc1, doc3});
+
+ ASSERT_BSONOBJ_EQ(doc1, unittest::assertGet(storage.deleteById(opCtx, nss, doc1["_id"])));
+ _assertDocumentsInCollectionEquals(opCtx, nss, {doc3});
+}
+
+TEST_F(StorageInterfaceImplTest, UpsertByIdThrowsIfUUIDNotInCatalog) {
+ auto op = makeOplogEntry({Timestamp(Seconds(1), 0), 1LL});
+ auto opCtx = getOperationContext();
+ auto obj = BSON("_id"
+ << "kyle");
+ StorageInterfaceImpl storage;
+ ASSERT_THROWS_CODE(storage.upsertById(opCtx, {"local", UUID::gen()}, obj["_id"], obj).ignore(),
+ DBException,
+ ErrorCodes::NamespaceNotFound);
}
TEST_F(StorageInterfaceImplTest,
@@ -1971,9 +2089,11 @@ TEST_F(StorageInterfaceImplTest,
TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentReplacesExistingDocumentInCollection) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto originalDoc = BSON("_id" << 1 << "x" << 1);
ASSERT_OK(storage.insertDocuments(
@@ -1984,19 +2104,29 @@ TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentReplacesExistingDocumentInC
{BSON("_id" << 2 << "x" << 2), Timestamp(2), OpTime::kUninitializedTerm}}));
ASSERT_OK(storage.upsertById(opCtx, nss, originalDoc["_id"], BSON("x" << 100)));
-
_assertDocumentsInCollectionEquals(opCtx,
nss,
{BSON("_id" << 0 << "x" << 0),
BSON("_id" << 1 << "x" << 100),
BSON("_id" << 2 << "x" << 2)});
+
+ // Again, but specify the collection's UUID.
+ ASSERT_OK(storage.upsertById(
+ opCtx, {nss.db().toString(), *options.uuid}, originalDoc["_id"], BSON("x" << 200)));
+ _assertDocumentsInCollectionEquals(opCtx,
+ nss,
+ {BSON("_id" << 0 << "x" << 0),
+ BSON("_id" << 1 << "x" << 200),
+ BSON("_id" << 2 << "x" << 2)});
}
TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentInsertsNewDocumentInCollectionIfIdIsNotFound) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
ASSERT_OK(storage.insertDocuments(
opCtx,
@@ -2013,6 +2143,17 @@ TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentInsertsNewDocumentInCollect
{BSON("_id" << 0 << "x" << 0),
BSON("_id" << 2 << "x" << 2),
BSON("_id" << 1 << "x" << 100)});
+
+ ASSERT_OK(storage.upsertById(opCtx,
+ {nss.db().toString(), *options.uuid},
+ BSON("" << 3).firstElement(),
+ BSON("x" << 300)));
+ _assertDocumentsInCollectionEquals(opCtx,
+ nss,
+ {BSON("_id" << 0 << "x" << 0),
+ BSON("_id" << 2 << "x" << 2),
+ BSON("_id" << 1 << "x" << 100),
+ BSON("_id" << 3 << "x" << 300)});
}
TEST_F(StorageInterfaceImplTest,
@@ -2023,8 +2164,9 @@ TEST_F(StorageInterfaceImplTest,
ASSERT_FALSE(nss.isLegalClientSystemNS());
auto opCtx = getOperationContext();
+ auto options = generateOptionsWithUuid();
StorageInterfaceImpl storage;
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto originalDoc = BSON("_id" << 1 << "x" << 1);
ASSERT_OK(storage.insertDocuments(
@@ -2035,25 +2177,40 @@ TEST_F(StorageInterfaceImplTest,
{BSON("_id" << 2 << "x" << 2), Timestamp(2), OpTime::kUninitializedTerm}}));
ASSERT_OK(storage.upsertById(opCtx, nss, originalDoc["_id"], BSON("x" << 100)));
-
_assertDocumentsInCollectionEquals(opCtx,
nss,
{BSON("_id" << 0 << "x" << 0),
BSON("_id" << 1 << "x" << 100),
BSON("_id" << 2 << "x" << 2)});
+
+ ASSERT_OK(storage.upsertById(
+ opCtx, {nss.db().toString(), *options.uuid}, originalDoc["_id"], BSON("x" << 200)));
+ _assertDocumentsInCollectionEquals(opCtx,
+ nss,
+ {BSON("_id" << 0 << "x" << 0),
+ BSON("_id" << 1 << "x" << 200),
+ BSON("_id" << 2 << "x" << 2)});
}
TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentReturnsFailedToParseOnNonSimpleIdQuery) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
+
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
auto status = storage.upsertById(
opCtx, nss, BSON("" << BSON("$gt" << 3)).firstElement(), BSON("x" << 100));
ASSERT_EQUALS(ErrorCodes::InvalidIdField, status);
ASSERT_STRING_CONTAINS(status.reason(),
"Unable to update document with a non-simple _id query:");
+
+ ASSERT_EQ(storage.upsertById(opCtx,
+ {nss.db().toString(), *options.uuid},
+ BSON("" << BSON("$gt" << 3)).firstElement(),
+ BSON("x" << 100)),
+ ErrorCodes::InvalidIdField);
}
TEST_F(StorageInterfaceImplTest,
@@ -2071,24 +2228,35 @@ TEST_F(StorageInterfaceImplTest,
ASSERT_EQUALS(ErrorCodes::IndexNotFound, status);
ASSERT_STRING_CONTAINS(status.reason(),
"Unable to update document in a collection without an _id index.");
+
+ ASSERT_EQ(storage.upsertById(opCtx, {nss.db().toString(), *options.uuid}, doc["_id"], doc),
+ ErrorCodes::IndexNotFound);
}
TEST_F(StorageInterfaceImplTest,
UpsertSingleDocumentReturnsFailedToParseWhenUpdateDocumentContainsUnknownOperator) {
auto opCtx = getOperationContext();
- StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- ASSERT_OK(storage.createCollection(opCtx, nss, generateOptionsWithUuid()));
+ auto options = generateOptionsWithUuid();
- ASSERT_THROWS_CODE_AND_WHAT(storage
- .upsertById(opCtx,
- nss,
- BSON("" << 1).firstElement(),
- BSON("$unknownUpdateOp" << BSON("x" << 1000)))
- .transitional_ignore(),
- AssertionException,
- ErrorCodes::FailedToParse,
- "Unknown modifier: $unknownUpdateOp");
+ StorageInterfaceImpl storage;
+ ASSERT_OK(storage.createCollection(opCtx, nss, options));
+
+ auto unknownUpdateOp = BSON("$unknownUpdateOp" << BSON("x" << 1000));
+ ASSERT_THROWS_CODE_AND_WHAT(
+ storage.upsertById(opCtx, nss, BSON("" << 1).firstElement(), unknownUpdateOp).ignore(),
+ AssertionException,
+ ErrorCodes::FailedToParse,
+ "Unknown modifier: $unknownUpdateOp");
+
+ ASSERT_THROWS_CODE(storage
+ .upsertById(opCtx,
+ {nss.db().toString(), *options.uuid},
+ BSON("" << 1).firstElement(),
+ unknownUpdateOp)
+ .ignore(),
+ DBException,
+ ErrorCodes::FailedToParse);
}
TEST_F(StorageInterfaceImplTest, DeleteByFilterReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) {
diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h
index cd02935179f..b2b3d22f276 100644
--- a/src/mongo/db/repl/storage_interface_mock.h
+++ b/src/mongo/db/repl/storage_interface_mock.h
@@ -91,11 +91,11 @@ public:
const BSONObj idIndexSpec,
const std::vector<BSONObj>& secondaryIndexSpecs)>;
using InsertDocumentFn = stdx::function<Status(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term)>;
using InsertDocumentsFn = stdx::function<Status(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs)>;
using DropUserDatabasesFn = stdx::function<Status(OperationContext* opCtx)>;
using CreateOplogFn =
@@ -141,16 +141,16 @@ public:
};
Status insertDocument(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) override {
- return insertDocumentFn(opCtx, nss, doc, term);
+ return insertDocumentFn(opCtx, nsOrUUID, doc, term);
};
Status insertDocuments(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs) override {
- return insertDocumentsFn(opCtx, nss, docs);
+ return insertDocumentsFn(opCtx, nsOrUUID, docs);
}
Status dropReplicatedDatabases(OperationContext* opCtx) override {
@@ -238,19 +238,19 @@ public:
}
StatusWith<BSONObj> findById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID&,
const BSONElement& idKey) override {
return Status{ErrorCodes::IllegalOperation, "findById not implemented."};
}
StatusWith<BSONObj> deleteById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID&,
const BSONElement& idKey) override {
return Status{ErrorCodes::IllegalOperation, "deleteById not implemented."};
}
Status upsertById(OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const BSONElement& idKey,
const BSONObj& update) override {
return Status{ErrorCodes::IllegalOperation, "upsertById not implemented."};
@@ -315,13 +315,13 @@ public:
return Status{ErrorCodes::IllegalOperation, "CreateCollectionForBulkFn not implemented."};
};
InsertDocumentFn insertDocumentFn = [](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const TimestampedBSONObj& doc,
long long term) {
return Status{ErrorCodes::IllegalOperation, "InsertDocumentFn not implemented."};
};
InsertDocumentsFn insertDocumentsFn = [](OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
const std::vector<InsertStatement>& docs) {
return Status{ErrorCodes::IllegalOperation, "InsertDocumentsFn not implemented."};
};