use std::{
fmt,
sync::{
Arc, Mutex,
atomic::{AtomicU64, Ordering},
},
time::Duration,
};
use tokio::{sync::mpsc::UnboundedSender, time::Instant};
use uuid::Uuid;
use crate::{
Credentials, DriverOptions, TransactionOptions, TransactionType,
common::address::Address,
connection::{
TransactionStream,
database::{export_stream::DatabaseExportStream, import_stream::DatabaseImportStream},
message::{DatabaseImportRequest, Request, Response, TransactionRequest},
network::transmitter::{
DatabaseExportTransmitter, DatabaseImportTransmitter, RPCTransmitter, TransactionTransmitter,
},
runtime::BackgroundRuntime,
},
error::{ConnectionError, InternalError},
info::{DatabaseInfo, UserInfo},
};
#[derive(Clone)]
pub(crate) struct ServerConnection {
background_runtime: Arc<BackgroundRuntime>,
username: String,
connection_id: Uuid,
request_transmitter: Arc<RPCTransmitter>,
shutdown_senders: Arc<Mutex<Vec<UnboundedSender<()>>>>,
latency_tracker: LatencyTracker,
}
impl ServerConnection {
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn new(
background_runtime: Arc<BackgroundRuntime>,
address: Address,
credentials: Credentials,
driver_options: DriverOptions,
driver_lang: &str,
driver_version: &str,
) -> crate::Result<(Self, Vec<DatabaseInfo>)> {
Self::validate_tls(&address, &driver_options)?;
let username = credentials.username().to_string();
let request_transmitter =
Arc::new(RPCTransmitter::start(address, credentials.clone(), driver_options, &background_runtime)?);
let (connection_id, latency, database_info) =
Self::open_connection(&request_transmitter, driver_lang, driver_version, credentials).await?;
let latency_tracker = LatencyTracker::new(latency);
let server_connection = Self {
background_runtime,
username,
connection_id,
request_transmitter,
shutdown_senders: Default::default(),
latency_tracker,
};
Ok((server_connection, database_info))
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
async fn open_connection(
request_transmitter: &RPCTransmitter,
driver_lang: &str,
driver_version: &str,
credentials: Credentials,
) -> crate::Result<(Uuid, Duration, Vec<DatabaseInfo>)> {
let message = Request::ConnectionOpen {
driver_lang: driver_lang.to_owned(),
driver_version: driver_version.to_owned(),
credentials,
};
let request_time = Instant::now();
match request_transmitter.request(message).await? {
Response::ConnectionOpen { connection_id, server_duration_millis, databases: database_info } => {
let latency =
Instant::now().duration_since(request_time) - Duration::from_millis(server_duration_millis);
Ok((connection_id, latency, database_info))
}
other => Err(ConnectionError::UnexpectedResponse { response: format!("{other:?}") }.into()),
}
}
pub fn username(&self) -> &str {
self.username.as_str()
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
async fn request(&self, request: Request) -> crate::Result<Response> {
if !self.background_runtime.is_open() {
return Err(ConnectionError::ServerConnectionIsClosed.into());
}
self.request_transmitter.request(request).await
}
fn request_blocking(&self, request: Request) -> crate::Result<Response> {
if !self.background_runtime.is_open() {
return Err(ConnectionError::ServerConnectionIsClosed.into());
}
self.request_transmitter.request_blocking(request)
}
pub(crate) fn force_close(&self) -> crate::Result {
for sender in self.shutdown_senders.lock().unwrap().drain(..) {
let _ = sender.send(());
}
self.request_transmitter.force_close()
}
pub(crate) fn servers_all(&self) -> crate::Result<Vec<Address>> {
match self.request_blocking(Request::ServersAll)? {
Response::ServersAll { servers } => Ok(servers),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn all_databases(&self) -> crate::Result<Vec<DatabaseInfo>> {
match self.request(Request::DatabasesAll).await? {
Response::DatabasesAll { databases } => Ok(databases),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn get_database_replicas(&self, database_name: String) -> crate::Result<DatabaseInfo> {
match self.request(Request::DatabaseGet { database_name }).await? {
Response::DatabaseGet { database } => Ok(database),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn contains_database(&self, database_name: String) -> crate::Result<bool> {
match self.request(Request::DatabasesContains { database_name }).await? {
Response::DatabasesContains { contains } => Ok(contains),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn create_database(&self, database_name: String) -> crate::Result<DatabaseInfo> {
match self.request(Request::DatabaseCreate { database_name }).await? {
Response::DatabaseCreate { database } => Ok(database),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn import_database(
&self,
database_name: String,
schema: String,
) -> crate::Result<DatabaseImportStream> {
match self
.request(Request::DatabaseImport(DatabaseImportRequest::Initial { name: database_name, schema }))
.await?
{
Response::DatabaseImport { request_sink, response_source } => {
let transmitter =
DatabaseImportTransmitter::new(self.background_runtime.clone(), request_sink, response_source);
let transmitter_shutdown_sink = transmitter.shutdown_sink().clone();
let import_stream = DatabaseImportStream::new(transmitter);
self.shutdown_senders.lock().unwrap().push(transmitter_shutdown_sink);
Ok(import_stream)
}
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn delete_database(&self, database_name: String) -> crate::Result {
self.request(Request::DatabaseDelete { database_name }).await?;
Ok(())
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn database_schema(&self, database_name: String) -> crate::Result<String> {
match self.request(Request::DatabaseSchema { database_name }).await? {
Response::DatabaseSchema { schema } => Ok(schema),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn database_type_schema(&self, database_name: String) -> crate::Result<String> {
match self.request(Request::DatabaseTypeSchema { database_name }).await? {
Response::DatabaseTypeSchema { schema } => Ok(schema),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn database_export(&self, database_name: String) -> crate::Result<DatabaseExportStream> {
match self.request(Request::DatabaseExport { database_name }).await? {
Response::DatabaseExportStream { response_source } => {
let transmitter = DatabaseExportTransmitter::new(self.background_runtime.clone(), response_source);
let transmitter_shutdown_sink = transmitter.shutdown_sink().clone();
let export_stream = DatabaseExportStream::new(transmitter);
self.shutdown_senders.lock().unwrap().push(transmitter_shutdown_sink);
Ok(export_stream)
}
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn open_transaction(
&self,
database_name: &str,
transaction_type: TransactionType,
options: TransactionOptions,
) -> crate::Result<TransactionStream> {
let network_latency = self.latency_tracker.current_latency();
let open_request_start = Instant::now();
match self
.request(Request::Transaction(TransactionRequest::Open {
database: database_name.to_owned(),
transaction_type,
options,
network_latency,
}))
.await?
{
Response::TransactionStream {
open_request_id: _,
request_sink,
response_source,
server_duration_millis,
} => {
let open_latency =
Instant::now().duration_since(open_request_start).as_millis() as u64 - server_duration_millis;
self.latency_tracker.update_latency(open_latency);
let transmitter =
TransactionTransmitter::new(self.background_runtime.clone(), request_sink, response_source);
let transmitter_shutdown_sink = transmitter.shutdown_sink().clone();
let transaction_stream = TransactionStream::new(transaction_type, options, transmitter);
self.shutdown_senders.lock().unwrap().push(transmitter_shutdown_sink);
Ok(transaction_stream)
}
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn all_users(&self) -> crate::Result<Vec<UserInfo>> {
match self.request(Request::UsersAll).await? {
Response::UsersAll { users } => Ok(users),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn get_user(&self, name: String) -> crate::Result<Option<UserInfo>> {
match self.request(Request::UsersGet { name }).await? {
Response::UsersGet { user } => Ok(user),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn contains_user(&self, name: String) -> crate::Result<bool> {
match self.request(Request::UsersContains { name }).await? {
Response::UsersContain { contains } => Ok(contains),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn create_user(&self, name: String, password: String) -> crate::Result {
match self.request(Request::UsersCreate { user: UserInfo { name, password: Some(password) } }).await? {
Response::UsersCreate => Ok(()),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn update_password(&self, name: String, password: String) -> crate::Result {
match self
.request(Request::UsersUpdate {
username: name,
user: UserInfo { name: "".to_string(), password: Some(password) }, })
.await?
{
Response::UsersUpdate => Ok(()),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
#[cfg_attr(feature = "sync", maybe_async::must_be_sync)]
pub(crate) async fn delete_user(&self, name: String) -> crate::Result {
match self.request(Request::UsersDelete { name }).await? {
Response::UsersDelete => Ok(()),
other => Err(InternalError::UnexpectedResponseType { response_type: format!("{other:?}") }.into()),
}
}
fn validate_tls(address: &Address, driver_options: &DriverOptions) -> crate::Result {
match driver_options.is_tls_enabled() {
true => {
if driver_options.tls_config().is_none() {
return Err(ConnectionError::AbsentTlsConfigForTlsConnection {}.into());
}
if !address.is_https() {
return Err(ConnectionError::TlsConnectionWithoutHttps {}.into());
}
}
false => {
if address.is_https() {
return Err(ConnectionError::NonTlsConnectionWithHttps {}.into());
}
}
}
Ok(())
}
}
impl fmt::Debug for ServerConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ServerConnection").field("connection_id", &self.connection_id).finish()
}
}
#[derive(Debug, Clone)]
pub(crate) struct LatencyTracker {
latency_millis: Arc<AtomicU64>,
}
impl LatencyTracker {
fn new(initial_latency: Duration) -> Self {
Self { latency_millis: Arc::new(AtomicU64::new(initial_latency.as_millis() as u64)) }
}
pub(crate) fn update_latency(&self, latency_millis: u64) {
let previous_latency = self.latency_millis.load(Ordering::Relaxed);
self.latency_millis.store((latency_millis + previous_latency) / 2, Ordering::Relaxed);
}
fn current_latency(&self) -> Duration {
Duration::from_millis(self.latency_millis.load(Ordering::Relaxed))
}
}