diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2017-10-12 18:17:31 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2017-10-17 09:25:40 -0400 |
commit | d77201bbde61bf9e19eca81d73bbcb8bd85c757c (patch) | |
tree | 74d3ab587646f620aadd8c6ad92730a17a32d907 | |
parent | 5eb95437e55acb07680c72a8a14a2f485bce94d7 (diff) | |
download | mongo-d77201bbde61bf9e19eca81d73bbcb8bd85c757c.tar.gz |
SERVER-30532 Remove IDL-generated parser for the findAndModify result
Instead only use a generator, but not parser. Also adds
lastErrorObject.upserted to the findAndModify retry result.
-rw-r--r-- | jstests/sharding/move_chunk_find_and_modify_with_write_retryability.js | 10 | ||||
-rw-r--r-- | jstests/sharding/retryable_writes.js | 9 | ||||
-rw-r--r-- | jstests/sharding/write_transactions_during_migration.js | 10 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.h | 5 | ||||
-rw-r--r-- | src/mongo/db/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.idl | 50 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/find_and_modify_result.cpp | 77 | ||||
-rw-r--r-- | src/mongo/db/ops/find_and_modify_result.h | 50 | ||||
-rw-r--r-- | src/mongo/db/ops/update_result.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/ops/update_result.h | 16 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_retryability.cpp | 78 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_retryability.h | 12 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_retryability_test.cpp | 242 |
15 files changed, 313 insertions, 293 deletions
diff --git a/jstests/sharding/move_chunk_find_and_modify_with_write_retryability.js b/jstests/sharding/move_chunk_find_and_modify_with_write_retryability.js index c06a4cfdc9c..f5af6f88310 100644 --- a/jstests/sharding/move_chunk_find_and_modify_with_write_retryability.js +++ b/jstests/sharding/move_chunk_find_and_modify_with_write_retryability.js @@ -1,20 +1,12 @@ load("jstests/sharding/move_chunk_with_session_helper.js"); (function() { - "use strict"; var checkFindAndModifyResult = function(expected, toCheck) { assert.eq(expected.ok, toCheck.ok); assert.eq(expected.value, toCheck.value); - - // TODO: SERVER-30532: after adding upserted, just compare the entire lastErrorObject - var expectedLE = expected.lastErrorObject; - var toCheckLE = toCheck.lastErrorObject; - - assert.neq(null, toCheckLE); - assert.eq(expected.updatedExisting, toCheck.updatedExisting); - assert.eq(expected.n, toCheck.n); + assert.eq(expected.lastErrorObject, toCheck.lastErrorObject); }; var lsid = UUID(); diff --git a/jstests/sharding/retryable_writes.js b/jstests/sharding/retryable_writes.js index f83c1b9cdc7..8d6269fb722 100644 --- a/jstests/sharding/retryable_writes.js +++ b/jstests/sharding/retryable_writes.js @@ -8,14 +8,7 @@ function checkFindAndModifyResult(expected, toCheck) { assert.eq(expected.ok, toCheck.ok); assert.eq(expected.value, toCheck.value); - - // TODO: SERVER-30532: after adding upserted, just compare the entire lastErrorObject - var expectedLE = expected.lastErrorObject; - var toCheckLE = toCheck.lastErrorObject; - - assert.neq(null, toCheckLE); - assert.eq(expected.updatedExisting, toCheck.updatedExisting); - assert.eq(expected.n, toCheck.n); + assert.docEq(expected.lastErrorObject, toCheck.lastErrorObject); } function runTests(mainConn, priConn) { diff --git a/jstests/sharding/write_transactions_during_migration.js b/jstests/sharding/write_transactions_during_migration.js index 7c52628e05e..c16ca9845f5 100644 --- a/jstests/sharding/write_transactions_during_migration.js +++ b/jstests/sharding/write_transactions_during_migration.js @@ -13,7 +13,6 @@ load('./jstests/libs/chunk_manipulation_util.js'); * 4. Retry writes and confirm that writes are not duplicated. */ (function() { - "use strict"; var staticMongod = MongoRunner.runMongod({}); // For startParallelOps. @@ -67,14 +66,7 @@ load('./jstests/libs/chunk_manipulation_util.js'); assert.eq(findAndModifyResult.ok, findAndModifyRetryResult.ok); assert.eq(findAndModifyResult.value, findAndModifyRetryResult.value); - - // TODO: SERVER-30532: after adding upserted, just compare the entire lastErrorObject - var expectedLE = findAndModifyResult.lastErrorObject; - var toCheckLE = findAndModifyRetryResult.lastErrorObject; - - assert.neq(null, toCheckLE); - assert.eq(findAndModifyResult.updatedExisting, findAndModifyRetryResult.updatedExisting); - assert.eq(findAndModifyResult.n, findAndModifyRetryResult.n); + assert.eq(findAndModifyResult.lastErrorObject, findAndModifyRetryResult.lastErrorObject); assert.eq(1, testDB.user.findOne({x: 30}).y); diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h index 3b263ca3355..38dae8234fa 100644 --- a/src/mongo/bson/bsonobjbuilder.h +++ b/src/mongo/bson/bsonobjbuilder.h @@ -500,11 +500,6 @@ public: return appendSymbol(fieldName, symbol.symbol); } - /** Implements builder interface but no-op in ObjBuilder */ - void appendNull() { - msgasserted(16234, "Invalid call to appendNull in BSONObj Builder."); - } - /** Append a Null element to the object */ BSONObjBuilder& appendNull(StringData fieldName) { _b.appendNum((char)jstNULL); diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 5c358768737..7448f3373da 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1606,6 +1606,7 @@ env.Library( target='write_ops', source=[ 'ops/delete.cpp', + 'ops/find_and_modify_result.cpp', 'ops/insert.cpp', 'ops/parsed_delete.cpp', 'ops/parsed_update.cpp', @@ -1617,7 +1618,6 @@ env.Library( 'session_catalog.cpp', 'session_txn_record.cpp', 'transaction_history_iterator.cpp', - env.Idlc('commands/find_and_modify.idl')[0], env.Idlc('ops/single_write_result.idl')[0], env.Idlc('session_txn_record.idl')[0], ], diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 3d31e47ee06..59c1c387708 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -48,6 +48,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/ops/delete_request.h" +#include "mongo/db/ops/find_and_modify_result.h" #include "mongo/db/ops/insert.h" #include "mongo/db/ops/parsed_delete.h" #include "mongo/db/ops/parsed_update.h" @@ -168,25 +169,18 @@ void appendCommandResponse(const PlanExecutor* exec, bool isRemove, const boost::optional<BSONObj>& value, BSONObjBuilder* result) { - BSONObjBuilder lastErrorObjBuilder(result->subobjStart("lastErrorObject")); if (isRemove) { - lastErrorObjBuilder.appendNumber("n", getDeleteStats(exec)->docsDeleted); + find_and_modify::serializeRemove(getDeleteStats(exec)->docsDeleted, value, result); } else { - const UpdateStats* updateStats = getUpdateStats(exec); - lastErrorObjBuilder.appendBool("updatedExisting", updateStats->nMatched > 0); - lastErrorObjBuilder.appendNumber("n", updateStats->inserted ? 1 : updateStats->nMatched); - // Note we have to use the objInserted from the stats here, rather than 'value' - // because the _id field could have been excluded by a projection. - if (!updateStats->objInserted.isEmpty()) { - lastErrorObjBuilder.appendAs(updateStats->objInserted["_id"], kUpsertedFieldName); - } - } - lastErrorObjBuilder.doneFast(); - - if (value) { - result->append("value", *value); - } else { - result->appendNull("value"); + const auto updateStats = getUpdateStats(exec); + + // Note we have to use the objInserted from the stats here, rather than 'value' because the + // _id field could have been excluded by a projection. + find_and_modify::serializeUpsert(updateStats->inserted ? 1 : updateStats->nMatched, + value, + updateStats->nMatched > 0, + updateStats->objInserted, + result); } } @@ -360,8 +354,7 @@ public: auto session = OperationContextSession::get(opCtx); if (auto entry = session->checkStatementExecuted(opCtx, *opCtx->getTxnNumber(), stmtId)) { - auto findAndModifyResult = parseOplogEntryForFindAndModify(opCtx, args, *entry); - findAndModifyResult.serialize(&result); + parseOplogEntryForFindAndModify(opCtx, args, *entry, &result); return true; } } diff --git a/src/mongo/db/commands/find_and_modify.idl b/src/mongo/db/commands/find_and_modify.idl deleted file mode 100644 index 694134c7392..00000000000 --- a/src/mongo/db/commands/find_and_modify.idl +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2017 MongoDB Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3, -# as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -# This IDL file describes the BSON format for a LogicalSessionId, and -# handles the serialization to and deserialization from its BSON representation -# for that class. - -global: - cpp_namespace: "mongo" - -imports: - - "mongo/idl/basic_types.idl" - -structs: - - FindAndModifyLastError: - description: "Contains execution details for the findAndModify command" - strict: true - fields: - updatedExisting: - description: "Specifies whether the command modified an existing document" - type: bool - optional: true - n: - description: "Specifies the number of documents that were inserted/deleted or - matched the update predicate" - type: safeInt64 - - FindAndModifyResult: - description: "Contains the result from a findAndModify command" - strict: false - fields: - lastErrorObject: - type: FindAndModifyLastError - value: - description: "Contains document before or after the write depending on the 'new' - field of the request was set to true or false" - type: object diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp index 79906de7f82..559b44bf72d 100644 --- a/src/mongo/db/op_observer_impl.cpp +++ b/src/mongo/db/op_observer_impl.cpp @@ -128,7 +128,7 @@ OpTimeBundle replLogUpdate(OperationContext* opCtx, BSONObj storeObj; if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PreImage) { invariant(args.preImageDoc); - storeObj = args.preImageDoc.value(); + storeObj = *args.preImageDoc; } else if (args.storeDocOption == OplogUpdateEntryArgs::StoreDocOption::PostImage) { storeObj = args.updatedDoc; } diff --git a/src/mongo/db/ops/find_and_modify_result.cpp b/src/mongo/db/ops/find_and_modify_result.cpp new file mode 100644 index 00000000000..30f8dad9b6d --- /dev/null +++ b/src/mongo/db/ops/find_and_modify_result.cpp @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kWrite + +#include "mongo/platform/basic.h" + +#include "mongo/db/ops/find_and_modify_result.h" + +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/lasterror.h" + +namespace mongo { +namespace find_and_modify { +namespace { + +void appendValue(const boost::optional<BSONObj>& value, BSONObjBuilder* builder) { + if (value) { + builder->append("value", *value); + } else { + builder->appendNull("value"); + } +} + +} // namespace + +void serializeRemove(size_t n, const boost::optional<BSONObj>& value, BSONObjBuilder* builder) { + BSONObjBuilder lastErrorObjBuilder(builder->subobjStart("lastErrorObject")); + builder->appendNumber("n", n); + lastErrorObjBuilder.doneFast(); + + appendValue(value, builder); +} + +void serializeUpsert(size_t n, + const boost::optional<BSONObj>& value, + bool updatedExisting, + const BSONObj& objInserted, + BSONObjBuilder* builder) { + BSONObjBuilder lastErrorObjBuilder(builder->subobjStart("lastErrorObject")); + lastErrorObjBuilder.appendNumber("n", n); + lastErrorObjBuilder.appendBool("updatedExisting", updatedExisting); + if (!objInserted.isEmpty()) { + lastErrorObjBuilder.appendAs(objInserted["_id"], kUpsertedFieldName); + } + lastErrorObjBuilder.doneFast(); + + appendValue(value, builder); +} + +} // namespace find_and_modify +} // namespace mongo diff --git a/src/mongo/db/ops/find_and_modify_result.h b/src/mongo/db/ops/find_and_modify_result.h new file mode 100644 index 00000000000..c335c5022fd --- /dev/null +++ b/src/mongo/db/ops/find_and_modify_result.h @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <boost/optional.hpp> + +#include "mongo/bson/bsonobj.h" + +namespace mongo { + +class BSONObjBuilder; + +namespace find_and_modify { + +void serializeRemove(size_t n, const boost::optional<BSONObj>& value, BSONObjBuilder* builder); + +void serializeUpsert(size_t n, + const boost::optional<BSONObj>& value, + bool updatedExisting, + const BSONObj& objInserted, + BSONObjBuilder* builder); + +} // namespace find_and_modify +} // namespace mongo diff --git a/src/mongo/db/ops/update_result.cpp b/src/mongo/db/ops/update_result.cpp index 97a5a5332df..93a9e152030 100644 --- a/src/mongo/db/ops/update_result.cpp +++ b/src/mongo/db/ops/update_result.cpp @@ -1,7 +1,5 @@ -//@file update_result.cpp - /** - * Copyright (C) 2008-2014 MongoDB Inc. + * Copyright (C) 2017 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -28,6 +26,7 @@ * it in the license file. */ + #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kWrite #include "mongo/platform/basic.h" @@ -36,6 +35,7 @@ #include "mongo/db/lasterror.h" #include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -55,4 +55,10 @@ UpdateResult::UpdateResult(bool existing_, LOG(4) << "UpdateResult -- " << redact(toString()); } +std::string UpdateResult::toString() const { + return str::stream() << " upserted: " << upserted << " modifiers: " << modifiers + << " existing: " << existing << " numDocsModified: " << numDocsModified + << " numMatched: " << numMatched; +} + } // namespace mongo diff --git a/src/mongo/db/ops/update_result.h b/src/mongo/db/ops/update_result.h index 2c3107e3ea1..1b0a40520ee 100644 --- a/src/mongo/db/ops/update_result.h +++ b/src/mongo/db/ops/update_result.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013 10gen Inc. + * Copyright (C) 2017 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -28,15 +28,10 @@ #pragma once -#include "mongo/db/curop.h" -#include "mongo/db/jsobj.h" -#include "mongo/db/namespace_string.h" -#include "mongo/util/mongoutils/str.h" +#include "mongo/bson/bsonobj.h" namespace mongo { -namespace str = mongoutils::str; - struct UpdateResult { UpdateResult(bool existing_, bool modifiers_, @@ -44,6 +39,7 @@ struct UpdateResult { unsigned long long numMatched_, const BSONObj& upsertedObject_); + std::string toString() const; // if existing objects were modified const bool existing; @@ -59,12 +55,6 @@ struct UpdateResult { // if something was upserted, the new _id of the object BSONObj upserted; - - const std::string toString() const { - return str::stream() << " upserted: " << upserted << " modifiers: " << modifiers - << " existing: " << existing << " numDocsModified: " << numDocsModified - << " numMatched: " << numMatched; - } }; } // namespace mongo diff --git a/src/mongo/db/ops/write_ops_retryability.cpp b/src/mongo/db/ops/write_ops_retryability.cpp index 3d4fc90fb7e..28ce5bed8be 100644 --- a/src/mongo/db/ops/write_ops_retryability.cpp +++ b/src/mongo/db/ops/write_ops_retryability.cpp @@ -28,18 +28,16 @@ #include "mongo/platform/basic.h" +#include "mongo/db/ops/write_ops_retryability.h" + #include "mongo/db/dbdirectclient.h" #include "mongo/db/namespace_string.h" -#include "mongo/db/operation_context.h" -#include "mongo/db/ops/single_write_result_gen.h" -#include "mongo/db/ops/write_ops.h" +#include "mongo/db/ops/find_and_modify_result.h" #include "mongo/db/ops/write_ops_exec.h" -#include "mongo/db/ops/write_ops_retryability.h" #include "mongo/db/query/find_and_modify_request.h" #include "mongo/logger/redaction.h" namespace mongo { - namespace { /** @@ -141,47 +139,26 @@ BSONObj extractPreOrPostImage(OperationContext* opCtx, const repl::OplogEntry& o * previous execution of the command. In the case of nested oplog entry where the correct links * are in the top level oplog, oplogWithCorrectLinks can be used to specify the outer oplog. */ -FindAndModifyResult parseOplogEntryForFindAndModify(OperationContext* opCtx, - const FindAndModifyRequest& request, - const repl::OplogEntry& oplogEntry, - const repl::OplogEntry& oplogWithCorrectLinks) { - const auto opType = oplogEntry.getOpType(); - +void parseOplogEntryForFindAndModify(OperationContext* opCtx, + const FindAndModifyRequest& request, + const repl::OplogEntry& oplogEntry, + const repl::OplogEntry& oplogWithCorrectLinks, + BSONObjBuilder* builder) { validateFindAndModifyRetryability(request, oplogEntry, oplogWithCorrectLinks); - FindAndModifyResult result; - - if (opType == repl::OpTypeEnum::kDelete) { - FindAndModifyLastError lastError; - lastError.setN(1); - result.setLastErrorObject(std::move(lastError)); - result.setValue(extractPreOrPostImage(opCtx, oplogWithCorrectLinks)); - - return result; + switch (oplogEntry.getOpType()) { + case repl::OpTypeEnum::kInsert: + return find_and_modify::serializeUpsert( + 1, oplogEntry.getObject(), false, oplogEntry.getObject(), builder); + case repl::OpTypeEnum::kUpdate: + return find_and_modify::serializeUpsert( + 1, extractPreOrPostImage(opCtx, oplogWithCorrectLinks), true, {}, builder); + case repl::OpTypeEnum::kDelete: + return find_and_modify::serializeRemove( + 1, extractPreOrPostImage(opCtx, oplogWithCorrectLinks), builder); + default: + MONGO_UNREACHABLE; } - - // Upsert case - if (opType == repl::OpTypeEnum::kInsert) { - FindAndModifyLastError lastError; - lastError.setN(1); - lastError.setUpdatedExisting(false); - // TODO: SERVER-30532 set upserted - - result.setLastErrorObject(std::move(lastError)); - result.setValue(oplogEntry.getObject().getOwned()); - - return result; - } - - // Update case - FindAndModifyLastError lastError; - lastError.setN(1); - lastError.setUpdatedExisting(true); - - result.setLastErrorObject(std::move(lastError)); - result.setValue(extractPreOrPostImage(opCtx, oplogWithCorrectLinks)); - - return result; } repl::OplogEntry getInnerNestedOplogEntry(const repl::OplogEntry& entry) { @@ -267,16 +244,17 @@ SingleWriteResult parseOplogEntryForDelete(const repl::OplogEntry& entry) { return res; } -FindAndModifyResult parseOplogEntryForFindAndModify(OperationContext* opCtx, - const FindAndModifyRequest& request, - const repl::OplogEntry& oplogEntry) { +void parseOplogEntryForFindAndModify(OperationContext* opCtx, + const FindAndModifyRequest& request, + const repl::OplogEntry& oplogEntry, + BSONObjBuilder* builder) { // Migrated op case. if (oplogEntry.getOpType() == repl::OpTypeEnum::kNoop) { - return parseOplogEntryForFindAndModify( - opCtx, request, getInnerNestedOplogEntry(oplogEntry), oplogEntry); + parseOplogEntryForFindAndModify( + opCtx, request, getInnerNestedOplogEntry(oplogEntry), oplogEntry, builder); + } else { + parseOplogEntryForFindAndModify(opCtx, request, oplogEntry, oplogEntry, builder); } - - return parseOplogEntryForFindAndModify(opCtx, request, oplogEntry, oplogEntry); } } // namespace mongo diff --git a/src/mongo/db/ops/write_ops_retryability.h b/src/mongo/db/ops/write_ops_retryability.h index 37f87a0d742..fa73a4d34ed 100644 --- a/src/mongo/db/ops/write_ops_retryability.h +++ b/src/mongo/db/ops/write_ops_retryability.h @@ -28,13 +28,13 @@ #pragma once -#include "mongo/db/commands/find_and_modify_gen.h" #include "mongo/db/ops/single_write_result_gen.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/repl/oplog_entry.h" namespace mongo { +class BSONObjBuilder; class FindAndModifyRequest; class OperationContext; @@ -49,10 +49,12 @@ SingleWriteResult parseOplogEntryForUpdate(const repl::OplogEntry& entry); SingleWriteResult parseOplogEntryForDelete(const repl::OplogEntry& entry); /** - * Returns the result of a findAndModify based on the oplog entries generated by the operation. + * Populates the passed-in builder with the result of a findAndModify based on the oplog entries + * generated by the operation. */ -FindAndModifyResult parseOplogEntryForFindAndModify(OperationContext* opCtx, - const FindAndModifyRequest& request, - const repl::OplogEntry& oplogEntry); +void parseOplogEntryForFindAndModify(OperationContext* opCtx, + const FindAndModifyRequest& request, + const repl::OplogEntry& oplogEntry, + BSONObjBuilder* builder); } // namespace mongo diff --git a/src/mongo/db/ops/write_ops_retryability_test.cpp b/src/mongo/db/ops/write_ops_retryability_test.cpp index 41774472760..4c6268c8c7f 100644 --- a/src/mongo/db/ops/write_ops_retryability_test.cpp +++ b/src/mongo/db/ops/write_ops_retryability_test.cpp @@ -29,16 +29,12 @@ #include "mongo/platform/basic.h" #include "mongo/bson/bsonmisc.h" -#include "mongo/db/curop.h" -#include "mongo/db/db_raii.h" -#include "mongo/db/dbdirectclient.h" #include "mongo/db/namespace_string.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/ops/write_ops_retryability.h" #include "mongo/db/query/find_and_modify_request.h" #include "mongo/db/repl/mock_repl_coord_server_fixture.h" #include "mongo/db/repl/oplog_entry.h" -#include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/service_context.h" #include "mongo/db/service_context_d_test_fixture.h" #include "mongo/unittest/unittest.h" @@ -46,21 +42,22 @@ namespace mongo { namespace { -using WriteOpsRetryability = ServiceContextMongoDTest; +using unittest::assertGet; const BSONObj kNestedOplog(BSON("$sessionMigrateInfo" << 1)); +using WriteOpsRetryability = ServiceContextMongoDTest; + TEST_F(WriteOpsRetryability, ParseOplogEntryForInsert) { - auto entry = + const auto entry = assertGet( repl::OplogEntry::parse(BSON("ts" << Timestamp(50, 10) << "t" << 1LL << "h" << 0LL << "op" << "i" << "ns" << "a.b" << "o" - << BSON("_id" << 1 << "x" << 5))); - ASSERT(entry.isOK()); + << BSON("_id" << 1 << "x" << 5)))); - auto res = mongo::parseOplogEntryForInsert(entry.getValue()); + auto res = parseOplogEntryForInsert(entry); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -80,7 +77,7 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedInsert) { kNestedOplog, innerOplog.toBSON()); - auto res = mongo::parseOplogEntryForInsert(insertOplog); + auto res = parseOplogEntryForInsert(insertOplog); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -88,7 +85,7 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedInsert) { } TEST_F(WriteOpsRetryability, ParseOplogEntryForUpdate) { - auto entry = + const auto entry = assertGet( repl::OplogEntry::parse(BSON("ts" << Timestamp(50, 10) << "t" << 1LL << "h" << 0LL << "op" << "u" << "ns" @@ -96,10 +93,9 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForUpdate) { << "o" << BSON("_id" << 1 << "x" << 5) << "o2" - << BSON("_id" << 1))); - ASSERT(entry.isOK()); + << BSON("_id" << 1)))); - auto res = mongo::parseOplogEntryForUpdate(entry.getValue()); + auto res = parseOplogEntryForUpdate(entry); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 1); @@ -120,7 +116,7 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedUpdate) { kNestedOplog, innerOplog.toBSON()); - auto res = mongo::parseOplogEntryForUpdate(updateOplog); + auto res = parseOplogEntryForUpdate(updateOplog); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 1); @@ -128,16 +124,15 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedUpdate) { } TEST_F(WriteOpsRetryability, ParseOplogEntryForUpsert) { - auto entry = + const auto entry = assertGet( repl::OplogEntry::parse(BSON("ts" << Timestamp(50, 10) << "t" << 1LL << "h" << 0LL << "op" << "i" << "ns" << "a.b" << "o" - << BSON("_id" << 1 << "x" << 5))); - ASSERT(entry.isOK()); + << BSON("_id" << 1 << "x" << 5)))); - auto res = mongo::parseOplogEntryForUpdate(entry.getValue()); + auto res = parseOplogEntryForUpdate(entry); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -157,7 +152,7 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedUpsert) { kNestedOplog, innerOplog.toBSON()); - auto res = mongo::parseOplogEntryForUpdate(insertOplog); + auto res = parseOplogEntryForUpdate(insertOplog); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -165,16 +160,15 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedUpsert) { } TEST_F(WriteOpsRetryability, ParseOplogEntryForDelete) { - auto entry = + const auto entry = assertGet( repl::OplogEntry::parse(BSON("ts" << Timestamp(50, 10) << "t" << 1LL << "h" << 0LL << "op" << "d" << "ns" << "a.b" << "o" - << BSON("_id" << 1 << "x" << 5))); - ASSERT(entry.isOK()); + << BSON("_id" << 1 << "x" << 5)))); - auto res = mongo::parseOplogEntryForDelete(entry.getValue()); + auto res = parseOplogEntryForDelete(entry); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -194,7 +188,7 @@ TEST_F(WriteOpsRetryability, ParseOplogEntryForNestedDelete) { kNestedOplog, innerOplog.toBSON()); - auto res = mongo::parseOplogEntryForDelete(deleteOplog); + auto res = parseOplogEntryForDelete(deleteOplog); ASSERT_EQ(res.getN(), 1); ASSERT_EQ(res.getNModified(), 0); @@ -231,31 +225,57 @@ TEST_F(WriteOpsRetryability, ShouldFailIfParsingInsertOplogForDelete) { ASSERT_THROWS(parseOplogEntryForDelete(insertOplog), AssertionException); } -using FindAndModifyRetryability = MockReplCoordServerFixture; - -NamespaceString kNs("test.user"); +class FindAndModifyRetryability : public MockReplCoordServerFixture { +public: + FindAndModifyRetryability() = default; + +protected: + /** + * Helper function to return a fully-constructed BSONObj instead of having to use + * BSONObjBuilder. + */ + static BSONObj constructFindAndModifyRetryResult(OperationContext* opCtx, + const FindAndModifyRequest& request, + const repl::OplogEntry& oplogEntry) { + BSONObjBuilder builder; + parseOplogEntryForFindAndModify(opCtx, request, oplogEntry, &builder); + return builder.obj(); + } +}; + +const NamespaceString kNs("test.user"); TEST_F(FindAndModifyRetryability, BasicUpsert) { auto request = FindAndModifyRequest::makeUpdate(kNs, BSONObj(), BSONObj()); request.setUpsert(true); - repl::OplogEntry insertOplog(repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("x" << 1)); - - auto result = parseOplogEntryForFindAndModify(nullptr, request, insertOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_FALSE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("x" << 1), result.getValue()); + repl::OplogEntry insertOplog(repl::OpTime(), + 0, + repl::OpTypeEnum::kInsert, + kNs, + BSON("_id" + << "ID value" + << "x" + << 1)); + + auto result = constructFindAndModifyRetryResult(opCtx(), request, insertOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" + << BSON("n" << 1 << "updatedExisting" << false << "upserted" + << "ID value") + << "value" + << BSON("_id" + << "ID value" + << "x" + << 1)), + result); } TEST_F(FindAndModifyRetryability, NestedUpsert) { auto request = FindAndModifyRequest::makeUpdate(kNs, BSONObj(), BSONObj()); request.setUpsert(true); - repl::OplogEntry innerOplog(repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("x" << 1)); + repl::OplogEntry innerOplog( + repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("_id" << 1)); repl::OplogEntry insertOplog(repl::OpTime(Timestamp(60, 10), 1), 0, repl::OpTypeEnum::kNoop, @@ -263,23 +283,22 @@ TEST_F(FindAndModifyRetryability, NestedUpsert) { kNestedOplog, innerOplog.toBSON()); - auto result = parseOplogEntryForFindAndModify(nullptr, request, insertOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_FALSE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("x" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, insertOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" + << BSON("n" << 1 << "updatedExisting" << false << "upserted" << 1) + << "value" + << BSON("_id" << 1)), + result); } TEST_F(FindAndModifyRetryability, AttemptingToRetryUpsertWithUpdateWithoutUpsertErrors) { auto request = FindAndModifyRequest::makeUpdate(kNs, BSONObj(), BSONObj()); request.setUpsert(false); - repl::OplogEntry insertOplog(repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("x" << 1)); + repl::OplogEntry insertOplog( + repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("_id" << 1)); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, insertOplog), + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, insertOplog), AssertionException); } @@ -289,7 +308,7 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPostImageButOplogHasPre) { repl::OpTime imageOpTime(Timestamp(120, 3), 1); repl::OplogEntry noteOplog( - imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("x" << 1 << "z" << 1)); + imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("_id" << 1 << "z" << 1)); insertOplogEntry(noteOplog); @@ -297,11 +316,11 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPostImageButOplogHasPre) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); updateOplog.setPreImageOpTime(imageOpTime); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, updateOplog), + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, updateOplog), AssertionException); } @@ -311,14 +330,14 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsUpdateButOplogIsDelete) { repl::OpTime imageOpTime(Timestamp(120, 3), 1); repl::OplogEntry noteOplog( - imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("x" << 1 << "z" << 1)); + imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("_id" << 1 << "z" << 1)); insertOplogEntry(noteOplog); repl::OplogEntry oplog(repl::OpTime(), 0, repl::OpTypeEnum::kDelete, kNs, BSON("_id" << 1)); oplog.setPreImageOpTime(imageOpTime); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, oplog), AssertionException); + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, oplog), AssertionException); } TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPreImageButOplogHasPost) { @@ -327,7 +346,7 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPreImageButOplogHasPost) { repl::OpTime imageOpTime(Timestamp(120, 3), 1); repl::OplogEntry noteOplog( - imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("x" << 1 << "z" << 1)); + imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("_id" << 1 << "z" << 1)); insertOplogEntry(noteOplog); @@ -335,11 +354,11 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPreImageButOplogHasPost) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); updateOplog.setPostImageOpTime(imageOpTime); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, updateOplog), + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, updateOplog), AssertionException); } @@ -349,7 +368,7 @@ TEST_F(FindAndModifyRetryability, UpdateWithPreImage) { repl::OpTime imageOpTime(Timestamp(120, 3), 1); repl::OplogEntry noteOplog( - imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("x" << 1 << "z" << 1)); + imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("_id" << 1 << "z" << 1)); insertOplogEntry(noteOplog); @@ -357,18 +376,15 @@ TEST_F(FindAndModifyRetryability, UpdateWithPreImage) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); updateOplog.setPreImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, updateOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_TRUE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("x" << 1 << "z" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, updateOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" << BSON("n" << 1 << "updatedExisting" << true) + << "value" + << BSON("_id" << 1 << "z" << 1)), + result); } TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) { @@ -377,7 +393,7 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) { repl::OpTime imageOpTime(Timestamp(120, 3), 1); repl::OplogEntry noteOplog( - imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("x" << 1 << "z" << 1)); + imageOpTime, 0, repl::OpTypeEnum::kNoop, kNs, BSON("_id" << 1 << "z" << 1)); insertOplogEntry(noteOplog); @@ -385,8 +401,8 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); repl::OplogEntry updateOplog(repl::OpTime(Timestamp(60, 10), 1), 0, @@ -396,14 +412,11 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) { innerOplog.toBSON()); updateOplog.setPreImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, updateOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_TRUE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("x" << 1 << "z" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, updateOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" << BSON("n" << 1 << "updatedExisting" << true) + << "value" + << BSON("_id" << 1 << "z" << 1)), + result); } TEST_F(FindAndModifyRetryability, UpdateWithPostImage) { @@ -420,18 +433,15 @@ TEST_F(FindAndModifyRetryability, UpdateWithPostImage) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); updateOplog.setPostImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, updateOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_TRUE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("a" << 1 << "b" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, updateOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" << BSON("n" << 1 << "updatedExisting" << true) + << "value" + << BSON("a" << 1 << "b" << 1)), + result); } TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) { @@ -448,8 +458,8 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) { 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); repl::OplogEntry updateOplog(repl::OpTime(Timestamp(60, 10), 1), 0, @@ -459,14 +469,11 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) { innerOplog.toBSON()); updateOplog.setPostImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, updateOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_TRUE(lastError.getUpdatedExisting()); - ASSERT_TRUE(lastError.getUpdatedExisting().value()); - - ASSERT_BSONOBJ_EQ(BSON("a" << 1 << "b" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, updateOplog); + ASSERT_BSONOBJ_EQ(BSON("lastErrorObject" << BSON("n" << 1 << "updatedExisting" << true) + << "value" + << BSON("a" << 1 << "b" << 1)), + result); } TEST_F(FindAndModifyRetryability, UpdateWithPostImageButOplogDoesNotExistShouldError) { @@ -478,11 +485,11 @@ TEST_F(FindAndModifyRetryability, UpdateWithPostImageButOplogDoesNotExistShouldE 0, repl::OpTypeEnum::kUpdate, kNs, - BSON("x" << 1 << "y" << 1), - BSON("x" << 1)); + BSON("_id" << 1 << "y" << 1), + BSON("_id" << 1)); updateOplog.setPostImageOpTime(imageOpTime); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, updateOplog), + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, updateOplog), AssertionException); } @@ -499,13 +506,10 @@ TEST_F(FindAndModifyRetryability, BasicRemove) { repl::OpTime(), 0, repl::OpTypeEnum::kDelete, kNs, BSON("_id" << 20)); removeOplog.setPreImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, removeOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_FALSE(lastError.getUpdatedExisting()); - - ASSERT_BSONOBJ_EQ(BSON("_id" << 20 << "a" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, removeOplog); + ASSERT_BSONOBJ_EQ( + BSON("lastErrorObject" << BSON("n" << 1) << "value" << BSON("_id" << 20 << "a" << 1)), + result); } TEST_F(FindAndModifyRetryability, NestedRemove) { @@ -528,21 +532,19 @@ TEST_F(FindAndModifyRetryability, NestedRemove) { innerOplog.toBSON()); removeOplog.setPreImageOpTime(imageOpTime); - auto result = parseOplogEntryForFindAndModify(opCtx(), request, removeOplog); - - auto lastError = result.getLastErrorObject(); - ASSERT_EQ(1, lastError.getN()); - ASSERT_FALSE(lastError.getUpdatedExisting()); - - ASSERT_BSONOBJ_EQ(BSON("_id" << 20 << "a" << 1), result.getValue()); + auto result = constructFindAndModifyRetryResult(opCtx(), request, removeOplog); + ASSERT_BSONOBJ_EQ( + BSON("lastErrorObject" << BSON("n" << 1) << "value" << BSON("_id" << 20 << "a" << 1)), + result); } TEST_F(FindAndModifyRetryability, AttemptingToRetryUpsertWithRemoveErrors) { auto request = FindAndModifyRequest::makeRemove(kNs, BSONObj()); - repl::OplogEntry insertOplog(repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("x" << 1)); + repl::OplogEntry insertOplog( + repl::OpTime(), 0, repl::OpTypeEnum::kInsert, kNs, BSON("_id" << 1)); - ASSERT_THROWS(parseOplogEntryForFindAndModify(opCtx(), request, insertOplog), + ASSERT_THROWS(constructFindAndModifyRetryResult(opCtx(), request, insertOplog), AssertionException); } |