perf(cache): cache COUNT(*) result separately to avoid redundant queries
- Add TotalPublishedPosts cache key for reusing total count across pages - list_published_posts now checks total cache before running COUNT(*) - Add note to get_posts_by_tag about total = posts.len() assumption - Remove unused invalidate_total_published_posts helper
This commit is contained in:
parent
bd9053132b
commit
311ddbe204
@ -20,15 +20,21 @@ pub async fn list_published_posts(
|
|||||||
|
|
||||||
let client = get_conn().await.map_err(AppError::db_conn)?;
|
let client = get_conn().await.map_err(AppError::db_conn)?;
|
||||||
|
|
||||||
// Get total count
|
// Get total count from cache or query
|
||||||
let count_row = client
|
let total = if let Some(cached_total) = crate::cache::get_total_published_posts().await {
|
||||||
.query_one(
|
cached_total
|
||||||
"SELECT COUNT(*) FROM posts WHERE status = 'published' AND deleted_at IS NULL",
|
} else {
|
||||||
&[],
|
let count_row = client
|
||||||
)
|
.query_one(
|
||||||
.await
|
"SELECT COUNT(*) FROM posts WHERE status = 'published' AND deleted_at IS NULL",
|
||||||
.map_err(AppError::query)?;
|
&[],
|
||||||
let total: i64 = count_row.get(0);
|
)
|
||||||
|
.await
|
||||||
|
.map_err(AppError::query)?;
|
||||||
|
let total: i64 = count_row.get(0);
|
||||||
|
crate::cache::set_total_published_posts(total).await;
|
||||||
|
total
|
||||||
|
};
|
||||||
|
|
||||||
let offset = ((page - 1).max(0) as i64) * (per_page as i64);
|
let offset = ((page - 1).max(0) as i64) * (per_page as i64);
|
||||||
let limit = per_page as i64;
|
let limit = per_page as i64;
|
||||||
@ -139,6 +145,9 @@ pub async fn get_posts_by_tag(tag_name: String) -> Result<PostListResponse, Serv
|
|||||||
posts.push(row_to_post_list(&client, row).await);
|
posts.push(row_to_post_list(&client, row).await);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: total = posts.len() is correct because get_posts_by_tag
|
||||||
|
// currently fetches ALL matching posts (no LIMIT/OFFSET).
|
||||||
|
// If pagination is added later, switch to a proper COUNT(*) query.
|
||||||
let total = posts.len() as i64;
|
let total = posts.len() as i64;
|
||||||
crate::cache::set_posts_by_tag(&tag_name, posts.clone(), total).await;
|
crate::cache::set_posts_by_tag(&tag_name, posts.clone(), total).await;
|
||||||
Ok(PostListResponse { posts, total })
|
Ok(PostListResponse { posts, total })
|
||||||
|
|||||||
11
src/cache.rs
11
src/cache.rs
@ -31,6 +31,7 @@ const TTL_TAG_POSTS: Duration = Duration::from_secs(120);
|
|||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
pub enum CacheKey {
|
pub enum CacheKey {
|
||||||
PublishedPosts { page: i32, per_page: i32 },
|
PublishedPosts { page: i32, per_page: i32 },
|
||||||
|
TotalPublishedPosts,
|
||||||
AllTags,
|
AllTags,
|
||||||
PostBySlug(String),
|
PostBySlug(String),
|
||||||
PostsByTag(String),
|
PostsByTag(String),
|
||||||
@ -109,6 +110,16 @@ pub async fn set_post_list(key: &CacheKey, posts: Vec<Post>, total: i64) {
|
|||||||
let _ = POST_LIST_CACHE.insert(key.clone(), (posts, total)).await;
|
let _ = POST_LIST_CACHE.insert(key.clone(), (posts, total)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
pub async fn get_total_published_posts() -> Option<i64> {
|
||||||
|
POST_LIST_CACHE.get(&CacheKey::TotalPublishedPosts).await.map(|(_, total)| total)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
pub async fn set_total_published_posts(total: i64) {
|
||||||
|
let _ = POST_LIST_CACHE.insert(CacheKey::TotalPublishedPosts, (vec![], total)).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
pub async fn get_tag_list() -> Option<Vec<Tag>> {
|
pub async fn get_tag_list() -> Option<Vec<Tag>> {
|
||||||
TAG_LIST_CACHE.get(&CacheKey::AllTags).await
|
TAG_LIST_CACHE.get(&CacheKey::AllTags).await
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user