Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOCSP-42104 CSOT #136

Merged
merged 12 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions source/connect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ Connect to MongoDB
Choose a Connection Target </connect/connection-targets>
Connection Option </connect/connection-options>
Configure TLS </connect/tls>
Limit Server Execution Time </connect/csot>
234 changes: 234 additions & 0 deletions source/connect/csot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
.. _ruby-csot:

===========================
Limit Server Execution Time
===========================

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

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: error, blocking, thread, task
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

Overview
--------

When you use the {+driver-short+} to perform a server operation, you can also
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved
limit the duration allowed for the server to finish the operation. To do so,
specify a **client-side operation timeout (CSOT)**. The timeout applies to all
steps needed to complete the operation, including server selection, connection
checkout, and server-side execution. When the timeout expires, the
{+driver-short+} raises a timeout exception.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

timeout_ms Option
-----------------

To specify a timeout when connecting to a MongoDB deployment, set the
``timeout_ms`` connection option to the timeout length in milliseconds. You can
do this in two ways: by passing an argument to the ``Mongo::Client`` constructor
or through a parameter in your connection string.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

The following code examples use the ``timeout_ms`` client option or the ``timeoutMS``
connection string option to specify a timeout of 30 seconds:
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

.. tabs::

.. tab:: MongoClient
:tabid: mongoclient

.. code-block:: ruby
:emphasize-lines: 2

uri = "<connection string>"
options = { timeout_ms: 30000 }

client = Mongo::Client.new(uri, options)

.. tab:: Connection String
:tabid: connection-string

.. code-block:: ruby
:emphasize-lines: 1

uri = "mongodb://<hostname>:<port>?timeoutMS=30000"
client = Mongo::Client.new(uri)

If you specify the ``timeout_ms`` option, the driver automatically applies the
specified timeout per each server operation.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

.. note::

The ``timeout_ms`` connection option unifies most timeout related options.
As of the {+driver-short+} v2.21, the following timeout options are deprecated:
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

- ``socket_timeout``
- ``wait_queue_timeout``
- ``wtimeout``
- ``max_time_ms``
- ``max_commit_time_ms``

The ``timeout_ms`` connection option takes precedence over these
deprecated timeout options.

Timeout Inheritance
~~~~~~~~~~~~~~~~~~~

When you specify a ``timeout_ms`` option, the driver applies the timeout
according to the same inheritance behaviors as the other {+driver-short+} options.
The following table describes how the timeout value is inherited at each level:

.. list-table::
:header-rows: 1
:widths: 30 70

* - Level
- Inheritance Description

* - Operation
- Takes the highest precedence and will override ``timeout_ms``
options set at any other level.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

* - Transaction
- Takes precedence over ``timeout_ms`` set at the session,
collection, database, or client level.

* - Session
- Applies to all transactions and operations within
that session, unless the option is overridden by options set at those levels.

* - Database
- Applies to all sessions and operations within that
database, unless the option is overridden by options set at those levels.

* - Collection
- Applies to all sessions and operations on that
collection, unless the option is overridden by options set at those levels.

* - Client
- Applies to all databases, collections, sessions, transactions, and
operations within that client that do not otherwise specify
``timeout_ms``.

For more information on overrides and specific options, see the :ref:`Overrides
<ruby-csot-overrides>` section.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

.. _ruby-csot-overrides:

Overrides
---------

You can specify a ``timeout_ms`` option at the operation level to override the
client-level configuration for a specific operation. This allows you to
customize timeouts based on the needs of individual queries.

The following example demonstrates how an operation-level ``timeout_ms``
configuration can override a client-level ``timeout_ms`` configuration:

.. literalinclude:: /includes/connect/csot.rb
:language: ruby
:start-after: start-csot-overrides
:end-before: end-csot-overrides

Transactions
~~~~~~~~~~~~

lindseymoore marked this conversation as resolved.
Show resolved Hide resolved
When you create a new ``Mongo::Session`` instance to implement a transaction, use
the ``default_timeout_ms`` option. You can set ``default_timeout_ms`` to specify the
``timeout_ms`` values to use for:
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

- `commit_transaction <{+api-root+}/Mongo/Session.html#commit_transaction-instance_method>`__
- `abort_transaction <{+api-root+}/Mongo/Session.html#abort_transaction-instance_method>`__
- `with_transaction <{+api-root+}/Mongo/Session.html#with_transaction-instance_method>`__
- `end_session <{+api-root+}/Mongo/Session.html#end_session-instance_method>`__

If you do not specify ``default_timeout_ms``, the driver uses the ``timeout_ms``
value set on the parent ``Mongo::Client``.

You cannot override the ``timeout_ms`` value of the ``Mongo::Client`` for a
call to ``start_session``.

