Skip to content

Commit 1af685e

Browse files
authored
RUST-774 Allow tests to specify backgroundThreadIntervalMS to fix a race condition. (#396)
1 parent 2bdf4f9 commit 1af685e

10 files changed

+83
-15
lines changed

src/cmap/options.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
#[cfg(test)]
2+
use std::cmp::Ordering;
13
use std::{sync::Arc, time::Duration};
24

35
use derivative::Derivative;
6+
#[cfg(test)]
7+
use serde::de::{Deserializer, Error};
48
use serde::Deserialize;
59
use typed_builder::TypedBuilder;
610

@@ -40,10 +44,10 @@ pub(crate) struct ConnectionPoolOptions {
4044
#[serde(skip)]
4145
pub(crate) event_handler: Option<Arc<dyn CmapEventHandler>>,
4246

43-
/// How often the background thread performs its maintenance (e.g. ensure minPoolSize).
47+
/// Interval between background thread maintenance runs (e.g. ensure minPoolSize).
4448
#[cfg(test)]
45-
#[serde(skip)]
46-
pub(crate) maintenance_frequency: Option<Duration>,
49+
#[serde(rename = "backgroundThreadIntervalMS")]
50+
pub(crate) background_thread_interval: Option<BackgroundThreadInterval>,
4751

4852
/// Connections that have been ready for usage in the pool for longer than `max_idle_time` will
4953
/// not be used.
@@ -101,7 +105,7 @@ impl ConnectionPoolOptions {
101105
credential: options.credential.clone(),
102106
event_handler: options.cmap_event_handler.clone(),
103107
#[cfg(test)]
104-
maintenance_frequency: None,
108+
background_thread_interval: None,
105109
#[cfg(test)]
106110
ready: None,
107111
}
@@ -116,6 +120,30 @@ impl ConnectionPoolOptions {
116120
}
117121
}
118122

123+
#[cfg(test)]
124+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
125+
pub(crate) enum BackgroundThreadInterval {
126+
Never,
127+
Every(Duration),
128+
}
129+
130+
#[cfg(test)]
131+
impl<'de> Deserialize<'de> for BackgroundThreadInterval {
132+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133+
where
134+
D: Deserializer<'de>,
135+
{
136+
let millis = i64::deserialize(deserializer)?;
137+
Ok(match millis.cmp(&0) {
138+
Ordering::Less => BackgroundThreadInterval::Never,
139+
Ordering::Equal => return Err(D::Error::custom("zero is not allowed")),
140+
Ordering::Greater => {
141+
BackgroundThreadInterval::Every(Duration::from_millis(millis as u64))
142+
}
143+
})
144+
}
145+
}
146+
119147
/// Options used for constructing a `Connection`.
120148
#[derive(Derivative)]
121149
#[derivative(Debug)]

src/cmap/test/mod.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -150,17 +150,13 @@ impl Executor {
150150
async fn execute_test(self) {
151151
let mut subscriber = self.state.handler.subscribe();
152152

153-
// CMAP spec requires setting this to 50ms.
154-
let mut options = self.pool_options;
155-
options.maintenance_frequency = Some(Duration::from_millis(50));
156-
157153
let (update_sender, mut update_receiver) = ServerUpdateSender::channel();
158154

159155
let pool = ConnectionPool::new(
160156
CLIENT_OPTIONS.hosts[0].clone(),
161157
Default::default(),
162158
update_sender,
163-
Some(options),
159+
Some(self.pool_options),
164160
);
165161

166162
// Mock a monitoring task responding to errors reported by the pool.

src/cmap/worker.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use derivative::Derivative;
22

3+
#[cfg(test)]
4+
use super::options::BackgroundThreadInterval;
35
use super::{
46
conn::PendingConnection,
57
connection_requester,
@@ -177,7 +179,13 @@ impl ConnectionPoolWorker {
177179
#[cfg(test)]
178180
let maintenance_frequency = options
179181
.as_ref()
180-
.and_then(|opts| opts.maintenance_frequency)
182+
.and_then(|opts| opts.background_thread_interval)
183+
.map(|i| match i {
184+
// One year is long enough to count as never for tests, but not so long that it
185+
// will overflow interval math.
186+
BackgroundThreadInterval::Never => Duration::from_secs(31_556_952),
187+
BackgroundThreadInterval::Every(d) => d,
188+
})
181189
.unwrap_or(MAINTENACE_FREQUENCY);
182190

183191
#[cfg(not(test))]

src/test/spec/json/connection-monitoring-and-pooling/README.rst

+14-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,20 @@ Unit Test Format:
3636

3737
All Unit Tests have some of the following fields:
3838

39-
- ``poolOptions``: if present, connection pool options to use when creating a pool
39+
- ``poolOptions``: If present, connection pool options to use when creating a pool;
40+
both `standard ConnectionPoolOptions <https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#connection-pool-options-1>`__
41+
and the following test-specific options are allowed:
42+
43+
- ``backgroundThreadIntervalMS``: A time interval between the end of a
44+
`Background Thread Run <https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#background-thread>`__
45+
and the beginning of the next Run. If a Connection Pool does not implement a Background Thread, the Test Runner MUST ignore the option.
46+
If the option is not specified, an implementation is free to use any value it finds reasonable.
47+
48+
Possible values (0 is not allowed):
49+
50+
- A negative value: never begin a Run.
51+
- A positive value: the interval between Runs in milliseconds.
52+
4053
- ``operations``: A list of operations to perform. All operations support the following fields:
4154

4255
- ``name``: A string describing which operation to issue.
@@ -155,8 +168,6 @@ For each YAML file with ``style: unit``:
155168

156169
- If ``poolOptions`` is specified, use those options to initialize both pools
157170
- The returned pool must have an ``address`` set as a string value.
158-
- If the pool uses a background thread to satisfy ``minPoolSize``, ensure it
159-
attempts to create a new connection every 50ms.
160171

161172
- Process each ``operation`` in ``operations`` (on the main thread)
162173

src/test/spec/json/connection-monitoring-and-pooling/pool-checkout-no-idle.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"style": "unit",
44
"description": "must destroy and must not check out an idle connection if found while iterating available connections",
55
"poolOptions": {
6-
"maxIdleTimeMS": 10
6+
"maxIdleTimeMS": 10,
7+
"backgroundThreadIntervalMS": -1
78
},
89
"operations": [
910
{
@@ -23,6 +24,11 @@
2324
},
2425
{
2526
"name": "checkOut"
27+
},
28+
{
29+
"name": "waitForEvent",
30+
"event": "ConnectionCheckedOut",
31+
"count": 2
2632
}
2733
],
2834
"events": [

src/test/spec/json/connection-monitoring-and-pooling/pool-checkout-no-idle.yml

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ style: unit
33
description: must destroy and must not check out an idle connection if found while iterating available connections
44
poolOptions:
55
maxIdleTimeMS: 10
6+
backgroundThreadIntervalMS: -1
67
operations:
78
- name: ready
89
- name: checkOut
@@ -12,6 +13,9 @@ operations:
1213
- name: wait
1314
ms: 50
1415
- name: checkOut
16+
- name: waitForEvent
17+
event: ConnectionCheckedOut
18+
count: 2
1519
events:
1620
- type: ConnectionPoolCreated
1721
address: 42

src/test/spec/json/connection-monitoring-and-pooling/pool-checkout-no-stale.json

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
"version": 1,
33
"style": "unit",
44
"description": "must destroy and must not check out a stale connection if found while iterating available connections",
5+
"poolOptions": {
6+
"backgroundThreadIntervalMS": -1
7+
},
58
"operations": [
69
{
710
"name": "ready"
@@ -22,6 +25,11 @@
2225
},
2326
{
2427
"name": "checkOut"
28+
},
29+
{
30+
"name": "waitForEvent",
31+
"event": "ConnectionCheckedOut",
32+
"count": 2
2533
}
2634
],
2735
"events": [

src/test/spec/json/connection-monitoring-and-pooling/pool-checkout-no-stale.yml

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
version: 1
22
style: unit
33
description: must destroy and must not check out a stale connection if found while iterating available connections
4+
poolOptions:
5+
backgroundThreadIntervalMS: -1
46
operations:
57
- name: ready
68
- name: checkOut
@@ -10,6 +12,9 @@ operations:
1012
- name: clear
1113
- name: ready
1214
- name: checkOut
15+
- name: waitForEvent
16+
event: ConnectionCheckedOut
17+
count: 2
1318
events:
1419
- type: ConnectionPoolCreated
1520
address: 42

src/test/spec/json/connection-monitoring-and-pooling/pool-clear-min-size.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"style": "unit",
44
"description": "pool clear halts background minPoolSize establishments",
55
"poolOptions": {
6-
"minPoolSize": 1
6+
"minPoolSize": 1,
7+
"backgroundThreadIntervalMS": 50
78
},
89
"operations": [
910
{

src/test/spec/json/connection-monitoring-and-pooling/pool-clear-min-size.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ style: unit
33
description: pool clear halts background minPoolSize establishments
44
poolOptions:
55
minPoolSize: 1
6+
backgroundThreadIntervalMS: 50
67
operations:
78
- name: ready
89
- name: waitForEvent

0 commit comments

Comments
 (0)