[[mongo.sessions]]
= MongoDB Sessions

As of version 3.6, MongoDB supports the concept of sessions. The use of sessions enables MongoDB's https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#causal-consistency[Causal Consistency] model, which guarantees running operations in an order that respects their causal relationships. Those are split into `ServerSession` instances and `ClientSession` instances. In this section, when we speak of a session, we refer to `ClientSession`.

WARNING: Operations within a client session are not isolated from operations outside the session.

Both `MongoOperations` and `ReactiveMongoOperations` provide gateway methods for tying a `ClientSession` to the operations. `MongoCollection` and `MongoDatabase` use session proxy objects that implement MongoDB's collection and database interfaces, so you need not add a session on each call. This means that a potential call to `MongoCollection#find()` is delegated to `MongoCollection#find(ClientSession)`.

NOTE: Methods such as `(Reactive)MongoOperations#getCollection` return native MongoDB Java Driver gateway objects (such as `MongoCollection`) that themselves offer dedicated methods for `ClientSession`. These methods are *NOT* session-proxied. You should provide the `ClientSession` where needed when interacting directly with a `MongoCollection` or `MongoDatabase` and not through one of the `#execute` callbacks on `MongoOperations`.

[[mongo.sessions.sync]]
== Synchronous `ClientSession` support.

The following example shows the usage of a session:

.`ClientSession` with `MongoOperations`
====
[source,java]
----
ClientSessionOptions sessionOptions = ClientSessionOptions.builder()
    .causallyConsistent(true)
    .build();

ClientSession session = client.startSession(sessionOptions); <1>

template.withSession(() -> session)
    .execute(action -> {

        Query query = query(where("name").is("Durzo Blint"));
        Person durzo = action.findOne(query, Person.class);  <2>

        Person azoth = new Person("Kylar Stern");
        azoth.setMaster(durzo);

        action.insert(azoth);                                <2>

        return azoth;
    });

session.close()                                              <4>
----
<1> Obtain a new session from the server.
<2> Use `MongoOperation` methods as before. The `ClientSession` gets applied automatically.
<3> Make sure to close the `ClientSession`.
<4> Close the session.
====

WARNING: When dealing with `DBRef` instances, especially lazily loaded ones, it is essential to *not* close the `ClientSession` before all data is loaded. Otherwise, lazy fetch fails.

[[mongo.sessions.reactive]]
== Reactive `ClientSession` support

The reactive counterpart uses the same building blocks as the imperative one, as the following example shows:

.ClientSession with `ReactiveMongoOperations`
====
[source,java]
----
ClientSessionOptions sessionOptions = ClientSessionOptions.builder()
    .causallyConsistent(true)
    .build();

Publisher<ClientSession> session = client.startSession(sessionOptions); <1>

template.withSession(session)
    .execute(action -> {

        Query query = query(where("name").is("Durzo Blint"));
        return action.findOne(query, Person.class)
            .flatMap(durzo -> {

                Person azoth = new Person("Kylar Stern");
                azoth.setMaster(durzo);

                return action.insert(azoth);                            <2>
            });
    }, ClientSession::close)                                            <3>
    .subscribe();                                                       <4>
----
<1> Obtain a `Publisher` for new session retrieval.
<2> Use `ReactiveMongoOperation` methods as before. The `ClientSession` is obtained and applied automatically.
<3> Make sure to close the `ClientSession`.
<4> Nothing happens until you subscribe. See http://projectreactor.io/docs/core/release/reference/#reactive.subscribe[the Project Reactor Reference Guide] for details.
====

By using a `Publisher` that provides the actual session, you can defer session acquisition to the point of actual subscription.
Still, you need to close the session when done, so as to not pollute the server with stale sessions. Use the `doFinally` hook on `execute` to call `ClientSession#close()` when you no longer need the session.
If you prefer having more control over the session itself, you can obtain the `ClientSession` through the driver and provide it through a `Supplier`.

NOTE: Reactive use of `ClientSession` is limited to Template API usage. There's currently no session integration with reactive repositories.

[[mongo.transactions]]
= MongoDB Transactions

As of version 4, MongoDB supports https://www.mongodb.com/transactions[Transactions]. Transactions are built on top of <<mongo.sessions,Sessions>> and, consequently, require an active `ClientSession`.

NOTE: Unless you specify a `MongoTransactionManager` within your application context, transaction support is *DISABLED*. You can use `setSessionSynchronization(ALWAYS)` to participate in ongoing non-native MongoDB transactions.

To get full programmatic control over transactions, you may want to use the session callback on `MongoOperations`.

The following example shows programmatic transaction control within a `SessionCallback`:

.Programmatic transactions
====
[source,java]
----
ClientSession session = client.startSession(options);                   <1>

