Skip to content

Commit 65e72d0

Browse files
authored
Forbid ordered/verbose unacknowledged bulk writes (#1570)
JAVA-5635
1 parent 3672152 commit 65e72d0

File tree

5 files changed

+117
-14
lines changed

5 files changed

+117
-14
lines changed

driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.mongodb.internal.operation;
1717

1818
import com.mongodb.ClientBulkWriteException;
19+
import com.mongodb.MongoClientException;
1920
import com.mongodb.MongoClientSettings;
2021
import com.mongodb.MongoCommandException;
2122
import com.mongodb.MongoException;
@@ -119,7 +120,6 @@
119120
import static com.mongodb.internal.operation.CommandOperationHelper.initialRetryState;
120121
import static com.mongodb.internal.operation.CommandOperationHelper.shouldAttemptToRetryWriteAndAddRetryableLabel;
121122
import static com.mongodb.internal.operation.CommandOperationHelper.transformWriteException;
122-
import static com.mongodb.internal.operation.CommandOperationHelper.validateAndGetEffectiveWriteConcern;
123123
import static com.mongodb.internal.operation.OperationHelper.isRetryableWrite;
124124
import static com.mongodb.internal.operation.SyncOperationHelper.cursorDocumentToBatchCursor;
125125
import static com.mongodb.internal.operation.SyncOperationHelper.decorateWriteWithRetries;
@@ -169,8 +169,7 @@ public ClientBulkWriteOperation(
169169

170170
@Override
171171
public ClientBulkWriteResult execute(final WriteBinding binding) throws ClientBulkWriteException {
172-
WriteConcern effectiveWriteConcern = validateAndGetEffectiveWriteConcern(
173-
writeConcernSetting, binding.getOperationContext().getSessionContext());
172+
WriteConcern effectiveWriteConcern = validateAndGetEffectiveWriteConcern(binding.getOperationContext().getSessionContext());
174173
ResultAccumulator resultAccumulator = new ResultAccumulator();
175174
MongoException transformedTopLevelError = null;
176175
try {
@@ -366,6 +365,19 @@ private ClientBulkWriteCommand createBulkWriteCommand(
366365
}));
367366
}
368367

368+
private WriteConcern validateAndGetEffectiveWriteConcern(final SessionContext sessionContext) {
369+
WriteConcern effectiveWriteConcern = CommandOperationHelper.validateAndGetEffectiveWriteConcern(writeConcernSetting, sessionContext);
370+
if (!effectiveWriteConcern.isAcknowledged()) {
371+
if (options.isVerboseResults()) {
372+
throw new MongoClientException("Cannot request unacknowledged write concern and verbose results");
373+
}
374+
if (options.isOrdered()) {
375+
throw new MongoClientException("Cannot request unacknowledged write concern and ordered writes");
376+
}
377+
}
378+
return effectiveWriteConcern;
379+
}
380+
369381
private <T> void encodeUsingRegistry(final BsonWriter writer, final T value) {
370382
encodeUsingRegistry(writer, value, DEFAULT_ENCODER_CONTEXT);
371383
}

driver-core/src/test/resources/unified-test-format/command-monitoring/unacknowledged-client-bulkWrite.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@
9191
}
9292
}
9393
}
94-
]
94+
],
95+
"ordered": false
9596
},
9697
"expectResult": {
9798
"insertedCount": {
@@ -158,7 +159,7 @@
158159
"command": {
159160
"bulkWrite": 1,
160161
"errorsOnly": true,
161-
"ordered": true,
162+
"ordered": false,
162163
"ops": [
163164
{
164165
"insert": 0,

driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-errors.json

+58
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,64 @@
450450
}
451451
}
452452
]
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+
]
453511
}
454512
]
455513
}

driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/CrudProseTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ protected void testBulkWriteErrorsForAutoEncryption() {
8282
assumeTrue(java.lang.Boolean.parseBoolean(toString()), "BULK-TODO implement");
8383
}
8484

