Skip to content

Commit 8dcc51f

Browse files
authored
CXX-2785 do not apply readConcern or writeConcern to search index commands (#1055)
* add regression specification tests * apply default read/write concerns to search index operations Apply the default read and write concern. The default read and write concern is not sent in the outgoing command. * add integration test
1 parent 1145f51 commit 8dcc51f

File tree

4 files changed

+317
-5
lines changed

4 files changed

+317
-5
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
{
2+
"description": "search index operations ignore read and write concern",
3+
"schemaVersion": "1.4",
4+
"createEntities": [
5+
{
6+
"client": {
7+
"id": "client0",
8+
"useMultipleMongoses": false,
9+
"uriOptions": {
10+
"readConcernLevel": "local",
11+
"w": 1
12+
},
13+
"observeEvents": [
14+
"commandStartedEvent"
15+
]
16+
}
17+
},
18+
{
19+
"database": {
20+
"id": "database0",
21+
"client": "client0",
22+
"databaseName": "database0"
23+
}
24+
},
25+
{
26+
"collection": {
27+
"id": "collection0",
28+
"database": "database0",
29+
"collectionName": "collection0"
30+
}
31+
}
32+
],
33+
"runOnRequirements": [
34+
{
35+
"minServerVersion": "7.0.0",
36+
"topologies": [
37+
"replicaset",
38+
"load-balanced",
39+
"sharded"
40+
],
41+
"serverless": "forbid"
42+
}
43+
],
44+
"tests": [
45+
{
46+
"description": "createSearchIndex ignores read and write concern",
47+
"operations": [
48+
{
49+
"name": "createSearchIndex",
50+
"object": "collection0",
51+
"arguments": {
52+
"model": {
53+
"definition": {
54+
"mappings": {
55+
"dynamic": true
56+
}
57+
}
58+
}
59+
},
60+
"expectError": {
61+
"isError": true,
62+
"errorContains": "Search index commands are only supported with Atlas"
63+
}
64+
}
65+
],
66+
"expectEvents": [
67+
{
68+
"client": "client0",
69+
"events": [
70+
{
71+
"commandStartedEvent": {
72+
"command": {
73+
"createSearchIndexes": "collection0",
74+
"indexes": [
75+
{
76+
"definition": {
77+
"mappings": {
78+
"dynamic": true
79+
}
80+
}
81+
}
82+
],
83+
"$db": "database0",
84+
"writeConcern": {
85+
"$$exists": false
86+
},
87+
"readConcern": {
88+
"$$exists": false
89+
}
90+
}
91+
}
92+
}
93+
]
94+
}
95+
]
96+
},
97+
{
98+
"description": "createSearchIndex ignores read and write concern",
99+
"operations": [
100+
{
101+
"name": "createSearchIndexes",
102+
"object": "collection0",
103+
"arguments": {
104+
"models": []
105+
},
106+
"expectError": {
107+
"isError": true,
108+
"errorContains": "Search index commands are only supported with Atlas"
109+
}
110+
}
111+
],
112+
"expectEvents": [
113+
{
114+
"client": "client0",
115+
"events": [
116+
{
117+
"commandStartedEvent": {
118+
"command": {
119+
"createSearchIndexes": "collection0",
120+
"indexes": [],
121+
"$db": "database0",
122+
"writeConcern": {
123+
"$$exists": false
124+
},
125+
"readConcern": {
126+
"$$exists": false
127+
}
128+
}
129+
}
130+
}
131+
]
132+
}
133+
]
134+
},
135+
{
136+
"description": "dropSearchIndex ignores read and write concern",
137+
"operations": [
138+
{
139+
"name": "dropSearchIndex",
140+
"object": "collection0",
141+
"arguments": {
142+
"name": "test index"
143+
},
144+
"expectError": {
145+
"isError": true,
146+
"errorContains": "Search index commands are only supported with Atlas"
147+
}
148+
}
149+
],
150+
"expectEvents": [
151+
{
152+
"client": "client0",
153+
"events": [
154+
{
155+
"commandStartedEvent": {
156+
"command": {
157+
"dropSearchIndex": "collection0",
158+
"name": "test index",
159+
"$db": "database0",
160+
"writeConcern": {
161+
"$$exists": false
162+
},
163+
"readConcern": {
164+
"$$exists": false
165+
}
166+
}
167+
}
168+
}
169+
]
170+
}
171+
]
172+
},
173+
{
174+
"description": "listSearchIndexes ignores read and write concern",
175+
"operations": [
176+
{
177+
"name": "listSearchIndexes",
178+
"object": "collection0",
179+
"expectError": {
180+
"isError": true,
181+
"errorContains": "Search index commands are only supported with Atlas"
182+
}
183+
}
184+
],
185+
"expectEvents": [
186+
{
187+
"client": "client0",
188+
"events": [
189+
{
190+
"commandStartedEvent": {
191+
"command": {
192+
"aggregate": "collection0",
193+
"pipeline": [
194+
{
195+
"$listSearchIndexes": {}
196+
}
197+
],
198+
"writeConcern": {
199+
"$$exists": false
200+
},
201+
"readConcern": {
202+
"$$exists": false
203+
}
204+
}
205+
}
206+
}
207+
]
208+
}
209+
]
210+
},
211+
{
212+
"description": "updateSearchIndex ignores the read and write concern",
213+
"operations": [
214+
{
215+
"name": "updateSearchIndex",
216+
"object": "collection0",
217+
"arguments": {
218+
"name": "test index",
219+
"definition": {}
220+
},
221+
"expectError": {
222+
"isError": true,
223+
"errorContains": "Search index commands are only supported with Atlas"
224+
}
225+
}
226+
],
227+
"expectEvents": [
228+
{
229+
"client": "client0",
230+
"events": [
231+
{
232+
"commandStartedEvent": {
233+
"command": {
234+
"updateSearchIndex": "collection0",
235+
"name": "test index",
236+
"definition": {},
237+
"$db": "database0",
238+
"writeConcern": {
239+
"$$exists": false
240+
},
241+
"readConcern": {
242+
"$$exists": false
243+
}
244+
}
245+
}
246+
}
247+
]
248+
}
249+
]
250+
}
251+
]
252+
}

data/index-management/test_files.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ createSearchIndex.json
22
createSearchIndexes.json
33
dropSearchIndex.json
44
listSearchIndexes.json
5-
updateSearchIndex.json
5+
updateSearchIndex.json
6+
searchIndexIgnoresReadWriteConcern.json

src/mongocxx/lib/mongocxx/v_noabi/mongocxx/private/search_index_view.hh

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,30 @@ inline namespace v_noabi {
1818
using bsoncxx::builder::basic::kvp;
1919
using bsoncxx::builder::basic::make_document;
2020

21+
struct collection_deleter {
22+
void operator()(mongoc_collection_t* ptr) noexcept {
23+
libmongoc::collection_destroy(ptr);
24+
}
25+
};
26+
27+
using collection_ptr = std::unique_ptr<mongoc_collection_t, collection_deleter>;
28+
29+
// `copy_and_apply_default_rw_concerns` copies the mongoc_collection_t and applies a default
30+
// readConcern and writeConcern. Used to prevent sending a readConcern or writeConcern.
31+
static collection_ptr copy_and_apply_default_rw_concerns(mongoc_collection_t* coll) {
32+
auto* wc_default = libmongoc::write_concern_new();
33+
auto* rc_default = libmongoc::read_concern_new();
34+
auto coll_copy = libmongoc::collection_copy(coll);
35+
36+
mongoc_collection_set_read_concern(coll_copy, rc_default);
37+
mongoc_collection_set_write_concern(coll_copy, wc_default);
38+
39+
libmongoc::read_concern_destroy(rc_default);
40+
libmongoc::write_concern_destroy(wc_default);
41+
42+
return collection_ptr(coll_copy);
43+
}
44+
2145
class search_index_view::impl {
2246
public:
2347
impl(mongoc_collection_t* collection, mongoc_client_t* client)
@@ -56,8 +80,9 @@ class search_index_view::impl {
5680

5781
libbson::scoped_bson_t opts_bson(opts_doc.view());
5882

83+
auto coll_copy = copy_and_apply_default_rw_concerns(_coll);
5984
return libmongoc::collection_aggregate(
60-
_coll, mongoc_query_flags_t(), stages.bson(), opts_bson.bson(), rp_ptr);
85+
coll_copy.get(), mongoc_query_flags_t(), stages.bson(), opts_bson.bson(), rp_ptr);
6186
}
6287

6388
std::string create_one(const client_session* session, const search_index_model& model) {
@@ -107,8 +132,9 @@ class search_index_view::impl {
107132
libbson::scoped_bson_t command_bson{command};
108133
libbson::scoped_bson_t opts_bson{opts_doc.view()};
109134

135+
auto coll_copy = copy_and_apply_default_rw_concerns(_coll);
110136
auto result = libmongoc::collection_write_command_with_opts(
111-
_coll, command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
137+
coll_copy.get(), command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
112138

113139
if (!result) {
114140
throw_exception<operation_exception>(reply.steal(), error);
@@ -133,8 +159,9 @@ class search_index_view::impl {
133159
libbson::scoped_bson_t opts_bson{opts_doc.view()};
134160
bson_error_t error;
135161

162+
auto coll_copy = copy_and_apply_default_rw_concerns(_coll);
136163
bool result = libmongoc::collection_write_command_with_opts(
137-
_coll, command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
164+
coll_copy.get(), command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
138165

139166
const uint32_t serverErrorNamespaceNotFound = 26;
140167
if (error.domain == MONGOC_ERROR_QUERY && error.code == serverErrorNamespaceNotFound) {
@@ -167,8 +194,9 @@ class search_index_view::impl {
167194
libbson::scoped_bson_t opts_bson{opts_doc.view()};
168195
bson_error_t error;
169196

197+
auto coll_copy = copy_and_apply_default_rw_concerns(_coll);
170198
bool result = libmongoc::collection_write_command_with_opts(
171-
_coll, command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
199+
coll_copy.get(), command_bson.bson(), opts_bson.bson(), reply.bson_for_init(), &error);
172200

173201
if (!result) {
174202
throw_exception<operation_exception>(reply.steal(), error);

src/mongocxx/test/search_index_view.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,36 @@ TEST_CASE("atlas search indexes prose tests", "") {
223223

224224
std::cout << "drop one supress namespace not found SUCCESS" << std::endl;
225225
}
226+
227+
SECTION("create and list search indexes with non-default readConcern and writeConcern") {
228+
bsoncxx::oid id;
229+
auto coll = db.create_collection(id.to_string());
230+
231+
// Apply non-default write concern.
232+
auto nondefault_wc = mongocxx::write_concern();
233+
nondefault_wc.nodes(1);
234+
coll.write_concern(nondefault_wc);
235+
236+
// Apply non-default read concern.
237+
auto nondefault_rc = mongocxx::read_concern();
238+
nondefault_rc.acknowledge_level(mongocxx::read_concern::level::k_local);
239+
coll.read_concern(nondefault_rc);
240+
241+
auto siv = coll.search_indexes();
242+
auto name = "test-search-index-case6";
243+
auto definition = make_document(kvp("mappings", make_document(kvp("dynamic", false))));
244+
auto model = search_index_model(name, definition.view());
245+
246+
REQUIRE(siv.create_one(name, definition.view()) == "test-search-index-case6");
247+
248+
assert_soon([&siv, &model](void) -> bool {
249+
auto cursor = siv.list();
250+
return does_search_index_exist_on_cursor(cursor, model, false);
251+
});
252+
253+
std::cout << "create and list search indexes with non-default readConcern and writeConcern "
254+
"SUCCESS"
255+
<< std::endl;
256+
}
226257
}
227258
} // namespace

0 commit comments

Comments
 (0)