mod command;
pub(crate) mod pooled;
mod stream_description;
pub(crate) mod wire;
use std::{sync::Arc, time::Instant};
use derive_where::derive_where;
use serde::Serialize;
use tokio::{
io::BufStream,
sync::{
broadcast::{self, error::RecvError},
mpsc,
Mutex,
},
};
use self::wire::{Message, MessageFlags};
use super::{conn::pooled::PooledConnection, manager::PoolManager};
use crate::{
bson::oid::ObjectId,
cmap::PoolGeneration,
error::{load_balanced_mode_mismatch, Error, ErrorKind, Redact, Result},
event::cmap::{CmapEventEmitter, ConnectionCreatedEvent},
options::ServerAddress,
runtime::AsyncStream,
};
pub(crate) use command::{Command, RawCommandResponse, WriteErrorBody};
pub(crate) use stream_description::StreamDescription;
#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
feature = "snappy-compression"
))]
use crate::options::Compressor;
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ConnectionInfo {
pub id: u32,
pub server_id: Option<i64>,
pub address: ServerAddress,
}
#[derive_where(Debug)]
pub(crate) struct Connection {
stream: BufStream<AsyncStream>,
pub(crate) stream_description: Option<StreamDescription>,
pub(crate) id: u32,
pub(crate) server_id: Option<i64>,
pub(crate) address: ServerAddress,
pub(crate) time_created: Instant,
command_executing: bool,
error: Option<Error>,
more_to_come: bool,
#[derive_where(skip)]
pub(crate) oidc_token_gen_id: tokio::sync::Mutex<u32>,
#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
feature = "snappy-compression"
))]
pub(crate) compressor: Option<Compressor>,
}
impl Connection {
pub(crate) fn new(
address: ServerAddress,
stream: AsyncStream,
id: u32,
time_created: Instant,
) -> Self {
Self {
stream: BufStream::new(stream),
stream_description: None,
address,
id,
server_id: None,
time_created,
command_executing: false,
error: None,
more_to_come: false,
oidc_token_gen_id: tokio::sync::Mutex::new(0),
#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
feature = "snappy-compression"
))]
compressor: None,
}
}
pub(crate) fn take(&mut self) -> Self {
Self {
stream: std::mem::replace(&mut self.stream, BufStream::new(AsyncStream::Null)),
stream_description: self.stream_description.take(),
address: self.address.clone(),
id: self.id,
server_id: self.server_id,
time_created: self.time_created,
command_executing: self.command_executing,
error: self.error.take(),
more_to_come: false,
oidc_token_gen_id: tokio::sync::Mutex::new(0),
#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
feature = "snappy-compression"
))]
compressor: self.compressor.clone(),
}
}
pub(crate) fn address(&self) -> &ServerAddress {
&self.address
}
pub(crate) fn stream_description(&self) -> Result<&StreamDescription> {
self.stream_description.as_ref().ok_or_else(|| {
ErrorKind::Internal {
message: "Stream checked out but not handshaked".to_string(),
}
.into()
})
}
pub(super) fn is_executing(&self) -> bool {
self.command_executing
}
pub(super) fn has_errored(&self) -> bool {
self.error.is_some()
}
pub(crate) async fn send_message_with_cancellation(
&mut self,
message: impl TryInto<Message, Error = impl Into<Error>>,
cancellation_receiver: &mut broadcast::Receiver<()>,
) -> Result<RawCommandResponse> {
tokio::select! {
biased;
Ok(_) | Err(RecvError::Lagged(_)) = cancellation_receiver.recv() => {
let error: Error = ErrorKind::ConnectionPoolCleared {
message: format!(
"Connection to {} interrupted due to server monitor timeout",
Redact(&self.address),
)
}.into();
self.error = Some(error.clone());
Err(error)
}
result = self.send_message(message) => result,
}
}
pub(crate) async fn send_message(
&mut self,
message: impl TryInto<Message, Error = impl Into<Error>>,
) -> Result<RawCommandResponse> {
let message = message.try_into().map_err(Into::into)?;
if self.more_to_come {
return Err(Error::internal(format!(
"attempted to send a new message to {} but moreToCome bit was set",
Redact(self.address())
)));
}
self.command_executing = true;
let max_message_size = self.max_message_size_bytes();
#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
feature = "snappy-compression"
))]
let write_result = match self.compressor {
Some(ref compressor) if message.should_compress => {
message
.write_op_compressed_to(&mut self.stream, compressor, max_message_size)
.await
}
_ => {
message
.write_op_msg_to(&mut self.stream, max_message_size)
.await
}
};
#[cfg(all(
not(feature = "zstd-compression"),
not(feature = "zlib-compression"),
not(feature = "snappy-compression")
))]
let write_result = message
.write_op_msg_to(&mut self.stream, max_message_size)
.await;
if let Err(ref err) = write_result {
self.error = Some(err.clone());
}
write_result?;
let response_message_result = Message::read_from(&mut self.stream, max_message_size).await;
self.command_executing = false;
if let Err(ref err) = response_message_result {
self.error = Some(err.clone());
}
let response_message = response_message_result?;
self.more_to_come = response_message.flags.contains(MessageFlags::MORE_TO_COME);
Ok(RawCommandResponse::new(
self.address.clone(),
response_message,
))
}
pub(crate) async fn receive_message(&mut self) -> Result<RawCommandResponse> {
if !self.more_to_come {
return Err(Error::internal(format!(
"attempted to stream response from connection to {} but moreToCome bit was not set",
Redact(self.address())
)));
}
self.command_executing = true;
let response_message_result = Message::read_from(
&mut self.stream,
self.stream_description
.as_ref()
.map(|d| d.max_message_size_bytes),
)
.await;
self.command_executing = false;
if let Err(ref err) = response_message_result {
self.error = Some(err.clone());
}
let response_message = response_message_result?;
self.more_to_come = response_message.flags.contains(MessageFlags::MORE_TO_COME);
Ok(RawCommandResponse::new(
self.address.clone(),
response_message,
))
}
pub(crate) fn is_streaming(&self) -> bool {
self.more_to_come
}
fn max_message_size_bytes(&self) -> Option<i32> {
self.stream_description
.as_ref()
.map(|d| d.max_message_size_bytes)
}
}
#[derive(Debug)]
pub(crate) struct PinnedConnectionHandle {
id: u32,
receiver: Arc<Mutex<mpsc::Receiver<PooledConnection>>>,
}
impl PinnedConnectionHandle {
pub(crate) fn replicate(&self) -> Self {
Self {
id: self.id,
receiver: self.receiver.clone(),
}
}
pub(crate) async fn take_connection(&self) -> Result<PooledConnection> {
use tokio::sync::mpsc::error::TryRecvError;
let mut receiver = self.receiver.lock().await;
let mut connection = match receiver.try_recv() {
Ok(conn) => conn,
Err(TryRecvError::Disconnected) => {
return Err(Error::internal(format!(
"cannot take connection after unpin (id={})",
self.id
)))
}
Err(TryRecvError::Empty) => {
return Err(Error::internal(format!(
"cannot take in-use connection (id={})",
self.id
)))
}
};
connection.mark_pinned_in_use();
Ok(connection)
}
pub(crate) fn id(&self) -> u32 {
self.id
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct LoadBalancedGeneration {
pub(crate) generation: u32,
pub(crate) service_id: ObjectId,
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum ConnectionGeneration {
Normal(u32),
LoadBalanced(Option<LoadBalancedGeneration>),
}
impl ConnectionGeneration {
pub(crate) fn service_id(self) -> Option<ObjectId> {
match self {
ConnectionGeneration::LoadBalanced(Some(gen)) => Some(gen.service_id),
_ => None,
}
}
pub(crate) fn is_stale(self, current_generation: &PoolGeneration) -> bool {
match (self, current_generation) {
(ConnectionGeneration::Normal(cgen), PoolGeneration::Normal(pgen)) => cgen != *pgen,
(ConnectionGeneration::LoadBalanced(cgen), PoolGeneration::LoadBalanced(gen_map)) => {
if let Some(cgen) = cgen {
cgen.generation != *gen_map.get(&cgen.service_id).unwrap_or(&0)
} else {
false
}
}
_ => load_balanced_mode_mismatch!(false),
}
}
}
impl From<LoadBalancedGeneration> for ConnectionGeneration {
fn from(gen: LoadBalancedGeneration) -> Self {
ConnectionGeneration::LoadBalanced(Some(gen))
}
}
pub(crate) struct PendingConnection {
pub(crate) id: u32,
pub(crate) address: ServerAddress,
pub(crate) generation: PoolGeneration,
pub(crate) event_emitter: CmapEventEmitter,
pub(crate) time_created: Instant,
pub(crate) cancellation_receiver: Option<broadcast::Receiver<()>>,
}
impl PendingConnection {
pub(super) fn created_event(&self) -> ConnectionCreatedEvent {
ConnectionCreatedEvent {
address: self.address.clone(),
connection_id: self.id,
}
}
}