diff options
author | Mathias Stearn <mathias@10gen.com> | 2017-06-09 19:20:07 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2017-06-19 17:08:35 -0400 |
commit | 1babec6a705c242628a765935ac9d98b56a41218 (patch) | |
tree | 1b917e39f921cce0a2cd41db170ea6c22e6348bc /src/mongo | |
parent | a9a849b21ced693f2345d4507ee14541818244d9 (diff) | |
download | mongo-1babec6a705c242628a765935ac9d98b56a41218.tar.gz |
SERVER-29564 BSONObjBuilder can now be seeded with a BSONObj prefix
This will avoid copying whenever it is safe.
Diffstat (limited to 'src/mongo')
32 files changed, 229 insertions, 92 deletions
diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index b1b3473c7d2..8d62bc8fd3d 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -204,11 +204,17 @@ public: shareOwnershipWith(other.sharedBuffer()); } - ConstSharedBuffer sharedBuffer() const { + const ConstSharedBuffer& sharedBuffer() const { invariant(isOwned()); return _ownedBuffer; } + ConstSharedBuffer releaseSharedBuffer() { + invariant(isOwned()); + BSONObj sink = std::move(*this); // Leave *this in a valid moved-from state. + return std::move(sink._ownedBuffer); + } + /** If the data buffer is under the control of this BSONObj, return it. Else return an owned copy. */ diff --git a/src/mongo/bson/bsonobjbuilder.cpp b/src/mongo/bson/bsonobjbuilder.cpp index 32c46b30a00..ae60c65b5ee 100644 --- a/src/mongo/bson/bsonobjbuilder.cpp +++ b/src/mongo/bson/bsonobjbuilder.cpp @@ -188,7 +188,7 @@ BSONObjBuilder& BSONObjBuilder::appendDate(StringData fieldName, Date_t dt) { } /* add all the fields from the object specified to this object */ -BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { +BSONObjBuilder& BSONObjBuilder::appendElements(const BSONObj& x) { if (!x.isEmpty()) _b.appendBuf(x.objdata() + 4, // skip over leading length x.objsize() - 5); // ignore leading length and trailing \0 @@ -196,7 +196,7 @@ BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { } /* add all the fields from the object specified to this object if they don't exist */ -BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { +BSONObjBuilder& BSONObjBuilder::appendElementsUnique(const BSONObj& x) { std::set<std::string> have; { BSONObjIterator i = iterator(); diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h index bd10a7b3d16..b5207af742e 100644 --- a/src/mongo/bson/bsonobjbuilder.h +++ b/src/mongo/bson/bsonobjbuilder.h @@ -124,6 +124,33 @@ public: _b.reserveBytes(1); } + /** + * Creates a new BSONObjBuilder prefixed with the fields in 'prefix'. + * + * If prefix is an rvalue referring to the only view of the underlying BSON buffer, it will be + * able to avoid copying and will just reuse the buffer. Therefore, you should try to std::move + * into this constructor where possible. + */ + BSONObjBuilder(BSONObj prefix) + : _b(_buf), _buf(0), _offset(0), _s(this), _tracker(0), _doneCalled(false) { + // If prefix wasn't owned or we don't have exclusive access to it, we must copy. + if (!prefix.isOwned() || prefix.sharedBuffer().isShared()) { + _b.grow(prefix.objsize()); // Make sure we won't need to realloc(). + _b.setlen(sizeof(int)); // Skip over size bytes (see first constructor). + _b.reserveBytes(1); // Reserve room for our EOO byte. + appendElements(prefix); + return; + } + + const auto size = prefix.objsize(); + const char* const firstByte = prefix.objdata(); + auto buf = prefix.releaseSharedBuffer().constCast(); + _offset = firstByte - buf.get(); + _b.useSharedBuffer(std::move(buf)); + _b.setlen(_offset + size - 1); // Position right before prefix's EOO byte. + _b.reserveBytes(1); // Reserve room for our EOO byte. + } + // Move constructible, but not assignable due to reference member. BSONObjBuilder(BSONObjBuilder&& other) : _b(&other._b == &other._buf ? _buf : other._b), @@ -146,10 +173,10 @@ public: } /** add all the fields from the object specified to this object */ - BSONObjBuilder& appendElements(BSONObj x); + BSONObjBuilder& appendElements(const BSONObj& x); /** add all the fields from the object specified to this object if they don't exist already */ - BSONObjBuilder& appendElementsUnique(BSONObj x); + BSONObjBuilder& appendElementsUnique(const BSONObj& x); /** append element to the object we are building */ BSONObjBuilder& append(const BSONElement& e) { @@ -637,8 +664,9 @@ public: */ BSONObj obj() { massert(10335, "builder does not own memory", owned()); - doneFast(); - return BSONObj(_b.release()); + auto out = done(); + out.shareOwnershipWith(_b.release()); + return out; } /** Fetch the object we have built. diff --git a/src/mongo/bson/bsonobjbuilder_test.cpp b/src/mongo/bson/bsonobjbuilder_test.cpp index 901b2d14965..6debaa2fa3a 100644 --- a/src/mongo/bson/bsonobjbuilder_test.cpp +++ b/src/mongo/bson/bsonobjbuilder_test.cpp @@ -366,5 +366,85 @@ TEST(BSONArrayBuilderTest, MovingABSONArrayBuilderWorks) { ASSERT_BSONOBJ_EQ(bob.obj(), BSON("a" << 1 << "array" << BSON_ARRAY(1 << "2" << 3 << "4"))); } +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithRootedUnsharedOwnedBsonWorks) { + auto origObj = BSON("a" << 1); + auto origObjPtr = origObj.objdata(); + + BSONObjBuilder bob(std::move(origObj)); // moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSONObj()); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_EQ(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithRootedSharedOwnedBsonWorks) { + const auto origObj = BSON("a" << 1); + auto origObjPtr = origObj.objdata(); + + BSONObjBuilder bob(origObj); // not moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSON("a" << 1)); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_NE(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithRootedUnownedBsonWorks) { + const auto origObjHolder = BSON("a" << 1); + auto origObjPtr = origObjHolder.objdata(); + const auto origObj = BSONObj(origObjPtr); + invariant(!origObj.isOwned()); + + BSONObjBuilder bob(origObj); // not moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSON("a" << 1)); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_NE(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithNonrootedUnsharedOwnedBsonWorks) { + auto origObjHolder = BSON("" << BSON("a" << 1)); + auto origObj = origObjHolder.firstElement().Obj(); + auto origObjPtr = origObj.objdata(); + origObj.shareOwnershipWith(origObjHolder.releaseSharedBuffer()); + + BSONObjBuilder bob(std::move(origObj)); // moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSONObj()); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_EQ(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithNonrootedSharedOwnedBsonWorks) { + auto origObjHolder = BSON("" << BSON("a" << 1)); + auto origObj = origObjHolder.firstElement().Obj(); + auto origObjPtr = origObj.objdata(); + origObj.shareOwnershipWith(origObjHolder.releaseSharedBuffer()); + + BSONObjBuilder bob(origObj); // not moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSON("a" << 1)); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_NE(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + +TEST(BSONObjBuilderTest, SeedingBSONObjBuilderWithNonrootedUnownedBsonWorks) { + auto origObjHolder = BSON("" << BSON("a" << 1)); + auto origObj = origObjHolder.firstElement().Obj(); + auto origObjPtr = origObj.objdata(); + invariant(!origObj.isOwned()); + + BSONObjBuilder bob(origObj); // not moving. + bob.append("b", 1); + const auto obj = bob.obj(); + ASSERT_BSONOBJ_EQ(origObj, BSON("a" << 1)); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1 << "b" << 1)); + ASSERT_NE(static_cast<const void*>(obj.objdata()), static_cast<const void*>(origObjPtr)); +} + } // namespace } // namespace mongo diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h index fbb470fd6fd..abd4dd95f90 100644 --- a/src/mongo/bson/util/builder.h +++ b/src/mongo/bson/util/builder.h @@ -80,6 +80,9 @@ class SharedBufferAllocator { public: SharedBufferAllocator() = default; + SharedBufferAllocator(SharedBuffer buf) : _buf(std::move(buf)) { + invariant(!_buf.isShared()); + } // Allow moving but not copying. It would be an error for two SharedBufferAllocators to use the // same underlying buffer. @@ -314,6 +317,18 @@ public: reservedBytes -= bytes; } + /** + * Replaces the buffer backing this BufBuilder with the passed in SharedBuffer. + * Only legal to call when this builder is empty and when the SharedBuffer isn't shared. + */ + void useSharedBuffer(SharedBuffer buf) { + MONGO_STATIC_ASSERT(std::is_same<BufferAllocator, SharedBufferAllocator>()); + invariant(l == 0); // Can only do this while empty. + invariant(reservedBytes == 0); + size = buf.capacity(); + _buf = SharedBufferAllocator(std::move(buf)); + } + private: template <typename T> void appendNumImpl(T t) { diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 21b88587ad1..36821188887 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -185,8 +185,7 @@ rpc::UniqueReply DBClientWithCommands::runCommandWithMetadata(StringData databas // call() oddly takes this by pointer, so we need to put it on the stack. auto host = getServerAddress(); - BSONObjBuilder metadataBob; - metadataBob.appendElements(metadata); + BSONObjBuilder metadataBob(std::move(metadata)); if (_metadataWriter) { uassertStatusOK( @@ -198,7 +197,7 @@ rpc::UniqueReply DBClientWithCommands::runCommandWithMetadata(StringData databas requestBuilder->setDatabase(database); requestBuilder->setCommandName(command); requestBuilder->setCommandArgs(commandArgs); - requestBuilder->setMetadata(metadataBob.done()); + requestBuilder->setMetadata(metadataBob.obj()); auto requestMsg = requestBuilder->done(); Message replyMsg; diff --git a/src/mongo/client/dbclientcursor.cpp b/src/mongo/client/dbclientcursor.cpp index a9a1f854006..e7675630ab6 100644 --- a/src/mongo/client/dbclientcursor.cpp +++ b/src/mongo/client/dbclientcursor.cpp @@ -72,8 +72,7 @@ Message assembleCommandRequest(DBClientWithCommands* cli, std::tie(upconvertedCommand, upconvertedMetadata) = rpc::upconvertRequestMetadata(std::move(legacyQuery), legacyQueryOptions); - BSONObjBuilder metadataBob; - metadataBob.appendElements(upconvertedMetadata); + BSONObjBuilder metadataBob(std::move(upconvertedMetadata)); if (cli->getRequestMetadataWriter()) { uassertStatusOK(cli->getRequestMetadataWriter()( (haveClient() ? cc().getOperationContext() : nullptr), &metadataBob)); @@ -84,7 +83,7 @@ Message assembleCommandRequest(DBClientWithCommands* cli, // been wrapped. requestBuilder->setCommandName(upconvertedCommand.firstElementFieldName()); requestBuilder->setCommandArgs(std::move(upconvertedCommand)); - requestBuilder->setMetadata(metadataBob.done()); + requestBuilder->setMetadata(metadataBob.obj()); return requestBuilder->done(); } diff --git a/src/mongo/client/query.cpp b/src/mongo/client/query.cpp index 349ed5ff96d..6d3e22d4c78 100644 --- a/src/mongo/client/query.cpp +++ b/src/mongo/client/query.cpp @@ -53,8 +53,7 @@ Query& Query::hint(const string& jsonKeyPatt) { Query& Query::where(const string& jscode, BSONObj scope) { /* use where() before sort() and hint() and explain(), else this will assert. */ verify(!isComplex()); - BSONObjBuilder b; - b.appendElements(obj); + BSONObjBuilder b(std::move(obj)); b.appendWhere(jscode, scope); obj = b.obj(); return *this; diff --git a/src/mongo/client/query.h b/src/mongo/client/query.h index 4a4a97bd9b8..3ced9c591f4 100644 --- a/src/mongo/client/query.h +++ b/src/mongo/client/query.h @@ -164,8 +164,7 @@ private: template <class T> void appendComplex(const char* fieldName, const T& val) { makeComplex(); - BSONObjBuilder b; - b.appendElements(obj); + BSONObjBuilder b(std::move(obj)); b.append(fieldName, val); obj = b.obj(); } diff --git a/src/mongo/client/scoped_db_conn_test.cpp b/src/mongo/client/scoped_db_conn_test.cpp index bda09d75263..1a9d7581424 100644 --- a/src/mongo/client/scoped_db_conn_test.cpp +++ b/src/mongo/client/scoped_db_conn_test.cpp @@ -118,7 +118,7 @@ private: commandResponse.append("minWireVersion", WireVersion::RELEASE_2_4_AND_BEFORE); } - auto response = reply->setCommandReply(commandResponse.done()) + auto response = reply->setCommandReply(commandResponse.obj()) .setMetadata(rpc::makeEmptyMetadata()) .done(); diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 045a4c3715c..dd75b69c6be 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -303,7 +303,7 @@ void Command::generateHelpResponse(OperationContext* opCtx, command.help(ss); helpBuilder.append("help", ss.str()); - replyBuilder->setCommandReply(helpBuilder.done()); + replyBuilder->setCommandReply(helpBuilder.obj()); replyBuilder->setMetadata(rpc::makeEmptyMetadata()); } diff --git a/src/mongo/db/repl/repl_set_commands.cpp b/src/mongo/db/repl/repl_set_commands.cpp index f34b91f4af2..2c35b250c9a 100644 --- a/src/mongo/db/repl/repl_set_commands.cpp +++ b/src/mongo/db/repl/repl_set_commands.cpp @@ -381,8 +381,7 @@ public: if (configObj.getField("version").eoo()) { // Missing version field defaults to version 1. - BSONObjBuilder builder; - builder.appendElements(configObj); + BSONObjBuilder builder(std::move(configObj)); builder.append("version", 1); configObj = builder.obj(); } diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp index 1ad0829b08b..7df2de5e2f8 100644 --- a/src/mongo/db/repl/storage_interface_impl_test.cpp +++ b/src/mongo/db/repl/storage_interface_impl_test.cpp @@ -118,8 +118,7 @@ void createCollection(OperationContext* opCtx, * Creates an oplog entry with given optime. */ BSONObj makeOplogEntry(OpTime opTime) { - BSONObjBuilder bob; - bob.appendElements(opTime.toBSON()); + BSONObjBuilder bob(opTime.toBSON()); bob.append("h", 1LL); bob.append("op", "c"); bob.append("ns", "test.t"); diff --git a/src/mongo/executor/network_interface_asio.cpp b/src/mongo/executor/network_interface_asio.cpp index 22666b05672..f2d5fa97c3b 100644 --- a/src/mongo/executor/network_interface_asio.cpp +++ b/src/mongo/executor/network_interface_asio.cpp @@ -230,8 +230,7 @@ Status attachMetadataIfNeeded(RemoteCommandRequest& request, // Append the metadata of the request with metadata from the metadata hook // if a hook is installed if (metadataHook) { - BSONObjBuilder augmentedBob; - augmentedBob.appendElements(request.metadata); + BSONObjBuilder augmentedBob(std::move(request.metadata)); auto writeStatus = callNoexcept(*metadataHook, &rpc::EgressMetadataHook::writeRequestMetadata, diff --git a/src/mongo/executor/network_test_env.cpp b/src/mongo/executor/network_test_env.cpp index 2d5ec28ebc3..79dbd27a992 100644 --- a/src/mongo/executor/network_test_env.cpp +++ b/src/mongo/executor/network_test_env.cpp @@ -47,12 +47,10 @@ void NetworkTestEnv::onCommand(OnCommandFunction func) { const NetworkInterfaceMock::NetworkOperationIterator noi = _mockNetwork->getNextReadyRequest(); const RemoteCommandRequest& request = noi->getRequest(); - const auto& resultStatus = func(request); - - BSONObjBuilder result; + auto resultStatus = func(request); if (resultStatus.isOK()) { - result.appendElements(resultStatus.getValue()); + BSONObjBuilder result(std::move(resultStatus.getValue())); Command::appendCommandStatus(result, resultStatus.getStatus()); const RemoteCommandResponse response(result.obj(), BSONObj(), Milliseconds(1)); @@ -72,12 +70,10 @@ void NetworkTestEnv::onCommandWithMetadata(OnCommandWithMetadataFunction func) { const NetworkInterfaceMock::NetworkOperationIterator noi = _mockNetwork->getNextReadyRequest(); const RemoteCommandRequest& request = noi->getRequest(); - const auto cmdResponseStatus = func(request); - - BSONObjBuilder result; + auto cmdResponseStatus = func(request); if (cmdResponseStatus.isOK()) { - result.appendElements(cmdResponseStatus.data); + BSONObjBuilder result(std::move(cmdResponseStatus.data)); Command::appendCommandStatus(result, cmdResponseStatus.status); const RemoteCommandResponse response( result.obj(), cmdResponseStatus.metadata, Milliseconds(1)); diff --git a/src/mongo/rpc/command_request.cpp b/src/mongo/rpc/command_request.cpp index a796f6f8a19..067abeef636 100644 --- a/src/mongo/rpc/command_request.cpp +++ b/src/mongo/rpc/command_request.cpp @@ -105,8 +105,7 @@ ParsedOpCommand ParsedOpCommand::parse(const Message& message) { OpMsgRequest opMsgRequestFromCommandRequest(const Message& message) { auto parsed = ParsedOpCommand::parse(message); - BSONObjBuilder bodyBuilder; - bodyBuilder.appendElements(parsed.body); + BSONObjBuilder bodyBuilder(std::move(parsed.body)); // OP_COMMAND is only used when communicating with 3.4 nodes and they serialize their metadata // fields differently. We do all up-conversion here so that the rest of the code only has to diff --git a/src/mongo/rpc/legacy_reply_builder.cpp b/src/mongo/rpc/legacy_reply_builder.cpp index 5fb373d12c8..340d892472f 100644 --- a/src/mongo/rpc/legacy_reply_builder.cpp +++ b/src/mongo/rpc/legacy_reply_builder.cpp @@ -52,7 +52,7 @@ LegacyReplyBuilder::LegacyReplyBuilder(Message&& message) : _message{std::move(m LegacyReplyBuilder::~LegacyReplyBuilder() {} LegacyReplyBuilder& LegacyReplyBuilder::setCommandReply(Status nonOKStatus, - const BSONObj& extraErrorInfo) { + BSONObj extraErrorInfo) { invariant(_state == State::kCommandReply); if (nonOKStatus == ErrorCodes::SendStaleConfig) { _staleConfigError = true; @@ -66,7 +66,7 @@ LegacyReplyBuilder& LegacyReplyBuilder::setCommandReply(Status nonOKStatus, setRawCommandReply(err.done()); } else { // All other errors proceed through the normal path, which also handles state transitions. - ReplyBuilderInterface::setCommandReply(std::move(nonOKStatus), extraErrorInfo); + ReplyBuilderInterface::setCommandReply(std::move(nonOKStatus), std::move(extraErrorInfo)); } return *this; } diff --git a/src/mongo/rpc/legacy_reply_builder.h b/src/mongo/rpc/legacy_reply_builder.h index beb7806147c..d72fdc6b93b 100644 --- a/src/mongo/rpc/legacy_reply_builder.h +++ b/src/mongo/rpc/legacy_reply_builder.h @@ -49,7 +49,7 @@ public: ~LegacyReplyBuilder() final; // Override of setCommandReply specifically used to handle SendStaleConfigException. - LegacyReplyBuilder& setCommandReply(Status nonOKStatus, const BSONObj& extraErrorInfo) final; + LegacyReplyBuilder& setCommandReply(Status nonOKStatus, BSONObj extraErrorInfo) final; LegacyReplyBuilder& setRawCommandReply(const BSONObj& commandReply) final; BSONObjBuilder getInPlaceReplyBuilder(std::size_t) final; diff --git a/src/mongo/rpc/legacy_request.cpp b/src/mongo/rpc/legacy_request.cpp index 9d5beae3af2..8247c7d3923 100644 --- a/src/mongo/rpc/legacy_request.cpp +++ b/src/mongo/rpc/legacy_request.cpp @@ -55,7 +55,7 @@ OpMsgRequest opMsgRequestFromLegacyRequest(const Message& message) { auto bodyAndMetadata = rpc::upconvertRequestMetadata(qm.query, qm.queryOptions); return OpMsgRequest::fromDBAndBody( - ns.db(), std::get<0>(bodyAndMetadata), std::get<1>(bodyAndMetadata)); + ns.db(), std::move(std::get<0>(bodyAndMetadata)), std::get<1>(bodyAndMetadata)); } } // namespace rpc diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp index bf3aab6c8e4..715532f453d 100644 --- a/src/mongo/rpc/metadata.cpp +++ b/src/mongo/rpc/metadata.cpp @@ -164,8 +164,7 @@ CommandAndMetadata upconvertRequestMetadata(BSONObj legacyCmdObj, int queryFlags LegacyCommandAndFlags downconvertRequestMetadata(BSONObj cmdObj, BSONObj metadata) { int legacyQueryFlags = 0; if (auto logicalTime = metadata[LogicalTimeMetadata::fieldName()]) { - BSONObjBuilder logicalTimeCommandBob; - logicalTimeCommandBob.appendElements(cmdObj); + BSONObjBuilder logicalTimeCommandBob(std::move(cmdObj)); logicalTimeCommandBob.append(logicalTime); cmdObj = logicalTimeCommandBob.obj(); } diff --git a/src/mongo/rpc/reply_builder_interface.cpp b/src/mongo/rpc/reply_builder_interface.cpp index 34a99cd4176..99fe2edcb1e 100644 --- a/src/mongo/rpc/reply_builder_interface.cpp +++ b/src/mongo/rpc/reply_builder_interface.cpp @@ -48,14 +48,13 @@ const char kErrorField[] = "errmsg"; // similar to appendCommandStatus (duplicating logic here to avoid cyclic library // dependency) -BSONObj augmentReplyWithStatus(const Status& status, const BSONObj& reply) { +BSONObj augmentReplyWithStatus(const Status& status, BSONObj reply) { auto okField = reply.getField(kOKField); if (!okField.eoo() && okField.trueValue()) { return reply; } - BSONObjBuilder bob; - bob.appendElements(reply); + BSONObjBuilder bob(std::move(reply)); if (okField.eoo()) { bob.append(kOKField, status.isOK() ? 1.0 : 0.0); } @@ -63,11 +62,11 @@ BSONObj augmentReplyWithStatus(const Status& status, const BSONObj& reply) { return bob.obj(); } - if (!reply.hasField(kErrorField)) { + if (!bob.asTempObj().hasField(kErrorField)) { bob.append(kErrorField, status.reason()); } - if (!reply.hasField(kCodeField)) { + if (!bob.asTempObj().hasField(kCodeField)) { bob.append(kCodeField, status.code()); bob.append(kCodeNameField, ErrorCodes::errorString(status.code())); } @@ -78,13 +77,13 @@ BSONObj augmentReplyWithStatus(const Status& status, const BSONObj& reply) { ReplyBuilderInterface& ReplyBuilderInterface::setCommandReply(StatusWith<BSONObj> commandReply) { auto reply = commandReply.isOK() ? std::move(commandReply.getValue()) : BSONObj(); - return setRawCommandReply(augmentReplyWithStatus(commandReply.getStatus(), reply)); + return setRawCommandReply(augmentReplyWithStatus(commandReply.getStatus(), std::move(reply))); } ReplyBuilderInterface& ReplyBuilderInterface::setCommandReply(Status nonOKStatus, - const BSONObj& extraErrorInfo) { + BSONObj extraErrorInfo) { invariant(!nonOKStatus.isOK()); - return setRawCommandReply(augmentReplyWithStatus(nonOKStatus, extraErrorInfo)); + return setRawCommandReply(augmentReplyWithStatus(nonOKStatus, std::move(extraErrorInfo))); } } // namespace rpc diff --git a/src/mongo/rpc/reply_builder_interface.h b/src/mongo/rpc/reply_builder_interface.h index b2631bfaf0e..a157f41167e 100644 --- a/src/mongo/rpc/reply_builder_interface.h +++ b/src/mongo/rpc/reply_builder_interface.h @@ -84,8 +84,7 @@ public: * interfacing with legacy code that adds additional data to a failed command reply and * its use is discouraged in new code. */ - virtual ReplyBuilderInterface& setCommandReply(Status nonOKStatus, - const BSONObj& extraErrorInfo); + virtual ReplyBuilderInterface& setCommandReply(Status nonOKStatus, BSONObj extraErrorInfo); /** * Gets the protocol used to serialize this reply. This should be used for validity checks diff --git a/src/mongo/s/commands/cluster_aggregate.cpp b/src/mongo/s/commands/cluster_aggregate.cpp index 72a860f1517..0453bfa1880 100644 --- a/src/mongo/s/commands/cluster_aggregate.cpp +++ b/src/mongo/s/commands/cluster_aggregate.cpp @@ -468,7 +468,8 @@ Status ClusterAggregate::aggPassthrough(OperationContext* opCtx, opCtx, ReadPreferenceSetting::get(opCtx), namespaces.executionNss.db().toString(), - !shard->isConfig() ? appendShardVersion(cmdObj, ChunkVersion::UNSHARDED()) : cmdObj, + !shard->isConfig() ? appendShardVersion(std::move(cmdObj), ChunkVersion::UNSHARDED()) + : std::move(cmdObj), Shard::RetryPolicy::kNoRetry)); if (ErrorCodes::isStaleShardingError(cmdResponse.commandStatus.code())) { diff --git a/src/mongo/s/commands/cluster_commands_helpers.cpp b/src/mongo/s/commands/cluster_commands_helpers.cpp index b7311ac4dbe..a8d44e4017a 100644 --- a/src/mongo/s/commands/cluster_commands_helpers.cpp +++ b/src/mongo/s/commands/cluster_commands_helpers.cpp @@ -213,9 +213,8 @@ StatusWith<std::vector<AsyncRequestsSender::Response>> gatherResponses( } // namespace -BSONObj appendShardVersion(const BSONObj& cmdObj, ChunkVersion version) { - BSONObjBuilder cmdWithVersionBob; - cmdWithVersionBob.appendElements(cmdObj); +BSONObj appendShardVersion(BSONObj cmdObj, ChunkVersion version) { + BSONObjBuilder cmdWithVersionBob(std::move(cmdObj)); version.appendForCommands(&cmdWithVersionBob); return cmdWithVersionBob.obj(); } diff --git a/src/mongo/s/commands/cluster_commands_helpers.h b/src/mongo/s/commands/cluster_commands_helpers.h index 184be2d6063..9445063e7e1 100644 --- a/src/mongo/s/commands/cluster_commands_helpers.h +++ b/src/mongo/s/commands/cluster_commands_helpers.h @@ -62,7 +62,7 @@ void appendWriteConcernErrorToCmdResponse(const ShardId& shardID, /** * Returns a copy of 'cmdObj' with 'version' appended. */ -BSONObj appendShardVersion(const BSONObj& cmdObj, ChunkVersion version); +BSONObj appendShardVersion(BSONObj cmdObj, ChunkVersion version); /** * Generic function for dispatching commands to the cluster. diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp index a3cadb1ddb7..398aa6f3123 100644 --- a/src/mongo/s/commands/commands_public.cpp +++ b/src/mongo/s/commands/commands_public.cpp @@ -1296,8 +1296,7 @@ public: // long as we keep getting more chunks. The end condition is when we go to // look for chunk n and it doesn't exist. This means that the file's last // chunk is n-1, so we return the computed md5 results. - BSONObjBuilder bb; - bb.appendElements(filterCommandRequestForPassthrough(cmdObj)); + BSONObjBuilder bb(filterCommandRequestForPassthrough(cmdObj)); bb.appendBool("partialOk", true); bb.append("startAt", n); if (!lastResult.isEmpty()) { diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index c6e69ee1bdd..afa4f04e55a 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -473,8 +473,7 @@ DbResponse Strategy::clientOpQueryCommand(OperationContext* opCtx, // If the slaveOK bit is set, behave as-if read preference secondary-preferred was // specified. const auto readPref = ReadPreferenceSetting(ReadPreference::SecondaryPreferred); - BSONObjBuilder finalCmdObjBuilder; - finalCmdObjBuilder.appendElements(cmdObj); + BSONObjBuilder finalCmdObjBuilder(std::move(cmdObj)); readPref.toContainingBSON(&finalCmdObjBuilder); cmdObj = finalCmdObjBuilder.obj(); } @@ -662,11 +661,9 @@ void Strategy::writeOp(OperationContext* opCtx, DbMessage* dbm) { // Adjust namespace for command const NamespaceString& fullNS(commandRequest->getNS()); - BSONObj commandBSON = commandRequest->toBSON(); - BSONObjBuilder builder; runAgainstRegistered( - opCtx, OpMsgRequest::fromDBAndBody(fullNS.db(), commandBSON), builder); + opCtx, OpMsgRequest::fromDBAndBody(fullNS.db(), commandRequest->toBSON()), builder); bool parsed = commandResponse.parseBSON(builder.done(), nullptr); (void)parsed; // for compile diff --git a/src/mongo/s/write_ops/batched_command_request.cpp b/src/mongo/s/write_ops/batched_command_request.cpp index e4811ffe763..48c77723db3 100644 --- a/src/mongo/s/write_ops/batched_command_request.cpp +++ b/src/mongo/s/write_ops/batched_command_request.cpp @@ -138,21 +138,18 @@ bool BatchedCommandRequest::isValid(std::string* errMsg) const { } BSONObj BatchedCommandRequest::toBSON() const { - BSONObjBuilder builder; - - switch (getBatchType()) { - case BatchedCommandRequest::BatchType_Insert: - builder.appendElements(_insertReq->toBSON()); - break; - case BatchedCommandRequest::BatchType_Update: - builder.appendElements(_updateReq->toBSON()); - break; - case BatchedCommandRequest::BatchType_Delete: - builder.appendElements(_deleteReq->toBSON()); - break; - default: - MONGO_UNREACHABLE; - } + BSONObjBuilder builder([&] { + switch (getBatchType()) { + case BatchedCommandRequest::BatchType_Insert: + return _insertReq->toBSON(); + case BatchedCommandRequest::BatchType_Update: + return _updateReq->toBSON(); + case BatchedCommandRequest::BatchType_Delete: + return _deleteReq->toBSON(); + default: + MONGO_UNREACHABLE; + } + }()); // Append the shard version if (_shardVersion) { diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp index 5e2c2236fc9..e8399fd10d8 100644 --- a/src/mongo/scripting/mozjs/mongo.cpp +++ b/src/mongo/scripting/mozjs/mongo.cpp @@ -583,9 +583,7 @@ void MongoBase::Functions::copyDatabaseWithSCRAM::call(JSContext* cx, JS::CallAr status = session->step(payload, &responsePayload); uassertStatusOK(status); - BSONObjBuilder commandBuilder; - - commandBuilder.appendElements(saslCommandPrefix); + BSONObjBuilder commandBuilder(std::move(saslCommandPrefix)); commandBuilder.appendBinData(saslCommandPayloadFieldName, static_cast<int>(responsePayload.size()), BinDataGeneral, diff --git a/src/mongo/tools/bridge.cpp b/src/mongo/tools/bridge.cpp index a6730e8a32c..0b33349a4f0 100644 --- a/src/mongo/tools/bridge.cpp +++ b/src/mongo/tools/bridge.cpp @@ -179,8 +179,9 @@ public: if (!status->isOK()) { commandReply = StatusWith<BSONObj>(*status); } - auto cmdResponse = - replyBuilder->setCommandReply(commandReply).setMetadata(metadata).done(); + auto cmdResponse = replyBuilder->setCommandReply(std::move(commandReply)) + .setMetadata(metadata) + .done(); _mp->say(cmdResponse, requestId); continue; } diff --git a/src/mongo/util/net/op_msg.h b/src/mongo/util/net/op_msg.h index dc8037146be..e32b370d5d2 100644 --- a/src/mongo/util/net/op_msg.h +++ b/src/mongo/util/net/op_msg.h @@ -109,12 +109,11 @@ struct OpMsgRequest : public OpMsg { } static OpMsgRequest fromDBAndBody(StringData db, - const BSONObj& body, + BSONObj body, const BSONObj& extraFields = {}) { OpMsgRequest request; request.body = ([&] { - BSONObjBuilder bodyBuilder; - bodyBuilder.appendElements(body); + BSONObjBuilder bodyBuilder(std::move(body)); bodyBuilder.appendElements(extraFields); bodyBuilder.append("$db", db); return bodyBuilder.obj(); diff --git a/src/mongo/util/shared_buffer.h b/src/mongo/util/shared_buffer.h index 09951dc80dc..dc8599f7e6e 100644 --- a/src/mongo/util/shared_buffer.h +++ b/src/mongo/util/shared_buffer.h @@ -48,7 +48,7 @@ public: } static SharedBuffer allocate(size_t bytes) { - return takeOwnership(mongoMalloc(sizeof(Holder) + bytes)); + return takeOwnership(mongoMalloc(sizeof(Holder) + bytes), bytes); } /** @@ -67,7 +67,7 @@ public: // Get newPtr into _holder with a ref-count of 1 without touching the current pointee of // _holder which is now invalid. - auto tmp = SharedBuffer::takeOwnership(newPtr); + auto tmp = SharedBuffer::takeOwnership(newPtr, size); _holder.detach(); _holder = std::move(tmp._holder); } @@ -80,11 +80,29 @@ public: return bool(_holder); } + /** + * Returns true if this object has exclusive access to the underlying buffer. + * (That is, reference count == 1). + */ + bool isShared() const { + return _holder && _holder->isShared(); + } + + /** + * Returns the allocation size of the underlying buffer. + * Users of this type must maintain the "used" size separately. + */ + size_t capacity() const { + return _holder ? _holder->_capacity : 0; + } + private: class Holder { public: - explicit Holder(AtomicUInt32::WordType initial = AtomicUInt32::WordType()) - : _refCount(initial) {} + explicit Holder(AtomicUInt32::WordType initial, size_t capacity) + : _refCount(initial), _capacity(capacity) { + invariant(capacity == _capacity); + } // these are called automatically by boost::intrusive_ptr friend void intrusive_ptr_add_ref(Holder* h) { @@ -113,6 +131,7 @@ private: } AtomicUInt32 _refCount; + uint32_t _capacity; }; explicit SharedBuffer(Holder* holder) : _holder(holder, /*add_ref=*/false) { @@ -127,12 +146,12 @@ private: * This class will call free(holderPrefixedData), so it must have been allocated in a way * that makes that valid. */ - static SharedBuffer takeOwnership(void* holderPrefixedData) { + static SharedBuffer takeOwnership(void* holderPrefixedData, size_t capacity) { // Initialize the refcount to 1 so we don't need to increment it in the constructor // (see private Holder* constructor above). // // TODO: Should dassert alignment of holderPrefixedData here if possible. - return SharedBuffer(new (holderPrefixedData) Holder(1U)); + return SharedBuffer(new (holderPrefixedData) Holder(1U, capacity)); } boost::intrusive_ptr<Holder> _holder; @@ -164,6 +183,19 @@ public: return bool(_buffer); } + bool isShared() const { + return _buffer.isShared(); + } + + /** + * Converts to a mutable SharedBuffer. + * This is only legal to call if you have exclusive access to the underlying buffer. + */ + SharedBuffer constCast() && { + invariant(!isShared()); + return std::move(_buffer); + } + private: SharedBuffer _buffer; }; |