Skip to content

Commit 3db6627

Browse files
authored
CDRIVER-5741 ban w:0 with verbose or ordered bulkWrite (#1752)
* fix referenced schema version. client-bulkWrite tests were merged to the specifications requiring schema version 1.21. An older revision of the tests was likely copied into the C driver. * ban verbose+`w:0` * ban ordered+`w:0` * sync `client-bulkWrite-errors.json` * sync `unacknowledged-client-bulkWrite.json` * update prose test 10. Test passes before due to order of error checking. But use unordered write for consistency with spec. * implement prose test 15 * use unordered in other unacknowledged test
1 parent 692bab2 commit 3db6627

File tree

6 files changed

+222
-7
lines changed

6 files changed

+222
-7
lines changed

src/libmongoc/src/mongoc/mongoc-bulkwrite.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,8 @@ mongoc_bulkwrite_execute (mongoc_bulkwrite_t *self, const mongoc_bulkwriteopts_t
15141514
}
15151515
}
15161516

1517+
bool is_ordered =
1518+
mongoc_optional_is_set (&opts->ordered) ? mongoc_optional_value (&opts->ordered) : true; // default.
15171519
bool verboseresults =
15181520
mongoc_optional_is_set (&opts->verboseresults) ? mongoc_optional_value (&opts->verboseresults) : false;
15191521
ret.res->verboseresults = verboseresults;
@@ -1593,6 +1595,24 @@ mongoc_bulkwrite_execute (mongoc_bulkwrite_t *self, const mongoc_bulkwriteopts_t
15931595
is_acknowledged = mongoc_write_concern_is_acknowledged (wc);
15941596
}
15951597

1598+
if (verboseresults && !is_acknowledged) {
1599+
bson_set_error (&error,
1600+
MONGOC_ERROR_COMMAND,
1601+
MONGOC_ERROR_COMMAND_INVALID_ARG,
1602+
"Cannot request unacknowledged write concern and verbose results.");
1603+
_bulkwriteexception_set_error (ret.exc, &error);
1604+
goto fail;
1605+
}
1606+
1607+
if (is_ordered && !is_acknowledged) {
1608+
bson_set_error (&error,
1609+
MONGOC_ERROR_COMMAND,
1610+
MONGOC_ERROR_COMMAND_INVALID_ARG,
1611+
"Cannot request unacknowledged write concern and ordered writes.");
1612+
_bulkwriteexception_set_error (ret.exc, &error);
1613+
goto fail;
1614+
}
1615+
15961616
if (!mongoc_cmd_parts_assemble (&parts, ss, &error)) {
15971617
_bulkwriteexception_set_error (ret.exc, &error);
15981618
goto fail;

src/libmongoc/tests/json/command-logging-and-monitoring/monitoring/unacknowledged-client-bulkWrite.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"schemaVersion": "1.7",
44
"runOnRequirements": [
55
{
6-
"minServerVersion": "8.0"
6+
"minServerVersion": "8.0",
7+
"serverless": "forbid"
78
}
89
],
910
"createEntities": [
@@ -90,7 +91,8 @@
9091
}
9192
}
9293
}
93-
]
94+
],
95+
"ordered": false
9496
},
9597
"expectResult": {
9698
"insertedCount": {
@@ -157,7 +159,7 @@
157159
"command": {
158160
"bulkWrite": 1,
159161
"errorsOnly": true,
160-
"ordered": true,
162+
"ordered": false,
161163
"ops": [
162164
{
163165
"insert": 0,

src/libmongoc/tests/json/crud/unified/client-bulkWrite-errors.json

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
"description": "client bulkWrite errors",
3-
"schemaVersion": "1.20",
3+
"schemaVersion": "1.21",
44
"runOnRequirements": [
55
{
6-
"minServerVersion": "8.0"
6+
"minServerVersion": "8.0",
7+
"serverless": "forbid"
78
}
89
],
910
"createEntities": [
@@ -449,6 +450,64 @@
449450
}
450451
}
451452
]
453+
},
454+
{
455+
"description": "Requesting unacknowledged write with verboseResults is a client-side error",
456+
"operations": [
457+
{
458+
"name": "clientBulkWrite",
459+
"object": "client0",
460+
"arguments": {
461+
"models": [
462+
{
463+
"insertOne": {
464+
"namespace": "crud-tests.coll0",
465+
"document": {
466+
"_id": 10
467+
}
468+
}
469+
}
470+
],
471+
"verboseResults": true,
472+
"ordered": false,
473+
"writeConcern": {
474+
"w": 0
475+
}
476+
},
477+
"expectError": {
478+
"isClientError": true,
479+
"errorContains": "Cannot request unacknowledged write concern and verbose results"
480+
}
481+
}
482+
]
483+
},
484+
{
485+
"description": "Requesting unacknowledged write with ordered is a client-side error",
486+
"operations": [
487+
{
488+
"name": "clientBulkWrite",
489+
"object": "client0",
490+
"arguments": {
491+
"models": [
492+
{
493+
"insertOne": {
494+
"namespace": "crud-tests.coll0",
495+
"document": {
496+
"_id": 10
497+
}
498+
}
499+
}
500+
],
501+
"writeConcern": {
502+
"w": 0
503+
}
504+
},
505+
"expectError": {
506+
"isClientError": true,
507+
"errorContains": "Cannot request unacknowledged write concern and ordered writes"
508+
}
509+
}
510+
]
452511
}
453512
]
454513
}

