use crate::tina::core::service::cache::ICacheService;
use crate::tina::data::json::ToJson;
use crate::tina::data::AppResult;
use crate::tina::redis::{IRedisClient, RedisConnection};
use crate::tina::server::application::Application;
use crate::tina::util::json::JsonUtil;
use crate::{app_error_from, app_system_error};
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use redis::{AsyncCommands, FromRedisValue, Pipeline, Value};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::any::type_name;
use std::collections::HashSet;
use std::fmt::Debug;
use std::hash::Hash;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tracing::{debug_span, Instrument};
pub struct RedisCachePipe<RedisOutput: FromRedisValue + Send + 'static, Output: Send + 'static> {
application: Application,
pipe: Pipeline,
transform: Box<dyn Fn(RedisOutput) -> AppResult<Output> + Send + 'static>,
}
impl<RedisOutput: FromRedisValue + Send + 'static, Output: Send + 'static> Debug for RedisCachePipe<RedisOutput, Output> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RedisCachePipe").finish()
}
}
impl<RedisOutput: FromRedisValue + Send + 'static, Output: Send + 'static> RedisCachePipe<RedisOutput, Output> {
fn map<RedisOut2: FromRedisValue + Send + 'static, R2: Send + 'static, F: Fn(RedisOut2) -> AppResult<R2> + Send + 'static>(
self,
f: F,
) -> RedisCachePipe<RedisOut2, R2> {
RedisCachePipe {
application: self.application,
pipe: self.pipe,
transform: Box::new(f),
}
}
pub fn ignore_return_val(self) -> RedisCachePipe<(), ()> {
self.map(|_| AppResult::Ok(()))
}
pub fn set_cache_object<Key: AsRef<str>, T: Serialize>(mut self, key: Key, value: &T) -> RedisCachePipe<(), ()> {
let json = value.to_json_string();
self.pipe.set(key.as_ref(), json);
self.map(|_| AppResult::Ok(()))
}
pub fn set_cache_object_with_timeout<Key: AsRef<str>, T: Serialize>(
mut self,
key: Key,
value: &T,
timeout: Duration,
) -> RedisCachePipe<(), ()> {
let json = value.to_json_string();
let seconds = timeout.as_secs();
self.pipe.set_ex(key.as_ref(), json, seconds as usize);
self.map(|_| AppResult::Ok(()))
}
pub fn expire<Key: AsRef<str>>(mut self, key: Key, timeout: Duration) -> RedisCachePipe<i64, bool> {
let seconds = timeout.as_secs();
self.pipe.expire(key.as_ref(), seconds as usize);
self.map(|v| AppResult::Ok(v > 0))
}
pub fn get_cache_object<Key: AsRef<str>, T: DeserializeOwned + Send + 'static>(
mut self,
key: Key,
) -> RedisCachePipe<Option<String>, Option<T>> {
self.pipe.get(key.as_ref());
self.map(|s: Option<String>| match s {
None => Ok(None),
Some(s) => {
let obj = JsonUtil::parse_json_string(s.as_str())?;
Ok(Some(obj))
}
})
}
pub fn delete_object<Key: AsRef<str>>(mut self, key: Key) -> RedisCachePipe<i64, bool> {
self.pipe.del(key.as_ref());
self.map(|v| Ok(v > 0))
}
pub fn set_cache_objects<Key: AsRef<str>, E: Serialize, T: AsRef<[E]>>(mut self, key: Key, datas: T) -> RedisCachePipe<i64, i64> {
let values: Vec<String> = datas.as_ref().iter().map(|v| v.to_json_string()).collect();
self.pipe.rpush(key.as_ref(), values);
self.map(Ok)
}
pub fn get_cache_objects_list<Key: AsRef<str>, T: DeserializeOwned + Send + 'static>(
mut self,
key: Key,
) -> RedisCachePipe<Vec<String>, Vec<T>> {
self.pipe.lrange(key.as_ref(), 0, -1);
self.map(|values: Vec<String>| {
let mut datas = Vec::with_capacity(values.len());
for item in values.into_iter() {
let data = JsonUtil::parse_json_string(item.as_str())?;
datas.push(data);
}
Ok(datas)
})
}
pub fn get_cache_objects_set<Key: AsRef<str>, T: DeserializeOwned + Eq + Hash + Send + 'static>(
mut self,
key: Key,
) -> RedisCachePipe<HashSet<String>, HashSet<T>> {
self.pipe.lrange(key.as_ref(), 0, -1);
self.map(|values: HashSet<String>| {
let mut datas = HashSet::with_capacity(values.len());
for item in values.into_iter() {
let data = JsonUtil::parse_json_string(item.as_str())?;
datas.insert(data);
}
Ok(datas)
})
}
pub fn set_cache_map<Key: AsRef<str>, T: Serialize>(mut self, key: Key, map: &IndexMap<String, T>) -> RedisCachePipe<(), ()> {
let values: Vec<(String, String)> = map.iter().map(|(k, v)| (k.to_string(), v.to_json_string())).collect();
self.pipe.hset_multiple(key.as_ref(), values.as_slice());
self.map(Ok)
}
pub fn get_cache_map<Key: AsRef<str>, T: DeserializeOwned, Map: FromIterator<(String, T)> + Send + 'static>(
mut self,
key: Key,
) -> RedisCachePipe<Vec<(String, String)>, Map> {
self.pipe.hgetall(key.as_ref());
self.map(|values: Vec<(String, String)>| {
values
.into_iter()
.map(|(k, v)| {
let v1 = JsonUtil::parse_json_string::<T>(v.as_str());
match v1 {
Ok(v1) => Ok((k, v1)),
Err(err) => Err(err),
}
})
.try_collect()
})
}
pub fn set_cache_map_object<Key: AsRef<str>, T: Serialize>(mut self, key: Key, hkey: Key, value: &T) -> RedisCachePipe<(), ()> {
let json = value.to_json_string();
self.pipe.hset(key.as_ref(), hkey.as_ref(), json);
self.map(Ok)
}
pub fn get_cache_map_object<Key: AsRef<str>, T: DeserializeOwned + Send + 'static>(
mut self,
key: Key,
hkey: Key,
) -> RedisCachePipe<Option<String>, Option<T>> {
self.pipe.hget(key.as_ref(), hkey.as_ref());
self.map(|s: Option<String>| match s {
None => Ok(None),
Some(s) => {
let obj = JsonUtil::parse_json_string(s.as_str())?;
Ok(Some(obj))
}
})
}
pub fn delete_map_object<Key: AsRef<str>>(mut self, key: Key, hkey: Key) -> RedisCachePipe<i64, bool> {
self.pipe.hdel(key.as_ref(), hkey.as_ref());
self.map(|c| Ok(c > 0))
}
pub fn keys<Key: AsRef<str>>(mut self, pattern: Key) -> RedisCachePipe<Vec<String>, Vec<String>> {
self.pipe.keys(pattern.as_ref());
self.map(Ok)
}
#[instrument]
pub async fn execute(self) -> AppResult<Output> {
let Self {
application,
pipe,
transform,
} = self;
let mut conn = application.get_redis_connection().await?;
let value = pipe.query_async::<RedisConnection, Value>(&mut conn).await.map_err(crate::app_error_from!())?;
match value {
Value::Bulk(bulk) => {
let v = match bulk.iter().rev().next() {
None => {
static NIL: Value = Value::Nil;
&NIL
}
Some(v) => v,
};
let r1 = RedisOutput::from_redis_value(v).map_err(|err| {
app_system_error!(
"retrieve value failed! value: {:?}, bulk: {:?}, target_type: {}, reason: {:?}",
v,
bulk,
type_name::<RedisOutput>(),
err
)
})?;
(*transform)(r1)
}
_ => Err(app_system_error!("value is not bulk: {:?}", value)),
}
}
}
pub struct RedisCache;
impl RedisCache {
pub fn pipe(application: &Application) -> RedisCachePipe<(), ()> {
RedisCachePipe {
application: application.clone(),
pipe: redis::pipe(),
transform: Box::new(|_| Ok(())),
}
}
}
#[async_trait]
impl ICacheService for RedisCache {
async fn set_cache_object<T: Serialize + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
value: &T,
) -> AppResult<()> {
async move {
let mut conn = application.get_redis_connection().await?;
let json = value.to_json_string();
let _: () = conn.set(key, json).await.map_err(app_error_from!())?;
Ok(())
}
.instrument(debug_span!("set_cache_object"))
.await
}
async fn set_cache_object_with_timeout<T: Serialize + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
value: &T,
timeout: Duration,
) -> AppResult<()> {
async move {
let mut conn = application.get_redis_connection().await?;
let json = value.to_json_string();
let seconds = timeout.as_secs();
let _: () = conn.set_ex(key, json, seconds as usize).await.map_err(app_error_from!())?;
Ok(())
}
.instrument(debug_span!("set_cache_object_with_timeout"))
.await
}
async fn expire(&self, application: &Application, key: &str, timeout: Duration) -> AppResult<bool> {
async move {
let mut conn = application.get_redis_connection().await?;
let seconds = timeout.as_secs();
let c: i64 = conn.expire(key, seconds as usize).await.map_err(app_error_from!())?;
Ok(c > 0)
}
.instrument(debug_span!("expire"))
.await
}
async fn get_cache_object<T: DeserializeOwned + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
) -> AppResult<Option<T>> {
async move {
let mut conn = application.get_redis_connection().await?;
let s: Option<String> = conn.get(key).await.map_err(app_error_from!())?;
match s {
None => Ok(None),
Some(s) => {
let obj = JsonUtil::parse_json_string(s.as_str())?;
Ok(Some(obj))
}
}
}
.instrument(debug_span!("get_cache_object"))
.await
}
async fn delete_object(&self, application: &Application, key: &str) -> AppResult<bool> {
async move {
let mut conn = application.get_redis_connection().await?;
let c: i64 = conn.del(key).await.map_err(app_error_from!())?;
Ok(c > 0)
}
.instrument(debug_span!("delete_object"))
.await
}
async fn delete_objects(&self, application: &Application, keys: &[&str]) -> AppResult<i64> {
async move {
let mut conn = application.get_redis_connection().await?;
if keys.is_empty() {
return Ok(0);
}
let c: i64 = conn.del(keys).await.map_err(app_error_from!())?;
Ok(c)
}
.instrument(debug_span!("delete_objects"))
.await
}
async fn set_cache_objects<E: Serialize + Send + Sync + 'static, T: AsRef<[E]> + Send + 'static>(
&self,
application: &Application,
key: &str,
datas: T,
) -> AppResult<i64> {
async move {
let mut conn = application.get_redis_connection().await?;
let values: Vec<String> = datas.as_ref().iter().map(|v| v.to_json_string()).collect();
let c: i64 = conn.rpush(key, values).await.map_err(app_error_from!())?;
Ok(c)
}
.instrument(debug_span!("set_cache_objects"))
.await
}
async fn get_cache_objects_list<T: DeserializeOwned + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
) -> AppResult<Vec<T>> {
async move {
let mut conn = application.get_redis_connection().await?;
let values: Vec<String> = conn.lrange(key, 0, -1).await.map_err(app_error_from!())?;
let mut datas = Vec::with_capacity(values.len());
for item in values.into_iter() {
let data = JsonUtil::parse_json_string(item.as_str())?;
datas.push(data);
}
Ok(datas)
}
.instrument(debug_span!("get_cache_objects_list"))
.await
}
async fn get_cache_objects_set<T: DeserializeOwned + Eq + Hash + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
) -> AppResult<IndexSet<T>> {
async move {
let mut conn = application.get_redis_connection().await?;
let values: Vec<String> = conn.lrange(key, 0, -1).await.map_err(app_error_from!())?;
let mut datas = IndexSet::with_capacity(values.len());
for item in values.into_iter() {
let data = JsonUtil::parse_json_string(item.as_str())?;
datas.insert(data);
}
Ok(datas)
}
.instrument(debug_span!("get_cache_objects_set"))
.await
}
async fn set_cache_map<T: Serialize + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
map: &IndexMap<String, T>,
) -> AppResult<()> {
async move {
let mut conn = application.get_redis_connection().await?;
let values: Vec<(String, String)> = map.iter().map(|(k, v)| (k.to_string(), v.to_json_string())).collect();
let _: () = conn.hset_multiple(key, values.as_slice()).await.map_err(app_error_from!())?;
Ok(())
}
.instrument(debug_span!("set_cache_map"))
.await
}
async fn get_cache_map<T: DeserializeOwned + Send + Sync + 'static, Map: FromIterator<(String, T)>>(
&self,
application: &Application,
key: &str,
) -> AppResult<Map> {
async move {
let mut conn = application.get_redis_connection().await?;
let values: Vec<(String, String)> = conn.hgetall(key).await.map_err(app_error_from!())?;
values
.into_iter()
.map(|(k, v)| {
let v1 = JsonUtil::parse_json_string::<T>(v.as_str());
match v1 {
Ok(v1) => Ok((k, v1)),
Err(err) => Err(err),
}
})
.try_collect()
}
.instrument(debug_span!("get_cache_map"))
.await
}
async fn set_cache_map_object<T: Serialize + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
hkey: &str,
value: &T,
) -> AppResult<()> {
async move {
let mut conn = application.get_redis_connection().await?;
let json = value.to_json_string();
let _: () = conn.hset(key, hkey, json).await.map_err(app_error_from!())?;
Ok(())
}
.instrument(debug_span!("set_cache_map_object"))
.await
}
async fn get_cache_map_object<T: DeserializeOwned + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
hkey: &str,
) -> AppResult<Option<T>> {
async move {
let mut conn = application.get_redis_connection().await?;
let s: Option<String> = conn.hget(key, hkey).await.map_err(app_error_from!())?;
match s {
None => Ok(None),
Some(s) => {
let obj = JsonUtil::parse_json_string(s.as_str())?;
Ok(Some(obj))
}
}
}
.instrument(debug_span!("get_cache_map_object"))
.await
}
async fn delete_map_object(&self, application: &Application, key: &str, hkey: &str) -> AppResult<bool> {
async move {
let mut conn = application.get_redis_connection().await?;
let c: i64 = conn.hdel(key, hkey).await.map_err(app_error_from!())?;
Ok(c > 0)
}
.instrument(debug_span!("delete_map_object"))
.await
}
async fn get_multi_cache_map_object<T: DeserializeOwned + Send + Sync + 'static>(
&self,
application: &Application,
key: &str,
hkeys: &[&str],
) -> AppResult<Vec<T>> {
async move {
let mut conn = application.get_redis_connection().await?;
match hkeys.len() > 1 {
true => {
let values: Vec<String> = conn.hget(key, hkeys).await.map_err(app_error_from!())?;
let mut datas = Vec::with_capacity(values.len());
for item in values.into_iter() {
let data = JsonUtil::parse_json_string(item.as_str())?;
datas.push(data);
}
Ok(datas)
}
false => match hkeys.is_empty() {
true => Ok(vec![]),
false => {
let value: Option<String> = conn.hget(key, hkeys).await.map_err(app_error_from!())?;
let mut datas = Vec::new();
if let Some(v) = value.as_ref() {
let data = JsonUtil::parse_json_string(v.as_str())?;
datas.push(data);
}
Ok(datas)
}
},
}
}
.instrument(debug_span!("get_multi_cache_map_object"))
.await
}
async fn keys(&self, application: &Application, pattern: &str) -> AppResult<Vec<String>> {
async move {
let mut conn = application.get_redis_connection().await?;
let keys: Vec<String> = conn.keys(pattern).await.map_err(app_error_from!())?;
Ok(keys)
}
.instrument(debug_span!("keys"))
.await
}
async fn time(&self, application: &Application) -> AppResult<SystemTime> {
async move {
let mut conn = application.get_redis_connection().await?;
let script = r##"local a=redis.call('TIME') ;return a[1]*1000000000+a[2]*1000"##;
let script = crate::redis::Script::new(script);
let nanoseconds: u64 =
script.prepare_invoke().invoke_async::<RedisConnection, u64>(&mut conn).await.map_err(crate::app_error_from!())?;
let duration = Duration::from_nanos(nanoseconds);
let time = UNIX_EPOCH + duration;
Ok(time)
}
.instrument(debug_span!("time"))
.await
}
}
#[cfg(test)]
#[allow(unused)]
mod test {
use crate::app_error_from;
use crate::tina::core::service::cache::ICacheService;
use crate::tina::data::AppResult;
use crate::tina::redis::cache::RedisCache;
use crate::tina::redis::{IRedisClient, PooledRedisClient, RedisClient};
use crate::tina::server::application::{AppConfig, Application, FromApplication};
use chrono::Local;
use deadpool::managed::{Manager, Pool};
use indexmap::{IndexMap, IndexSet};
use once_cell::sync::Lazy;
use redis::Client;
use std::collections::HashSet;
use std::time::Duration;
use tokio::runtime::Runtime;
static RUNTIME: Lazy<Runtime> =
Lazy::new(|| tokio::runtime::Builder::new_multi_thread().enable_all().build().expect("build tokio failed"));
async fn get_application() -> AppResult<Application> {
let mut app_config = AppConfig::new();
let client = Client::open("redis://:P@ssw0rd@dev.server:56379/20").map_err(app_error_from!())?;
let redis_client = RedisClient::from(client);
let mut connection = redis_client.create().await.map_err(app_error_from!())?;
redis_client.recycle(&mut connection).await.map_err(app_error_from!())?;
let pool = Pool::builder(redis_client)
.max_size(50)
.wait_timeout(Some(Duration::from_secs(5)))
.create_timeout(Some(Duration::from_secs(5)))
.recycle_timeout(Some(Duration::from_secs(5)))
.build()
.map_err(app_error_from!())?;
app_config.set_redis_client(PooledRedisClient::from(pool));
Ok(Application::from(app_config))
}
#[derive(Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, Clone)]
#[serde(crate = "crate::serde")]
struct TestObject {
name: Option<String>,
timestamp: Option<i64>,
}
impl TestObject {
async fn new(name: &str) -> Self {
tokio::time::sleep(Duration::from_millis(10)).await;
Self {
name: Some(name.to_string()),
timestamp: Some(Local::now().timestamp_millis()),
}
}
}
#[test]
#[ignore]
fn test_set_cache_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_set_cache_object";
let application = get_application().await?;
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
Ok(())
})
}
#[test]
#[ignore]
fn test_set_cache_object_with_timeout() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_set_cache_object_with_timeout";
let application = get_application().await?;
let obj = TestObject::new(key).await;
RedisCache.set_cache_object_with_timeout(&application, key, &obj, Duration::from_secs(20)).await?;
Ok(())
})
}
#[test]
#[ignore]
fn test_expire() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_expire";
let application = get_application().await?;
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_object";
let application = get_application().await?;
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
let obj2 = RedisCache.get_cache_object::<TestObject>(&application, key).await?.expect("get failed");
assert_eq!(obj2, obj);
let r = RedisCache.get_cache_object::<TestObject>(&application, "abcd").await?;
assert_eq!(r, None);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_object_pipe() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_object_pipe";
let application = get_application().await?;
let obj = TestObject::new(key).await;
let pipe = RedisCache::pipe(&application)
.set_cache_object(key, &obj)
.expire(key, Duration::from_secs(20))
.get_cache_object::<&str, TestObject>(key);
let obj2 = pipe.execute().await?;
assert_eq!(obj2, Some(obj));
Ok(())
})
}
#[test]
#[ignore]
fn test_delete_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_delete_object";
let application = get_application().await?;
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
let r = RedisCache.delete_object(&application, key).await?;
assert!(r);
let r = RedisCache.delete_object(&application, "abc").await?;
assert!(!r);
Ok(())
})
}
#[test]
#[ignore]
fn test_delete_objects() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let application = get_application().await?;
{
let key = "test_delete_objects_1";
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
}
{
let key = "test_delete_objects_2";
let obj = TestObject::new(key).await;
RedisCache.set_cache_object(&application, key, &obj).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
}
let r = RedisCache.delete_objects(&application, Vec::<&str>::new().as_slice()).await?;
assert_eq!(r, 0);
let r = RedisCache.delete_objects(&application, vec!["test_delete_objects"].as_slice()).await?;
assert_eq!(r, 0);
let r = RedisCache.delete_objects(&application, vec!["test_delete_objects_1", "test_delete_objects_2"].as_slice()).await?;
assert_eq!(r, 2);
Ok(())
})
}
#[test]
#[ignore]
fn test_set_cache_objects() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_set_cache_objects";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
let c = RedisCache.set_cache_objects(&application, key, vec![obj1.clone()]).await?;
assert_eq!(c, 1);
let c = RedisCache.set_cache_objects(&application, key, vec![obj2.clone()]).await?;
assert_eq!(c, 2);
let c = RedisCache.set_cache_objects(&application, key, vec![obj1, obj2]).await?;
assert_eq!(c, 4);
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_objects_list() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_objects_list";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
let c = RedisCache.set_cache_objects(&application, key, vec![obj1.clone()]).await?;
assert_eq!(c, 1);
let r: Vec<TestObject> = RedisCache.get_cache_objects_list(&application, key).await?;
assert_eq!(r, vec![obj1.clone()]);
let c = RedisCache.set_cache_objects(&application, key, vec![obj2.clone()]).await?;
assert_eq!(c, 2);
let r: Vec<TestObject> = RedisCache.get_cache_objects_list(&application, key).await?;
assert_eq!(r, vec![obj1.clone(), obj2.clone()]);
let r: Vec<TestObject> = RedisCache.get_cache_objects_list(&application, "abcd").await?;
assert_eq!(r, Vec::<TestObject>::new());
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_objects_set() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_objects_set";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
let c = RedisCache.set_cache_objects(&application, key, vec![obj1.clone(), obj2.clone(), obj2.clone()]).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
assert_eq!(c, 3);
let r: IndexSet<TestObject> = RedisCache.get_cache_objects_set(&application, key).await?;
assert_eq!(r, IndexSet::<TestObject>::from_iter(vec![obj1.clone(), obj2.clone()].into_iter()));
assert_eq!(r, IndexSet::<TestObject>::from_iter(vec![obj2, obj1].into_iter()));
let r: IndexSet<TestObject> = RedisCache.get_cache_objects_set(&application, "abcd").await?;
assert_eq!(r, IndexSet::<TestObject>::new());
Ok(())
})
}
#[test]
#[ignore]
fn test_set_cache_map_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_set_cache_map_object";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
RedisCache.set_cache_map_object(&application, key, "1", &obj1).await?;
RedisCache.set_cache_map_object(&application, key, "2", &obj2).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_set_cache_map() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_set_cache_map";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
let mut map = IndexMap::new();
map.insert("1".to_string(), obj1);
map.insert("2".to_string(), obj2);
RedisCache.set_cache_map(&application, key, &map).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_map() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_map";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
let mut map = IndexMap::new();
map.insert("1".to_string(), obj1);
map.insert("2".to_string(), obj2);
RedisCache.set_cache_map(&application, key, &map).await?;
let m1: IndexMap<String, TestObject> = RedisCache.get_cache_map(&application, key).await?;
assert_eq!(m1, map);
let mut map1 = IndexMap::new();
let obj3 = TestObject::new(key).await;
map1.insert("3".to_string(), obj3);
RedisCache.set_cache_map(&application, key, &map1).await?;
let m2: IndexMap<String, TestObject> = RedisCache.get_cache_map(&application, key).await?;
let mut m3 = IndexMap::new();
m3.extend(map.into_iter());
m3.extend(map1.into_iter());
assert_eq!(m2, m3);
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_cache_map_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_cache_map_object";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
RedisCache.set_cache_map_object(&application, key, "1", &obj1).await?;
RedisCache.set_cache_map_object(&application, key, "2", &obj2).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
let o2: TestObject = RedisCache.get_cache_map_object(&application, key, "2").await?.expect("get failed");
let o1: TestObject = RedisCache.get_cache_map_object(&application, key, "1").await?.expect("get failed");
assert_eq!(o1, obj1);
assert_eq!(o2, obj2);
let o3: Option<TestObject> = RedisCache.get_cache_map_object(&application, key, "3").await?;
assert_eq!(o3, None);
let o4: Option<TestObject> = RedisCache.get_cache_map_object(&application, "abc", "3").await?;
assert_eq!(o4, None);
Ok(())
})
}
#[test]
#[ignore]
fn test_delete_map_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_delete_map_object";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
RedisCache.set_cache_map_object(&application, key, "1", &obj1).await?;
RedisCache.set_cache_map_object(&application, key, "2", &obj2).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
let o2: TestObject = RedisCache.get_cache_map_object(&application, key, "2").await?.expect("get failed");
let o1: TestObject = RedisCache.get_cache_map_object(&application, key, "1").await?.expect("get failed");
assert_eq!(o1, obj1);
assert_eq!(o2, obj2);
let r = RedisCache.delete_map_object(&application, key, "2").await?;
assert!(r);
let r = RedisCache.delete_map_object(&application, key, "3").await?;
assert!(!r);
let r = RedisCache.delete_map_object(&application, "abc", "3").await?;
assert!(!r);
let o2: Option<TestObject> = RedisCache.get_cache_map_object(&application, key, "2").await?;
let o1: TestObject = RedisCache.get_cache_map_object(&application, key, "1").await?.expect("get failed");
assert_eq!(o1, obj1);
assert_eq!(o2, None);
Ok(())
})
}
#[test]
#[ignore]
fn test_get_multi_cache_map_object() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_get_multi_cache_map_object";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
RedisCache.set_cache_map_object(&application, key, "1", &obj1).await?;
RedisCache.set_cache_map_object(&application, key, "2", &obj2).await?;
let r = RedisCache.expire(&application, key, Duration::from_secs(20)).await?;
assert!(r);
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, key, vec!["1", "2"].as_slice()).await?;
assert_eq!(v, vec![obj1.clone(), obj2.clone()]);
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, key, vec!["2", "1"].as_slice()).await?;
assert_eq!(v, vec![obj2.clone(), obj1.clone()]);
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, key, vec!["2"].as_slice()).await?;
assert_eq!(v, vec![obj2.clone()]);
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, key, vec!["1"].as_slice()).await?;
assert_eq!(v, vec![obj1.clone()]);
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, key, vec!["3"].as_slice()).await?;
assert_eq!(v, Vec::<TestObject>::new());
let v: Vec<TestObject> = RedisCache.get_multi_cache_map_object(&application, "abc", vec!["3"].as_slice()).await?;
assert_eq!(v, Vec::<TestObject>::new());
Ok(())
})
}
#[test]
#[ignore]
fn test_keys() -> Result<(), Box<dyn std::error::Error>> {
RUNTIME.block_on(async {
let key = "test_keys";
let application = get_application().await?;
let obj1 = TestObject::new(key).await;
let obj2 = TestObject::new(key).await;
RedisCache
.set_cache_object_with_timeout(&application, format!("{}_{}_{}", 1, key, 1).as_str(), &obj1, Duration::from_secs(20))
.await?;
RedisCache
.set_cache_object_with_timeout(&application, format!("{}_{}_{}", 1, key, 2).as_str(), &obj1, Duration::from_secs(20))
.await?;
let keys = RedisCache.keys(&application, key).await?;
assert_eq!(keys, Vec::<String>::new());
let keys = RedisCache.keys(&application, format!("{}_{}", "*", key).as_str()).await?;
assert_eq!(keys, Vec::<String>::new());
let keys = RedisCache.keys(&application, format!("{}_{}_{}", "*", key, "*").as_str()).await?;
assert_eq!(keys, vec![format!("{}_{}_{}", 1, key, 1).as_str(), format!("{}_{}_{}", 1, key, 2).as_str()].as_slice());
Ok(())
})
}
}