mongodb/event/cmap.rs
1//! Contains the events and functionality for monitoring behavior of the connection pooling of a
2//! `Client`.
3
4use std::time::Duration;
5
6use derive_more::From;
7#[cfg(feature = "tracing-unstable")]
8use derive_where::derive_where;
9use serde::{Deserialize, Serialize};
10
11use crate::{bson::oid::ObjectId, options::ServerAddress, serde_util};
12
13#[cfg(feature = "tracing-unstable")]
14use crate::trace::{
15 connection::ConnectionTracingEventEmitter,
16 trace_or_log_enabled,
17 TracingOrLogLevel,
18 CONNECTION_TRACING_EVENT_TARGET,
19};
20
21use super::EventHandler;
22
23/// We implement `Deserialize` for all of the event types so that we can more easily parse the CMAP
24/// spec tests. However, we have no need to parse the address field from the JSON files (if it's
25/// even present). To facilitate populating the address field with an empty value when
26/// deserializing, we define a private `empty_address` function that the events can specify as the
27/// custom deserialization value for each address field.
28fn empty_address() -> ServerAddress {
29 ServerAddress::Tcp {
30 host: Default::default(),
31 port: None,
32 }
33}
34
35/// Event emitted when a connection pool is created.
36#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
37#[non_exhaustive]
38pub struct PoolCreatedEvent {
39 /// The address of the server that the pool's connections will connect to.
40 #[serde(default = "self::empty_address")]
41 #[serde(skip_deserializing)]
42 pub address: ServerAddress,
43
44 /// The options used for the pool.
45 pub options: Option<ConnectionPoolOptions>,
46}
47
48/// Contains the options for creating a connection pool. While these options are specified at the
49/// client-level, `ConnectionPoolOptions` is exposed for the purpose of CMAP event handling.
50#[derive(Clone, Default, Deserialize, Debug, PartialEq, Serialize)]
51#[serde(rename_all = "camelCase")]
52#[non_exhaustive]
53pub struct ConnectionPoolOptions {
54 /// Connections that have been ready for usage in the pool for longer than `max_idle_time` will
55 /// not be used.
56 ///
57 /// The default is that connections will not be closed due to being idle.
58 #[serde(rename = "maxIdleTimeMS")]
59 #[serde(default)]
60 #[serde(deserialize_with = "serde_util::deserialize_duration_option_from_u64_millis")]
61 pub max_idle_time: Option<Duration>,
62
63 /// The maximum number of connections that the pool can have at a given time. This includes
64 /// connections which are currently checked out of the pool.
65 ///
66 /// The default is 10.
67 pub max_pool_size: Option<u32>,
68
69 /// The minimum number of connections that the pool can have at a given time. This includes
70 /// connections which are currently checked out of the pool. If fewer than `min_pool_size`
71 /// connections are in the pool, connections will be added to the pool in the background.
72 ///
73 /// The default is that no minimum is enforced
74 pub min_pool_size: Option<u32>,
75}
76
77/// Event emitted when a connection pool becomes ready.
78#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
79#[non_exhaustive]
80pub struct PoolReadyEvent {
81 /// The address of the server that the pool's connections will connect to.
82 #[serde(default = "self::empty_address")]
83 #[serde(skip_deserializing)]
84 pub address: ServerAddress,
85}
86
87/// Event emitted when a connection pool is cleared.
88#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
89#[non_exhaustive]
90pub struct PoolClearedEvent {
91 /// The address of the server that the pool's connections will connect to.
92 #[serde(default = "self::empty_address")]
93 #[serde(skip_deserializing)]
94 pub address: ServerAddress,
95
96 /// If the connection is to a load balancer, the id of the selected backend.
97 pub service_id: Option<ObjectId>,
98
99 /// Whether in-use connections were interrupted when the pool cleared.
100 #[serde(default)]
101 pub interrupt_in_use_connections: bool,
102}
103
104/// Event emitted when a connection pool is cleared.
105#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
106#[non_exhaustive]
107pub struct PoolClosedEvent {
108 /// The address of the server that the pool's connections will connect to.
109 #[serde(default = "self::empty_address")]
110 #[serde(skip_deserializing)]
111 pub address: ServerAddress,
112}
113
114/// Event emitted when a connection is created.
115#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
116#[serde(rename_all = "camelCase")]
117#[non_exhaustive]
118pub struct ConnectionCreatedEvent {
119 /// The address of the server that the connection will connect to.
120 #[serde(default = "self::empty_address")]
121 #[serde(skip_deserializing)]
122 pub address: ServerAddress,
123
124 /// The unique ID of the connection. This is not used for anything internally, but can be used
125 /// to identify other events related to this connection.
126 #[serde(default = "default_connection_id")]
127 pub connection_id: u32,
128}
129
130/// Event emitted when a connection is ready to be used. This indicates that all the necessary
131/// prerequisites for using a connection (handshake, authentication, etc.) have been completed.
132#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
133#[serde(rename_all = "camelCase")]
134#[non_exhaustive]
135pub struct ConnectionReadyEvent {
136 /// The address of the server that the connection is connected to.
137 #[serde(default = "self::empty_address")]
138 #[serde(skip_deserializing)]
139 pub address: ServerAddress,
140
141 /// The unique ID of the connection. This is not used for anything internally, but can be used
142 /// to identify other events related to this connection.
143 #[serde(default = "default_connection_id")]
144 pub connection_id: u32,
145
146 /// The time it took to establish the connection.
147 #[serde(skip_deserializing)]
148 pub duration: Duration,
149}
150
151/// Event emitted when a connection is closed.
152#[derive(Clone, Debug, Deserialize, Serialize)]
153#[cfg_attr(feature = "tracing-unstable", derive_where(PartialEq))]
154#[cfg_attr(not(feature = "tracing-unstable"), derive(PartialEq))]
155#[serde(rename_all = "camelCase")]
156#[non_exhaustive]
157pub struct ConnectionClosedEvent {
158 /// The address of the server that the connection was connected to.
159 #[serde(default = "self::empty_address")]
160 #[serde(skip_deserializing)]
161 pub address: ServerAddress,
162
163 /// The unique ID of the connection. This is not used for anything internally, but can be used
164 /// to identify other events related to this connection.
165 #[serde(default)]
166 pub connection_id: u32,
167
168 /// The reason that the connection was closed.
169 #[cfg_attr(test, serde(default = "unset_connection_closed_reason"))]
170 pub reason: ConnectionClosedReason,
171
172 /// If the `reason` connection checkout failed was `Error`,the associated
173 /// error is contained here. This is attached so we can include it in log messages;
174 /// in future work we may add this to public API on the event itself. TODO: DRIVERS-2495
175 #[cfg(feature = "tracing-unstable")]
176 #[serde(skip)]
177 #[derive_where(skip)]
178 pub(crate) error: Option<crate::error::Error>,
179
180 pub(crate) service_id: Option<ObjectId>,
181}
182
183/// The reasons that a connection may be closed.
184#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
185#[serde(rename_all = "camelCase")]
186#[non_exhaustive]
187pub enum ConnectionClosedReason {
188 /// The connection pool has been cleared since the connection was created.
189 Stale,
190
191 /// The connection has been available for longer than `max_idle_time` without being used.
192 Idle,
193
194 /// An error occurred while using the connection.
195 Error,
196
197 /// The connection was dropped during read or write.
198 Dropped,
199
200 /// The pool that the connection belongs to has been closed.
201 PoolClosed,
202
203 #[cfg(test)]
204 /// The value was not set in the test file.
205 Unset,
206}
207
208#[cfg(test)]
209fn unset_connection_closed_reason() -> ConnectionClosedReason {
210 ConnectionClosedReason::Unset
211}
212
213/// Event emitted when a thread begins checking out a connection to use for an operation.
214#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
215#[non_exhaustive]
216pub struct ConnectionCheckoutStartedEvent {
217 /// The address of the server that the connection will connect to.
218 #[serde(default = "self::empty_address")]
219 #[serde(skip_deserializing)]
220 pub address: ServerAddress,
221}
222
223/// Event emitted when a thread is unable to check out a connection.
224#[derive(Clone, Debug, Deserialize, Serialize)]
225#[cfg_attr(feature = "tracing-unstable", derive_where(PartialEq))]
226#[cfg_attr(not(feature = "tracing-unstable"), derive(PartialEq))]
227#[non_exhaustive]
228pub struct ConnectionCheckoutFailedEvent {
229 /// The address of the server that the connection would have connected to.
230 #[serde(default = "self::empty_address")]
231 #[serde(skip_deserializing)]
232 pub address: ServerAddress,
233
234 /// The reason a connection was unable to be checked out.
235 #[cfg_attr(test, serde(default = "unset_connection_checkout_failed_reason"))]
236 pub reason: ConnectionCheckoutFailedReason,
237
238 /// If the `reason` connection checkout failed was `ConnectionError`,the associated
239 /// error is contained here. This is attached so we can include it in log messages;
240 /// in future work we may add this to public API on the event itself. TODO: DRIVERS-2495
241 #[cfg(feature = "tracing-unstable")]
242 #[serde(skip)]
243 #[derive_where(skip)]
244 pub(crate) error: Option<crate::error::Error>,
245
246 /// See [ConnectionCheckedOutEvent::duration].
247 #[serde(skip_deserializing)]
248 pub duration: Duration,
249}
250
251/// The reasons a connection may not be able to be checked out.
252#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
253#[serde(rename_all = "camelCase")]
254#[non_exhaustive]
255pub enum ConnectionCheckoutFailedReason {
256 /// The `wait_queue_timeout` has elapsed while waiting for a connection to be available.
257 Timeout,
258
259 /// An error occurred while trying to establish a connection (e.g. during the handshake or
260 /// authentication).
261 ConnectionError,
262
263 #[cfg(test)]
264 /// The value was not set in the test file.
265 Unset,
266}
267
268#[cfg(test)]
269fn unset_connection_checkout_failed_reason() -> ConnectionCheckoutFailedReason {
270 ConnectionCheckoutFailedReason::Unset
271}
272
273/// Event emitted when a connection is successfully checked out.
274#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
275#[serde(rename_all = "camelCase")]
276#[non_exhaustive]
277pub struct ConnectionCheckedOutEvent {
278 /// The address of the server that the connection will connect to.
279 #[serde(default = "self::empty_address")]
280 #[serde(skip_deserializing)]
281 pub address: ServerAddress,
282
283 /// The unique ID of the connection. This is not used for anything internally, but can be used
284 /// to identify other events related to this connection.
285 #[serde(default = "default_connection_id")]
286 pub connection_id: u32,
287
288 /// The time it took to check out the connection.
289 #[serde(skip_deserializing)]
290 pub duration: Duration,
291}
292
293/// Event emitted when a connection is checked back into a connection pool.
294#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
295#[serde(rename_all = "camelCase")]
296#[non_exhaustive]
297pub struct ConnectionCheckedInEvent {
298 /// The address of the server that the connection was connected to.
299 #[serde(default = "self::empty_address")]
300 #[serde(skip_deserializing)]
301 pub address: ServerAddress,
302
303 /// The unique ID of the connection. This is not used for anything internally, but can be used
304 /// to identify other events related to this connection.
305 #[serde(default = "default_connection_id")]
306 pub connection_id: u32,
307}
308
309/// The default connection ID to use for deserialization of events from test files.
310/// This value will "match" any connection ID.
311fn default_connection_id() -> u32 {
312 42
313}
314
315/// Usage of this trait is deprecated. Applications should use the [`EventHandler`] API.
316///
317/// Applications can implement this trait to specify custom logic to run on each CMAP event sent
318/// by the driver.
319///
320/// ```rust
321/// # #![allow(deprecated)]
322/// # use std::sync::Arc;
323/// #
324/// # use mongodb::{
325/// # error::Result,
326/// # event::cmap::{
327/// # CmapEventHandler,
328/// # ConnectionCheckoutFailedEvent
329/// # },
330/// # options::ClientOptions,
331/// # };
332/// # #[cfg(feature = "sync")]
333/// # use mongodb::sync::Client;
334/// # #[cfg(not(feature = "sync"))]
335/// # use mongodb::Client;
336/// #
337/// struct FailedCheckoutLogger;
338///
339/// impl CmapEventHandler for FailedCheckoutLogger {
340/// fn handle_connection_checkout_failed_event(&self, event: ConnectionCheckoutFailedEvent) {
341/// eprintln!("Failed connection checkout: {:?}", event);
342/// }
343/// }
344///
345/// # fn do_stuff() -> Result<()> {
346/// let handler = Arc::new(FailedCheckoutLogger);
347/// let options = ClientOptions::builder()
348/// .cmap_event_handler(handler)
349/// .build();
350/// let client = Client::with_options(options)?;
351///
352/// // Do things with the client, and failed connection pool checkouts will be logged to stderr.
353/// # Ok(())
354/// # }
355/// ```
356#[deprecated = "use the EventHandler API"]
357pub trait CmapEventHandler: Send + Sync {
358 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
359 /// whenever a connection pool is created.
360 fn handle_pool_created_event(&self, _event: PoolCreatedEvent) {}
361
362 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
363 /// whenever a connection pool marked as ready for use.
364 ///
365 /// Connections may not be created by or checked out from the pool until it has been marked as
366 /// ready.
367 fn handle_pool_ready_event(&self, _event: PoolReadyEvent) {}
368
369 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
370 /// whenever a connection pool is cleared.
371 fn handle_pool_cleared_event(&self, _event: PoolClearedEvent) {}
372
373 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
374 /// whenever a connection pool is cleared.
375 fn handle_pool_closed_event(&self, _event: PoolClosedEvent) {}
376
377 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
378 /// whenever a connection is created.
379 fn handle_connection_created_event(&self, _event: ConnectionCreatedEvent) {}
380
381 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
382 /// whenever a connection is ready to be used.
383 fn handle_connection_ready_event(&self, _event: ConnectionReadyEvent) {}
384
385 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
386 /// whenever a connection is closed.
387 fn handle_connection_closed_event(&self, _event: ConnectionClosedEvent) {}
388
389 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
390 /// whenever a thread begins checking out a connection to use for an operation.
391 fn handle_connection_checkout_started_event(&self, _event: ConnectionCheckoutStartedEvent) {}
392
393 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
394 /// whenever a thread is unable to check out a connection.
395 fn handle_connection_checkout_failed_event(&self, _event: ConnectionCheckoutFailedEvent) {}
396
397 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
398 /// whenever a connection is successfully checked out.
399 fn handle_connection_checked_out_event(&self, _event: ConnectionCheckedOutEvent) {}
400
401 /// A [`Client`](../../struct.Client.html) will call this method on each registered handler
402 /// whenever a connection is checked back into a connection pool.
403 fn handle_connection_checked_in_event(&self, _event: ConnectionCheckedInEvent) {}
404}
405
406#[derive(Clone, Debug, PartialEq, From)]
407#[non_exhaustive]
408#[allow(missing_docs)]
409pub enum CmapEvent {
410 PoolCreated(PoolCreatedEvent),
411 PoolReady(PoolReadyEvent),
412 PoolCleared(PoolClearedEvent),
413 PoolClosed(PoolClosedEvent),
414 ConnectionCreated(ConnectionCreatedEvent),
415 ConnectionReady(ConnectionReadyEvent),
416 ConnectionClosed(ConnectionClosedEvent),
417 ConnectionCheckoutStarted(ConnectionCheckoutStartedEvent),
418 ConnectionCheckoutFailed(ConnectionCheckoutFailedEvent),
419 ConnectionCheckedOut(ConnectionCheckedOutEvent),
420 ConnectionCheckedIn(ConnectionCheckedInEvent),
421}
422
423#[derive(Clone)]
424pub(crate) struct CmapEventEmitter {
425 user_handler: Option<EventHandler<CmapEvent>>,
426
427 #[cfg(feature = "tracing-unstable")]
428 tracing_emitter: ConnectionTracingEventEmitter,
429}
430
431impl CmapEventEmitter {
432 pub(crate) fn new(
433 user_handler: Option<EventHandler<CmapEvent>>,
434 #[cfg(feature = "tracing-unstable")] topology_id: ObjectId,
435 #[cfg(feature = "tracing-unstable")] max_document_length_bytes: Option<usize>,
436 ) -> CmapEventEmitter {
437 Self {
438 user_handler,
439 #[cfg(feature = "tracing-unstable")]
440 tracing_emitter: ConnectionTracingEventEmitter::new(
441 topology_id,
442 max_document_length_bytes,
443 ),
444 }
445 }
446
447 #[cfg(not(feature = "tracing-unstable"))]
448 pub(crate) fn emit_event(&self, generate_event: impl FnOnce() -> CmapEvent) {
449 if let Some(ref handler) = self.user_handler {
450 handler.handle(generate_event());
451 }
452 }
453
454 #[cfg(feature = "tracing-unstable")]
455 pub(crate) fn emit_event(&self, generate_event: impl FnOnce() -> CmapEvent) {
456 // if the user isn't actually interested in debug-level connection messages, we shouldn't
457 // bother with the expense of generating and emitting these events.
458 let tracing_emitter_to_use = if trace_or_log_enabled!(
459 target: CONNECTION_TRACING_EVENT_TARGET,
460 TracingOrLogLevel::Debug
461 ) {
462 Some(&self.tracing_emitter)
463 } else {
464 None
465 };
466
467 match (&self.user_handler, tracing_emitter_to_use) {
468 (None, None) => {}
469 (None, Some(tracing_emitter)) => {
470 let event = generate_event();
471 tracing_emitter.handle(event);
472 }
473 (Some(user_handler), None) => {
474 let event = generate_event();
475 user_handler.handle(event);
476 }
477 (Some(user_handler), Some(tracing_emitter)) => {
478 let event = generate_event();
479 user_handler.handle(event.clone());
480 tracing_emitter.handle(event);
481 }
482 };
483 }
484}