Skip to content

Commit 7266083

Browse files
RUST-824 Add Snapshot variant to ReadConcernLevel (#345)
1 parent 1a096bd commit 7266083

16 files changed

+1511
-6
lines changed

src/concern/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ impl ReadConcern {
5252
ReadConcernLevel::Available.into()
5353
}
5454

55+
/// Creates a read concern with level "snapshot".
56+
/// See the specific documentation for this read concern level [here](https://docs.mongodb.com/manual/reference/read-concern-snapshot/).
57+
pub fn snapshot() -> Self {
58+
ReadConcernLevel::Snapshot.into()
59+
}
60+
5561
/// Creates a read concern with a custom read concern level. This is present to provide forwards
5662
/// compatibility with any future read concerns which may be added to new versions of
5763
/// MongoDB.
@@ -85,6 +91,9 @@ pub enum ReadConcernLevel {
8591
/// See the specific documentation for this read concern level [here](https://docs.mongodb.com/manual/reference/read-concern-available/).
8692
Available,
8793

94+
/// See the specific documentation for this read concern level [here](https://docs.mongodb.com/manual/reference/read-concern-snapshot/).
95+
Snapshot,
96+
8897
/// Specify a custom read concern level. This is present to provide forwards compatibility with
8998
/// any future read concerns which may be added to new versions of MongoDB.
9099
Custom(String),
@@ -97,6 +106,7 @@ impl ReadConcernLevel {
97106
"majority" => ReadConcernLevel::Majority,
98107
"linearizable" => ReadConcernLevel::Linearizable,
99108
"available" => ReadConcernLevel::Available,
109+
"snapshot" => ReadConcernLevel::Snapshot,
100110
s => ReadConcernLevel::Custom(s.to_string()),
101111
}
102112
}
@@ -108,6 +118,7 @@ impl ReadConcernLevel {
108118
ReadConcernLevel::Majority => "majority",
109119
ReadConcernLevel::Linearizable => "linearizable",
110120
ReadConcernLevel::Available => "available",
121+
ReadConcernLevel::Snapshot => "snapshot",
111122
ReadConcernLevel::Custom(ref s) => s,
112123
}
113124
}

src/concern/test.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ use std::time::Duration;
22
use tokio::sync::RwLockReadGuard;
33

44
use crate::{
5-
bson::{doc, Bson},
5+
bson::{doc, Bson, Document},
66
error::ErrorKind,
7-
options::{Acknowledgment, InsertOneOptions, WriteConcern},
8-
test::{TestClient, LOCK},
7+
options::{
8+
Acknowledgment,
9+
FindOneOptions,
10+
InsertOneOptions,
11+
ReadConcern,
12+
TransactionOptions,
13+
WriteConcern,
14+
},
15+
test::{EventClient, TestClient, LOCK},
916
};
1017

1118
#[test]
@@ -144,3 +151,62 @@ async fn unacknowledged_write_concern_rejected() {
144151
.expect_err("insert should fail");
145152
assert!(matches!(*error.kind, ErrorKind::InvalidArgument { .. }));
146153
}
154+
155+
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
156+
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
157+
#[function_name::named]
158+
async fn snapshot_read_concern() {
159+
let _guard: RwLockReadGuard<()> = LOCK.run_concurrently().await;
160+
161+
let client = EventClient::new().await;
162+
// snapshot read concern was introduced in 4.0
163+
if client.server_version_lt(4, 0) {
164+
return;
165+
}
166+
167+
let coll = client
168+
.database(function_name!())
169+
.collection::<Document>(function_name!());
170+
171+
// TODO RUST-122 run this test on sharded clusters
172+
if client.is_replica_set() && client.server_version_gte(4, 0) {
173+
let mut session = client.start_session(None).await.unwrap();
174+
let options = TransactionOptions::builder()
175+
.read_concern(ReadConcern::snapshot())
176+
.build();
177+
session.start_transaction(options).await.unwrap();
178+
let result = coll.find_one_with_session(None, None, &mut session).await;
179+
assert!(result.is_ok());
180+
assert_event_contains_read_concern(&client).await;
181+
}
182+
183+
if client.server_version_lt(4, 9) {
184+
let options = FindOneOptions::builder()
185+
.read_concern(ReadConcern::snapshot())
186+
.build();
187+
let error = coll
188+
.find_one(None, options)
189+
.await
190+
.expect_err("non-transaction find one with snapshot read concern should fail");
191+
// ensure that an error from the server is returned
192+
assert!(matches!(*error.kind, ErrorKind::Command(_)));
193+
assert_event_contains_read_concern(&client).await;
194+
}
195+
}
196+
197+
async fn assert_event_contains_read_concern(client: &EventClient) {
198+
let event = client
199+
.get_command_started_events(&["find"])
200+
.into_iter()
201+
.next()
202+
.unwrap();
203+
assert_eq!(
204+
event
205+
.command
206+
.get_document("readConcern")
207+
.unwrap()
208+
.get_str("level")
209+
.unwrap(),
210+
"snapshot"
211+
);
212+
}

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
)]
8383
#![cfg_attr(docsrs, feature(doc_cfg))]
8484
#![cfg_attr(test, type_length_limit = "80000000")]
85-
8685
#![doc(html_root_url = "https://docs.rs/mongodb/2.0.0-beta")]
8786

8887
macro_rules! define_if_single_runtime_enabled {

src/srv.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ impl SrvResolver {
7777
.map(|record| {
7878
let hostname = record.target().to_utf8();
7979
let port = Some(record.port());
80-
ServerAddress::Tcp { host: hostname, port }
80+
ServerAddress::Tcp {
81+
host: hostname,
82+
port,
83+
}
8184
})
8285
.collect();
8386

@@ -112,7 +115,10 @@ impl SrvResolver {
112115

113116
// The spec tests list the seeds without the trailing '.', so we remove it by
114117
// joining the parts we split rather than manipulating the string.
115-
address = ServerAddress::Tcp { host: hostname_parts.join("."), port: address.port() };
118+
address = ServerAddress::Tcp {
119+
host: hostname_parts.join("."),
120+
port: address.port(),
121+
};
116122

117123
Ok(address)
118124
});

src/test/spec/json/read-write-concern/README.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ array of test case objects, each of which have the following keys:
5252
- ``isServerDefault:`` Indicates whether the read or write concern is considered the server's default.
5353
- ``isAcknowledged:`` Indicates if the write concern should be considered acknowledged.
5454

55+
Operation
56+
~~~~~~~~~
57+
58+
These tests check that the default write concern is omitted in operations.
59+
60+
The spec test format is an extension of `transactions spec tests <https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst>`_ with the following additions:
61+
62+
- ``writeConcern`` in the ``databaseOptions`` or ``collectionOptions`` may be an empty document to indicate a `server default write concern <https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst#servers-default-writeconcern>`_. For example, in libmongoc:
63+
64+
.. code:: c
65+
66+
/* Create a default write concern, and set on a collection object. */
67+
mongoc_write_concern_t *wc = mongoc_write_concern_new ();
68+
mongoc_collection_set_write_concern (collection, wc);
69+
70+
If the driver has no way to explicitly set a default write concern on a database or collection, ignore the empty ``writeConcern`` document and continue with the test.
71+
- The operations ``createIndex``, ``dropIndex`` are introduced.
72+
73+
5574
Use as unit tests
5675
=================
5776

0 commit comments

Comments
 (0)