diff options
| author | Fuwn <[email protected]> | 2026-02-14 22:28:21 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-14 22:28:21 -0800 |
| commit | 37c55340359f8ca52645820fc2891ee56ba96236 (patch) | |
| tree | c04abd5b77b18811bfaccc6d0c0cab09a2d5ca3a /src | |
| parent | feat(blog): Add hidden query flag to trigger manual Notion sync (diff) | |
| download | locus-37c55340359f8ca52645820fc2891ee56ba96236.tar.xz locus-37c55340359f8ca52645820fc2891ee56ba96236.zip | |
perf(blog): Make manual and startup Notion refresh non-blocking with in-progress guard
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/blog/module.rs | 60 |
1 files changed, 47 insertions, 13 deletions
diff --git a/src/modules/blog/module.rs b/src/modules/blog/module.rs index b1ca8c6..e3a78ef 100644 --- a/src/modules/blog/module.rs +++ b/src/modules/blog/module.rs @@ -10,7 +10,10 @@ use { url::ROOT_GEMINI_URL, xml::{Item as XmlItem, Writer as XmlWriter}, }, - std::sync::{LazyLock, Mutex, RwLock}, + std::sync::{ + LazyLock, Mutex, RwLock, + atomic::{AtomicBool, Ordering}, + }, }; pub static POSTS: LazyLock<Mutex<Vec<Post>>> = @@ -19,12 +22,11 @@ static BLOG_CATEGORIES: LazyLock<RwLock<Vec<BlogCategory>>> = LazyLock::new(|| RwLock::new(Vec::new())); static BLOG_POSTS: LazyLock<RwLock<Vec<BlogPost>>> = LazyLock::new(|| RwLock::new(Vec::new())); +static REFRESH_IN_PROGRESS: AtomicBool = AtomicBool::new(false); #[allow(clippy::too_many_lines)] pub fn module(router: &mut windmark::router::Router) { - std::thread::spawn(fetch_from_notion) - .join() - .expect("initial Notion fetch failed"); + trigger_background_refresh("initial"); track_mount(router, "/blog", "Fuwn's blogs", |context| { if should_trigger_manual_refresh(context.url.query()) { trigger_manual_refresh(); @@ -126,13 +128,37 @@ fn should_trigger_manual_refresh(query: Option<&str>) -> bool { }) } -fn trigger_manual_refresh() { +fn begin_refresh() -> bool { + REFRESH_IN_PROGRESS + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_ok() +} + +fn finish_refresh() { REFRESH_IN_PROGRESS.store(false, Ordering::Release); } + +fn refresh_once(refresh_origin: &str) { + if !begin_refresh() { + info!( + "{refresh_origin} Notion refresh skipped because another refresh is already in progress" + ); + + return; + } + match std::panic::catch_unwind(fetch_from_notion) { - Ok(()) => info!("manually refreshed blog data from Notion"), - Err(_) => warn!("manual Notion refresh failed"), + Ok(()) => info!("{refresh_origin} refreshed blog data from Notion"), + Err(_) => warn!("{refresh_origin} Notion refresh failed"), } + + finish_refresh(); +} + +fn trigger_background_refresh(refresh_origin: &'static str) { + std::thread::spawn(move || refresh_once(refresh_origin)); } +fn trigger_manual_refresh() { trigger_background_refresh("manual"); } + fn construct_header(post: &BlogPost) -> String { let title_line = format!("# {}", post.title); let has_metadata = post.author.is_some() @@ -453,18 +479,16 @@ pub fn refresh_loop() { loop { std::thread::sleep(std::time::Duration::from_secs(refresh_interval_seconds)); - match std::panic::catch_unwind(fetch_from_notion) { - Ok(()) => info!("refreshed blog data from Notion"), - Err(_) => warn!("failed to refresh blog data from Notion"), - } + refresh_once("scheduled"); } } #[cfg(test)] mod tests { use super::{ - build_global_posts, build_rss_feed, find_visible_post_by_slug, - render_blog_page, should_trigger_manual_refresh, slugify, + begin_refresh, build_global_posts, build_rss_feed, + find_visible_post_by_slug, finish_refresh, render_blog_page, + should_trigger_manual_refresh, slugify, visible_posts_for_blog, BLOG_CATEGORIES, BLOG_POSTS, }; use crate::modules::blog::config::{BlogCategory, BlogPost}; @@ -751,4 +775,14 @@ mod tests { assert!(!should_trigger_manual_refresh(Some("foo=bar"))); assert!(!should_trigger_manual_refresh(None)); } + + #[test] + fn manual_refresh_guard_prevents_parallel_refreshes() { + finish_refresh(); + assert!(begin_refresh()); + assert!(!begin_refresh()); + finish_refresh(); + assert!(begin_refresh()); + finish_refresh(); + } } |