From d847e2cb254e9c5c0e70826ecf0b9c35622984d4 Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 15 Jun 2021 21:26:38 +0300 Subject: [PATCH 01/21] - Snapshot session spec and test --- .../read-write-concern/read-write-concern.rst | 18 +- source/read-write-concern/tests/README.rst | 2 +- source/sessions/snapshot-sessions.rst | 358 +++++++++ source/sessions/tests/README.rst | 8 +- .../{ => legacy}/dirty-session-errors.json | 0 .../{ => legacy}/dirty-session-errors.yml | 0 .../tests/{ => legacy}/server-support.json | 0 .../tests/{ => legacy}/server-support.yml | 0 .../tests/unified/snapshot-sessions.json | 706 ++++++++++++++++++ .../unified-test-format.rst | 1 + 10 files changed, 1087 insertions(+), 6 deletions(-) create mode 100644 source/sessions/snapshot-sessions.rst rename source/sessions/tests/{ => legacy}/dirty-session-errors.json (100%) rename source/sessions/tests/{ => legacy}/dirty-session-errors.yml (100%) rename source/sessions/tests/{ => legacy}/server-support.json (100%) rename source/sessions/tests/{ => legacy}/server-support.yml (100%) create mode 100644 source/sessions/tests/unified/snapshot-sessions.json diff --git a/source/read-write-concern/read-write-concern.rst b/source/read-write-concern/read-write-concern.rst index c32bac2169..bae6c8d5ef 100644 --- a/source/read-write-concern/read-write-concern.rst +++ b/source/read-write-concern/read-write-concern.rst @@ -12,8 +12,8 @@ Read and Write Concern :Status: Approved :Type: Standards :Server Versions: 2.4+ -:Last Modified: 2021-04-07 -:Version: 1.5.5 +:Last Modified: 2021-06-15 +:Version: 1.5.6 .. contents:: @@ -81,6 +81,11 @@ Defined below are the constructs for drivers. * This is rendered as "available" (lower-case) on the wire. */ available + + /** + * This is rendered as "snapshot" (lower-case) on the wire. + */ + snapshot } class ReadConcern { @@ -131,6 +136,14 @@ considered the server’s default ``ReadConcern``. default ``ReadConcern`` while the latter is the user explicitly specifying a ``ReadConcern`` with a ``level`` of “local”. +Snapshot Read Concern +--------------------- + +When a ``ReadConcern`` ``level`` ``snapshot`` is used, ``atClusterTime`` MAY be specified to indicate +the desired point in time for reading. ``find`` and ``aggregate`` operations executed with ``ReadConcern`` ``snapshot`` but without ``atClusterTime`` +will return ``atClusterTime`` timestamp in resulting cursor. The obtained ``atClusterTime`` timestamp can be used for subsequent +read operations. +``ReadConcern`` ``level`` ``snapshot`` with ``clusterTime`` is supported in ``find``, ``aggregate`` and ``distinct`` operations. On the Wire ----------- @@ -701,3 +714,4 @@ Version History - 2019-10-31: Explicitly define write concern option mappings. - 2020-02-13: Inconsistent write concern must be considered an error. - 2021-04-07: Updated to use hello command. + - 2021-06-15: Added "snapshot" to Readconcern level diff --git a/source/read-write-concern/tests/README.rst b/source/read-write-concern/tests/README.rst index 5995590136..56a2cd6cda 100644 --- a/source/read-write-concern/tests/README.rst +++ b/source/read-write-concern/tests/README.rst @@ -1,5 +1,5 @@ ======================= -Connection String Tests +Read and Write Concern Tests ======================= The YAML and JSON files in this directory tree are platform-independent tests diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst new file mode 100644 index 0000000000..7b56e5a5d0 --- /dev/null +++ b/source/sessions/snapshot-sessions.rst @@ -0,0 +1,358 @@ +================================ +Snapshot Reads Specification +================================ + +:Spec Title: Snapshot Reads Specification (See the registry of specs) +: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) +:Type: Standards +:Minimum Server Version: 5.0 (The minimum server version this spec applies to) +:Last Modified: 15-Jun-2021 + +.. contents:: + +-------- + +Abstract +======== + +Version 5.0 of the server introduces support for readConcern level "snapshot" (non-speculative) +for read commands outside of transactions, including on secondaries. +This spec builds upon the Sessions Specification to define how an application +requests "snapshot" level readConcern 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 `_. + +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 readconcern level ``snapshot`` that occur outside of transactions on + both the primary and secondary nodes, including in sharded clusters. + +Snapshot cluster time + Snapshot cluster time, representing timestamp of the first read (find/aggregate operations) in the session. + The server creates a cursor in response to a snapshot read command and + reports ``atClusterTime`` field in the cursor response. ``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(isSnapshot = true); + session = client.startSession(options); + +All read operations performed using this session will be read from same snapshot. + +If no value is provided for ``isSnapshot`` a value of false is +implied. + +MongoClient changes +=================== + +There are no API changes to ``MongoClient`` to support snapshot reads. +Applications indicate whether they want snapshot reads by setting the +``isSnapshot`` field in the options passed to the ``startSession`` method. + +SessionOptions changes +====================== + +``SessionOptions`` change summary + +.. code:: typescript + + class SessionOptions { + Optional isSnapshot; + + // other options defined by other specs + } + +In order to support snapshot reads a new property named +``isSnapshot`` is added to ``SessionOptions``. Applications set +``isSnapshot`` 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. + +isSnapshot +--------- + +Applications set ``isSnapshot`` when starting a session to +indicate whether they want snapshot reads. + +Note that the ``isSnapshot`` property is optional. The default value of +this property is false. + +Snapshot reads and causal consistency are mutually exclusive. Therefore if ``isSnapshot`` is set to true, +``causalConsistency`` property is set to false. Client MUST throw an Error if both ``isSnapshot`` and ``causalConsistency`` are set to true. +Snapshot reads are supported both on primaries and secondaries. + +ClientSession changes +===================== + +``ClientSession`` changes summary + +.. code:: typescript + + interface ClientSession { + Optional snapshotClusterTime; + + // other members as defined in other specs + } + +Each new member is documented below. + +snapshotClusterTime +------------------- + +This property returns the cluster time of the first find/aggregate operation performed +using this session. If no operations that support the snapshot read concern have been performed +using this session the value will be null. + +MongoDatabase changes +===================== + +There are no additional API changes to ``MongoDatabase`` beyond those specified in +the Sessions Specification. All ``MongoDatabase`` methods that talk to the server +have been overloaded to take a session parameter. If that session was started +with ``isSnapshot = true`` then all read operations using that session will +will share the same snapshot. + +MongoCollection changes +======================= + +There are no additional API changes to ``MongoCollection`` beyond those specified +in the Sessions Specification. All ``MongoCollection`` methods that talk to the +server have been overloaded to take a session parameter. If that session was +started with ``isSnapshot = true`` then all operations using that +session will share the same snapshot. + +ReadConcern changes +=================== + +``shapshot`` 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 operation in a + property ``snapshotClusterTime`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` + in the ``ClientSession`` object. + +2. Passing that ``snapshotClusterTime`` in the ``atClusterTime`` field of the ``readConcern`` field + for subsequent snapshot read operations (for find/aggregate/distinct commands). + +Server Command Responses +======================== + +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 : + } + } + +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 cursor response of find/aggregate command, +by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``snapshotClusterTime`` in +``ClientSession`` object. + +.. code:: typescript + + { + find : , // or other read command + ... // the rest of the command parameters + readConcern : + { + level : "snapshot" + } + } + +For subsequent reads from same snapshot driver MUST send the ``snapshotClusterTime`` saved in +the ``ClientSession`` as the value of the ``atClusterTime`` field of the +``readConcern`` with ``snapshot`` level field: + +.. code:: typescript + + { + find : , // or other read command + ... // the rest of the command parameters + readConcern : + { + level : "snapshot", + afterClusterTime : + } + } + +Lists of commands that support snapshot reads: + +1. find +2. aggregate +3. distinct + +Snapshot read commands errors +============================= + +Drivers MUST NOT retry errors in SnapshotError category for snapshot reads if ``atClusterTime`` is supplied. + +Test Plan +========= + +Note: some tests are only relevant to certain deployments. For the purpose of deciding +which tests to run assume that any deployment that is version 5.0 or higher and is either a +replica set or a sharded cluster supports snapshot reads. +The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured to match the test execution time. + +1. | The first read in a snapshot session must not send ``atClusterTime`` + | to the server (because the ``atClusterTime`` has not yet been determined) + + * ``session = client.startSession(isSnapshot = true)`` + * ``document = collection.anyReadOperation(session, ...)`` + * capture the command sent to the server (using APM or other mechanism). + * assert that the command does not have an ``atClusterTime``. + +2. | Subsequent snapshot reads on a ``ClientSession`` should read from the snapshot of the first read in that session. + | Test SHOULD introduce two variations one for primaries and additional for secondary reads + + * ``session1 = client.startSession(isSnapshot = true)`` + * ``session2 = client.startSession(isSnapshot = true)`` + * ``readBeforeUpdateSession1 = collection.anyReadOrOperation(session1, ...)`` + * ``collection.anyUpdateOpertation(...)`` + * ``readBeforeUpdateSession2 = collection.anyReadOrOperation(session2, ...)`` + * ``collection.anyUpdateOpertation(...)`` + * ``readAfterUpdateSession1 = collection.anyReadOrOperation(session1, ...)`` + * ``readAfterUpdateSession2 = collection.anyReadOrOperation(session2, ...)`` + * | Assert that `readBeforeUpdateSession1` is equivalent to `readAfterUpdateSession1` and + | `readBeforeUpdateSession2` to `readAfterUpdateSession2`. + +3. | Snapshot reads that fail with ``SnapshotError`` are not retried. + | This test is OPTIONAL as it requires setting ``session.snapshotClusterTime`` + | which is not part of the public API + + * ``session = client.startSession(isSnapshot = true)`` + * ``session.snapshotClusterTime = 0`` + * ``collection.anyReadOrOperation(session1, ...)`` + * Assert that only single read command was issued. + +4. | A read operation in a ``ClientSession`` that is not snapshot read + | should not include the ``atClusterTime`` parameter in the command sent to the server + + * ``session = client.startSession(isSnapshot = false)`` + * ``collection.anyReadOperation(session, {})`` + * ``operationTime = session.operationTime`` + * capture the command sent to the server (using APM or other mechanism). + * assert that the command does not have an ``atClusterTime`` field. + +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 readconcern 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 +======================== + +A reference implementation must be completed before any spec is given status +"Final", but it need not be completed before the spec is “Accepted”. C# driver will +provide the reference implementation. +While there is merit to the approach of reaching consensus on the specification and +rationale before writing code, the principle of "rough consensus and running +code" is still useful when it comes to resolving many discussions of spec +details. A final reference implementation must include test code and +documentation. + +Q&A +=== + +Changelog +========= + +:2021-06-15: Initial version. diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index 3ed7eea96a..52bf7f76b9 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -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 @@ -82,3 +83,4 @@ Changelog ========= :2019-05-15: Initial version. +:2021-06-15: Added snapshot-session tests. Introduced legacy and unified folders. diff --git a/source/sessions/tests/dirty-session-errors.json b/source/sessions/tests/legacy/dirty-session-errors.json similarity index 100% rename from source/sessions/tests/dirty-session-errors.json rename to source/sessions/tests/legacy/dirty-session-errors.json diff --git a/source/sessions/tests/dirty-session-errors.yml b/source/sessions/tests/legacy/dirty-session-errors.yml similarity index 100% rename from source/sessions/tests/dirty-session-errors.yml rename to source/sessions/tests/legacy/dirty-session-errors.yml diff --git a/source/sessions/tests/server-support.json b/source/sessions/tests/legacy/server-support.json similarity index 100% rename from source/sessions/tests/server-support.json rename to source/sessions/tests/legacy/server-support.json diff --git a/source/sessions/tests/server-support.yml b/source/sessions/tests/legacy/server-support.yml similarity index 100% rename from source/sessions/tests/server-support.yml rename to source/sessions/tests/legacy/server-support.yml diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json new file mode 100644 index 0000000000..d90ccecae8 --- /dev/null +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -0,0 +1,706 @@ +{ + "description": "snapshot-sessions", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.9.0", + "topologies": [ + "sharded-replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0", + "collectionOptions": { + "readPreference": { + "mode": "secondary" + } + } + } + }, + { + "session": { + "id": "session_find0", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_find1", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_distinct0", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_distinct1", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_aggregate0", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_aggregate1", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_mixed", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_first_read", + "client": "client0", + "sessionOptions": { + "isSnapshot": true + } + } + }, + { + "session": { + "id": "session_invalid_snapshot", + "client": "client0", + "sessionOptions": { + "isSnapshot": true, + "snapshotClusterTime": 0 + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + }, + { + "_id": 3, + "x": 11 + }, + { + "_id": 4, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Find operation with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session_find0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session_find1", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 13 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session_find0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session_find1", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 13 + } + ] + } + ], + "outcome": [ + ] + }, + { + "description": "Distinct operations with snapshot", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + }, + "session": "session_distinct0" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 2, + "x": 12 + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + }, + "session": "session_distinct1" + }, + "expectResult": [ + 11, + 12 + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 2, + "x": 13 + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 11, + 13 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + }, + "session": "session_distinct0" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + }, + "session": "session_distinct1" + }, + "expectResult": [ + 11, + 12 + ] + } + ], + "outcome": [ + ] + }, + { + "description": "Aggregate operation with snapshot", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 3 + } + } + ], + "session": "session_aggregate0" + }, + "expectResult": [ + { + "_id": 3, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 3, + "x": 12 + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 3 + } + } + ], + "session": "session_aggregate1" + }, + "expectResult": [ + { + "_id": 3, + "x": 12 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 3, + "x": 13 + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 3 + } + } + ] + }, + "expectResult": [ + { + "_id": 3, + "x": 13 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 3 + } + } + ], + "session": "session_aggregate0" + }, + "expectResult": [ + { + "_id": 3, + "x": 11 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 3 + } + } + ], + "session": "session_aggregate1" + }, + "expectResult": [ + { + "_id": 3, + "x": 12 + } + ] + } + ], + "outcome": [ + ] + }, + { + "description": "Mixed operations with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session_mixed", + "filter": { + "_id": 4 + } + }, + "expectResult": [ + { + "_id": 4, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 4, + "x": 12 + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 4 + } + }, + "expectResult": [ + { + "_id": 4, + "x": 12 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 4 + } + } + ], + "session": "session_mixed" + }, + "expectResult": [ + { + "_id": 4, + "x": 11 + } + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 3 + } + }, + "session": "session_mixed" + }, + "expectResult": [ + 11 + ] + } + ], + "outcome": [ + ] + }, + { + "description": "Snapshot error is not retried", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session_invalid_snapshot" + }, + "expectError": { + "errorCode": 72 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "filter": { + "_id": 3 + } + }, + "commandName": "find", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "First snapshot read does send atClusterTime", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "session": "session_first_read" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot" + } + }, + "commandName": "find", + "databaseName": "database0" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/unified-test-format.rst b/source/unified-test-format/unified-test-format.rst index 4b7fcbded7..9be419848e 100644 --- a/source/unified-test-format/unified-test-format.rst +++ b/source/unified-test-format/unified-test-format.rst @@ -624,6 +624,7 @@ The structure of this object is as follows: specifications: - `Causal Consistency <../causal-consistency/causal-consistency.rst#sessionoptions-changes>`__ + - `Snapshot Reads <../sessions/snapshot-sessions.rst#sessionoptions-changes>`__ - `Transactions <../transactions/transactions.rst#sessionoptions-changes>`__ When specifying TransactionOptions for ``defaultTransactionOptions``, the From 7e827b1adfd012798e79cf4f11dacf31e9d6226c Mon Sep 17 00:00:00 2001 From: Boris Date: Wed, 16 Jun 2021 17:04:04 +0300 Subject: [PATCH 02/21] - PR comments --- source/sessions/snapshot-sessions.rst | 24 +++++++++---------- .../tests/unified/snapshot-sessions.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 7b56e5a5d0..49ddac052e 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -64,8 +64,8 @@ Snapshot reads Reads with readconcern level ``snapshot`` that occur outside of transactions on both the primary and secondary nodes, including in sharded clusters. -Snapshot cluster time - Snapshot cluster time, representing timestamp of the first read (find/aggregate operations) in the session. +Snapshot timestamp + Snapshot timestamp, representing timestamp of the first read (find/aggregate operations) in the session. The server creates a cursor in response to a snapshot read command and reports ``atClusterTime`` field in the cursor response. ``atClusterTime`` field represents the timestamp of the read and is guaranteed to be majority committed. @@ -146,17 +146,17 @@ ClientSession changes .. code:: typescript interface ClientSession { - Optional snapshotClusterTime; + Optional snapshotTimestamp; // other members as defined in other specs } Each new member is documented below. -snapshotClusterTime +snapshotTimestamp ------------------- -This property returns the cluster time of the first find/aggregate operation performed +This property returns the timestamp the first find/aggregate/distinct operation performed using this session. If no operations that support the snapshot read concern have been performed using this session the value will be null. @@ -181,7 +181,7 @@ session will share the same snapshot. ReadConcern changes =================== -``shapshot`` added to `ReadConcernLevel enumeration <../read-write-concern/read-write-concern.rst#read-concern>`_.`. +``snapshot`` added to `ReadConcernLevel enumeration <../read-write-concern/read-write-concern.rst#read-concern>`_.`. Server Commands =============== @@ -190,10 +190,10 @@ 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 operation in a - property ``snapshotClusterTime`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` + property ``snapshotTimestamp`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` in the ``ClientSession`` object. -2. Passing that ``snapshotClusterTime`` in the ``atClusterTime`` field of the ``readConcern`` field +2. Passing that ``snapshotTimestamp`` in the ``atClusterTime`` field of the ``readConcern`` field for subsequent snapshot read operations (for find/aggregate/distinct commands). Server Command Responses @@ -225,7 +225,7 @@ Snapshot read commands ====================== For snapshot reads the driver MUST first obtain ``atClusterTime`` from cursor response of find/aggregate command, -by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``snapshotClusterTime`` in +by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``snapshotTimestamp`` in ``ClientSession`` object. .. code:: typescript @@ -239,7 +239,7 @@ by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``s } } -For subsequent reads from same snapshot driver MUST send the ``snapshotClusterTime`` saved in +For subsequent reads from same snapshot driver MUST send the ``snapshotTimestamp`` saved in the ``ClientSession`` as the value of the ``atClusterTime`` field of the ``readConcern`` with ``snapshot`` level field: @@ -297,11 +297,11 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured | `readBeforeUpdateSession2` to `readAfterUpdateSession2`. 3. | Snapshot reads that fail with ``SnapshotError`` are not retried. - | This test is OPTIONAL as it requires setting ``session.snapshotClusterTime`` + | This test is OPTIONAL as it requires setting ``session.snapshotTimestamp`` | which is not part of the public API * ``session = client.startSession(isSnapshot = true)`` - * ``session.snapshotClusterTime = 0`` + * ``session.snapshotTimestamp = 0`` * ``collection.anyReadOrOperation(session1, ...)`` * Assert that only single read command was issued. diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index d90ccecae8..ed0208988b 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -115,7 +115,7 @@ "client": "client0", "sessionOptions": { "isSnapshot": true, - "snapshotClusterTime": 0 + "snapshotTimestamp": 0 } } } From bf0e81122ec5e00ccbec256963a7ef3bede9fe04 Mon Sep 17 00:00:00 2001 From: Boris Date: Wed, 16 Jun 2021 17:21:03 +0300 Subject: [PATCH 03/21] - PR comments --- source/sessions/tests/unified/snapshot-sessions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index ed0208988b..506337d613 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -3,7 +3,7 @@ "schemaVersion": "1.0", "runOnRequirements": [ { - "minServerVersion": "4.9.0", + "minServerVersion": "5.0", "topologies": [ "sharded-replicaset" ] From 617a6afd4a656446858d313e8201a09ec9782a77 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 13:56:39 +0300 Subject: [PATCH 04/21] - PR comments --- source/read-write-concern/read-write-concern.rst | 2 +- source/read-write-concern/tests/README.rst | 4 ++-- source/sessions/snapshot-sessions.rst | 7 +++---- source/sessions/tests/unified/snapshot-sessions.json | 8 -------- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/source/read-write-concern/read-write-concern.rst b/source/read-write-concern/read-write-concern.rst index bae6c8d5ef..be99bfb714 100644 --- a/source/read-write-concern/read-write-concern.rst +++ b/source/read-write-concern/read-write-concern.rst @@ -13,7 +13,7 @@ Read and Write Concern :Type: Standards :Server Versions: 2.4+ :Last Modified: 2021-06-15 -:Version: 1.5.6 +:Version: 1.6 .. contents:: diff --git a/source/read-write-concern/tests/README.rst b/source/read-write-concern/tests/README.rst index 56a2cd6cda..2f2b84dc9c 100644 --- a/source/read-write-concern/tests/README.rst +++ b/source/read-write-concern/tests/README.rst @@ -1,6 +1,6 @@ -======================= +============================ Read and Write Concern Tests -======================= +============================ The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their conformance to the Read and Write Concern diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 49ddac052e..8cc2fd1bfc 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -1,6 +1,6 @@ -================================ +============================ Snapshot Reads Specification -================================ +============================ :Spec Title: Snapshot Reads Specification (See the registry of specs) :Spec Version: 1.0 @@ -8,7 +8,7 @@ Snapshot Reads Specification :Advisors: Jeff Yemin, A. Jesse Jiryu Davis, Judah Schvimer :Status: Draft (Could be Draft, Accepted, Rejected, Final, or Replaced) :Type: Standards -:Minimum Server Version: 5.0 (The minimum server version this spec applies to) +:Minimum Server Version: 5.0 :Last Modified: 15-Jun-2021 .. contents:: @@ -283,7 +283,6 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured * assert that the command does not have an ``atClusterTime``. 2. | Subsequent snapshot reads on a ``ClientSession`` should read from the snapshot of the first read in that session. - | Test SHOULD introduce two variations one for primaries and additional for secondary reads * ``session1 = client.startSession(isSnapshot = true)`` * ``session2 = client.startSession(isSnapshot = true)`` diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index 506337d613..a35e95a2bd 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -265,8 +265,6 @@ } ] } - ], - "outcome": [ ] }, { @@ -392,8 +390,6 @@ 12 ] } - ], - "outcome": [ ] }, { @@ -536,8 +532,6 @@ } ] } - ], - "outcome": [ ] }, { @@ -629,8 +623,6 @@ 11 ] } - ], - "outcome": [ ] }, { From 24b994c325d859025090a2280c08d3affb70d961 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 17:20:48 +0300 Subject: [PATCH 05/21] - PR comments --- source/read-write-concern/read-write-concern.rst | 2 +- source/sessions/snapshot-sessions.rst | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/source/read-write-concern/read-write-concern.rst b/source/read-write-concern/read-write-concern.rst index be99bfb714..a8e6a11d8d 100644 --- a/source/read-write-concern/read-write-concern.rst +++ b/source/read-write-concern/read-write-concern.rst @@ -139,7 +139,7 @@ default ``ReadConcern`` while the latter is the user explicitly specifying a Snapshot Read Concern --------------------- -When a ``ReadConcern`` ``level`` ``snapshot`` is used, ``atClusterTime`` MAY be specified to indicate +When a ``ReadConcern`` ``level`` ``snapshot`` is used, ``atClusterTime`` may be specified to indicate the desired point in time for reading. ``find`` and ``aggregate`` operations executed with ``ReadConcern`` ``snapshot`` but without ``atClusterTime`` will return ``atClusterTime`` timestamp in resulting cursor. The obtained ``atClusterTime`` timestamp can be used for subsequent read operations. diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 8cc2fd1bfc..fa4828cd24 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -339,14 +339,8 @@ snapshot reads continue to compile and run correctly. Reference Implementation ======================== -A reference implementation must be completed before any spec is given status -"Final", but it need not be completed before the spec is “Accepted”. C# driver will -provide the reference implementation. -While there is merit to the approach of reaching consensus on the specification and -rationale before writing code, the principle of "rough consensus and running -code" is still useful when it comes to resolving many discussions of spec -details. A final reference implementation must include test code and -documentation. +C# driver will provide the reference implementation. +The corresponding ticket is `CSHARP-3668 `_. Q&A === From d1c4066797fd35f4de62bdc28e327fc495d694b6 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 17:28:17 +0300 Subject: [PATCH 06/21] - tests validation --- source/unified-test-format/tests/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/unified-test-format/tests/Makefile b/source/unified-test-format/tests/Makefile index f168bb6620..b9aceb3aae 100644 --- a/source/unified-test-format/tests/Makefile +++ b/source/unified-test-format/tests/Makefile @@ -32,6 +32,9 @@ crud: HAS_AJV collection-management: HAS_AJV @ajv test -s $(SCHEMA) -d "../../collection-management/tests/*.yml" --valid +sessions: HAS_AJV + @ajv test -s $(SCHEMA) -d "../../sessions/tests/*.yml" --valid + HAS_AJV: @if ! command -v ajv > /dev/null; then \ echo 'Error: need "npm install -g ajv-cli"' 1>&2; \ From d6fc58c7758c58f74421548c60e2dff3289dcf00 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 17:29:29 +0300 Subject: [PATCH 07/21] - tests validation path update --- source/unified-test-format/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/unified-test-format/tests/Makefile b/source/unified-test-format/tests/Makefile index b9aceb3aae..9eddc90d3c 100644 --- a/source/unified-test-format/tests/Makefile +++ b/source/unified-test-format/tests/Makefile @@ -30,7 +30,7 @@ crud: HAS_AJV @ajv test -s $(SCHEMA) -d "../../crud/tests/unified/*.yml" --valid collection-management: HAS_AJV - @ajv test -s $(SCHEMA) -d "../../collection-management/tests/*.yml" --valid + @ajv test -s $(SCHEMA) -d "../../collection-management/tests/unified/*.yml" --valid sessions: HAS_AJV @ajv test -s $(SCHEMA) -d "../../sessions/tests/*.yml" --valid From e02e2a73ce2deea52473c70c925036a93a9536f2 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 17:31:48 +0300 Subject: [PATCH 08/21] - tests validation paths fixes --- source/unified-test-format/tests/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/unified-test-format/tests/Makefile b/source/unified-test-format/tests/Makefile index 9eddc90d3c..460c8bae16 100644 --- a/source/unified-test-format/tests/Makefile +++ b/source/unified-test-format/tests/Makefile @@ -30,10 +30,10 @@ crud: HAS_AJV @ajv test -s $(SCHEMA) -d "../../crud/tests/unified/*.yml" --valid collection-management: HAS_AJV - @ajv test -s $(SCHEMA) -d "../../collection-management/tests/unified/*.yml" --valid + @ajv test -s $(SCHEMA) -d "../../collection-management/tests/*.yml" --valid sessions: HAS_AJV - @ajv test -s $(SCHEMA) -d "../../sessions/tests/*.yml" --valid + @ajv test -s $(SCHEMA) -d "../../sessions/tests/unified/*.yml" --valid HAS_AJV: @if ! command -v ajv > /dev/null; then \ From c27b5614301a822671b0a1716bfa3d69cac3978d Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 18:03:59 +0300 Subject: [PATCH 09/21] - PR comments --- source/sessions/tests/unified/snapshot-sessions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index a35e95a2bd..6c31ddaea4 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -5,6 +5,7 @@ { "minServerVersion": "5.0", "topologies": [ + "replicaset", "sharded-replicaset" ] } From 7d753e0b6d0b53bfdffa4409207aae15b84f3123 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 17 Jun 2021 22:45:29 +0300 Subject: [PATCH 10/21] - Tests added and updated - atClusterTime in Distinct command result documented --- source/sessions/snapshot-sessions.rst | 35 ++- .../tests/unified/snapshot-sessions.json | 261 ++++++------------ 2 files changed, 111 insertions(+), 185 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index fa4828cd24..d60f5db264 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -65,9 +65,9 @@ Snapshot reads both the primary and secondary nodes, including in sharded clusters. Snapshot timestamp - Snapshot timestamp, representing timestamp of the first read (find/aggregate operations) in the session. - The server creates a cursor in response to a snapshot read command and - reports ``atClusterTime`` field in the cursor response. ``atClusterTime`` field represents the 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 @@ -213,6 +213,17 @@ cursor object it sends to the driver (for both find/aggregate commands). } } +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 : + } + 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. @@ -295,16 +306,7 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured * | Assert that `readBeforeUpdateSession1` is equivalent to `readAfterUpdateSession1` and | `readBeforeUpdateSession2` to `readAfterUpdateSession2`. -3. | Snapshot reads that fail with ``SnapshotError`` are not retried. - | This test is OPTIONAL as it requires setting ``session.snapshotTimestamp`` - | which is not part of the public API - - * ``session = client.startSession(isSnapshot = true)`` - * ``session.snapshotTimestamp = 0`` - * ``collection.anyReadOrOperation(session1, ...)`` - * Assert that only single read command was issued. - -4. | A read operation in a ``ClientSession`` that is not snapshot read +3. | A read operation in a ``ClientSession`` that is not snapshot read | should not include the ``atClusterTime`` parameter in the command sent to the server * ``session = client.startSession(isSnapshot = false)`` @@ -313,6 +315,13 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured * capture the command sent to the server (using APM or other mechanism). * assert that the command does not have an ``atClusterTime`` field. +4. Write operations with snapshot ``ClientSession`` do not affect snapshot reads with that session + + * ``session = client.startSession(isSnapshot = true)`` + * ``read1 = collection.anyReadOperation(session, {})`` + * ``collection.anyWriteOperation(session, {})`` + * ``read2 = collection.anyReadOperation(session, {})`` + * Assert that ``read1`` is equivalent to ``read2`` Motivation ========== diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index 6c31ddaea4..c9d7b3fd3b 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -30,26 +30,12 @@ "collection": { "id": "collection0", "database": "database0", - "collectionName": "collection0", - "collectionOptions": { - "readPreference": { - "mode": "secondary" - } - } - } - }, - { - "session": { - "id": "session_find0", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } + "collectionName": "collection0" } }, { "session": { - "id": "session_find1", + "id": "session0", "client": "client0", "sessionOptions": { "isSnapshot": true @@ -58,67 +44,12 @@ }, { "session": { - "id": "session_distinct0", + "id": "session1", "client": "client0", "sessionOptions": { "isSnapshot": true } } - }, - { - "session": { - "id": "session_distinct1", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } - } - }, - { - "session": { - "id": "session_aggregate0", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } - } - }, - { - "session": { - "id": "session_aggregate1", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } - } - }, - { - "session": { - "id": "session_mixed", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } - } - }, - { - "session": { - "id": "session_first_read", - "client": "client0", - "sessionOptions": { - "isSnapshot": true - } - } - }, - { - "session": { - "id": "session_invalid_snapshot", - "client": "client0", - "sessionOptions": { - "isSnapshot": true, - "snapshotTimestamp": 0 - } - } } ], "initialData": [ @@ -133,14 +64,6 @@ { "_id": 2, "x": 11 - }, - { - "_id": 3, - "x": 11 - }, - { - "_id": 4, - "x": 11 } ] } @@ -153,7 +76,7 @@ "name": "find", "object": "collection0", "arguments": { - "session": "session_find0", + "session": "session0", "filter": { "_id": 1 } @@ -188,7 +111,7 @@ "name": "find", "object": "collection0", "arguments": { - "session": "session_find1", + "session": "session1", "filter": { "_id": 1 } @@ -223,7 +146,7 @@ "name": "find", "object": "collection0", "arguments": { - "session": "session_find0", + "session": "session0", "filter": { "_id": 1 } @@ -239,7 +162,7 @@ "name": "find", "object": "collection0", "arguments": { - "session": "session_find1", + "session": "session1", "filter": { "_id": 1 } @@ -269,19 +192,15 @@ ] }, { - "description": "Distinct operations with snapshot", + "description": "Distinct operation with snapshot", "operations": [ { "name": "distinct", "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - }, - "session": "session_distinct0" + "filter": {}, + "session": "session0" }, "expectResult": [ 11 @@ -311,12 +230,8 @@ "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - }, - "session": "session_distinct1" + "filter": {}, + "session": "session1" }, "expectResult": [ 11, @@ -347,11 +262,7 @@ "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } + "filter": {} }, "expectResult": [ 11, @@ -363,12 +274,8 @@ "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - }, - "session": "session_distinct0" + "filter": {}, + "session": "session0" }, "expectResult": [ 11 @@ -379,12 +286,8 @@ "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - }, - "session": "session_distinct1" + "filter": {}, + "session": "session1" }, "expectResult": [ 11, @@ -403,15 +306,15 @@ "pipeline": [ { "$match": { - "_id": 3 + "_id": 1 } } ], - "session": "session_aggregate0" + "session": "session0" }, "expectResult": [ { - "_id": 3, + "_id": 1, "x": 11 } ] @@ -421,7 +324,7 @@ "object": "collection0", "arguments": { "filter": { - "_id": 3 + "_id": 1 }, "update": { "$inc": { @@ -431,7 +334,7 @@ "returnDocument": "After" }, "expectResult": { - "_id": 3, + "_id": 1, "x": 12 } }, @@ -442,15 +345,15 @@ "pipeline": [ { "$match": { - "_id": 3 + "_id": 1 } } ], - "session": "session_aggregate1" + "session": "session1" }, "expectResult": [ { - "_id": 3, + "_id": 1, "x": 12 } ] @@ -460,7 +363,7 @@ "object": "collection0", "arguments": { "filter": { - "_id": 3 + "_id": 1 }, "update": { "$inc": { @@ -470,7 +373,7 @@ "returnDocument": "After" }, "expectResult": { - "_id": 3, + "_id": 1, "x": 13 } }, @@ -481,14 +384,14 @@ "pipeline": [ { "$match": { - "_id": 3 + "_id": 1 } } ] }, "expectResult": [ { - "_id": 3, + "_id": 1, "x": 13 } ] @@ -500,15 +403,15 @@ "pipeline": [ { "$match": { - "_id": 3 + "_id": 1 } } ], - "session": "session_aggregate0" + "session": "session0" }, "expectResult": [ { - "_id": 3, + "_id": 1, "x": 11 } ] @@ -520,15 +423,15 @@ "pipeline": [ { "$match": { - "_id": 3 + "_id": 1 } } ], - "session": "session_aggregate1" + "session": "session1" }, "expectResult": [ { - "_id": 3, + "_id": 1, "x": 12 } ] @@ -536,20 +439,20 @@ ] }, { - "description": "Mixed operations with snapshot", + "description": "Mixed operation with snapshot", "operations": [ { "name": "find", "object": "collection0", "arguments": { - "session": "session_mixed", + "session": "session0", "filter": { - "_id": 4 + "_id": 1 } }, "expectResult": [ { - "_id": 4, + "_id": 1, "x": 11 } ] @@ -559,7 +462,7 @@ "object": "collection0", "arguments": { "filter": { - "_id": 4 + "_id": 1 }, "update": { "$inc": { @@ -569,7 +472,7 @@ "returnDocument": "After" }, "expectResult": { - "_id": 4, + "_id": 1, "x": 12 } }, @@ -578,12 +481,12 @@ "object": "collection0", "arguments": { "filter": { - "_id": 4 + "_id": 1 } }, "expectResult": [ { - "_id": 4, + "_id": 1, "x": 12 } ] @@ -595,15 +498,15 @@ "pipeline": [ { "$match": { - "_id": 4 + "_id": 1 } } ], - "session": "session_mixed" + "session": "session0" }, "expectResult": [ { - "_id": 4, + "_id": 1, "x": 11 } ] @@ -613,12 +516,8 @@ "object": "collection0", "arguments": { "fieldName": "x", - "filter": { - "_id": { - "$gt": 3 - } - }, - "session": "session_mixed" + "filter": {}, + "session": "session0" }, "expectResult": [ 11 @@ -627,51 +526,69 @@ ] }, { - "description": "Snapshot error is not retried", + "description": "Write commands with snapshot session do not affect snapshot reads", "operations": [ { "name": "find", "object": "collection0", + "arguments": { + "filter": {}, + "session": "session0" + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 22, + "x": 33 + } + } + }, + { + "name": "updateOne", + "object": "collection0", "arguments": { "filter": { - "_id": 3 + "_id": 1 }, - "session": "session_invalid_snapshot" - }, - "expectError": { - "errorCode": 72 + "session": "session0", + "update": { + "$inc": { + "x": 1 + } + } } - } - ], - "expectEvents": [ + }, { - "client": "client0", - "events": [ + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "session": "session0" + }, + "expectResult": [ { - "commandStartedEvent": { - "command": { - "find": "collection0", - "filter": { - "_id": 3 - } - }, - "commandName": "find", - "databaseName": "database0" - } + "_id": 1, + "x": 11 } ] } ] }, { - "description": "First snapshot read does send atClusterTime", + "description": "First snapshot read does not send atClusterTime", "operations": [ { "name": "find", "object": "collection0", "arguments": { "filter": {}, - "session": "session_first_read" + "session": "session0" } } ], From 96f4471b91fda4109a978f5e7ecc1b77313120b5 Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 21 Jun 2021 12:43:40 +0300 Subject: [PATCH 11/21] - PR comments --- source/sessions/snapshot-sessions.rst | 33 +- .../tests/unified/snapshot-sessions.json | 317 +++++++++++++++++- 2 files changed, 317 insertions(+), 33 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index d60f5db264..74c0838082 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -138,28 +138,6 @@ Snapshot reads and causal consistency are mutually exclusive. Therefore if ``isS ``causalConsistency`` property is set to false. Client MUST throw an Error if both ``isSnapshot`` and ``causalConsistency`` are set to true. Snapshot reads are supported both on primaries and secondaries. -ClientSession changes -===================== - -``ClientSession`` changes summary - -.. code:: typescript - - interface ClientSession { - Optional snapshotTimestamp; - - // other members as defined in other specs - } - -Each new member is documented below. - -snapshotTimestamp -------------------- - -This property returns the timestamp the first find/aggregate/distinct operation performed -using this session. If no operations that support the snapshot read concern have been performed -using this session the value will be null. - MongoDatabase changes ===================== @@ -272,11 +250,6 @@ Lists of commands that support snapshot reads: 2. aggregate 3. distinct -Snapshot read commands errors -============================= - -Drivers MUST NOT retry errors in SnapshotError category for snapshot reads if ``atClusterTime`` is supplied. - Test Plan ========= @@ -322,6 +295,12 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured * ``collection.anyWriteOperation(session, {})`` * ``read2 = collection.anyReadOperation(session, {})`` * Assert that ``read1`` is equivalent to ``read2`` + +5. Setting both ``isSnapshot`` and ``causalConsistency`` is not allowed + + * ``client.startSession(isSnapshot = true, causalConsistency = true)`` + * Assert that an error was raised by driver + Motivation ========== diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index c9d7b3fd3b..3eeaea8907 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -16,6 +16,11 @@ "id": "client0", "observeEvents": [ "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "findAndModify", + "insert", + "update" ] } }, @@ -142,11 +147,11 @@ "x": 13 } }, + { "name": "find", "object": "collection0", "arguments": { - "session": "session0", "filter": { "_id": 1 } @@ -154,7 +159,7 @@ "expectResult": [ { "_id": 1, - "x": 11 + "x": 13 } ] }, @@ -162,7 +167,7 @@ "name": "find", "object": "collection0", "arguments": { - "session": "session1", + "session": "session0", "filter": { "_id": 1 } @@ -170,7 +175,7 @@ "expectResult": [ { "_id": 1, - "x": 12 + "x": 11 } ] }, @@ -178,6 +183,7 @@ "name": "find", "object": "collection0", "arguments": { + "session": "session1", "filter": { "_id": 1 } @@ -185,7 +191,76 @@ "expectResult": [ { "_id": 1, - "x": 13 + "x": 12 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } } ] } @@ -294,6 +369,75 @@ 12 ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } ] }, { @@ -436,6 +580,75 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } ] }, { @@ -523,6 +736,62 @@ 11 ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } ] }, { @@ -578,6 +847,39 @@ } ] } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": true + } + } + } + } + } + ] + } ] }, { @@ -601,7 +903,10 @@ "command": { "find": "collection0", "readConcern": { - "level": "snapshot" + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } } }, "commandName": "find", From 16f8685530781d2dedc5862aa8b379e5ebd18176 Mon Sep 17 00:00:00 2001 From: Boris Date: Wed, 23 Jun 2021 12:03:44 +0300 Subject: [PATCH 12/21] - PR comments --- .../read-write-concern/read-write-concern.rst | 4 +- source/sessions/snapshot-sessions.rst | 70 +++---------------- source/sessions/tests/README.rst | 13 ++++ .../tests/unified/snapshot-sessions.json | 7 +- 4 files changed, 31 insertions(+), 63 deletions(-) diff --git a/source/read-write-concern/read-write-concern.rst b/source/read-write-concern/read-write-concern.rst index a8e6a11d8d..20ac08e9a9 100644 --- a/source/read-write-concern/read-write-concern.rst +++ b/source/read-write-concern/read-write-concern.rst @@ -140,8 +140,8 @@ Snapshot Read Concern --------------------- When a ``ReadConcern`` ``level`` ``snapshot`` is used, ``atClusterTime`` may be specified to indicate -the desired point in time for reading. ``find`` and ``aggregate`` operations executed with ``ReadConcern`` ``snapshot`` but without ``atClusterTime`` -will return ``atClusterTime`` timestamp in resulting cursor. The obtained ``atClusterTime`` timestamp can be used for subsequent +the desired point in time for reading. ``find``, ``aggregate`` and ``distinct`` operations executed with ``ReadConcern`` ``snapshot`` but without ``atClusterTime`` +will return ``atClusterTime`` timestamp in the server response. The obtained ``atClusterTime`` timestamp can be used for subsequent read operations. ``ReadConcern`` ``level`` ``snapshot`` with ``clusterTime`` is supported in ``find``, ``aggregate`` and ``distinct`` operations. diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 74c0838082..a17d0f498c 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -20,7 +20,7 @@ Abstract Version 5.0 of the server introduces support for readConcern level "snapshot" (non-speculative) for read commands outside of transactions, including on secondaries. -This spec builds upon the Sessions Specification to define how an application +This spec builds upon the `Sessions Specification <../driver-sessions.rst>`_ to define how an application requests "snapshot" level readConcern and how a driver interacts with the server to implement snapshot reads. @@ -63,12 +63,13 @@ Session Snapshot reads Reads with readconcern level ``snapshot`` that occur outside of transactions on 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. + 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 ============= @@ -92,7 +93,7 @@ this: options = new SessionOptions(isSnapshot = true); session = client.startSession(options); -All read operations performed using this session will be read from same snapshot. +All read operations performed using this session will be read from the same snapshot. If no value is provided for ``isSnapshot`` a value of false is implied. @@ -126,7 +127,7 @@ using that client session will share the same snapshot. Each new member is documented below. isSnapshot ---------- +---------- Applications set ``isSnapshot`` when starting a session to indicate whether they want snapshot reads. @@ -167,7 +168,7 @@ 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 operation in a +1. Saving the ``atClusterTime`` returned by 5.0+ servers for the first find/aggregate/distinct operation in a property ``snapshotTimestamp`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` in the ``ClientSession`` object. @@ -213,7 +214,7 @@ Server Errors Snapshot read commands ====================== -For snapshot reads the driver MUST first obtain ``atClusterTime`` from cursor response of find/aggregate command, +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 ``snapshotTimestamp`` in ``ClientSession`` object. @@ -240,7 +241,7 @@ the ``ClientSession`` as the value of the ``atClusterTime`` field of the readConcern : { level : "snapshot", - afterClusterTime : + atClusterTime : } } @@ -250,58 +251,7 @@ Lists of commands that support snapshot reads: 2. aggregate 3. distinct -Test Plan -========= - -Note: some tests are only relevant to certain deployments. For the purpose of deciding -which tests to run assume that any deployment that is version 5.0 or higher and is either a -replica set or a sharded cluster supports snapshot reads. -The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured to match the test execution time. - -1. | The first read in a snapshot session must not send ``atClusterTime`` - | to the server (because the ``atClusterTime`` has not yet been determined) - - * ``session = client.startSession(isSnapshot = true)`` - * ``document = collection.anyReadOperation(session, ...)`` - * capture the command sent to the server (using APM or other mechanism). - * assert that the command does not have an ``atClusterTime``. - -2. | Subsequent snapshot reads on a ``ClientSession`` should read from the snapshot of the first read in that session. - - * ``session1 = client.startSession(isSnapshot = true)`` - * ``session2 = client.startSession(isSnapshot = true)`` - * ``readBeforeUpdateSession1 = collection.anyReadOrOperation(session1, ...)`` - * ``collection.anyUpdateOpertation(...)`` - * ``readBeforeUpdateSession2 = collection.anyReadOrOperation(session2, ...)`` - * ``collection.anyUpdateOpertation(...)`` - * ``readAfterUpdateSession1 = collection.anyReadOrOperation(session1, ...)`` - * ``readAfterUpdateSession2 = collection.anyReadOrOperation(session2, ...)`` - * | Assert that `readBeforeUpdateSession1` is equivalent to `readAfterUpdateSession1` and - | `readBeforeUpdateSession2` to `readAfterUpdateSession2`. - -3. | A read operation in a ``ClientSession`` that is not snapshot read - | should not include the ``atClusterTime`` parameter in the command sent to the server - - * ``session = client.startSession(isSnapshot = false)`` - * ``collection.anyReadOperation(session, {})`` - * ``operationTime = session.operationTime`` - * capture the command sent to the server (using APM or other mechanism). - * assert that the command does not have an ``atClusterTime`` field. - -4. Write operations with snapshot ``ClientSession`` do not affect snapshot reads with that session - - * ``session = client.startSession(isSnapshot = true)`` - * ``read1 = collection.anyReadOperation(session, {})`` - * ``collection.anyWriteOperation(session, {})`` - * ``read2 = collection.anyReadOperation(session, {})`` - * Assert that ``read1`` is equivalent to ``read2`` - -5. Setting both ``isSnapshot`` and ``causalConsistency`` is not allowed - - * ``client.startSession(isSnapshot = true, causalConsistency = true)`` - * Assert that an error was raised by driver - -Motivation +Motivation ========== To support snapshot reads. Only supported with server version 5.0+ or newer. diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index 52bf7f76b9..ded3d5dbed 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -79,6 +79,19 @@ the given session is *not* marked dirty:: arguments: session: session0 +Snapshot session tests +====================== +Snapshot sessions tests require server version of version 5.0 or higher and +replica set or a sharded cluster deployment. +The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured to match the test execution time. + +Prose tests +``````````` +- Setting both ``isSnapshot`` and ``causalConsistency`` is not allowed + + * ``client.startSession(isSnapshot = true, causalConsistency = true)`` + * Assert that an error was raised by driver + Changelog ========= diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index 3eeaea8907..1529827a5c 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -35,7 +35,12 @@ "collection": { "id": "collection0", "database": "database0", - "collectionName": "collection0" + "collectionName": "collection0", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } } }, { From dfb2472e96e1e3f4f374eb526c5190c8282239b0 Mon Sep 17 00:00:00 2001 From: Boris Date: Wed, 23 Jun 2021 12:05:30 +0300 Subject: [PATCH 13/21] - Cleanup --- source/sessions/tests/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index ded3d5dbed..b392264c02 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -81,7 +81,7 @@ the given session is *not* marked dirty:: Snapshot session tests ====================== -Snapshot sessions tests require server version of version 5.0 or higher and +Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured to match the test execution time. From 694f14634a6c8ea29f20c6299ea3af57da725058 Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 24 Jun 2021 22:27:01 +0300 Subject: [PATCH 14/21] CSHARP-3306: PR comments --- source/causal-consistency/causal-consistency.rst | 4 ++-- source/sessions/snapshot-sessions.rst | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/causal-consistency/causal-consistency.rst b/source/causal-consistency/causal-consistency.rst index 65e688b493..77b2485dac 100644 --- a/source/causal-consistency/causal-consistency.rst +++ b/source/causal-consistency/causal-consistency.rst @@ -130,8 +130,8 @@ this: All read operations performed using this session will now be causally consistent. -If no value is provided for ``causalConsistency`` a value of true is -implied. See the ``causalConsistency`` section. +If no value is provided for ``causalConsistency`` and snapshot reads are not requested +a value of true is implied. See the ``causalConsistency`` section. MongoClient changes =================== diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index a17d0f498c..854db0ae69 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -18,10 +18,10 @@ Snapshot Reads Specification Abstract ======== -Version 5.0 of the server introduces support for readConcern level "snapshot" (non-speculative) +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 readConcern and how a driver interacts with the server +requests "snapshot" level read concern and how a driver interacts with the server to implement snapshot reads. Definitions @@ -61,7 +61,7 @@ Session specification defines how sessions are used to implement snapshot reads. Snapshot reads - Reads with readconcern level ``snapshot`` that occur outside of transactions on + Reads with read concern level ``snapshot`` that occur outside of transactions on both the primary and secondary nodes, including in sharded clusters. Snapshots reads are majority committed reads. @@ -263,7 +263,7 @@ 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 readconcern object was considered initially. +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. From d306a012a3da59b1a9bd51c4b7718fc341b6d13f Mon Sep 17 00:00:00 2001 From: Boris Date: Thu, 24 Jun 2021 23:16:07 +0300 Subject: [PATCH 15/21] - PR comments --- source/sessions/snapshot-sessions.rst | 36 +++++++++---------- source/sessions/tests/README.rst | 4 +-- .../tests/unified/snapshot-sessions.json | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 854db0ae69..fe25cc1f97 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -90,12 +90,12 @@ this: .. code:: typescript - options = new SessionOptions(isSnapshot = true); + 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 ``isSnapshot`` a value of false is +If no value is provided for ``snapshot`` a value of false is implied. MongoClient changes @@ -103,7 +103,7 @@ MongoClient changes There are no API changes to ``MongoClient`` to support snapshot reads. Applications indicate whether they want snapshot reads by setting the -``isSnapshot`` field in the options passed to the ``startSession`` method. +``snapshot`` field in the options passed to the ``startSession`` method. SessionOptions changes ====================== @@ -113,30 +113,30 @@ SessionOptions changes .. code:: typescript class SessionOptions { - Optional isSnapshot; + Optional snapshot; // other options defined by other specs } In order to support snapshot reads a new property named -``isSnapshot`` is added to ``SessionOptions``. Applications set -``isSnapshot`` when starting a client session to indicate +``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. -isSnapshot ----------- +snapshot +-------- -Applications set ``isSnapshot`` when starting a session to +Applications set ``snapshot`` when starting a session to indicate whether they want snapshot reads. -Note that the ``isSnapshot`` property is optional. The default value of +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 ``isSnapshot`` is set to true, -``causalConsistency`` property is set to false. Client MUST throw an Error if both ``isSnapshot`` and ``causalConsistency`` are set to true. +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. MongoDatabase changes @@ -145,7 +145,7 @@ MongoDatabase changes There are no additional API changes to ``MongoDatabase`` beyond those specified in the Sessions Specification. All ``MongoDatabase`` methods that talk to the server have been overloaded to take a session parameter. If that session was started -with ``isSnapshot = true`` then all read operations using that session will +with ``snapshot = true`` then all read operations using that session will will share the same snapshot. MongoCollection changes @@ -154,7 +154,7 @@ MongoCollection changes There are no additional API changes to ``MongoCollection`` beyond those specified in the Sessions Specification. All ``MongoCollection`` methods that talk to the server have been overloaded to take a session parameter. If that session was -started with ``isSnapshot = true`` then all operations using that +started with ``snapshot = true`` then all operations using that session will share the same snapshot. ReadConcern changes @@ -169,10 +169,10 @@ 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 - property ``snapshotTimestamp`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` + private property ``snapshotTime`` of the ``ClientSession`` object. Drivers MUST save the ``atClusterTime`` in the ``ClientSession`` object. -2. Passing that ``snapshotTimestamp`` in the ``atClusterTime`` field of the ``readConcern`` field +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 @@ -215,7 +215,7 @@ 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 ``snapshotTimestamp`` in +by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``snapshotTime`` in ``ClientSession`` object. .. code:: typescript @@ -229,7 +229,7 @@ by specifying ``readConcern`` with ``snapshot`` level field, and store it as ``s } } -For subsequent reads from same snapshot driver MUST send the ``snapshotTimestamp`` saved in +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: diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index b392264c02..eeaa1dac35 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -87,9 +87,9 @@ The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured Prose tests ``````````` -- Setting both ``isSnapshot`` and ``causalConsistency`` is not allowed +- Setting both ``snapshot`` and ``causalConsistency`` is not allowed - * ``client.startSession(isSnapshot = true, causalConsistency = true)`` + * ``client.startSession(snapshot = true, causalConsistency = true)`` * Assert that an error was raised by driver Changelog diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index 1529827a5c..bbbdfc3c44 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -48,7 +48,7 @@ "id": "session0", "client": "client0", "sessionOptions": { - "isSnapshot": true + "snapshot": true } } }, @@ -57,7 +57,7 @@ "id": "session1", "client": "client0", "sessionOptions": { - "isSnapshot": true + "snapshot": true } } } From 2d420d23e729b2b91352fa986b9466dd176734e5 Mon Sep 17 00:00:00 2001 From: Boris Date: Fri, 25 Jun 2021 00:01:21 +0300 Subject: [PATCH 16/21] - PR comments --- source/sessions/snapshot-sessions.rst | 26 +------------------------- source/sessions/tests/README.rst | 7 ++++++- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index fe25cc1f97..2b1e97095a 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -97,13 +97,7 @@ All read operations performed using this session will be read from the same snap If no value is provided for ``snapshot`` a value of false is implied. - -MongoClient changes -=================== - -There are no API changes to ``MongoClient`` to support snapshot reads. -Applications indicate whether they want snapshot reads by setting the -``snapshot`` field in the options passed to the ``startSession`` method. +There are no MongoDatabase, MongoClient or MongoCollection API changes. SessionOptions changes ====================== @@ -139,24 +133,6 @@ Snapshot reads and causal consistency are mutually exclusive. Therefore if ``sna ``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. -MongoDatabase changes -===================== - -There are no additional API changes to ``MongoDatabase`` beyond those specified in -the Sessions Specification. All ``MongoDatabase`` methods that talk to the server -have been overloaded to take a session parameter. If that session was started -with ``snapshot = true`` then all read operations using that session will -will share the same snapshot. - -MongoCollection changes -======================= - -There are no additional API changes to ``MongoCollection`` beyond those specified -in the Sessions Specification. All ``MongoCollection`` methods that talk to the -server have been overloaded to take a session parameter. If that session was -started with ``snapshot = true`` then all operations using that -session will share the same snapshot. - ReadConcern changes =================== diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index eeaa1dac35..cfbebb0a0d 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -83,7 +83,12 @@ Snapshot session tests ====================== Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. -The server ``minSnapshotHistoryWindowInSeconds`` parameter SHOULD be configured to match the test execution time. +Default snapshot history window on the server is 5 seconds. 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:: typescript + + client.admin.command('setParameter', 1, minSnapshotHistoryWindowInSeconds=60) Prose tests ``````````` From 9551b41555355179d8bbfe6fa8b554a1a9bf6e5d Mon Sep 17 00:00:00 2001 From: Boris Date: Fri, 25 Jun 2021 19:16:52 +0300 Subject: [PATCH 17/21] - Transactions not allowed with snapshot sessions --- source/sessions/snapshot-sessions.rst | 6 ++++++ source/sessions/tests/README.rst | 4 ++-- .../sessions/tests/unified/snapshot-sessions.json | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst index 2b1e97095a..d1bffd16e0 100644 --- a/source/sessions/snapshot-sessions.rst +++ b/source/sessions/snapshot-sessions.rst @@ -133,6 +133,12 @@ Snapshot reads and causal consistency are mutually exclusive. Therefore if ``sna ``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 =================== diff --git a/source/sessions/tests/README.rst b/source/sessions/tests/README.rst index cfbebb0a0d..d88b5c7ba6 100644 --- a/source/sessions/tests/README.rst +++ b/source/sessions/tests/README.rst @@ -83,10 +83,10 @@ 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 seconds. Running the test in debug mode, or in any other slow configuration +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:: typescript +.. code:: python client.admin.command('setParameter', 1, minSnapshotHistoryWindowInSeconds=60) diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index bbbdfc3c44..f4061ca9f8 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -921,6 +921,20 @@ ] } ] + }, + { + "description": "StartTransaction fails in snapshot session", + "operations": [ + { + "name": "startTransaction", + "object": "session0", + + "expectError": { + "isError": true, + "errorContains": "Transactions are not supported in snapshot sessions" + } + } + ] } ] } From 5fa46e74ffbb5066a989ed7eea5777e0092a3746 Mon Sep 17 00:00:00 2001 From: Boris Date: Fri, 25 Jun 2021 19:34:51 +0300 Subject: [PATCH 18/21] - Not supported snapshot error validation test for older servers --- ...t-sessions-not-supported-server-error.json | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json diff --git a/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json new file mode 100644 index 0000000000..b61c14fcf1 --- /dev/null +++ b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json @@ -0,0 +1,105 @@ +{ + "description": "snapshot-sessions-not-supported-server-error", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "maxServerVersion": "4.4.99", + "topologies": [ + "sharded-replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "snapshot": true + } + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Server returns an error on find with snapshot", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "commandName": "find", + "databaseName": "database0" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} From ed6639e843370b70514e2bb8e842cc0448f18a0d Mon Sep 17 00:00:00 2001 From: Boris Date: Fri, 25 Jun 2021 19:53:01 +0300 Subject: [PATCH 19/21] - PR comments --- source/sessions/tests/unified/snapshot-sessions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index f4061ca9f8..85d713e089 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -931,6 +931,7 @@ "expectError": { "isError": true, + "isClientError": true, "errorContains": "Transactions are not supported in snapshot sessions" } } From c1739c4cad1e81e27b799699407ac37e697ac1e2 Mon Sep 17 00:00:00 2001 From: Boris Date: Sat, 26 Jun 2021 00:03:46 +0300 Subject: [PATCH 20/21] - yaml tests introduced --- ...t-sessions-not-supported-server-error.json | 2 +- ...ot-sessions-not-supported-server-error.yml | 57 +++ .../tests/unified/snapshot-sessions.yml | 452 ++++++++++++++++++ 3 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml create mode 100644 source/sessions/tests/unified/snapshot-sessions.yml diff --git a/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json index b61c14fcf1..896a11c6eb 100644 --- a/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json +++ b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.json @@ -6,7 +6,7 @@ "minServerVersion": "3.6", "maxServerVersion": "4.4.99", "topologies": [ - "sharded-replicaset" + "replicaset, sharded-replicaset" ] } ], diff --git a/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml new file mode 100644 index 0000000000..55f3c84b10 --- /dev/null +++ b/source/sessions/tests/unified/snapshot-sessions-not-supported-server-error.yml @@ -0,0 +1,57 @@ +description: snapshot-sessions-not-supported-server-error + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "3.6" + maxServerVersion: "4.4.99" + topologies: [replicaset, sharded-replicaset] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandFailedEvent ] + ignoreCommandMonitoringEvents: [ findAndModify, insert, update ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name collection0 + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: +- description: Server returns an error on find with snapshot + operations: + - name: find + object: collection0 + arguments: + session: session0 + filter: {} + expectError: + isError: true + isClientError: false + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandFailedEvent: + commandName: find diff --git a/source/sessions/tests/unified/snapshot-sessions.yml b/source/sessions/tests/unified/snapshot-sessions.yml new file mode 100644 index 0000000000..9beda1ac0e --- /dev/null +++ b/source/sessions/tests/unified/snapshot-sessions.yml @@ -0,0 +1,452 @@ +description: snapshot-sessions + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + topologies: [replicaset, sharded-replicaset] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent] + ignoreCommandMonitoringEvents: [ findAndModify, insert, update ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name collection0 + collectionOptions: + writeConcern: { w: majority } + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + - session: + id: session1 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 11 } + +tests: +- description: Find operation with snapshot + operations: + - name: find + object: collection0 + arguments: + session: session0 + filter: { _id: 1 } + expectResult: + - {_id: 1, x: 11} + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 12 } + - name: find + object: collection0 + arguments: + session: session1 + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 12 } + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 13 } + - name: find + object: collection0 + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 13 } + - name: find + object: collection0 + arguments: + session: session0 + filter: { _id: 1 } + expectResult: + - {_id: 1, x: 11} + - name: find + object: collection0 + arguments: + session: session1 + filter: { _id: 1 } + expectResult: + - {_id: 1, x: 12} + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + find: collection0 + readConcern: + "$$exists": false + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + +- description: Distinct operation with snapshot + operations: + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session0 + expectResult: + - 11 + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 2, x: 12 } + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session1 + expectResult: [11, 12] + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 2, x: 13 } + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + expectResult: [ 11, 13 ] + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session0 + expectResult: [ 11 ] + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session1 + expectResult: [ 11, 12 ] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + "$$exists": false + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + +- description: Aggregate operation with snapshot + operations: + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": { _id: 1 } + session: session0 + expectResult: + - { _id: 1, x: 11 } + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 12 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": + _id: 1 + session: session1 + expectResult: + - {_id: 1, x: 12} + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 13 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": { _id: 1 } + expectResult: + - { _id: 1, x: 13 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": + _id: 1 + session: session0 + expectResult: + - { _id: 1, x: 11 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": { _id: 1 } + session: session1 + expectResult: + - { _id: 1, x: 12 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + "$$exists": false + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + +- description: Mixed operation with snapshot + operations: + - name: find + object: collection0 + arguments: + session: session0 + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 12 } + - name: find + object: collection0 + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 12 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": + _id: 1 + session: session0 + expectResult: + - { _id: 1, x: 11 } + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session0 + expectResult: [ 11 ] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + find: collection0 + readConcern: + "$$exists": false + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + +- description: Write commands with snapshot session do not affect snapshot reads + operations: + - name: find + object: collection0 + arguments: + filter: {} + session: session0 + - name: insertOne + object: collection0 + arguments: + session: session0 + document: + _id: 22 + x: 33 + - name: updateOne + object: collection0 + arguments: + filter: { _id: 1 } + session: session0 + update: { $inc: { x: 1 } } + - name: find + object: collection0 + arguments: + filter: { _id: 1 } + session: session0 + expectResult: + - {_id: 1, x: 11} + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": true + +- description: First snapshot read does not send atClusterTime + operations: + - name: find + object: collection0 + arguments: + filter: {} + session: session0 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + commandName: find + databaseName: database0 + +- description: StartTransaction fails in snapshot session + operations: + - name: startTransaction + object: session0 + expectError: + isError: true + isClientError: true + errorContains: Transactions are not supported in snapshot sessions From e1e0672414ff7756de2a2fb9a5d4b7ddea7c1066 Mon Sep 17 00:00:00 2001 From: Boris Date: Sat, 26 Jun 2021 00:23:49 +0300 Subject: [PATCH 21/21] - yaml to json autogen --- source/sessions/tests/unified/snapshot-sessions.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/sessions/tests/unified/snapshot-sessions.json b/source/sessions/tests/unified/snapshot-sessions.json index 85d713e089..4170a96699 100644 --- a/source/sessions/tests/unified/snapshot-sessions.json +++ b/source/sessions/tests/unified/snapshot-sessions.json @@ -152,7 +152,6 @@ "x": 13 } }, - { "name": "find", "object": "collection0", @@ -928,7 +927,6 @@ { "name": "startTransaction", "object": "session0", - "expectError": { "isError": true, "isClientError": true,