diff --git a/docs/ruby-driver-tutorials.txt b/docs/ruby-driver-tutorials.txt index 6ce883c3f6..19d16b1572 100644 --- a/docs/ruby-driver-tutorials.txt +++ b/docs/ruby-driver-tutorials.txt @@ -19,6 +19,8 @@ operations available in the Ruby driver. /tutorials/ruby-driver-crud-operations /tutorials/ruby-driver-collection-tasks /tutorials/ruby-driver-projections + /tutorials/ruby-driver-sessions + /tutorials/ruby-driver-change-streams /tutorials/ruby-driver-admin-tasks /tutorials/ruby-driver-indexing /tutorials/ruby-driver-collations diff --git a/docs/tutorials/ruby-driver-change-streams.txt b/docs/tutorials/ruby-driver-change-streams.txt new file mode 100644 index 0000000000..56e475154b --- /dev/null +++ b/docs/tutorials/ruby-driver-change-streams.txt @@ -0,0 +1,75 @@ +============== +Change Streams +============== + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +As of version 3.6 of the MongoDB server, a new ``$changeStream`` pipeline stage is supported in the aggregation +framework. The Ruby driver provides an API for receiving notifications for changes to a particular collection using this +new pipeline stage. Although you can create a change stream using the pipeline operator and aggregation framework +directly, it is recommended to use the driver API described below as the driver resumes the change stream if there is +timeout or network error. + +Change streams on the server requires a ``"majority"`` read concern or no read concern. + +Change streams do not work properly with JRuby because of the issue documented here_. + +.. _here: https://github.com/jruby/jruby/issues/4212 + +Namely, JRuby eagerly evaluates ```#next`` on an Enumerator in a background green thread. +So calling ```#next`` on the change stream will cause getmores to be called in a loop in the background. + +Watching for changes on a particular collection +----------------------------------------------- + +A change stream is created by calling the ``#watch`` method on a collection: + +.. code-block:: ruby + + stream = collection.watch + collection.insert_one(a: 1) + doc = stream.to_enum.next + process(doc) + + +You can also receive the notifications as they are available: + +.. code-block:: ruby + + stream = client[:test].watch + enum = stream.to_enum + while doc = enum.next + process(doc) + end + + +The change stream can take filters in the aggregation framework pipeline operator format: + +.. code-block:: ruby + + stream = collection.watch([{'$match' => { 'operationType' => {'$in' => ['insert', 'replace'] } } }, + {'$match' => { 'fullDocument.n' => { '$gte' => 1 } } } + ]) + enum = stream.to_enum + while doc = enum.next + process(doc) + end + +Close a Change Stream +--------------------- + +You can close a Change Stream by calling the ``#close`` method: + +.. code-block:: ruby + + stream = collection.watch + collection.insert_one(a: 1) + doc = stream.to_enum.next + process(doc) + stream.close diff --git a/docs/tutorials/ruby-driver-create-client.txt b/docs/tutorials/ruby-driver-create-client.txt index f57c2afe2d..73a72b72c7 100644 --- a/docs/tutorials/ruby-driver-create-client.txt +++ b/docs/tutorials/ruby-driver-create-client.txt @@ -407,6 +407,12 @@ Ruby Options - ``Integer`` - none + * - ``:retry_writes`` + - If a single-statement write operation fails from a network error, the driver automatically retries it once + when connected to server versions 3.6+. + - ``Boolean`` + - false + Details on Timeout Options -------------------------- @@ -530,3 +536,20 @@ When ``#close`` is called on a client by any thread, all connections are closed: .. code-block:: ruby client.close + +Details on Retryable Writes +--------------------------- + +If the ``retry_writes`` option is set to true, the driver will retry single-statement write operations that fail from +a network error. The driver automatically retries the operation once. + +Most of the write methods you use day-to-day on a collection will be retryable in 3.6. They are: + +- ``collection#insert_one`` +- ``collection#update_one`` +- ``collection#delete_one`` +- ``collection#replace_one`` +- ``collection#find_one_and_update`` +- ``collection#find_one_and_replace`` +- ``collection#find_one_and_delete`` +- ``collection#bulk_write`` # for all single statement ops (i.e. not for ``update_many`` or ``delete_many``) diff --git a/docs/tutorials/ruby-driver-sessions.txt b/docs/tutorials/ruby-driver-sessions.txt new file mode 100644 index 0000000000..e40bb58967 --- /dev/null +++ b/docs/tutorials/ruby-driver-sessions.txt @@ -0,0 +1,114 @@ +============================ +Creating and using Sessions +============================ + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + + +Version 3.6 of the 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 create via a ``Mongo::Client`` +and passed to operation methods that should be executed in the context of that session. + +Please note that sessions are not thread safe. They can only be used by one thread at a time. + +Creating a session from a ``Mongo::Client`` +------------------------------------------- + +A session can be created by calling the ``start_session`` method on a client: + +.. code-block:: ruby + + session = client.start_session + + +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. That said, +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 + + session = client.start_session + client[:artists].insert_one({ :name => 'FKA Twigs' }, session: session) + client[:artists].find({ :name => 'FKA Twigs' }, limit: 1, session: session).first + +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 + + session = client.start_session(causal_consistency: true) + view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) + view.count # will use the session + +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 + + session = client.start_session + second_session = client.start_session + view = client[:artists].find({ :name => 'FKA Twigs' }, session: session) + view.count(session: second_session) # will use the second_session + +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 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.