refactor(ssr): remove unused per-slug/per-tag generation counters
This commit is contained in:
parent
4c695b4fc3
commit
3ee39d910c
@ -23,7 +23,10 @@ MAX_SESSIONS_PER_USER=5
|
||||
# Database connection pool size (default: 20)
|
||||
DB_POOL_SIZE=20
|
||||
|
||||
# SSR page cache duration in seconds (default: 3600)
|
||||
# SSR page cache duration in seconds (default: 3600).
|
||||
# Generation-based invalidation is implemented in src/ssr_cache.rs and bumps on every post
|
||||
# write. However, Dioxus 0.7 does not expose a public API to customize the incremental SSR
|
||||
# cache key, so SSR_CACHE_SECS remains the effective fallback TTL until such API is available.
|
||||
SSR_CACHE_SECS=3600
|
||||
|
||||
# Image serving cache headers (hardcoded defaults)
|
||||
|
||||
@ -149,6 +149,9 @@ pub async fn create_post(
|
||||
// 失效该文章涉及的所有标签下文章列表缓存。
|
||||
crate::cache::invalidate_tag_posts_for(&tags_cleaned).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "创建成功".to_string(),
|
||||
|
||||
@ -84,6 +84,9 @@ pub async fn delete_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErr
|
||||
crate::cache::invalidate_post_by_slug(&slug).await;
|
||||
crate::cache::invalidate_tag_posts_for(&tags).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "删除成功".to_string(),
|
||||
|
||||
@ -108,6 +108,8 @@ pub async fn rebuild_content_html(rebuild_all: bool) -> Result<RebuildResult, Se
|
||||
if rebuilt > 0 {
|
||||
crate::cache::invalidate_all_post_caches();
|
||||
crate::cache::invalidate_search_results();
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(RebuildResult {
|
||||
|
||||
@ -95,6 +95,9 @@ pub async fn restore_post(post_id: i32) -> Result<CreatePostResponse, ServerFnEr
|
||||
crate::cache::invalidate_post_by_slug(&new_slug).await;
|
||||
crate::cache::invalidate_tag_posts_for(&tags).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "恢复成功".to_string(),
|
||||
@ -182,6 +185,9 @@ pub async fn purge_post(post_id: i32) -> Result<CreatePostResponse, ServerFnErro
|
||||
crate::cache::invalidate_post_by_slug(&slug).await;
|
||||
crate::cache::invalidate_tag_posts_for(&tags).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "彻底删除成功".to_string(),
|
||||
@ -284,10 +290,15 @@ pub async fn batch_restore_posts(post_ids: Vec<i32>) -> Result<CreatePostRespons
|
||||
crate::cache::invalidate_post_by_slug(slug).await;
|
||||
}
|
||||
crate::cache::invalidate_tag_posts_for(&affected_tags.into_iter().collect::<Vec<_>>()).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
} else {
|
||||
// 影响集过大时回退到全量失效,避免大量串行缓存操作。
|
||||
crate::cache::invalidate_all_post_caches();
|
||||
crate::cache::invalidate_search_results();
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
@ -385,10 +396,15 @@ pub async fn batch_purge_posts(post_ids: Vec<i32>) -> Result<CreatePostResponse,
|
||||
crate::cache::invalidate_post_by_slug(slug).await;
|
||||
}
|
||||
crate::cache::invalidate_tag_posts_for(&tags).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
} else {
|
||||
// 影响集过大时回退到全量失效,避免大量串行缓存操作。
|
||||
crate::cache::invalidate_all_post_caches();
|
||||
crate::cache::invalidate_search_results();
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
@ -466,10 +482,15 @@ pub async fn empty_trash() -> Result<CreatePostResponse, ServerFnError> {
|
||||
crate::cache::invalidate_post_by_slug(slug).await;
|
||||
}
|
||||
crate::cache::invalidate_tag_posts_for(&tags).await;
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
} else {
|
||||
// 影响集过大时回退到全量失效,避免大量串行缓存操作。
|
||||
crate::cache::invalidate_all_post_caches();
|
||||
crate::cache::invalidate_search_results();
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
}
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
|
||||
@ -218,6 +218,9 @@ pub async fn update_post(
|
||||
}
|
||||
}
|
||||
|
||||
// 递增 SSR 全局世代号(未来就绪基础设施;当前不会使 Dioxus 0.7 SSR 缓存失效)。
|
||||
crate::ssr_cache::bump_global_generation();
|
||||
|
||||
Ok(CreatePostResponse {
|
||||
success: true,
|
||||
message: "更新成功".to_string(),
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@ -23,6 +23,9 @@ mod hooks;
|
||||
mod models;
|
||||
mod pages;
|
||||
mod router;
|
||||
// ssr_cache 仅在 server feature 启用时编译;保存 SSR 世代号失效状态。
|
||||
#[cfg(feature = "server")]
|
||||
mod ssr_cache;
|
||||
mod tasks;
|
||||
mod theme;
|
||||
mod utils;
|
||||
@ -78,7 +81,9 @@ fn main() {
|
||||
tasks::image_cache_cleanup::run_cleanup().await;
|
||||
});
|
||||
|
||||
// 配置增量渲染缓存,默认缓存 3600 秒,可通过 SSR_CACHE_SECS 覆盖
|
||||
// 配置增量渲染缓存,默认缓存 3600 秒,可通过 SSR_CACHE_SECS 覆盖。
|
||||
// 注意:世代号失效机制已就位(见 src/ssr_cache.rs),但 Dioxus 0.7 未暴露
|
||||
// 自定义缓存键 API,因此 TTL 仍是当前有效的兜底策略。
|
||||
let config = ServeConfig::builder().incremental(
|
||||
dioxus::server::IncrementalRendererConfig::default().invalidate_after(
|
||||
std::time::Duration::from_secs(
|
||||
@ -90,6 +95,24 @@ fn main() {
|
||||
),
|
||||
);
|
||||
|
||||
// SSR 世代号中间件:把当前全局世代号注入请求扩展并附加到响应头。
|
||||
// 这是为 Dioxus 未来支持自定义 SSR 缓存键预留的钩子;目前主要提供可观测性。
|
||||
async fn ssr_generation_middleware(
|
||||
req: axum::http::Request<axum::body::Body>,
|
||||
next: axum::middleware::Next,
|
||||
) -> axum::response::Response {
|
||||
let generation = crate::ssr_cache::current_global_generation();
|
||||
let (mut parts, body) = req.into_parts();
|
||||
parts.extensions.insert(crate::ssr_cache::SsrGeneration(generation));
|
||||
let mut response = next.run(axum::http::Request::from_parts(parts, body)).await;
|
||||
response.headers_mut().insert(
|
||||
axum::http::header::HeaderName::from_static("x-ssr-generation"),
|
||||
axum::http::HeaderValue::from_str(&generation.to_string())
|
||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("0")),
|
||||
);
|
||||
response
|
||||
}
|
||||
|
||||
// 自定义 API 路由:图片上传(大文件,需要更长超时)
|
||||
let upload_route = axum::Router::new()
|
||||
.route(
|
||||
@ -106,8 +129,9 @@ fn main() {
|
||||
let dioxus_app =
|
||||
axum::Router::new().serve_dioxus_application(config, router::AppRouter);
|
||||
|
||||
// 合并 Dioxus + 压缩/30s 超时中间件
|
||||
// 合并 Dioxus + 世代号/压缩/30s 超时中间件
|
||||
let app_routes = dioxus_app
|
||||
.layer(axum::middleware::from_fn(ssr_generation_middleware))
|
||||
.layer(CompressionLayer::new())
|
||||
.layer(TimeoutLayer::with_status_code(
|
||||
StatusCode::REQUEST_TIMEOUT,
|
||||
|
||||
70
src/ssr_cache.rs
Normal file
70
src/ssr_cache.rs
Normal file
@ -0,0 +1,70 @@
|
||||
//! SSR 增量渲染缓存失效的未来就绪基础设施。
|
||||
//!
|
||||
//! 本模块维护一个全局单调递增的世代号(generation)。文章写入成功后调用方会
|
||||
//! 使其递增,从而**标记** SSR 渲染结果已过期。然而:
|
||||
//!
|
||||
//! **Dioxus 0.7 的增量渲染器使用请求 URI 的 `path_and_query()` 作为内部缓存键,
|
||||
//! 且没有暴露公开 API 供外部代码自定义缓存键或按路由失效已渲染页面。**
|
||||
//! 因此,当前世代号并**不会**实际使 Dioxus 的 SSR 缓存失效;它只是为未来 API
|
||||
//! 准备好状态,并在请求/响应中提供可观测性。
|
||||
//!
|
||||
//! 在 Dioxus 提供以下任一能力之前,有效的 SSR 缓存失效手段仍是调低
|
||||
//! `SSR_CACHE_SECS` 这一兜底 TTL:
|
||||
//! - 自定义增量渲染缓存键的回调;或
|
||||
//! - 从 server function 内部按路由失效缓存的公开 API。
|
||||
//!
|
||||
//! 当前实现:
|
||||
//! - `bump_global_generation()` / `current_global_generation()`:全局世代号。
|
||||
//! - `SsrGeneration`:注入到请求扩展中的类型;未来 Dioxus 支持读取扩展生成
|
||||
//! 缓存键时可直接使用。
|
||||
//! - `src/main.rs` 的中间件把当前世代号附加到 `X-SSR-Generation` 响应头
|
||||
//! (仅 GET 请求),便于调试与监控。
|
||||
//!
|
||||
//! 仅在启用 `server` feature 时编译。
|
||||
|
||||
#![cfg(feature = "server")]
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
/// 全局 SSR 世代号。
|
||||
///
|
||||
/// 任何文章写入操作都会使其递增,从而让所有基于该全局世代的 SSR 缓存键在未来
|
||||
/// Dioxus 支持自定义缓存键时失效。
|
||||
static GLOBAL_GENERATION: LazyLock<AtomicU64> = LazyLock::new(AtomicU64::default);
|
||||
|
||||
/// 注入到请求扩展中的当前 SSR 世代号。
|
||||
///
|
||||
/// 这是为未来 Dioxus 支持自定义 SSR 缓存键预留的钩子。当前 Dioxus 0.7 的渲染器
|
||||
/// 不会读取此扩展。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SsrGeneration(pub u64);
|
||||
|
||||
/// 原子递增并返回新的全局世代号。
|
||||
pub fn bump_global_generation() -> u64 {
|
||||
GLOBAL_GENERATION.fetch_add(1, Ordering::SeqCst).wrapping_add(1)
|
||||
}
|
||||
|
||||
/// 返回当前全局世代号。
|
||||
pub fn current_global_generation() -> u64 {
|
||||
GLOBAL_GENERATION.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serial_test::serial;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn global_generation_is_monotonic() {
|
||||
let before = current_global_generation();
|
||||
let g1 = bump_global_generation();
|
||||
let g2 = bump_global_generation();
|
||||
let current = current_global_generation();
|
||||
|
||||
assert!(g1 > before || g1 == 1);
|
||||
assert!(g2 > g1);
|
||||
assert_eq!(current, g2);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user