src/libmongoc/tests/test-mongoc-bulkwrite.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ test_bulkwrite_session_with_unacknowledged (void *ctx)
180180
mongoc_write_concern_t *wc = mongoc_write_concern_new ();
181181
mongoc_write_concern_set_w (wc, MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
182182
mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
183+
mongoc_bulkwriteopts_set_ordered (opts, false);
183184
mongoc_bulkwriteopts_set_writeconcern (opts, wc);
184185

185186
// Execute bulk write:

src/libmongoc/tests/test-mongoc-crud.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ typedef struct {
155155
// `operation_ids` is a BSON document of this form:
156156
// { "0": <int64>, "1": <int64> ... }
157157
bson_t operation_ids;
158+
// `write_concerns` is a BSON document of this form:
159+
// { "0": <document|null>, "1": <document|null> ... }
160+
bson_t write_concerns;
158161
int numGetMore;
159162
int numKillCursors;
160163
} bulkWrite_ctx;
@@ -180,6 +183,13 @@ bulkWrite_cb (const mongoc_apm_command_started_t *event)
180183
char *key = bson_strdup_printf ("%" PRIu32, bson_count_keys (&ctx->ops_counts));
181184
BSON_APPEND_INT64 (&ctx->ops_counts, key, ops_count);
182185
BSON_APPEND_INT64 (&ctx->operation_ids, key, mongoc_apm_command_started_get_operation_id (event));
186+
// Record write concern (if present).
187+
bson_iter_t wc_iter;
188+
if (bson_iter_init_find (&wc_iter, cmd, "writeConcern")) {
189+
BSON_APPEND_ITER (&ctx->write_concerns, key, &wc_iter);
190+
} else {
191+
BSON_APPEND_NULL (&ctx->write_concerns, key);
192+
}
183193
bson_free (key);
184194
}
185195

@@ -199,6 +209,7 @@ capture_bulkWrite_info (mongoc_client_t *client)
199209
bulkWrite_ctx *cb_ctx = bson_malloc0 (sizeof (*cb_ctx));
200210
bson_init (&cb_ctx->ops_counts);
201211
bson_init (&cb_ctx->operation_ids);
212+
bson_init (&cb_ctx->write_concerns);
202213
mongoc_apm_callbacks_t *cbs = mongoc_apm_callbacks_new ();
203214
mongoc_apm_set_command_started_cb (cbs, bulkWrite_cb);
204215
mongoc_client_set_apm_callbacks (client, cbs, cb_ctx);
@@ -211,6 +222,7 @@ bulkWrite_ctx_reset (bulkWrite_ctx *cb_ctx)
211222
{
212223
bson_reinit (&cb_ctx->ops_counts);
213224
bson_reinit (&cb_ctx->operation_ids);
225+
bson_reinit (&cb_ctx->write_concerns);
214226
cb_ctx->numGetMore = 0;
215227
cb_ctx->numKillCursors = 0;
216228
}
@@ -223,6 +235,7 @@ bulkWrite_ctx_destroy (bulkWrite_ctx *cb_ctx)
223235
}
224236
bson_destroy (&cb_ctx->ops_counts);
225237
bson_destroy (&cb_ctx->operation_ids);
238+
bson_destroy (&cb_ctx->write_concerns);
226239
bson_free (cb_ctx);
227240
}
228241

