Skip to content

Commit 4cc6191

Browse files
committed
JAVA-2788: Prohibit unacknowledged writes within an explicit session
1 parent 6b5ffac commit 4cc6191

File tree

5 files changed

+118
-3
lines changed

5 files changed

+118
-3
lines changed

driver-async/src/test/functional/com/mongodb/async/client/MongoClientSessionSpecification.groovy

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ import spock.lang.IgnoreIf
3838

3939
import java.util.concurrent.TimeUnit
4040

41+
import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet
4142
import static com.mongodb.ClusterFixture.isStandalone
4243
import static com.mongodb.ClusterFixture.serverVersionAtLeast
44+
import static com.mongodb.async.client.Fixture.getMongoClient
4345
import static com.mongodb.async.client.TestHelper.run
4446

4547
class MongoClientSessionSpecification extends FunctionalSpecification {
@@ -283,10 +285,45 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
283285
then:
284286
def pingCommandStartedEvent = commandListener.events.get(0)
285287
!(pingCommandStartedEvent as CommandStartedEvent).command.containsKey('lsid')
288+
286289
cleanup:
287290
client?.close()
288291
}
289292

293+
@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
294+
def 'should throw exception if unacknowledged write used with explicit session'() {
295+
given:
296+
def session = run(getMongoClient().&startSession)
297+
298+
when:
299+
run(getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
300+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED).&insertOne, session, new Document())
301+
302+
then:
303+
thrown(MongoClientException)
304+
305+
cleanup:
306+
session?.close()
307+
}
308+
309+
310+
@IgnoreIf({ !serverVersionAtLeast(3, 7) || !isDiscoverableReplicaSet() })
311+
def 'should ignore unacknowledged write concern when in a transaction'() {
312+
given:
313+
def session = run(getMongoClient().&startSession)
314+
session.startTransaction()
315+
316+
when:
317+
run(getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
318+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED).&insertOne, session, new Document())
319+
320+
then:
321+
noExceptionThrown()
322+
323+
cleanup:
324+
session.close()
325+
}
326+
290327
// This test attempts attempts to demonstrate that causal consistency works correctly by inserting a document and then immediately
291328
// searching for that document on a secondary by its _id and failing the test if the document is not found. Without causal consistency
292329
// enabled the expectation is that eventually that test would fail since generally the find will execute on the secondary before

driver-async/src/test/unit/com/mongodb/async/client/ClientSessionBindingSpecification.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class ClientSessionBindingSpecification extends Specification {
163163
where:
164164
ownsSession << [true, false]
165165
}
166-
166+
167167
private AsyncReadWriteBinding createStubBinding() {
168168
def cluster = Mock(Cluster) {
169169
selectServerAsync(_, _) >> {

driver-core/src/main/com/mongodb/operation/BulkWriteBatch.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb.operation;
1818

1919
import com.mongodb.MongoBulkWriteException;
20+
import com.mongodb.MongoClientException;
2021
import com.mongodb.MongoInternalException;
2122
import com.mongodb.MongoNamespace;
2223
import com.mongodb.WriteConcern;
@@ -97,6 +98,10 @@ public static BulkWriteBatch createBulkWriteBatch(final MongoNamespace namespace
9798
final Boolean bypassDocumentValidation, final boolean retryWrites,
9899
final List<? extends WriteRequest> writeRequests,
99100
final SessionContext sessionContext) {
101+
if (sessionContext.hasSession() && !sessionContext.isImplicitSession() && !sessionContext.hasActiveTransaction()
102+
&& !writeConcern.isAcknowledged()) {
103+
throw new MongoClientException("Unacknowledged writes are not supported when using an explicit session");
104+
}
100105
boolean canRetryWrites = isRetryableWrite(retryWrites, writeConcern, serverDescription, connectionDescription, sessionContext);
101106
List<WriteRequestWithIndex> writeRequestsWithIndex = new ArrayList<WriteRequestWithIndex>();
102107
boolean writeRequestsAreRetryable = true;

driver-legacy/src/test/functional/com/mongodb/MongoClientSessionSpecification.groovy

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ import java.util.concurrent.TimeUnit
3333
import static Fixture.getDefaultDatabaseName
3434
import static Fixture.getMongoClientURI
3535
import static com.mongodb.ClusterFixture.isAuthenticated
36+
import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet
3637
import static com.mongodb.ClusterFixture.isStandalone
3738
import static com.mongodb.ClusterFixture.serverVersionAtLeast
38-
import static com.mongodb.Fixture.getMongoClient
3939
import static com.mongodb.MongoCredential.createCredential
40+
import static com.mongodb.Fixture.getMongoClient
4041

4142
class MongoClientSessionSpecification extends FunctionalSpecification {
4243

@@ -404,4 +405,39 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
404405
cleanup:
405406
client?.close()
406407
}
408+
409+
@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
410+
def 'should throw exception if unacknowledged write used with explicit session'() {
411+
given:
412+
def session = getMongoClient().startSession()
413+
414+
when:
415+
getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
416+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED)
417+
.insertOne(session, new Document())
418+
419+
then:
420+
thrown(MongoClientException)
421+
422+
cleanup:
423+
session?.close()
424+
}
425+
426+
@IgnoreIf({ !serverVersionAtLeast(3, 7) || !isDiscoverableReplicaSet() })
427+
def 'should ignore unacknowledged write concern when in a transaction'() {
428+
given:
429+
def session = getMongoClient().startSession()
430+
session.startTransaction()
431+
432+
when:
433+
getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
434+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED)
435+
.insertOne(session, new Document())
436+
437+
then:
438+
noExceptionThrown()
439+
440+
cleanup:
441+
session.close()
442+
}
407443
}

driver-sync/src/test/functional/com/mongodb/client/MongoClientSessionSpecification.groovy

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import spock.lang.IgnoreIf
3737

3838
import java.util.concurrent.TimeUnit
3939

40+
import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet
4041
import static com.mongodb.ClusterFixture.isStandalone
4142
import static com.mongodb.ClusterFixture.serverVersionAtLeast
4243
import static com.mongodb.client.Fixture.getDefaultDatabaseName
@@ -321,7 +322,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
321322
}
322323

323324
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
324-
def 'should not use a session for an unacknowledged write'() {
325+
def 'should not use an implicit session for an unacknowledged write'() {
325326
given:
326327
def commandListener = new TestCommandListener()
327328
def settings = MongoClientSettings.builder(getMongoClientSettings()).commandListenerList([commandListener]).build()
@@ -339,4 +340,40 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
339340
cleanup:
340341
client?.close()
341342
}
343+
344+
@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
345+
def 'should throw exception if unacknowledged write used with explicit session'() {
346+
given:
347+
def session = getMongoClient().startSession()
348+
349+
when:
350+
getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
351+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED)
352+
.insertOne(session, new Document())
353+
354+
then:
355+
thrown(MongoClientException)
356+
357+
cleanup:
358+
session?.close()
359+
}
360+
361+
362+
@IgnoreIf({ !serverVersionAtLeast(3, 7) || !isDiscoverableReplicaSet() })
363+
def 'should ignore unacknowledged write concern when in a transaction'() {
364+
given:
365+
def session = getMongoClient().startSession()
366+
session.startTransaction()
367+
368+
when:
369+
getMongoClient().getDatabase(getDatabaseName()).getCollection(getCollectionName())
370+
.withWriteConcern(WriteConcern.UNACKNOWLEDGED)
371+
.insertOne(session, new Document())
372+
373+
then:
374+
noExceptionThrown()
375+
376+
cleanup:
377+
session.close()
378+
}
342379
}

0 commit comments

Comments
 (0)