102 lines
2.6 KiB
Rust
102 lines
2.6 KiB
Rust
use crate::web::ApiError;
|
|
use lru::LruCache;
|
|
use magnetar_common::config::MagnetarConfig;
|
|
use magnetar_model::{ck, CalckeyDbError, CalckeyModel};
|
|
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))
|
|
}
|
|
}
|