use std::time::{Duration, Instant};
use super::settings::{RedisServerConnectionSettings, RedisServerSettings};
use super::states::{
CheckingRedisServerEnvironment, FaultyRedisServer, ReadyRedisServerProcess, ReadyToStart,
RedisServerRunningInAnotherProcess, StartedRedisServerProcess,
};
use crate::error::{EmbeddedRedisClientError, ErrorType};
pub enum EmbeddedRedisServer {
CheckingEnvironment(CheckingRedisServerEnvironment),
RunningInAnotherProcess(RedisServerRunningInAnotherProcess),
ReadyToStart(ReadyToStart),
Started(StartedRedisServerProcess),
Ready(ReadyRedisServerProcess),
Faulty(FaultyRedisServer),
}
impl EmbeddedRedisServer {
pub fn start(
settings: RedisServerSettings,
) -> Result<EmbeddedRedisServer, EmbeddedRedisClientError> {
let ready_to_start: ReadyToStart;
let redis_server = CheckingRedisServerEnvironment::start(
settings.connection.clone(),
settings.startup.startup_timeout,
)?;
match redis_server {
EmbeddedRedisServer::RunningInAnotherProcess(_) => return Ok(redis_server),
EmbeddedRedisServer::ReadyToStart(ready) => {
ready_to_start = ready;
}
_ => {
return Err(EmbeddedRedisClientError::new(
ErrorType::Other,
String::from("Incorrect start-up sequence"),
))
}
}
let started_server =
StartedRedisServerProcess::start(ready_to_start, settings.startup.clone())?;
let ready_server = ReadyRedisServerProcess::new(started_server, settings)?;
Ok(EmbeddedRedisServer::Ready(ready_server))
}
pub fn start_with_retry(
settings: RedisServerSettings,
) -> Result<EmbeddedRedisServer, EmbeddedRedisClientError> {
let mut number_of_startup_attempts = 0;
let mut startup_error = EmbeddedRedisClientError::new(ErrorType::Other, String::from(""));
while number_of_startup_attempts < settings.startup.maximum_startup_retries {
match Self::start(settings.clone()) {
Ok(redis_server) => return Ok(redis_server),
Err(error) => {
number_of_startup_attempts += 1;
startup_error = error;
}
}
}
return Err(EmbeddedRedisClientError::new(
ErrorType::ServerStartup(Box::new(startup_error)),
format! {"Redis server startup failed after {} attempts",number_of_startup_attempts},
));
}
pub fn state(&self) -> String {
match self {
EmbeddedRedisServer::CheckingEnvironment(_) => String::from("CheckingEnvironment"),
EmbeddedRedisServer::RunningInAnotherProcess(_) => {
String::from("RunningInAnotherProcess")
}
EmbeddedRedisServer::ReadyToStart(_) => String::from("ReadyToStart"),
EmbeddedRedisServer::Started(_) => String::from("Started"),
EmbeddedRedisServer::Ready(_) => String::from("Ready"),
EmbeddedRedisServer::Faulty(_) => String::from("Faulty"),
}
}
}
pub mod connection {
use redis;
use super::*;
pub fn get_redis_server_connection(
connection_settings: RedisServerConnectionSettings,
) -> Result<redis::Connection, EmbeddedRedisClientError> {
let client = redis::Client::open(connection_settings)?;
let connection = client.get_connection()?;
Ok(connection)
}
pub async fn get_multiplexed_redis_server_connection(
connection_settings: RedisServerConnectionSettings,
) -> Result<redis::aio::MultiplexedConnection, EmbeddedRedisClientError> {
let client = redis::Client::open(connection_settings).unwrap();
let connection: redis::aio::MultiplexedConnection =
client.get_multiplexed_async_std_connection().await?;
Ok(connection)
}
pub fn try_to_get_redis_server_connection_until_timeout(
connection_settings: RedisServerConnectionSettings,
timeout: Duration,
) -> Result<redis::Connection, EmbeddedRedisClientError> {
let time_start_polling = Instant::now();
let client = redis::Client::open(connection_settings)?;
while Instant::now() - time_start_polling < timeout {
match client.get_connection() {
Ok(server_connection) => return Ok(server_connection),
Err(_) => continue,
}
}
Err(EmbeddedRedisClientError::new(
ErrorType::ServerTimeout,
format!("Unable to connect to redis server within {:#?}", timeout),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::redis_server_settings_for_testing;
#[test]
fn start_redis_server() {
let _redis_server = EmbeddedRedisServer::start(redis_server_settings_for_testing()).unwrap();
}
}