Skip to content

Commit

Permalink
Support explicit and implicit sessions on standalone servers
Browse files Browse the repository at this point in the history
  • Loading branch information
jyemin committed Jul 15, 2020
1 parent cbacdcc commit cea4b53
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 313 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
import com.mongodb.TransactionOptions;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ClusterType;
import com.mongodb.connection.ServerDescription;
import com.mongodb.connection.ServerType;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.Server;
import com.mongodb.internal.session.ServerSessionPool;
Expand Down Expand Up @@ -58,8 +56,7 @@ void createClientSession(final ClientSessionOptions options, final OperationExec
final SingleResultCallback<AsyncClientSession> callback) {
ClusterDescription clusterDescription = mongoClient.getCluster().getCurrentDescription();
if (!getServerDescriptionListToConsiderForSessionSupport(clusterDescription).isEmpty()
&& clusterDescription.getLogicalSessionTimeoutMinutes() != null
&& clusterDescription.getType() != ClusterType.STANDALONE) {
&& clusterDescription.getLogicalSessionTimeoutMinutes() != null) {
callback.onResult(createClientSession(options, executor), null);
} else {
mongoClient.getCluster().selectServerAsync(new ServerSelector() {
Expand All @@ -72,8 +69,7 @@ public List<ServerDescription> select(final ClusterDescription clusterDescriptio
public void onResult(final Server server, final Throwable t) {
if (t != null) {
callback.onResult(null, null);
} else if (server.getDescription().getLogicalSessionTimeoutMinutes() == null
|| server.getDescription().getType() == ServerType.STANDALONE) {
} else if (server.getDescription().getLogicalSessionTimeoutMinutes() == null) {
callback.onResult(null, null);
} else {
callback.onResult(createClientSession(options, executor), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,27 @@ import java.util.concurrent.TimeUnit
import static Fixture.getMongoClient
import static TestHelper.run
import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet
import static com.mongodb.ClusterFixture.isStandalone
import static com.mongodb.ClusterFixture.serverVersionAtLeast

class MongoClientSessionSpecification extends FunctionalSpecification {

def 'should throw IllegalArgumentException if options are null'() {
when:
Fixture.getMongoClient().startSession(null, Stub(SingleResultCallback))
getMongoClient().startSession(null, Stub(SingleResultCallback))

then:
thrown(IllegalArgumentException)
}

@IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() })
def 'should throw MongoClientException starting a session when sessions are not supported'() {
when:
startSession(ClientSessionOptions.builder().build())

then:
thrown(MongoClientException)
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'should create session with correct defaults'() {
when:
def options = ClientSessionOptions.builder().build()
def clientSession = startSession(options)

then:
clientSession != null
clientSession.getOriginator() == Fixture.getMongoClient()
clientSession.getOriginator() == getMongoClient()
clientSession.isCausallyConsistent()
clientSession.getOptions() == ClientSessionOptions.builder()
.defaultTransactionOptions(TransactionOptions.builder()
Expand All @@ -88,7 +78,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
clientSession.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'cluster time should advance'() {
given:
def firstOperationTime = new BsonTimestamp(42, 1)
Expand Down Expand Up @@ -132,7 +122,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
clientSession.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'operation time should advance'() {
given:
def firstOperationTime = new BsonTimestamp(42, 1)
Expand Down Expand Up @@ -173,7 +163,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
clientSession.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'methods that use the session should throw if the session is closed'() {
given:
def options = ClientSessionOptions.builder().build()
Expand Down Expand Up @@ -202,7 +192,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
clientSession.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'informational methods should not throw if the session is closed'() {
given:
def options = ClientSessionOptions.builder().build()
Expand All @@ -219,7 +209,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
true
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'should apply causally consistent session option to client session'() {
when:
def clientSession = startSession(ClientSessionOptions.builder().causallyConsistent(causallyConsistent).build())
Expand All @@ -235,7 +225,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
causallyConsistent << [true, false]
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'client session should have server session with valid identifier'() {
given:
def clientSession = startSession(ClientSessionOptions.builder().build())
Expand All @@ -254,7 +244,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
clientSession.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'should use a default session'() {
given:
def commandListener = new TestCommandListener()
Expand All @@ -273,25 +263,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
client?.close()
}

@IgnoreIf({ serverVersionAtLeast(3, 6) && !isStandalone() })
def 'should not use a default session when sessions are not supported'() {
given:
def commandListener = new TestCommandListener()
def options = Fixture.getMongoClientBuilderFromConnectionString().addCommandListener(commandListener).build()
def client = AsyncMongoClients.create(options)

when:
run(client.getDatabase('admin').&runCommand, new BsonDocument('ping', new BsonInt32(1)))

then:
def pingCommandStartedEvent = commandListener.events.get(0)
!(pingCommandStartedEvent as CommandStartedEvent).command.containsKey('lsid')

cleanup:
client?.close()
}

@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
def 'should throw exception if unacknowledged write used with explicit session'() {
given:
def session = run(getMongoClient().&startSession)
Expand Down Expand Up @@ -334,7 +306,7 @@ class MongoClientSessionSpecification extends FunctionalSpecification {
// This test is inherently racy as it's possible that the server _does_ replicate fast enough and therefore the test passes anyway
// even if causal consistency was not actually in effect. For that reason the test iterates a number of times in order to increase
// confidence that it's really causal consistency that is causing the test to succeed
@IgnoreIf({ !serverVersionAtLeast(3, 6) || isStandalone() })
@IgnoreIf({ !serverVersionAtLeast(3, 6) })
@Category(Slow)
def 'should find inserted document on a secondary when causal consistency is enabled'() {
given:
Expand Down
84 changes: 84 additions & 0 deletions driver-core/src/test/resources/sessions/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
====================
Driver Session Tests
====================

.. contents::

----

Introduction
============

The YAML and JSON files in this directory are platform-independent tests that
drivers can use to prove their conformance to the Driver Sessions Spec. They are
designed with the intention of sharing most test-runner code with the
Transactions spec tests.

Several prose tests, which are not easily expressed in YAML, are also presented
in the Driver Sessions Spec. Those tests will need to be manually implemented
by each driver.

Test Format
===========

The same as the `Transactions Spec Test format
<../../transactions/tests/README.rst#test-format>`_.

Special Test Operations
```````````````````````

Certain operations that appear in the "operations" array do not correspond to
API methods but instead represent special test operations. Such operations are
defined on the "testRunner" object and are documented in the
`Transactions Spec Test
<../../transactions/tests/README.rst#special-test-operations>`_.
Additional, session test specific operations are documented here:

assertDifferentLsidOnLastTwoCommands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The "assertDifferentLsidOnLastTwoCommands" operation instructs the test runner
to assert that the last two command started events from the test's MongoClient
have different "lsid" fields. This assertion is used to ensure that dirty
server sessions are discarded from the pool::

- name: assertDifferentLsidOnLastTwoCommands
object: testRunner

assertSameLsidOnLastTwoCommands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The "assertSameLsidOnLastTwoCommands" operation instructs the test runner
to assert that the last two command started events from the test's MongoClient
have the same "lsid" field. This assertion is used to ensure that non-dirty
server sessions are not discarded from the pool::

- name: assertSameLsidOnLastTwoCommands
object: testRunner

assertSessionDirty
~~~~~~~~~~~~~~~~~~

The "assertSessionDirty" operation instructs the test runner to assert that
the given session is marked dirty::

- name: assertSessionDirty
object: testRunner
arguments:
session: session0

assertSessionNotDirty
~~~~~~~~~~~~~~~~~~~~~

The "assertSessionNotDirty" operation instructs the test runner to assert that
the given session is *not* marked dirty::

- name: assertSessionNotDirty
object: testRunner
arguments:
session: session0

Changelog
=========

:2019-05-15: Initial version.
Loading

0 comments on commit cea4b53

Please sign in to comment.