diff --git a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java index 0fba93cc832..b48031c06c6 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java @@ -16,6 +16,7 @@ package com.mongodb.internal.operation; import com.mongodb.ClientBulkWriteException; +import com.mongodb.MongoClientException; import com.mongodb.MongoClientSettings; import com.mongodb.MongoCommandException; import com.mongodb.MongoException; @@ -119,7 +120,6 @@ import static com.mongodb.internal.operation.CommandOperationHelper.initialRetryState; import static com.mongodb.internal.operation.CommandOperationHelper.shouldAttemptToRetryWriteAndAddRetryableLabel; import static com.mongodb.internal.operation.CommandOperationHelper.transformWriteException; -import static com.mongodb.internal.operation.CommandOperationHelper.validateAndGetEffectiveWriteConcern; import static com.mongodb.internal.operation.OperationHelper.isRetryableWrite; import static com.mongodb.internal.operation.SyncOperationHelper.cursorDocumentToBatchCursor; import static com.mongodb.internal.operation.SyncOperationHelper.decorateWriteWithRetries; @@ -169,8 +169,7 @@ public ClientBulkWriteOperation( @Override public ClientBulkWriteResult execute(final WriteBinding binding) throws ClientBulkWriteException { - WriteConcern effectiveWriteConcern = validateAndGetEffectiveWriteConcern( - writeConcernSetting, binding.getOperationContext().getSessionContext()); + WriteConcern effectiveWriteConcern = validateAndGetEffectiveWriteConcern(binding.getOperationContext().getSessionContext()); ResultAccumulator resultAccumulator = new ResultAccumulator(); MongoException transformedTopLevelError = null; try { @@ -366,6 +365,19 @@ private ClientBulkWriteCommand createBulkWriteCommand( })); } + private WriteConcern validateAndGetEffectiveWriteConcern(final SessionContext sessionContext) { + WriteConcern effectiveWriteConcern = CommandOperationHelper.validateAndGetEffectiveWriteConcern(writeConcernSetting, sessionContext); + if (!effectiveWriteConcern.isAcknowledged()) { + if (options.isVerboseResults()) { + throw new MongoClientException("Cannot request unacknowledged write concern and verbose results"); + } + if (options.isOrdered()) { + throw new MongoClientException("Cannot request unacknowledged write concern and ordered writes"); + } + } + return effectiveWriteConcern; + } + private void encodeUsingRegistry(final BsonWriter writer, final T value) { encodeUsingRegistry(writer, value, DEFAULT_ENCODER_CONTEXT); } diff --git a/driver-core/src/test/resources/unified-test-format/command-monitoring/unacknowledged-client-bulkWrite.json b/driver-core/src/test/resources/unified-test-format/command-monitoring/unacknowledged-client-bulkWrite.json index b30e1540f45..61bb00726c0 100644 --- a/driver-core/src/test/resources/unified-test-format/command-monitoring/unacknowledged-client-bulkWrite.json +++ b/driver-core/src/test/resources/unified-test-format/command-monitoring/unacknowledged-client-bulkWrite.json @@ -91,7 +91,8 @@ } } } - ] + ], + "ordered": false }, "expectResult": { "insertedCount": { @@ -158,7 +159,7 @@ "command": { "bulkWrite": 1, "errorsOnly": true, - "ordered": true, + "ordered": false, "ops": [ { "insert": 0, diff --git a/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-errors.json b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-errors.json index 8cc45bb5f2d..015bd95c990 100644 --- a/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-errors.json +++ b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-errors.json @@ -450,6 +450,64 @@ } } ] + }, + { + "description": "Requesting unacknowledged write with verboseResults is a client-side error", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 10 + } + } + } + ], + "verboseResults": true, + "ordered": false, + "writeConcern": { + "w": 0 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "Cannot request unacknowledged write concern and verbose results" + } + } + ] + }, + { + "description": "Requesting unacknowledged write with ordered is a client-side error", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 10 + } + } + } + ], + "writeConcern": { + "w": 0 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "Cannot request unacknowledged write concern and ordered writes" + } + } + ] } ] } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/CrudProseTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/CrudProseTest.java index eced992df90..22bb1a23d77 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/CrudProseTest.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/CrudProseTest.java @@ -82,6 +82,12 @@ protected void testBulkWriteErrorsForAutoEncryption() { assumeTrue(java.lang.Boolean.parseBoolean(toString()), "BULK-TODO implement"); } + @DisplayName("15. MongoClient.bulkWrite with unacknowledged write concern uses w:0 for all batches") + @Test + protected void testWriteConcernOfAllBatchesWhenUnacknowledgedRequested() { + assumeTrue(java.lang.Boolean.parseBoolean(toString()), "BULK-TODO implement"); + } + @ParameterizedTest @MethodSource("insertMustGenerateIdAtMostOnceArgs") @Override diff --git a/driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java index ddbe64f2183..72e2fdea0e0 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/CrudProseTest.java @@ -24,6 +24,7 @@ import com.mongodb.MongoNamespace; import com.mongodb.MongoWriteConcernException; import com.mongodb.MongoWriteException; +import com.mongodb.WriteConcern; import com.mongodb.assertions.Assertions; import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.Filters; @@ -312,15 +313,6 @@ private void assertBulkWriteHandlesCursorRequiringGetMore(final boolean transact } } - private static Stream testBulkWriteErrorsForUnacknowledgedTooLargeInsertArgs() { - return Stream.of( - arguments("insert", false), - arguments("insert", true), - arguments("replace", false), - arguments("replace", true) - ); - } - @DisplayName("11. MongoClient.bulkWrite batch splits when the addition of a new namespace exceeds the maximum message size") @Test protected void testBulkWriteSplitsWhenExceedingMaxMessageSizeBytesDueToNsInfo() { @@ -442,6 +434,40 @@ protected void testBulkWriteErrorsForAutoEncryption() { } } + @DisplayName("15. MongoClient.bulkWrite with unacknowledged write concern uses w:0 for all batches") + @Test + protected void testWriteConcernOfAllBatchesWhenUnacknowledgedRequested() { + assumeTrue(serverVersionAtLeast(8, 0)); + assumeFalse(isServerlessTest()); + TestCommandListener commandListener = new TestCommandListener(); + try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder().addCommandListener(commandListener) + .writeConcern(WriteConcern.UNACKNOWLEDGED))) { + MongoDatabase database = droppedDatabase(client); + database.createCollection(NAMESPACE.getCollectionName()); + Document helloResponse = database.runCommand(new Document("hello", 1)); + int maxBsonObjectSize = helloResponse.getInteger("maxBsonObjectSize"); + int maxMessageSizeBytes = helloResponse.getInteger("maxMessageSizeBytes"); + ClientNamespacedWriteModel model = ClientNamespacedWriteModel.insertOne( + NAMESPACE, + new Document("a", join("", nCopies(maxBsonObjectSize - 500, "b")))); + int numModels = maxMessageSizeBytes / maxBsonObjectSize + 1; + ClientBulkWriteResult result = client.bulkWrite(nCopies(numModels, model), clientBulkWriteOptions().ordered(false)); + assertFalse(result.isAcknowledged()); + List startedBulkWriteCommandEvents = commandListener.getCommandStartedEvents("bulkWrite"); + assertEquals(2, startedBulkWriteCommandEvents.size()); + CommandStartedEvent firstEvent = startedBulkWriteCommandEvents.get(0); + BsonDocument firstCommand = firstEvent.getCommand(); + CommandStartedEvent secondEvent = startedBulkWriteCommandEvents.get(1); + BsonDocument secondCommand = secondEvent.getCommand(); + assertEquals(numModels - 1, firstCommand.getArray("ops").size()); + assertEquals(1, secondCommand.getArray("ops").size()); + assertEquals(firstEvent.getOperationId(), secondEvent.getOperationId()); + assertEquals(0, firstCommand.getDocument("writeConcern").getInt32("w").intValue()); + assertEquals(0, secondCommand.getDocument("writeConcern").getInt32("w").intValue()); + assertEquals(numModels, database.getCollection(NAMESPACE.getCollectionName()).countDocuments()); + } + } + /** * This test is not from the specification. */