perf(posts): offload Markdown rendering to spawn_blocking

create/update/rebuild 三处 Markdown 渲染(含 syntect 高亮)移到阻塞
线程池。rebuild 用 spawn_blocking 的 JoinError 替代 catch_unwind 捕获
渲染 panic,避免单条记录拖垮整批。
This commit is contained in:
xfy 2026-06-18 11:18:36 +08:00
parent 45e92795de
commit 033b89ccb8
3 changed files with 22 additions and 8 deletions

View File

@ -76,8 +76,13 @@ pub async fn create_post(
{
let mut client = get_conn().await.map_err(AppError::db_conn)?;
// 渲染 Markdown 为 HTML并提取目录。
let rendered = crate::api::markdown::render_markdown_enhanced(&content_md);
// Markdown 渲染(含 syntect 高亮)是 CPU 密集任务,移到阻塞线程池执行。
let md_for_render = content_md.clone();
let rendered = tokio::task::spawn_blocking(move || {
crate::api::markdown::render_markdown_enhanced(&md_for_render)
})
.await
.map_err(|_| AppError::Internal("Markdown 渲染任务失败"))?;
let content_html = rendered.html;
let toc_html = if rendered.toc_html.is_empty() {
None::<String>

View File

@ -55,10 +55,14 @@ pub async fn rebuild_content_html(rebuild_all: bool) -> Result<RebuildResult, Se
let id: i32 = row.get(0);
let content_md: String = row.get(1);
// 捕获 Markdown 渲染 panic避免单条记录导致整批失败。
let rendered = match std::panic::catch_unwind(|| {
crate::api::markdown::render_markdown_enhanced(&content_md)
}) {
// Markdown 渲染在阻塞线程池执行spawn_blocking 的 JoinError 自动捕获 panic
// 替代原先的 catch_unwind。
let md_for_render = content_md.clone();
let rendered = match tokio::task::spawn_blocking(move || {
crate::api::markdown::render_markdown_enhanced(&md_for_render)
})
.await
{
Ok(r) => r,
Err(_) => {
failed += 1;

View File

@ -40,8 +40,13 @@ pub async fn update_post(
{
let mut client = get_conn().await.map_err(AppError::db_conn)?;
// 重新渲染 Markdown 与目录。
let rendered = crate::api::markdown::render_markdown_enhanced(&content_md);
// Markdown 渲染移到阻塞线程池执行。
let md_for_render = content_md.clone();
let rendered = tokio::task::spawn_blocking(move || {
crate::api::markdown::render_markdown_enhanced(&md_for_render)
})
.await
.map_err(|_| AppError::Internal("Markdown 渲染任务失败"))?;
let content_html = rendered.html;
let toc_html = if rendered.toc_html.is_empty() {
None::<String>