Skip to main content

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}