You can only set a timeout value for the
`start_transaction <{+api-root+}/Mongo/Session.html#start_transaction-instance_method>`__
method by using the ``timeout_ms`` option.
Comment on lines +164 to +166
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For tech reviewer, can you confirm that this statement is true? Thank you!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you asking to confirm that default_timeout_ms is not applied to start_transaction? If so, yes, that is true. Note that start_transaction doesn't actually perform any operations on the server, but only applies any options given to it, to subsequent transaction-related commands.


You cannot override ``default_timeout_ms`` by setting the ``timeout_ms`` option on an
operation in a transaction session provided by the ``with_transaction`` callback.
Doing so throws an error.
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved

Client Encryption
~~~~~~~~~~~~~~~~~

When you use Client-Side Field Level Encryption (CSFLE), the driver uses the
``timeout_ms`` option to limit the time allowed for encryption and decryption
operations.

If you specify the ``timeout_ms`` option when you construct a
``ClientEncryption`` instance, it controls the lifetime of all operations
performed on that instance. If you do not provide ``timeout_ms``, the instance
inherits the ``timeout_ms`` setting from the ``Mongo::Client`` used in the
``ClientEncryption`` constructor.

If you set ``timeout_ms`` on both the client and directly in
``ClientEncryption``, the value provided to ``ClientEncryption`` takes
precedence.

Cursors
-------

Cursors offer configurable timeout settings when using the CSOT feature. You can
adjust cursor handling by configuring either the cursor lifetime or cursor
iteration mode if needed. To configure the mode, set the ``timeoutMode`` option
to ``cursorLifetime``, which is the default, or ``iteration``.

Cursor Lifetime Mode
~~~~~~~~~~~~~~~~~~~~

The cursor lifetime mode uses ``timeout_ms`` to limit the entire lifetime of a
cursor. In this mode, the initialization of the cursor and all subsequent calls
to the cursor methods must complete within the limit specified by the
``timeout_ms`` option. All documents must be returned within this limit.
Otherwise, the cursor's lifetime expires and a timeout error occurs.

When you close a cursor by calling the ``to_a`` or ``close`` method, the
timeout resets to ensure server-side resources are cleaned up.

The following example shows how to set the ``timeout_ms`` option to ensure that
the cursor is initialized and all documents are retrieved within 10 seconds:

.. code-block:: ruby

docs = collection.find({}, timeout_ms:10000).to_a

Cursor Iteration Mode
~~~~~~~~~~~~~~~~~~~~~

The cursor iteration mode uses the ``timeout_ms`` option to limit each call to
the ``try_next`` method. The timeout refreshes after each call completes.
This is the default mode for all tailable cursors,
such as the tailable cursors returned by the ``find`` method on capped
collections or change streams.

The following code example iterates over documents in the ``mflix`` collection
by using an iterable cursor, and then fetches and logs the ``imdb`` information for
lindseymoore marked this conversation as resolved.
Show resolved Hide resolved
each movie document:

.. literalinclude:: /includes/connect/csot.rb
:language: ruby
:start-after: start-csot-iterable
:end-before: end-csot-iterable

API Documentation
-----------------

To learn more about using timeouts with the {+driver-short+}, see the following
API documentation:

- `Mongo::Client <{+api-root+}/Mongo/Client.html>`__
- `Mongo::Session <{+api-root+}/Mongo/Session.html>`__
31 changes: 31 additions & 0 deletions source/includes/connect/csot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# start-csot-overrides
require 'mongo'

# Replace the placeholder with your connection string
uri = "<connection string>"

# Sets a client-level timeout configuration
options = { timeoutMS: 30000 }
client = Mongo::Client.new(uri, options)

begin
db = client.use('test-db')
collection = db[:test-collection]

# Perform a query with an operation-level timeout configuration,
# overriding the client-level configuration
docs = collection.find({}, timeout_ms: 10000).to_a

docs.each { |doc| puts doc }
ensure
client.close
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly recommend using a block for this pattern; it will automatically ensure the client is closed. (On the other hand, if you're wanting to emphasize--for documentation purposes--the creation and disposal of a client, you can ignore my recommendation.)

Suggested change
client = Mongo::Client.new(uri, options)
begin
db = client.use('test-db')
collection = db[:test-collection]
# Perform a query with an operation-level timeout configuration,
# overriding the client-level configuration
docs = collection.find({}, timeout_ms: 10000).to_a
docs.each { |doc| puts doc }
ensure
client.close
end
Mongo::Client.new(uri, options) do |client|
db = client.use('test-db')
collection = db[:test-collection]
# Perform a query with an operation-level timeout configuration,
# overriding the client-level configuration
docs = collection.find({}, timeout_ms: 10000).to_a
docs.each { |doc| puts doc }
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

# end-csot-overrides

# start-csot-iterable
cursor = collection.find()

cursor.each do |movie|
puts movie['imdb']
end
# end-csot-iterable
Loading