|
| 1 | +============================ |
| 2 | +Creating and using Sessions |
| 3 | +============================ |
| 4 | + |
| 5 | +.. default-domain:: mongodb |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 1 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | + |
| 14 | +Version 3.6 of the server introduces the concept of logical sessions for clients. |
| 15 | +A session is an abstract concept that represents a set of sequential operations executed |
| 16 | +by an application that are related in some way. A session object can be create via a ``Mongo::Client`` |
| 17 | +and passed to operation methods that should be executed in the context of that session. |
| 18 | + |
| 19 | +Please note that sessions are not thread safe. They can only be used by one thread at a time. |
| 20 | + |
| 21 | +Creating a session from a ``Mongo::Client`` |
| 22 | +------------------------------------------- |
| 23 | + |
| 24 | +A session can be created by calling the ``start_session`` method on a client: |
| 25 | + |
| 26 | +.. code-block:: ruby |
| 27 | + |
| 28 | + session = client.start_session |
| 29 | + |
| 30 | + |
| 31 | +It is valid to call ``start_session`` with no options set. This will result in a |
| 32 | +session that has no effect on the operations performed in the context of that session, |
| 33 | +other than to include a session ID in commands sent to the server. Please see the API docs for all supported |
| 34 | +session options. |
| 35 | + |
| 36 | +An error will be thrown if the driver is connected to a deployment that does not support sessions and the |
| 37 | +``start_session`` method is called. |
| 38 | + |
| 39 | +Note that server sessions are discarded server-side if not used for a certain period of time. That said, |
| 40 | +be aware that if the application calls ``#start_session`` on a client and waits more than 1 minute to use |
| 41 | +the session, it risks getting errors due to the session going stale before it is used. |
| 42 | + |
| 43 | + |
| 44 | +Using a session |
| 45 | +--------------- |
| 46 | +A session object can be passed to most driver methods so that the operation can be executed in the |
| 47 | +context of that session. Please see the API docs for which methods support a session argument. |
| 48 | + |
| 49 | +Create a session and execute an insert, then a find using that session: |
| 50 | + |
| 51 | +.. code-block:: ruby |
| 52 | + |
| 53 | + session = client.start_session |
| 54 | + client[:artists].insert_one({ :name => 'FKA Twigs' }, session: session) |
| 55 | + client[:artists].find({ :name => 'FKA Twigs' }, limit: 1, session: session).first |
| 56 | + |
| 57 | +If you like to call methods on a ``Mongo::Collection::View`` in the context of a particular session, you can create the |
| 58 | +``Mongo::Collection::View`` with the session and then call methods on it: |
| 59 | + |
| 60 | +.. code-block:: ruby |
| 61 | + |
| 62 | + session = client.start_session(causal_consistency: true) |
| 63 | + view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) |
| 64 | + view.count # will use the session |
| 65 | + |
| 66 | +You can also pass the session option to the methods directly. This session will override any session associated with |
| 67 | +the ``Mongo::Collection::View``: |
| 68 | + |
| 69 | +.. code-block:: ruby |
| 70 | + |
| 71 | + session = client.start_session |
| 72 | + second_session = client.start_session |
| 73 | + view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) |
| 74 | + view.count(session: second_session) # will use the second_session |
| 75 | + |
| 76 | +Causal Consistency |
| 77 | +------------------ |
| 78 | +A causally consistent session will let you read your writes and guarantee monotonically increasing |
| 79 | +reads from secondaries. |
| 80 | +To create a causally consistent session, set the ``causal_consistency`` option to true: |
| 81 | + |
| 82 | +.. code-block:: ruby |
| 83 | + |
| 84 | + session = client.start_session(causal_consistency: true) |
| 85 | + |
| 86 | + # The update message goes to the primary. |
| 87 | + collection = client[:artists] |
| 88 | + collection.update_one({ '_id' => 1 }, { '$set' => { 'x' => 0 } }, session: session) |
| 89 | + |
| 90 | + # Read your write, even when reading from a secondary! |
| 91 | + collection.find({ '_id' => 1 }, session: session).first |
| 92 | + |
| 93 | + # This query returns data at least as new as the previous query, |
| 94 | + # even if it chooses a different secondary. |
| 95 | + collection.find({ '_id' => 2 }, session: session).first |
| 96 | + |
| 97 | +Since unacknowledged writes don't receive a response from the server (or don't wait for a response), the driver |
| 98 | +has no way of keeping track of where the unacknowledged write is in logical time. Therefore, causally |
| 99 | +consistent reads are not causally consistent with unacknowledged writes. |
| 100 | + |
| 101 | +Note that if you set the causal_consistency option to nil as in ``(causal_consistency: nil)``, it will be interpreted |
| 102 | +as false. |
| 103 | + |
| 104 | +End a session |
| 105 | +------------- |
| 106 | +To end a session, call the ``end_session`` method: |
| 107 | + |
| 108 | +.. code-block:: ruby |
| 109 | + |
| 110 | + session.end_session |
| 111 | + |
| 112 | +The Ruby driver will then add the id for the corresponding server session to a pool for reuse. |
| 113 | +When a client is closed, the driver will send a command to the server to end all sessions it has cached |
| 114 | +in its server session pool. You may see this command in your logs when a client is closed. |
0 commit comments