feat: actor cache TTL with staleness-aware re-fetch
Adds fetched_at to RemoteActor, configurable TTL via builder (.actor_cache_ttl_secs, default 24h), and get_or_refresh_remote_actor helper that re-fetches stale actors from origin. Closes #3
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use url::Url;
|
||||
|
||||
use crate::actors::DbActor;
|
||||
use crate::repository::RemoteActor;
|
||||
|
||||
use super::ActivityPubService;
|
||||
|
||||
impl ActivityPubService {
|
||||
@@ -17,4 +21,42 @@ impl ActivityPubService {
|
||||
.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
Ok(res.object)
|
||||
}
|
||||
|
||||
/// Get a cached remote actor, re-fetching from origin if stale.
|
||||
///
|
||||
/// Returns `None` if the actor has never been seen. Staleness is
|
||||
/// determined by `actor_cache_ttl_secs` (builder config).
|
||||
pub async fn get_or_refresh_remote_actor(
|
||||
&self,
|
||||
actor_url: &str,
|
||||
) -> anyhow::Result<Option<RemoteActor>> {
|
||||
let data = self.federation_config.to_request_data();
|
||||
let cached = data.actor_repo.get_remote_actor(actor_url).await?;
|
||||
if let Some(ref actor) = cached {
|
||||
let is_fresh = actor
|
||||
.fetched_at
|
||||
.map(|t| {
|
||||
let age = chrono::Utc::now().signed_duration_since(t);
|
||||
age < chrono::Duration::from_std(data.actor_cache_ttl).unwrap_or_default()
|
||||
})
|
||||
.unwrap_or(true);
|
||||
if is_fresh {
|
||||
return Ok(cached);
|
||||
}
|
||||
}
|
||||
let url = match Url::parse(actor_url) {
|
||||
Ok(u) => u,
|
||||
Err(_) => return Ok(cached),
|
||||
};
|
||||
match ObjectId::<DbActor>::from(url)
|
||||
.dereference_forced(&data)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(data.actor_repo.get_remote_actor(actor_url).await?),
|
||||
Err(e) => {
|
||||
tracing::warn!(actor_url, error = %e, "re-fetch failed, using stale cache");
|
||||
Ok(cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user