magnetar/src/service/instance_cache.rs

102 lines
2.6 KiB
Rust

use crate::web::ApiError;
use lru::LruCache;
use magnetar_calckey_model::{ck, CalckeyDbError, CalckeyModel};
use magnetar_common::config::MagnetarConfig;
use std::sync::Arc;
use std::time::{Duration, Instant};
use strum::EnumVariantNames;
use thiserror::Error;
use tokio::sync::Mutex;
#[derive(Debug, Error, EnumVariantNames)]
pub enum RemoteInstanceCacheError {
#[error("Database error: {0}")]
DbError(#[from] CalckeyDbError),
}
impl From<RemoteInstanceCacheError> for ApiError {
fn from(err: RemoteInstanceCacheError) -> Self {
let mut api_error: ApiError = match err {
RemoteInstanceCacheError::DbError(err) => err.into(),
};
api_error.message = format!("Remote instance cache error: {}", api_error.message);
api_error
}
}
#[derive(Debug)]
struct CacheEntry {
created: Instant,
data: Arc<ck::instance::Model>,
}
impl CacheEntry {
fn new(data: Arc<ck::instance::Model>) -> Self {
Self {
created: Instant::now(),
data,
}
}
}
pub struct RemoteInstanceCacheService {
cache: Mutex<LruCache<String, CacheEntry>>,
lifetime_max: Duration,
db: CalckeyModel,
config: &'static MagnetarConfig,
}
impl RemoteInstanceCacheService {
pub(super) fn new(
db: CalckeyModel,
config: &'static MagnetarConfig,
cache_size: usize,
entry_lifetime: Duration,
) -> Self {
const CACHE_SIZE: usize = 256;
Self {
cache: Mutex::new(LruCache::new(
cache_size
.try_into()
.unwrap_or(CACHE_SIZE.try_into().unwrap()),
)),
lifetime_max: entry_lifetime,
db,
config,
}
}
pub async fn get(
&self,
host: &str,
) -> Result<Option<Arc<ck::instance::Model>>, RemoteInstanceCacheError> {
if host == self.config.networking.host {
return Ok(None);
}
let mut read = self.cache.lock().await;
if let Some(item) = read.peek(host) {
if item.created + self.lifetime_max >= Instant::now() {
let data = item.data.clone();
read.promote(host);
return Ok(Some(data));
}
}
drop(read);
let val = self.db.get_instance(host).await?;
if val.is_none() {
return Ok(None);
}
let mut write = self.cache.lock().await;
let data = Arc::new(val.unwrap());
write.put(host.to_string(), CacheEntry::new(data.clone()));
Ok(Some(data))
}
}