85+
@DisplayName("15. MongoClient.bulkWrite with unacknowledged write concern uses w:0 for all batches")
86+
@Test
87+
protected void testWriteConcernOfAllBatchesWhenUnacknowledgedRequested() {
88+
assumeTrue(java.lang.Boolean.parseBoolean(toString()), "BULK-TODO implement");
89+
}
90+
8591
@ParameterizedTest
8692
@MethodSource("insertMustGenerateIdAtMostOnceArgs")
8793
@Override

driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java

+35-9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.mongodb.MongoNamespace;
2525
import com.mongodb.MongoWriteConcernException;
2626
import com.mongodb.MongoWriteException;
27+
import com.mongodb.WriteConcern;
2728
import com.mongodb.assertions.Assertions;
2829
import com.mongodb.client.model.CreateCollectionOptions;
2930
import com.mongodb.client.model.Filters;
@@ -312,15 +313,6 @@ private void assertBulkWriteHandlesCursorRequiringGetMore(final boolean transact
312313
}
313314
}
314315

315-
private static Stream<Arguments> testBulkWriteErrorsForUnacknowledgedTooLargeInsertArgs() {
316-
return Stream.of(
317-
arguments("insert", false),
318-
arguments("insert", true),
319-
arguments("replace", false),
320-
arguments("replace", true)
321-
);
322-
}
323-
324316
@DisplayName("11. MongoClient.bulkWrite batch splits when the addition of a new namespace exceeds the maximum message size")
325317
@Test
326318
protected void testBulkWriteSplitsWhenExceedingMaxMessageSizeBytesDueToNsInfo() {
@@ -442,6 +434,40 @@ protected void testBulkWriteErrorsForAutoEncryption() {
442434
}
443435
}
444436

437+
@DisplayName("15. MongoClient.bulkWrite with unacknowledged write concern uses w:0 for all batches")
438+
@Test
439+
protected void testWriteConcernOfAllBatchesWhenUnacknowledgedRequested() {
440+
assumeTrue(serverVersionAtLeast(8, 0));
441+
assumeFalse(isServerlessTest());
442+
TestCommandListener commandListener = new TestCommandListener();
443+
try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder().addCommandListener(commandListener)
444+
.writeConcern(WriteConcern.UNACKNOWLEDGED))) {
445+
MongoDatabase database = droppedDatabase(client);
446+
database.createCollection(NAMESPACE.getCollectionName());
447+
Document helloResponse = database.runCommand(new Document("hello", 1));
448+
int maxBsonObjectSize = helloResponse.getInteger("maxBsonObjectSize");
449+
int maxMessageSizeBytes = helloResponse.getInteger("maxMessageSizeBytes");
450+
ClientNamespacedWriteModel model = ClientNamespacedWriteModel.insertOne(
451+
NAMESPACE,
452+
new Document("a", join("", nCopies(maxBsonObjectSize - 500, "b"))));
453+
int numModels = maxMessageSizeBytes / maxBsonObjectSize + 1;
454+
ClientBulkWriteResult result = client.bulkWrite(nCopies(numModels, model), clientBulkWriteOptions().ordered(false));
455+
assertFalse(result.isAcknowledged());
456+
List<CommandStartedEvent> startedBulkWriteCommandEvents = commandListener.getCommandStartedEvents("bulkWrite");
457+
assertEquals(2, startedBulkWriteCommandEvents.size());
458+
CommandStartedEvent firstEvent = startedBulkWriteCommandEvents.get(0);
459+
BsonDocument firstCommand = firstEvent.getCommand();
460+
CommandStartedEvent secondEvent = startedBulkWriteCommandEvents.get(1);
461+
BsonDocument secondCommand = secondEvent.getCommand();
462+
assertEquals(numModels - 1, firstCommand.getArray("ops").size());
463+
assertEquals(1, secondCommand.getArray("ops").size());
464+
assertEquals(firstEvent.getOperationId(), secondEvent.getOperationId());
465+
assertEquals(0, firstCommand.getDocument("writeConcern").getInt32("w").intValue());
466+
assertEquals(0, secondCommand.getDocument("writeConcern").getInt32("w").intValue());
467+
assertEquals(numModels, database.getCollection(NAMESPACE.getCollectionName()).countDocuments());
468+
}
469+
}
470+
445471
/**
446472
* This test is not from the specification.
447473
*/

0 commit comments

Comments
 (0)