feat(base): Store last media cleanup time with EventCacheStoreMedia

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
This commit is contained in:
Kévin Commaille
2025-02-02 11:52:42 +01:00
parent 582b3a91d6
commit 4e1ae3d5e9
4 changed files with 57 additions and 5 deletions

View File

@@ -53,6 +53,9 @@ pub trait EventCacheStoreMediaIntegrationTests {
/// Test [`IgnoreMediaRetentionPolicy`] with the media content's retention
/// policy expiry.
async fn test_media_ignore_expiry(&self);
/// Test last media cleanup time storage.
async fn test_store_last_media_cleanup_time(&self);
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -941,6 +944,25 @@ where
let stored = self.get_media_content_inner(&request_5, time).await.unwrap();
assert!(stored.is_none());
}
async fn test_store_last_media_cleanup_time(&self) {
let initial = self.last_media_cleanup_time_inner().await.unwrap();
let new_time = initial.unwrap_or_else(SystemTime::now) + Duration::from_secs(60);
// With an empty policy.
let policy = MediaRetentionPolicy::empty();
self.clean_up_media_cache_inner(policy, new_time).await.unwrap();
let stored = self.last_media_cleanup_time_inner().await.unwrap();
assert_eq!(stored, initial);
// With the default policy.
let policy = MediaRetentionPolicy::default();
self.clean_up_media_cache_inner(policy, new_time).await.unwrap();
let stored = self.last_media_cleanup_time_inner().await.unwrap();
assert_eq!(stored, Some(new_time));
}
}
/// Macro building to allow your [`EventCacheStoreMedia`] implementation to run
@@ -1031,5 +1053,11 @@ macro_rules! event_cache_store_media_integration_tests {
let event_cache_store_media = get_event_cache_store().await.unwrap();
event_cache_store_media.test_media_ignore_expiry().await;
}
#[async_test]
async fn test_store_last_media_cleanup_time() {
let event_cache_store_media = get_event_cache_store().await.unwrap();
event_cache_store_media.test_store_last_media_cleanup_time().await;
}
};
}

View File

@@ -340,12 +340,15 @@ pub trait EventCacheStoreMedia: AsyncTraitDeps {
/// `cleanup_frequency` will be ignored.
///
/// * `current_time` - The current time, to be used to check for expired
/// content.
/// content and to be stored as the time of the last media cache cleanup.
async fn clean_up_media_cache_inner(
&self,
policy: MediaRetentionPolicy,
current_time: SystemTime,
) -> Result<(), Self::Error>;
/// The time of the last media cache cleanup.
async fn last_media_cleanup_time_inner(&self) -> Result<Option<SystemTime>, Self::Error>;
}
/// Whether the [`MediaRetentionPolicy`] should be ignored for the current
@@ -617,6 +620,10 @@ mod tests {
Ok(())
}
async fn last_media_cleanup_time_inner(&self) -> Result<Option<SystemTime>, Self::Error> {
Ok(self.inner().cleanup_time)
}
}
#[derive(Debug)]
@@ -712,12 +719,12 @@ mod tests {
assert!(media_content.ignore_policy);
// Try a cleanup. With the empty policy the store should not be accessed.
assert_eq!(store.inner().cleanup_time, None);
assert_eq!(store.last_media_cleanup_time_inner().await.unwrap(), None);
store.reset_accessed();
service.clean_up_media_cache(&store).await.unwrap();
assert!(!store.accessed());
assert_eq!(store.inner().cleanup_time, None);
assert_eq!(store.last_media_cleanup_time_inner().await.unwrap(), None);
}
#[async_test]
@@ -871,7 +878,7 @@ mod tests {
assert_eq!(media.last_access, now);
// Try a cleanup, the store should be accessed.
assert_eq!(store.inner().cleanup_time, None);
assert_eq!(store.last_media_cleanup_time_inner().await.unwrap(), None);
let now = now + Duration::from_secs(60);
service.time_provider.set_now(now);
@@ -879,6 +886,6 @@ mod tests {
service.clean_up_media_cache(&store).await.unwrap();
assert!(store.accessed());
assert_eq!(store.inner().cleanup_time, Some(now));
assert_eq!(store.last_media_cleanup_time_inner().await.unwrap(), Some(now));
}
}

View File

@@ -50,6 +50,7 @@ struct MemoryStoreInner {
leases: HashMap<String, (String, Instant)>,
events: RelationalLinkedChunk<Event, Gap>,
media_retention_policy: Option<MediaRetentionPolicy>,
last_media_cleanup_time: SystemTime,
}
/// A media content in the `MemoryStore`.
@@ -82,6 +83,8 @@ impl Default for MemoryStore {
leases: Default::default(),
events: RelationalLinkedChunk::new(),
media_retention_policy: None,
// Given that the store is empty, we won't need to clean it up right away.
last_media_cleanup_time: SystemTime::now(),
}),
// No need to call `restore()` since nothing is persisted.
media_service: MediaService::new(),
@@ -431,8 +434,14 @@ impl EventCacheStoreMedia for MemoryStore {
}
}
inner.last_media_cleanup_time = current_time;
Ok(())
}
async fn last_media_cleanup_time_inner(&self) -> Result<Option<SystemTime>, Self::Error> {
Ok(Some(self.inner.read().unwrap().last_media_cleanup_time))
}
}
#[cfg(test)]

View File

@@ -52,6 +52,7 @@ use crate::{
mod keys {
// Entries in Key-value store
pub const MEDIA_RETENTION_POLICY: &str = "media_retention_policy";
pub const LAST_MEDIA_CLEANUP_TIME: &str = "last_media_cleanup_time";
// Tables
pub const LINKED_CHUNKS: &str = "linked_chunks";
@@ -948,6 +949,8 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
}
}
txn.set_serialized_kv(keys::LAST_MEDIA_CLEANUP_TIME, current_time)?;
Ok(removed)
})
.await?;
@@ -969,6 +972,11 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
Ok(())
}
async fn last_media_cleanup_time_inner(&self) -> Result<Option<SystemTime>, Self::Error> {
let conn = self.acquire().await?;
conn.get_serialized_kv(keys::LAST_MEDIA_CLEANUP_TIME).await
}
}
/// Like `deadpool::managed::Object::with_transaction`, but starts the