-
Notifications
You must be signed in to change notification settings - Fork 245
DRIVERS-1607: Snapshot reads on Secondaries #1022
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
Changes from all commits
d847e2c
7e827b1
bf0e811
617a6af
24b994c
d1c4066
d6fc58c
e02e2a7
c27b561
7d753e0
96f4471
16f8685
dfb2472
694f146
d306a01
2d420d2
9551b41
5fa46e7
ed6639e
c1739c4
e1e0672
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,271 @@ | ||||||
============================ | ||||||
Snapshot Reads Specification | ||||||
============================ | ||||||
|
||||||
:Spec Title: Snapshot Reads Specification (See the registry of specs) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to call out a registry of specs here
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what's the convention here as this referral appears in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe those are leftovers from the templates. I'll give the specs a quick look to clean these up. Thanks for pointing out other instances. |
||||||
:Spec Version: 1.0 | ||||||
:Author: Boris Dogadov | ||||||
:Advisors: Jeff Yemin, A. Jesse Jiryu Davis, Judah Schvimer | ||||||
:Status: Draft (Could be Draft, Accepted, Rejected, Final, or Replaced) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reminder to change this to "Accepted" before merging.
Suggested change
|
||||||
:Type: Standards | ||||||
:Minimum Server Version: 5.0 | ||||||
:Last Modified: 15-Jun-2021 | ||||||
|
||||||
.. contents:: | ||||||
|
||||||
-------- | ||||||
|
||||||
Abstract | ||||||
======== | ||||||
|
||||||
Version 5.0 of the server introduces support for read concern level "snapshot" (non-speculative) | ||||||
for read commands outside of transactions, including on secondaries. | ||||||
This spec builds upon the `Sessions Specification <../driver-sessions.rst>`_ to define how an application | ||||||
requests "snapshot" level read concern and how a driver interacts with the server | ||||||
to implement snapshot reads. | ||||||
|
||||||
Definitions | ||||||
=========== | ||||||
|
||||||
META | ||||||
---- | ||||||
|
||||||
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, | ||||||
“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be | ||||||
interpreted as described in `RFC 2119 <https://www.ietf.org/rfc/rfc2119.txt>`_. | ||||||
|
||||||
Terms | ||||||
----- | ||||||
|
||||||
ClientSession | ||||||
The driver object representing a client session and the operations that can be | ||||||
performed on it. | ||||||
|
||||||
MongoClient | ||||||
The root object of a driver's API. MAY be named differently in some drivers. | ||||||
|
||||||
MongoCollection | ||||||
The driver object representing a collection and the operations that can be | ||||||
performed on it. MAY be named differently in some drivers. | ||||||
|
||||||
MongoDatabase | ||||||
The driver object representing a database and the operations that can be | ||||||
performed on it. MAY be named differently in some drivers. | ||||||
|
||||||
ServerSession | ||||||
The driver object representing a server session. | ||||||
|
||||||
Session | ||||||
A session is an abstract concept that represents a set of sequential | ||||||
operations executed by an application that are related in some way. This | ||||||
specification defines how sessions are used to implement snapshot reads. | ||||||
|
||||||
Snapshot reads | ||||||
Reads with read concern level ``snapshot`` that occur outside of transactions on | ||||||
benjirewis marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
both the primary and secondary nodes, including in sharded clusters. | ||||||
Snapshots reads are majority committed reads. | ||||||
|
||||||
Snapshot timestamp | ||||||
Snapshot timestamp, representing timestamp of the first read (find/aggregate/distinct operation) in the session. | ||||||
The server creates a cursor in response to a snapshot find/aggregate command and | ||||||
reports ``atClusterTime`` field in the cursor response. For distinct command server adds ``atClusterTime`` field to the distinct response object. | ||||||
``atClusterTime`` field represents the timestamp of the read and is guaranteed to be majority committed. | ||||||
|
||||||
Specification | ||||||
============= | ||||||
|
||||||
An application requests snapshot reads by creating a ``ClientSession`` | ||||||
with options that specify that snapshot reads are desired. An | ||||||
application then passes the session as an argument to methods in the | ||||||
``MongoDatabase`` and ``MongoCollection`` classes. Read operations (find/aggregate/distinct) performed against | ||||||
that session will be read from the same snapshot. | ||||||
|
||||||
High level summary of the API changes for snapshot reads | ||||||
======================================================== | ||||||
|
||||||
Snapshot reads are built on top of client sessions. | ||||||
|
||||||
Applications will start a new client session for snapshot reads like | ||||||
this: | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
options = new SessionOptions(snapshot = true); | ||||||
session = client.startSession(options); | ||||||
|
||||||
All read operations performed using this session will be read from the same snapshot. | ||||||
|
||||||
If no value is provided for ``snapshot`` a value of false is | ||||||
implied. | ||||||
There are no MongoDatabase, MongoClient or MongoCollection API changes. | ||||||
|
||||||
SessionOptions changes | ||||||
====================== | ||||||
|
||||||
``SessionOptions`` change summary | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
class SessionOptions { | ||||||
jyemin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
Optional<bool> snapshot; | ||||||
|
||||||
// other options defined by other specs | ||||||
} | ||||||
|
||||||
In order to support snapshot reads a new property named | ||||||
``snapshot`` is added to ``SessionOptions``. Applications set | ||||||
``snapshot`` when starting a client session to indicate | ||||||
whether they want snapshot reads. All read operations performed | ||||||
using that client session will share the same snapshot. | ||||||
|
||||||
Each new member is documented below. | ||||||
|
||||||
snapshot | ||||||
-------- | ||||||
|
||||||
Applications set ``snapshot`` when starting a session to | ||||||
indicate whether they want snapshot reads. | ||||||
|
||||||
Note that the ``snapshot`` property is optional. The default value of | ||||||
this property is false. | ||||||
|
||||||
Snapshot reads and causal consistency are mutually exclusive. Therefore if ``snapshot`` is set to true, | ||||||
``causalConsistency`` property is set to false. Client MUST throw an Error if both ``snapshot`` and ``causalConsistency`` are set to true. | ||||||
Snapshot reads are supported both on primaries and secondaries. | ||||||
|
||||||
ClientSession changes | ||||||
===================== | ||||||
|
||||||
Transaction are not allowed with snapshot sessions. | ||||||
Calling ``session.startTransaction(options)`` on snapshot session should raise an error. | ||||||
|
||||||
ReadConcern changes | ||||||
=================== | ||||||
|
||||||
``snapshot`` added to `ReadConcernLevel enumeration <../read-write-concern/read-write-concern.rst#read-concern>`_.`. | ||||||
|
||||||
Server Commands | ||||||
=============== | ||||||
|
||||||
There are no new server commands related to snapshot reads. Instead, | ||||||
snapshot reads are implemented by: | ||||||
|
||||||
1. Saving the ``atClusterTime`` returned by 5.0+ servers for the first find/aggregate/distinct operation in a | ||||||
private property ``snapshotTime`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` | ||||||
in the ``ClientSession`` object. | ||||||
|
||||||
2. Passing that ``snapshotTime`` in the ``atClusterTime`` field of the ``readConcern`` field | ||||||
for subsequent snapshot read operations (for find/aggregate/distinct commands). | ||||||
|
||||||
Server Command Responses | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the driver starts a snapshot session but the server does not return the atClusterTime field the driver needs to raise a client side error indicating that server does not support snapshot reads. If we don't do this then snapshot reads can silently fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. False alarm. The problem was that my implementation neglected to set readConcern.level to ‘snapshot’. When the driver correctly sends ‘snapshot’ old servers will reject the request with this error:
Can we add a unified test case that confirms that the server returns an error when drivers try to use a snapshot session? It should run on 3.6-4.4. |
||||||
======================== | ||||||
|
||||||
To support snapshot reads the server returns the ``atClusterTime`` in | ||||||
cursor object it sends to the driver (for both find/aggregate commands). | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
{ | ||||||
ok : 1 or 0, | ||||||
... // the rest of the command reply | ||||||
cursor : { | ||||||
... // the rest of the cursor reply | ||||||
atClusterTime : <BsonTimestamp> | ||||||
} | ||||||
} | ||||||
|
||||||
For distinct commands server returns the ``atClusterTime`` in | ||||||
distinct response object it sends to the driver. | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
{ | ||||||
ok : 1 or 0, | ||||||
... // the rest of the command reply | ||||||
atClusterTime : <BsonTimestamp> | ||||||
} | ||||||
|
||||||
The ``atClusterTime`` MUST be stored in the ``ClientSession`` to later be passed as the | ||||||
``atClusterTime`` field of the ``readConcern`` with ``snapshot`` level field in subsequent read operations. | ||||||
|
||||||
Server Errors | ||||||
============= | ||||||
1. The server may reply to read commands with a ``SnapshotTooOld`` error if the client's ``atClusterTime`` value is not available in the server's history. | ||||||
2. The server will return ``InvalidOptions`` error if both ``atClusterTime`` and ``afterClusterTime`` options are set to true. | ||||||
|
||||||
Snapshot read commands | ||||||
====================== | ||||||
|
||||||
For snapshot reads the driver MUST first obtain ``atClusterTime`` from the server response of find/aggregate/distinct command, | ||||||
by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``snapshotTime`` in | ||||||
``ClientSession`` object. | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
{ | ||||||
find : <string>, // or other read command | ||||||
... // the rest of the command parameters | ||||||
readConcern : | ||||||
{ | ||||||
level : "snapshot" | ||||||
} | ||||||
} | ||||||
|
||||||
For subsequent reads from same snapshot driver MUST send the ``snapshotTime`` saved in | ||||||
the ``ClientSession`` as the value of the ``atClusterTime`` field of the | ||||||
``readConcern`` with ``snapshot`` level field: | ||||||
|
||||||
.. code:: typescript | ||||||
|
||||||
{ | ||||||
find : <string>, // or other read command | ||||||
... // the rest of the command parameters | ||||||
readConcern : | ||||||
{ | ||||||
level : "snapshot", | ||||||
atClusterTime : <BsonTimestamp> | ||||||
} | ||||||
} | ||||||
|
||||||
Lists of commands that support snapshot reads: | ||||||
|
||||||
1. find | ||||||
2. aggregate | ||||||
3. distinct | ||||||
|
||||||
Motivation | ||||||
========== | ||||||
|
||||||
To support snapshot reads. Only supported with server version 5.0+ or newer. | ||||||
|
||||||
Design Rationale | ||||||
================ | ||||||
|
||||||
The goal is to modify the driver API as little as possible so that existing | ||||||
programs that don't need snapshot reads don't have to be changed. | ||||||
This goal is met by defining a ``SessionOptions`` field that applications use to | ||||||
start a ``ClientSession`` that can be used for snapshot reads. Alternative explicit approach of | ||||||
obtaining ``atClusterTime`` from ``cursor`` object and passing it to read concern object was considered initially. | ||||||
Session based approach was chosen as it aligns better with the existing API, and requires minimal API changes. | ||||||
Future extensibility for snapshot reads would be better served by session based approach, as no API changes will be required. | ||||||
|
||||||
Backwards Compatibility | ||||||
======================= | ||||||
|
||||||
The API changes to support sessions extend the existing API but do not | ||||||
introduce any backward breaking changes. Existing programs that don't use | ||||||
snapshot reads continue to compile and run correctly. | ||||||
|
||||||
Reference Implementation | ||||||
======================== | ||||||
|
||||||
C# driver will provide the reference implementation. | ||||||
The corresponding ticket is `CSHARP-3668 <https://jira.mongodb.org/browse/CSHARP-3668>`_. | ||||||
|
||||||
Q&A | ||||||
=== | ||||||
|
||||||
Changelog | ||||||
========= | ||||||
|
||||||
:2021-06-15: Initial version. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,10 +9,11 @@ Driver Session Tests | |
Introduction | ||
============ | ||
|
||
The YAML and JSON files in this directory are platform-independent tests that | ||
drivers can use to prove their conformance to the Driver Sessions Spec. They are | ||
The YAML and JSON files in the ``legacy`` and ``unified`` sub-directories are platform-independent tests | ||
that drivers can use to prove their conformance to the Driver Sessions Spec. They are | ||
designed with the intention of sharing most test-runner code with the | ||
Transactions spec tests. | ||
`Transactions Spec tests <../../transactions/tests/README.rst#test-format>`_.. Tests in the | ||
``unified`` directory are written using the `Unified Test Format <../../unified-test-format/unified-test-format.rst>`_. | ||
|
||
Several prose tests, which are not easily expressed in YAML, are also presented | ||
in the Driver Sessions Spec. Those tests will need to be manually implemented | ||
|
@@ -78,7 +79,26 @@ the given session is *not* marked dirty:: | |
arguments: | ||
session: session0 | ||
|
||
Snapshot session tests | ||
====================== | ||
Snapshot sessions tests require server of version 5.0 or higher and | ||
replica set or a sharded cluster deployment. | ||
Default snapshot history window on the server is 5 minutes. Running the test in debug mode, or in any other slow configuration | ||
may lead to `SnapshotTooOld` errors. Drivers can work around this issue by increasing the server's `minSnapshotHistoryWindowInSeconds` parameter, for example: | ||
|
||
.. code:: python | ||
|
||
client.admin.command('setParameter', 1, minSnapshotHistoryWindowInSeconds=60) | ||
|
||
Prose tests | ||
``````````` | ||
- Setting both ``snapshot`` and ``causalConsistency`` is not allowed | ||
|
||
* ``client.startSession(snapshot = true, causalConsistency = true)`` | ||
* Assert that an error was raised by driver | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a unified test for this? It could run the client.startSession operation with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That might be a good idea, but session entities creation stage does not have error validation, and we don't have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The unified test runner allows us to call any method on the client including Edit: or maybe this would require a "schemaVersion" bump in the unified test format. Still I think it's worthwhile to be able to call startSession within tests. |
||
|
||
Changelog | ||
========= | ||
|
||
:2019-05-15: Initial version. | ||
:2021-06-15: Added snapshot-session tests. Introduced legacy and unified folders. |
Uh oh!
There was an error while loading. Please reload this page.