Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/mongocxx/collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,7 @@ class change_stream collection::_watch(const client_session* session,
}

class index_view collection::indexes() {
return index_view{_get_impl().collection_t};
return index_view{_get_impl().collection_t, _get_impl().client_impl->client_t};
}

class bulk_write collection::_init_insert_many(const options::insert& options,
Expand Down
5 changes: 3 additions & 2 deletions src/mongocxx/index_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
namespace mongocxx {
MONGOCXX_INLINE_NAMESPACE_BEGIN

index_view::index_view(void* coll)
: _impl{stdx::make_unique<impl>(static_cast<mongoc_collection_t*>(coll))} {}
index_view::index_view(void* coll, void* client)
: _impl{stdx::make_unique<impl>(static_cast<mongoc_collection_t*>(coll),
static_cast<mongoc_client_t*>(client))} {}

index_view::index_view(index_view&&) noexcept = default;
index_view& index_view::operator=(index_view&&) noexcept = default;
Expand Down
2 changes: 1 addition & 1 deletion src/mongocxx/index_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ class MONGOCXX_API index_view {
friend class collection;
class MONGOCXX_PRIVATE impl;

MONGOCXX_PRIVATE index_view(void* coll);
MONGOCXX_PRIVATE index_view(void* coll, void* client);

MONGOCXX_PRIVATE impl& _get_impl();

Expand Down
23 changes: 22 additions & 1 deletion src/mongocxx/options/index_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/builder/basic/kvp.hpp>
#include <bsoncxx/types.hpp>
#include <mongocxx/config/private/prelude.hh>
#include <mongocxx/options/index_view.hpp>

#include <mongocxx/config/private/prelude.hh>
using bsoncxx::builder::basic::kvp;
using bsoncxx::builder::basic::make_document;

namespace mongocxx {
MONGOCXX_INLINE_NAMESPACE_BEGIN
Expand All @@ -30,6 +35,10 @@ const bsoncxx::stdx::optional<std::chrono::milliseconds>& index_view::max_time()
return _max_time;
}

const stdx::optional<bsoncxx::document::value> index_view::commit_quorum() const {
return _commit_quorum;
}

index_view& index_view::max_time(std::chrono::milliseconds max_time) {
_max_time = max_time;
return *this;
Expand All @@ -40,6 +49,18 @@ index_view& index_view::write_concern(mongocxx::write_concern write_concern) {
return *this;
}

index_view& index_view::commit_quorum(int commit_quorum) {
_commit_quorum = stdx::make_optional<bsoncxx::document::value>(
make_document(kvp("commitQuorum", bsoncxx::types::b_int32{commit_quorum})));
return *this;
}

index_view& index_view::commit_quorum(std::string commit_quorum) {
_commit_quorum = stdx::make_optional<bsoncxx::document::value>(
make_document(kvp("commitQuorum", commit_quorum)));
return *this;
}
} // namespace options

MONGOCXX_INLINE_NAMESPACE_END
} // namespace mongocxx
52 changes: 52 additions & 0 deletions src/mongocxx/options/index_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,61 @@ class MONGOCXX_API index_view {
///
const bsoncxx::stdx::optional<mongocxx::write_concern>& write_concern() const;

///
/// Sets the commit quorum for this operation.
///
/// This option may only be used with MongoDB version 4.4 or later.
///
/// @param commit_quorum
/// Integer representing the minimum number of data-bearing voting replica set members (i.e.
/// commit quorum), including the primary, that must report a successful index build before
/// the primary marks the indexes as ready. A value of @c 0 disables quorum-voting behavior.
///
/// @return
/// A reference to the object on which this member function is being called. This facilitates
/// method chaining.
///
/// @see
/// https://docs.mongodb.com/master/reference/command/createIndexes
///
index_view& commit_quorum(std::int32_t commit_quorum);

///
/// Sets the commit quorum for this operation.
///
/// This option may only be used with MongoDB version 4.4 or later.
///
/// @param commit_quorum
/// String representing the minimum number of data-bearing voting replica set members (i.e.
/// commit quorum), including the primary, that must report a successful index build before
/// the primary marks the indexes as ready.
///
/// @return
/// A reference to the object on which this member function is being called. This facilitates
/// method chaining.
///
/// @see
/// https://docs.mongodb.com/master/reference/command/createIndexes
///
index_view& commit_quorum(std::string commit_quorum);

///
/// Gets the current commitQuorum setting.
///
/// This option may only be used with MongoDB version 4.4 or later.
///
/// @return
/// The current commitQuorum setting.
///
/// @see
/// https://docs.mongodb.com/master/reference/command/createIndexes
///
const stdx::optional<bsoncxx::document::value> commit_quorum() const;

private:
bsoncxx::stdx::optional<std::chrono::milliseconds> _max_time;
bsoncxx::stdx::optional<mongocxx::write_concern> _write_concern;
bsoncxx::stdx::optional<bsoncxx::document::value> _commit_quorum;
};

} // namespace options
Expand Down
40 changes: 35 additions & 5 deletions src/mongocxx/private/index_view.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,21 @@

#pragma once

#include <vector>

#include <bsoncxx/builder/basic/array.hpp>
#include <bsoncxx/builder/basic/document.hpp>
#include <bsoncxx/document/view_or_value.hpp>
#include <bsoncxx/string/to_string.hpp>
#include <bsoncxx/types/bson_value/view.hpp>
#include <mongocxx/config/private/prelude.hh>
#include <mongocxx/exception/error_code.hpp>
#include <mongocxx/exception/logic_error.hpp>
#include <mongocxx/exception/operation_exception.hpp>
#include <mongocxx/exception/write_exception.hpp>
#include <mongocxx/options/index_view.hpp>
#include <mongocxx/private/client_session.hh>
#include <mongocxx/private/libbson.hh>
#include <mongocxx/private/libmongoc.hh>

#include <mongocxx/config/private/prelude.hh>
#include <vector>

namespace mongocxx {
MONGOCXX_INLINE_NAMESPACE_BEGIN
Expand All @@ -39,7 +38,8 @@ using bsoncxx::builder::basic::kvp;

class index_view::impl {
public:
impl(mongoc_collection_t* collection) : _coll{collection} {}
impl(mongoc_collection_t* collection, mongoc_client_t* client)
: _coll{collection}, _client{client} {}

impl(const impl& i) = default;

Expand Down Expand Up @@ -134,6 +134,26 @@ class index_view::impl {
opts_doc.append(bsoncxx::builder::concatenate_doc{session->_get_impl().to_document()});
}

if (options.commit_quorum()) {
auto server_description = scoped_server_description(libmongoc::client_select_server(
_client, true /* for_writes */, nullptr /* read_prefs */, &error));
if (!server_description.sd)
throw_exception<write_exception>(error);

auto is_master = libmongoc::server_description_ismaster(server_description.sd);

bson_iter_t iter;
if (!bson_iter_init_find(&iter, is_master, "maxWireVersion") ||
bson_iter_int32(&iter) < 9) {
throw write_exception{
error_code::k_invalid_parameter,
"option 'commitQuorum' not available on the current server version"};
}

command =
make_document(concatenate(command), concatenate(options.commit_quorum()->view()));
}

libbson::scoped_bson_t command_bson{command};
libbson::scoped_bson_t opts_bson{opts_doc.view()};

Expand Down Expand Up @@ -219,6 +239,16 @@ class index_view::impl {
}

mongoc_collection_t* _coll;
mongoc_client_t* _client;

class scoped_server_description {
public:
explicit scoped_server_description(mongoc_server_description_t* sd) : sd(sd) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice, I forgot about marking that explicit.

~scoped_server_description() {
mongoc_server_description_destroy(sd);
}
mongoc_server_description_t* sd;
};
};
MONGOCXX_INLINE_NAMESPACE_END
} // namespace mongocxx
1 change: 1 addition & 0 deletions src/mongocxx/private/libmongoc_symbols.hh
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ MONGOCXX_LIBMONGOC_SYMBOL(client_pool_push)
MONGOCXX_LIBMONGOC_SYMBOL(client_pool_set_apm_callbacks)
MONGOCXX_LIBMONGOC_SYMBOL(client_pool_try_pop)
MONGOCXX_LIBMONGOC_SYMBOL(client_reset)
MONGOCXX_LIBMONGOC_SYMBOL(client_select_server)
MONGOCXX_LIBMONGOC_SYMBOL(client_session_abort_transaction)
MONGOCXX_LIBMONGOC_SYMBOL(client_session_advance_cluster_time)
MONGOCXX_LIBMONGOC_SYMBOL(client_session_advance_operation_time)
Expand Down
40 changes: 40 additions & 0 deletions src/mongocxx/test/index_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,46 @@ TEST_CASE("create_one", "[index_view]") {
REQUIRE_NOTHROW(indexes.create_one(keys1.view(), options.view()));
REQUIRE_THROWS_AS(indexes.create_one(keys2.view(), options.view()), operation_exception);
}

SECTION("commitQuorum option") {
if (test_util::get_topology(mongodb_client) == "single") {
WARN("skip: commitQuorum option requires a replica set");
return;
}

collection coll = db["index_view_create_one_commit_quorum"];
coll.drop();
coll.insert_one({}); // Ensure that the collection exists.
index_view indexes = coll.indexes();

auto key = make_document(kvp("a", 1));
index_model model(key.view());
options::index_view options;

auto commit_quorum_regex =
Catch::Matches("(.*)commit( )?quorum(.*)", Catch::CaseSensitive::No);

bool is_supported = test_util::get_max_wire_version(mongodb_client) >= 9;
CAPTURE(is_supported);

SECTION("works with int") {
options.commit_quorum(1);
if (is_supported) {
REQUIRE_NOTHROW(indexes.create_one(model, options));
} else {
REQUIRE_THROWS_WITH(indexes.create_one(model, options), commit_quorum_regex);
}
}

SECTION("works with string") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section, and the section below, appear to be the ones failing.

As the capturing of is_supported shows, is_supported is false, an an exception is expected.

IIUC an exception is expected because the test runs against a single topology, but "majority" should only be supported on a replica set. And the section below expects a server to always reject the value "bad_str"

I still believe this is due to the intermediate 4.3.0 server version this is running against on zSeries. The server may have implemented validation after 4.3.0, so the reported wire version is still 9, but invalid commitQuorum values are still accepted. https://jira.mongodb.org/browse/SERVER-41846 in particular (which has fixVersion 4.3.1) seems related.

We could investigate further and check with the server team. But IMO, it is less important that we test server side validation of these values, and more important that we test that commitQuroum works when supported, and that the driver returns a client side error if the wire version is < 9.

I'd argue for removing the "fails with invalid string" test below, and only testing the "is_supported" case here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our discussion, I skipped single typologies and got rid of the invalid with... tests.

options.commit_quorum("majority");
if (is_supported) {
REQUIRE_NOTHROW(indexes.create_one(model, options));
} else {
REQUIRE_THROWS_WITH(indexes.create_one(model, options), commit_quorum_regex);
}
}
}
}

TEST_CASE("create_many", "[index_view]") {
Expand Down