Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/ruby-driver-tutorials.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 75 additions & 0 deletions docs/tutorials/ruby-driver-change-streams.txt
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions docs/tutorials/ruby-driver-create-client.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------------
Expand Down Expand Up @@ -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``)
114 changes: 114 additions & 0 deletions docs/tutorials/ruby-driver-sessions.txt
Original file line number Diff line number Diff line change
@@ -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.