refactor (backend-rs): use chrono::Duration for Redis cache TTL

This commit is contained in:
naskya 2024-08-01 23:55:08 +09:00
parent 47018b4cff
commit e541efd80a
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
6 changed files with 47 additions and 19 deletions

View file

@ -1,6 +1,7 @@
//! Utilities for using Redis cache //! Utilities for using Redis cache
use crate::database::{redis_conn, redis_key, RedisConnError}; use crate::database::{redis_conn, redis_key, RedisConnError};
use chrono::Duration;
use redis::{AsyncCommands, RedisError}; use redis::{AsyncCommands, RedisError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -23,6 +24,8 @@ pub enum Error {
RedisConn(#[from] RedisConnError), RedisConn(#[from] RedisConnError),
#[error("failed to encode data for Redis")] #[error("failed to encode data for Redis")]
Encode(#[from] rmp_serde::encode::Error), Encode(#[from] rmp_serde::encode::Error),
#[error("invalid cache TTL")]
TTL,
} }
#[inline] #[inline]
@ -56,18 +59,19 @@ fn wildcard(category: Category) -> String {
/// ///
/// * `key` : key (prefixed automatically) /// * `key` : key (prefixed automatically)
/// * `value` : (de)serializable value /// * `value` : (de)serializable value
/// * `expire_seconds` : TTL /// * `ttl` : cache lifetime
/// ///
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use backend_rs::cache; /// # use backend_rs::cache;
/// use chrono::Duration;
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> { /// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
/// let key = "apple"; /// let key = "apple";
/// let data = "I want to cache this string".to_owned(); /// let data = "I want to cache this string".to_owned();
/// ///
/// // caches the data for 10 seconds /// // caches the data for 10 seconds
/// cache::set(key, &data, 10).await?; /// cache::set(key, &data, Duration::seconds(10)).await?;
/// ///
/// // get the cache /// // get the cache
/// let cached_data = cache::get::<String>(key).await?; /// let cached_data = cache::get::<String>(key).await?;
@ -80,14 +84,14 @@ fn wildcard(category: Category) -> String {
pub async fn set<V: for<'a> Deserialize<'a> + Serialize>( pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
key: &str, key: &str,
value: &V, value: &V,
expire_seconds: u64, ttl: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
redis_conn() redis_conn()
.await? .await?
.set_ex( .set_ex(
prefix_key(key), prefix_key(key),
rmp_serde::encode::to_vec(&value)?, rmp_serde::encode::to_vec(&value)?,
expire_seconds, ttl.num_seconds().try_into().map_err(|_| Error::TTL)?,
) )
.await?; .await?;
Ok(()) Ok(())
@ -176,14 +180,14 @@ pub async fn delete(key: &str) -> Result<(), Error> {
/// * `category` : one of [Category] /// * `category` : one of [Category]
/// * `key` : key (prefixed automatically) /// * `key` : key (prefixed automatically)
/// * `value` : (de)serializable value /// * `value` : (de)serializable value
/// * `expire_seconds` : TTL /// * `ttl` : cache lifetime
pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>( pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
category: Category, category: Category,
key: &str, key: &str,
value: &V, value: &V,
expire_seconds: u64, ttl: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
set(&categorize(category, key), value, expire_seconds).await set(&categorize(category, key), value, ttl).await
} }
/// Gets a Redis cache under a `category`. /// Gets a Redis cache under a `category`.
@ -233,6 +237,7 @@ pub async fn delete_all(category: Category) -> Result<(), Error> {
mod unit_test { mod unit_test {
use super::{delete_all, get, get_one, set, set_one, Category::Test}; use super::{delete_all, get, get_one, set, set_one, Category::Test};
use crate::cache::delete_one; use crate::cache::delete_one;
use chrono::Duration;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[tokio::test] #[tokio::test]
@ -256,9 +261,9 @@ mod unit_test {
kind: "prime number".to_owned(), kind: "prime number".to_owned(),
}; };
set(key_1, &value_1, 1).await.unwrap(); set(key_1, &value_1, Duration::seconds(1)).await.unwrap();
set(key_2, &value_2, 1).await.unwrap(); set(key_2, &value_2, Duration::seconds(1)).await.unwrap();
set(key_3, &value_3, 1).await.unwrap(); set(key_3, &value_3, Duration::seconds(1)).await.unwrap();
let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap(); let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap();
let cached_value_2: String = get(key_2).await.unwrap().unwrap(); let cached_value_2: String = get(key_2).await.unwrap().unwrap();
@ -291,9 +296,15 @@ mod unit_test {
let value_2 = 998244353u32; let value_2 = 998244353u32;
let value_3 = 'あ'; let value_3 = 'あ';
set_one(Test, key_1, &value_1, 5 * 60).await.unwrap(); set_one(Test, key_1, &value_1, Duration::minutes(5))
set_one(Test, key_2, &value_2, 5 * 60).await.unwrap(); .await
set_one(Test, key_3, &value_3, 5 * 60).await.unwrap(); .unwrap();
set_one(Test, key_2, &value_2, Duration::minutes(5))
.await
.unwrap();
set_one(Test, key_3, &value_3, Duration::minutes(5))
.await
.unwrap();
assert_eq!( assert_eq!(
get_one::<String>(Test, key_1).await.unwrap().unwrap(), get_one::<String>(Test, key_1).await.unwrap().unwrap(),

View file

@ -1,4 +1,5 @@
use crate::{cache, util::http_client}; use crate::{cache, util::http_client};
use chrono::Duration;
use futures_util::AsyncReadExt; use futures_util::AsyncReadExt;
use image::{ImageError, ImageFormat, ImageReader}; use image::{ImageError, ImageFormat, ImageReader};
use isahc::AsyncReadResponseExt; use isahc::AsyncReadResponseExt;
@ -63,7 +64,7 @@ pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
.is_some(); .is_some();
if !attempted { if !attempted {
cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?; cache::set_one(cache::Category::FetchUrl, url, &true, Duration::minutes(10)).await?;
} }
} }

View file

@ -1,6 +1,7 @@
//! Fetch latest Firefish version from the Firefish repository //! Fetch latest Firefish version from the Firefish repository
use crate::{cache, util::http_client}; use crate::{cache, util::http_client};
use chrono::Duration;
use futures_util::AsyncReadExt; use futures_util::AsyncReadExt;
use isahc::AsyncReadResponseExt; use isahc::AsyncReadResponseExt;
use serde::Deserialize; use serde::Deserialize;
@ -65,7 +66,7 @@ pub async fn latest_version() -> Result<String, Error> {
cache::Category::FetchUrl, cache::Category::FetchUrl,
UPSTREAM_PACKAGE_JSON_URL, UPSTREAM_PACKAGE_JSON_URL,
&fetched_version, &fetched_version,
3 * 60 * 60, Duration::hours(3),
) )
.await?; .await?;
Ok(fetched_version) Ok(fetched_version)

View file

@ -1,4 +1,5 @@
use crate::cache; use crate::cache;
use chrono::Duration;
use identicon_rs::{error::IdenticonError, Identicon}; use identicon_rs::{error::IdenticonError, Identicon};
#[macros::errors] #[macros::errors]
@ -18,7 +19,13 @@ pub async fn generate(id: &str) -> Result<Vec<u8>, Error> {
.set_border(16) .set_border(16)
.set_scale(96)? .set_scale(96)?
.export_png_data()?; .export_png_data()?;
cache::set_one(cache::Category::RandomIcon, id, &icon, 10 * 60).await?; cache::set_one(
cache::Category::RandomIcon,
id,
&icon,
Duration::minutes(10),
)
.await?;
Ok(icon) Ok(icon)
} }
} }

View file

@ -1,6 +1,7 @@
//! Determine whether to enable the cat language conversion //! Determine whether to enable the cat language conversion
use crate::{cache, database::db_conn, model::entity::user}; use crate::{cache, database::db_conn, model::entity::user};
use chrono::Duration;
use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns}; use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns};
#[macros::errors] #[macros::errors]
@ -35,7 +36,7 @@ pub async fn should_nyaify(reader_user_id: &str) -> Result<bool, Error> {
cache::Category::CatLang, cache::Category::CatLang,
reader_user_id, reader_user_id,
&fetched_value, &fetched_value,
10 * 60, Duration::minutes(10),
) )
.await?; .await?;

View file

@ -5,6 +5,7 @@ use crate::{
federation::acct::Acct, federation::acct::Acct,
model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*}, model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*},
}; };
use chrono::Duration;
use sea_orm::{prelude::*, QuerySelect}; use sea_orm::{prelude::*, QuerySelect};
#[macros::errors] #[macros::errors]
@ -109,7 +110,13 @@ pub(super) async fn check_hit_antenna(
.into_tuple::<String>() .into_tuple::<String>()
.all(db) .all(db)
.await?; .await?;
cache::set_one(cache::Category::Block, &note.user_id, &blocks, 10 * 60).await?; cache::set_one(
cache::Category::Block,
&note.user_id,
&blocks,
Duration::minutes(10),
)
.await?;
blocks blocks
}; };
@ -138,7 +145,7 @@ pub(super) async fn check_hit_antenna(
cache::Category::Follow, cache::Category::Follow,
&antenna.user_id, &antenna.user_id,
&following, &following,
10 * 60, Duration::minutes(10),
) )
.await?; .await?;
following following