template.withSession(session)
    .execute(action -> {

        session.startTransaction();                                     <2>

        try {

            Step step = // ...;
            action.insert(step);

            process(step);

            action.update(Step.class).apply(Update.set("state", // ...

            session.commitTransaction();                                <3>

        } catch (RuntimeException e) {
            session.abortTransaction();                                 <4>
        }
    }, ClientSession::close)                                            <5>
    .subscribe();
----
<1> Obtain a new `ClientSession`.
<2> Start the transaction.
<3> If everything works out as expected, commit the changes.
<4> Something broke, so roll back everything.
<5> Do not forget to close the session when done.
====

The preceding example lets you have full control over transactional behavior while using the session scoped `MongoOperations` instance within the callback to ensure the session is passed on to every server call.
To avoid some of the overhead that comes with this approach, you can use a `TransactionTemplate` to take away some of the noise of manual transaction flow.

== Transactions with `TransactionTemplate`

Spring Data MongoDB transactions support a `TransactionTemplate`. The following example shows how to create and use a `TransactionTemplate`:

.Transactions with `TransactionTemplate`
====
[source,java]
----
template.setSessionSynchronization(ALWAYS);                                     <1>

// ...

TransactionTemplate txTemplate = new TransactionTemplate(anyTxManager);         <2>

txTemplate.execute(new TransactionCallbackWithoutResult() {

	@Override
	protected void doInTransactionWithoutResult(TransactionStatus status) {     <3>

		Step step = // ...;
		template.insert(step);

		process(step);

		template.update(Step.class).apply(Update.set("state", // ...
	};
});
----
<1> Enable transaction synchronization during Template API configuration.
<2> Create the `TransactionTemplate` using the provided `PlatformTransactionManager`.
<3> Within the callback the `ClientSession` and transaction are already registered.
====

CAUTION: Changing state of `MongoTemplate` during runtime (as you might think would be possible in item 1 of the preceding listing) can cause threading and visibility issues.

== Transactions with `MongoTransactionManager`

`MongoTransactionManager` is the gateway to the well known Spring transaction support. It lets applications use  http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/transaction.html[the managed transaction features of Spring].
The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` detects the session and operates on these resources which are associated with the transaction accordingly. `MongoTemplate` can also participate in other, ongoing transactions. The following example shows how to create and use transactions with a `MongoTransactionManager`:

.Transactions with `MongoTransactionManager`
====
[source,java]
----
@Configuration
static class Config extends AbstractMongoConfiguration {

	@Bean
	MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {  <1>
		return new MongoTransactionManager(dbFactory);
	}

	// ...
}

@Component
public class StateService {

	@Transactional
	void someBusinessFunction(Step step) {                                  <2>

		template.insert(step);

		process(step);

		template.update(Step.class).apply(Update.set("state", // ...
	};
});

----
<1> Register `MongoTransactionManager` in the application context.
<2> Mark methods as transactional.
====

NOTE: `@Transactional(readOnly = true)` advises `MongoTransactionManager` to also start a transaction that adds the
 `ClientSession` to outgoing requests.

== Reactive transactions

Same as with the reactive `ClientSession` support, the `ReactiveMongoTemplate` offers dedicated methods for operating
within a transaction without having to worry about the commit/abort actions depending on the operations outcome.

NOTE: Reactive use of `ClientSession` and transactions is limited to Template API usage. There's currently no session or transaction integration with reactive repositories.

Using the plain MongoDB reactive driver API a `delete` within a transactional flow may look like this.

.Native driver support
====
[source,java]
----
Mono<DeleteResult> result = Mono
      .from(client.startSession())                                                              <1>

      .flatMap(session -> {
         session.startTransaction();                                                            <2>

         return Mono.from(collection.deleteMany(session, ...))                                  <3>

               .onErrorResume(e -> Mono.from(session.abortTransaction()).then(Mono.error(e)))   <4>

               .flatMap(val -> Mono.from(session.commitTransaction()).then(Mono.just(val)))     <5>

               .doFinally(signal -> session.close());                                           <6>
      });
----
<1> First we obviously need to initiate the session.
<2> Once we have the `ClientSession` at hand, start the transaction.
<3> Operate within the transaction by passing on the `ClientSession` to the operation.
<4> If the operations completes exceptionally, we need to abort the transaction and preserve the error.
<5> Or of course, commit the changes in case of success. Still preserving the operations result.
<6> Lastly, we need to make sure to close the session.
====

The culprit of the above operation is in keeping the main flows `DeleteResult` instead of the transaction outcome
published via either `commitTransaction()` or `abortTransaction()`, which leads to a rather complicated setup.

`MongoOperations.inTransaction()` allows you to utilize the callback from for the <<mongo.sessions.reactive,
reactive session support>> to actually preserve the flows outcome but also perform commit and abort actions
accordingly. This allows you to express the above flow simply as the following:

.`ReactiveMongoTemplate` Transactions
====
[source,java]
----
Mono<DeleteResult> result = template.inTransaction()                                        <1>

      .execute(action -> action.remove(query(where("id").is("step-1")), Step.class));       <2>
----
<1> Initiate the transaction.
<2> Operate within the `ClientSession`. Each `execute(…)` unit of work callback initiates a new transaction in the scope of the same `ClientSession`.
====

NOTE: In case you need access to the `ClientSession` within the flow, you can use `ReactiveMongoContext.getSession()`
 to obtain in from the Reactor `Context`.

Everything happening inside the transactional callback is executed within a managed transaction. Errors within the
reactive flow of `execute(…)` that are not propagated to outside of the callback do not affect the operations within the transaction.

====
[source,java]
----
template.inTransaction()                                                                    <1>

	.execute(action -> action.find(query(where("state").is("active")), Step.class)
	                     .flatMap(step -> action.update(Step.class)
			                                    .matching(query(where("id").is(step.id)))
			                                    .apply(update("state", "paused"))
			                                    .all()))                                    <2>

	.flatMap(updated -> {
		// Exception could happen here                                                      <3>
	});
----
<1> Initiate the managed transaction.
<2> Operate within the `ClientSession`. The transaction is committed after this is done or rolled back if an
error occurs here.
<3> An error outside the transaction flow has no affect on the previous transactional execution.
====
