refactor(cache): 抽取 invalidate_post_metadata() 封装 4 行失效组合

文章写操作后总要一起失效 lists/tags/stats/search 四项元数据缓存。
新增 invalidate_post_metadata() 封装这个固定组合,替换 create/delete/
update/trash 中的 8 处重复 4 行序列。

保留定向失效(invalidate_post_by_slug / invalidate_tag_posts_for)
和批量回退路径(invalidate_all_post_caches + invalidate_search_results)
的显式调用——它们是按实际涉及数据细粒度控制的,不应被封装吞掉。
This commit is contained in:
xfy 2026-07-14 14:44:00 +08:00
parent 22750ef89e
commit 8da678b3b4
5 changed files with 21 additions and 32 deletions

View File

@ -130,10 +130,7 @@ pub async fn create_post(
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 写入成功后按粒度失效相关缓存。 // 写入成功后按粒度失效相关缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
// 失效按 slug 缓存,避免之前缓存的 404 继续命中。 // 失效按 slug 缓存,避免之前缓存的 404 继续命中。
crate::cache::invalidate_post_by_slug(&final_slug).await; crate::cache::invalidate_post_by_slug(&final_slug).await;
// 失效该文章涉及的所有标签下文章列表缓存。 // 失效该文章涉及的所有标签下文章列表缓存。

View File

@ -67,10 +67,7 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 删除后按影响范围精准失效缓存。 // 删除后按影响范围精准失效缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
crate::cache::invalidate_post_by_slug(&slug).await; crate::cache::invalidate_post_by_slug(&slug).await;
crate::cache::invalidate_tag_posts_for(&tags).await; crate::cache::invalidate_tag_posts_for(&tags).await;

View File

@ -77,10 +77,7 @@ pub async fn restore_post(post_id: i32) -> Result<CreatePostResponse, ServerFnEr
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 精准失效:列表、标签云、统计、旧 slug 与新 slug、相关标签文章。 // 精准失效:列表、标签云、统计、旧 slug 与新 slug、相关标签文章。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
crate::cache::invalidate_post_by_slug(&current_slug).await; crate::cache::invalidate_post_by_slug(&current_slug).await;
crate::cache::invalidate_post_by_slug(&new_slug).await; crate::cache::invalidate_post_by_slug(&new_slug).await;
crate::cache::invalidate_tag_posts_for(&tags).await; crate::cache::invalidate_tag_posts_for(&tags).await;
@ -148,10 +145,7 @@ pub async fn purge_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErro
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 精准失效相关缓存。 // 精准失效相关缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
crate::cache::invalidate_post_by_slug(&slug).await; crate::cache::invalidate_post_by_slug(&slug).await;
crate::cache::invalidate_tag_posts_for(&tags).await; crate::cache::invalidate_tag_posts_for(&tags).await;
@ -236,10 +230,7 @@ pub async fn batch_restore_posts(post_ids: Vec<i32>) -> Result<CreatePostRespons
// 精准失效:先去重 slug再统一失效列表/标签云/统计/标签文章。 // 精准失效:先去重 slug再统一失效列表/标签云/统计/标签文章。
let unique_slugs: std::collections::HashSet<String> = let unique_slugs: std::collections::HashSet<String> =
affected_slugs.into_iter().collect(); affected_slugs.into_iter().collect();
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
for slug in &unique_slugs { for slug in &unique_slugs {
crate::cache::invalidate_post_by_slug(slug).await; crate::cache::invalidate_post_by_slug(slug).await;
} }
@ -327,10 +318,7 @@ pub async fn batch_purge_posts(post_ids: Vec<i32>) -> Result<CreatePostResponse,
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
if use_precise { if use_precise {
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
for slug in &slugs { for slug in &slugs {
crate::cache::invalidate_post_by_slug(slug).await; crate::cache::invalidate_post_by_slug(slug).await;
} }
@ -403,10 +391,7 @@ pub async fn empty_trash() -> Result<CreatePostResponse, ServerFnError> {
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
if use_precise { if use_precise {
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
for slug in &slugs { for slug in &slugs {
crate::cache::invalidate_post_by_slug(slug).await; crate::cache::invalidate_post_by_slug(slug).await;
} }

View File

@ -186,10 +186,7 @@ pub async fn update_post(
tx.commit().await.map_err(AppError::tx)?; tx.commit().await.map_err(AppError::tx)?;
// 失效文章列表、标签、当前 slug 与统计缓存。 // 失效文章列表、标签、当前 slug 与统计缓存。
crate::cache::invalidate_post_lists(); crate::cache::invalidate_post_metadata();
crate::cache::invalidate_all_tags();
crate::cache::invalidate_post_stats();
crate::cache::invalidate_search_results();
crate::cache::invalidate_post_by_slug(&final_slug).await; crate::cache::invalidate_post_by_slug(&final_slug).await;
// 合并旧标签与新标签,统一失效标签下的文章列表缓存。 // 合并旧标签与新标签,统一失效标签下的文章列表缓存。

View File

@ -501,6 +501,19 @@ pub fn invalidate_all_post_caches() {
TAG_POSTS_CACHE.invalidate_all(); TAG_POSTS_CACHE.invalidate_all();
} }
/// 失效文章「元数据」类缓存:列表、标签、统计、搜索结果。
///
/// 这四项在每次文章写操作(创建/更新/删除/恢复/清空回收站)后都需要一起失效。
/// 单篇正文与标签下文章列表是定向失效(按 slug / tag不在此处处理由调用方
/// 根据实际涉及的 slug/tags 额外调用 `invalidate_post_by_slug` / `invalidate_tag_posts_for`。
#[cfg(feature = "server")]
pub fn invalidate_post_metadata() {
invalidate_post_lists();
invalidate_all_tags();
invalidate_post_stats();
invalidate_search_results();
}
/// 按文章主键读取评论列表缓存。 /// 按文章主键读取评论列表缓存。
#[cfg(feature = "server")] #[cfg(feature = "server")]
pub async fn get_comments_by_post(post_id: i32) -> Option<Vec<PublicComment>> { pub async fn get_comments_by_post(post_id: i32) -> Option<Vec<PublicComment>> {