mongodb/mongo-ruby-driver

View on GitHub
docs/reference/sessions.txt

Summary

Maintainability
Test Coverage
.. _sessions:

********
Sessions
********

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 1
   :class: singlecol


Version 3.6 of the MongoDB server introduces the concept of logical sessions for clients.
A session is an abstract concept that represents a set of sequential operations executed
by an application that are related in some way. A session object can be created via a ``Mongo::Client``
and passed to operation methods that should be executed in the context of that session.

Please note that session objects are not thread safe. They must only be used by one thread at a time.

.. _create-session:

Creating a session from a ``Mongo::Client``
===========================================

A session can be created by calling the ``start_session`` method on a client and passing it a block:

.. code-block:: ruby

  client.start_session do |session| 
    # work with the session
  end

When using the block form, the session will be automatically ended by the driver after the block finishes executing.

It is valid to call ``start_session`` with no options set. This will result in a
session that has no effect on the operations performed in the context of that session,
other than to include a session ID in commands sent to the server. Please see the API docs for all supported
session options.

An error will be thrown if the driver is connected to a deployment that does not support sessions and the
``start_session`` method is called.

Note that server sessions are discarded server-side if not used for a certain period of time.
Be aware that if the application calls ``#start_session`` on a client and waits more than 1 minute to use
the session, it risks getting errors due to the session going stale before it is used.



Using a session
===============
A session object can be passed to most driver methods so that the operation can be executed in the
context of that session. Please see the API docs for which methods support a session argument.

Create a session and execute an insert, then a find using that session:

.. code-block:: ruby

  client.start_session do |session|
    client[:artists].insert_one({ :name => 'FKA Twigs' }, session: session)
    client[:artists].find({ :name => 'FKA Twigs' }, limit: 1, session: session).first
  end

If you like to call methods on a ``Mongo::Collection::View`` in the context of a particular session, you can create the
``Mongo::Collection::View`` with the session and then call methods on it:

.. code-block:: ruby

  client.start_session(causal_consistency: true) do |session| 
    view = client[:artists].find({ :name => 'FKA Twigs' }, session: session)
    view.count # will use the session
  end

You can also pass the session option to the methods directly. This session will override any session associated with
the ``Mongo::Collection::View``:

.. code-block:: ruby

  client.start_session do |session|
    client.start_session do |second_session|
      view = client[:artists].find({ :name => 'FKA Twigs' }, session: session)
      view.count(session: second_session) # will use the second_session
    end
  end

Alternative way to create a session
===================================

A session can be created by calling the ``start_session`` method on a client:

.. code-block:: ruby

  session = client.start_session

When ``start_session`` is used without passing a block to it, the driver does not automatically clean up the session which can result in an accumulation of sessions on the server. Use `end_session <#end-a-session>`_ to manually end the session created. The server will automatically clean up old sessions after a timeout but the application should end sessions when the sessions are no longer needed.

Unacknowledged Writes
=====================

Unacknowledged writes are only allowed outside the session mechanism; if an explicit session is supplied for an
unacknowledged write, the driver will not send the session id with the operation. Similarly, the driver will not use
an implicit session for an unacknowledged write.

Causal Consistency
==================
A causally consistent session will let you read your writes and guarantee monotonically increasing
reads from secondaries.
To create a causally consistent session, set the ``causal_consistency`` option to true:

.. code-block:: ruby

  session = client.start_session(causal_consistency: true)

  # The update message goes to the primary.
  collection = client[:artists]
  collection.update_one({ '_id' => 1 }, { '$set' => { 'x' => 0 } }, session: session)

  # Read your write, even when reading from a secondary!
  collection.find({ '_id' => 1 }, session: session).first

  # This query returns data at least as new as the previous query,
  # even if it chooses a different secondary.
  collection.find({ '_id' => 2 }, session: session).first

Since unacknowledged writes don't receive a response from the server (or don't wait for a response), the driver
has no way of keeping track of where the unacknowledged write is in logical time. Therefore, causally
consistent reads are not causally consistent with unacknowledged writes.

Note that if you set the causal_consistency option to nil as in ``(causal_consistency: nil)``, it will be interpreted
as false.

.. _end-session:

End a session
=============
To end a session, call the ``end_session`` method:

.. code-block:: ruby

  session.end_session

The Ruby driver will then add the id for the corresponding server session to a pool for reuse.
When a client is closed, the driver will send a command to the server to end all sessions it has cached
in its server session pool. You may see this command in your logs when a client is closed.

Note that when using the `block syntax <#creating-a-session-from-a-mongo-client>`_ for ``start_session`` the session is automatically ended after
the block finishes executing.