@@ -888,6 +901,7 @@ prose_test_10 (void *ctx)
888901
wc = mongoc_write_concern_new ();
889902
mongoc_write_concern_set_w (wc, MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
890903
mongoc_bulkwriteopts_t *opts = mongoc_bulkwriteopts_new ();
904+
mongoc_bulkwriteopts_set_ordered (opts, false);
891905
mongoc_bulkwriteopts_set_writeconcern (opts, wc);
892906

893907
// Test a large insert.
@@ -1286,6 +1300,117 @@ prose_test_13 (void *ctx)
12861300
mongoc_client_destroy (client);
12871301
}
12881302

1303+
static void
1304+
prose_test_15 (void *ctx)
1305+
{
1306+
/*
1307+
15. `MongoClient.bulkWrite` with unacknowledged write concern uses `w:0` for all batches
1308+
*/
1309+
mongoc_client_t *client;
1310+
BSON_UNUSED (ctx);
1311+
bool ok;
1312+
bson_error_t error;
1313+
1314+
client = test_framework_new_default_client ();
1315+
1316+
// Drop collection.
1317+
{
1318+
mongoc_collection_t *coll = mongoc_client_get_collection (client, "db", "coll");
1319+
mongoc_collection_drop (coll, NULL); // Ignore error.
1320+
mongoc_collection_destroy (coll);
1321+
}
1322+
1323+
// Create collection to workaround SERVER-95537.
1324+
{
1325+
mongoc_database_t *db = mongoc_client_get_database (client, "db");
1326+
mongoc_collection_t *coll = mongoc_database_create_collection (db, "coll", NULL, &error);
1327+
ASSERT_OR_PRINT (coll, error);
1328+
mongoc_collection_destroy (coll);
1329+
mongoc_database_destroy (db);
1330+
}
1331+
1332+
// Set callbacks to count the number of bulkWrite commands sent.
1333+
bulkWrite_ctx *cb_ctx = capture_bulkWrite_info (client);
1334+
1335+
// Get `maxWriteBatchSize` and `maxBsonObjectSize` from the server.
1336+
server_limits_t sl = get_server_limits (client);
1337+
int32_t maxMessageSizeBytes = sl.maxMessageSizeBytes;
1338+
int32_t maxBsonObjectSize = sl.maxBsonObjectSize;
1339+
1340+
1341+
// Make a large document.
1342+
bson_t doc = BSON_INITIALIZER;
1343+
{
1344+
char *large_str = repeat_char ('b', (size_t) maxBsonObjectSize - 500);
1345+
BSON_APPEND_UTF8 (&doc, "a", large_str);
1346+
bson_free (large_str);
1347+
}
1348+
1349+
// Execute bulkWrite.
1350+
{
1351+
mongoc_bulkwrite_t *bw = mongoc_client_bulkwrite_new (client);
1352+
for (int32_t i = 0; i < maxMessageSizeBytes / maxBsonObjectSize + 1; i++) {
1353+
ok = mongoc_bulkwrite_append_insertone (bw, "db.coll", &doc, NULL, &error);
1354+
ASSERT_OR_PRINT (ok, error);
1355+
}
1356+
1357+
// Configure options with unacknowledge write concern and unordered writes.
1358+
mongoc_bulkwriteopts_t *bwo;
1359+
{
1360+
mongoc_write_concern_t *wc = mongoc_write_concern_new ();
1361+
mongoc_write_concern_set_w (wc, MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
1362+
bwo = mongoc_bulkwriteopts_new ();
1363+
mongoc_bulkwriteopts_set_writeconcern (bwo, wc);
1364+
mongoc_bulkwriteopts_set_ordered (bwo, false);
1365+
mongoc_write_concern_destroy (wc);
1366+
}
1367+
1368+
mongoc_bulkwritereturn_t ret = mongoc_bulkwrite_execute (bw, bwo);
1369+
ASSERT (!ret.res); // No result due to unacknowledged.
1370+
ASSERT_NO_BULKWRITEEXCEPTION (ret);
1371+
mongoc_bulkwriteexception_destroy (ret.exc);
1372+
mongoc_bulkwriteresult_destroy (ret.res);
1373+
mongoc_bulkwriteopts_destroy (bwo);
1374+
mongoc_bulkwrite_destroy (bw);
1375+
}
1376+
1377+
// Check command started events.
1378+
{
1379+
bson_t expect = BSON_INITIALIZER;
1380+
// Assert first `bulkWrite` sends `maxWriteBatchSize` ops.
1381+
BSON_APPEND_INT64 (&expect, "0", maxMessageSizeBytes / maxBsonObjectSize);
1382+
// Assert second `bulkWrite` sends 1 op.
1383+
BSON_APPEND_INT64 (&expect, "1", 1);
1384+
ASSERT_EQUAL_BSON (&expect, &cb_ctx->ops_counts);
1385+
bson_destroy (&expect);
1386+
1387+
// Assert both have the same `operation_id`.
1388+
int64_t operation_id_0 = bson_lookup_int64 (&cb_ctx->operation_ids, "0");
1389+
int64_t operation_id_1 = bson_lookup_int64 (&cb_ctx->operation_ids, "1");
1390+
ASSERT_CMPINT64 (operation_id_0, ==, operation_id_1);
1391+
1392+
// Assert both use unacknowledged write concern.
1393+
bson_init (&expect);
1394+
BCON_APPEND (&expect, "0", "{", "w", BCON_INT32 (0), "}");
1395+
BCON_APPEND (&expect, "1", "{", "w", BCON_INT32 (0), "}");
1396+
ASSERT_EQUAL_BSON (&expect, &cb_ctx->write_concerns);
1397+
bson_destroy (&expect);
1398+
}
1399+
1400+
// Count documents in collection.
1401+
{
1402+
mongoc_collection_t *coll = mongoc_client_get_collection (client, "db", "coll");
1403+
int64_t expected = maxMessageSizeBytes / maxBsonObjectSize + 1;
1404+
int64_t got = mongoc_collection_count_documents (coll, tmp_bson ("{}"), NULL, NULL, NULL, &error);
1405+
ASSERT_CMPINT64 (got, ==, expected);
1406+
mongoc_collection_destroy (coll);
1407+
}
1408+
1409+
bson_destroy (&doc);
1410+
bulkWrite_ctx_destroy (cb_ctx);
1411+
mongoc_client_destroy (client);
1412+
}
1413+
12891414

12901415
void
12911416
test_crud_install (TestSuite *suite)
@@ -1392,4 +1517,12 @@ test_crud_install (TestSuite *suite)
13921517
NULL /* ctx */,
13931518
test_framework_skip_if_max_wire_version_less_than_25, // require server 8.0
13941519
test_framework_skip_if_no_client_side_encryption);
1520+
1521+
TestSuite_AddFull (suite,
1522+
"/crud/prose_test_15",
1523+
prose_test_15,
1524+
NULL /* dtor */,
1525+
NULL /* ctx */,
1526+
test_framework_skip_if_max_wire_version_less_than_25 // require server 8.0
1527+
);
13951528
}

src/libmongoc/tests/unified/runner.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,9 @@ check_schema_version (test_file_t *test_file)
576576
// 1.8 is fully supported. Later minor versions are partially supported.
577577
// 1.12 is partially supported (expectedError.errorResponse assertions)
578578
// 1.18 is partially supported (additional properties in kmsProviders)
579-
// 1.20 is partially supported (expectedError.writeErrors and expectedError.writeConcernErrors)
579+
// 1.21 is partially supported (expectedError.writeErrors and expectedError.writeConcernErrors)
580580
semver_t schema_version;
581-
semver_parse ("1.20", &schema_version);
581+
semver_parse ("1.21", &schema_version);
582582

583583
if (schema_version.major != test_file->schema_version.major) {
584584
goto fail;

0 commit comments

Comments
 (0)