yggdrasil/src/ssr_cache.rs
xfy 962bef2276 fix(ssr-cache): log generation bump and effective SSR_CACHE_SECS
Dioxus 0.7 的增量渲染器把 IncrementalRenderer 封装在 FullstackState 内部
(pub(crate)),应用侧无法拿到句柄调用 invalidate(),世代号机制只是"未来就绪"
状态。实际失效仍依赖 SSR_CACHE_SECS 兜底 TTL,但此前写入路径完全静默,部署者
无法从日志判断"文章已写入、只是 SSR 缓存未失效",易误判成 DB/发布问题。

- bump_global_generation() 增加 info 日志(带 new_generation),诚实说明 Dioxus
  0.7 不读取此值、需等 TTL 过期。
- main.rs 启动时打印生效的 SSR_CACHE_SECS(滞后上界),提示可调小缩短滞后。
2026-06-29 15:42:40 +08:00

84 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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);
/// 原子递增并返回新的全局世代号。
///
/// 任何文章写入操作都会调用本函数,标记 SSR 渲染结果已过期。当前 Dioxus 0.7
/// 不读取此世代号(详见模块文档),实际失效仍依赖 `SSR_CACHE_SECS` 兜底 TTL。
/// 此处打一条 info 日志,既证明写入路径已执行,也提醒部署者此处存在滞后——
/// 避免"我改了文章怎么没变"被误判成 DB/发布链路问题。
pub fn bump_global_generation() -> u64 {
let new = GLOBAL_GENERATION
.fetch_add(1, Ordering::SeqCst)
.wrapping_add(1);
tracing::info!(
new_generation = new,
"SSR 世代号已递增写入路径触发。注意Dioxus 0.7 不读取此值,\
SSR 缓存需等待 SSR_CACHE_SECS 过期后才反映新内容"
);
new
}
/// 返回当前全局世代号。
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);
}
}