From 2d373da4c5f452bede2fbe6fae651c5a6acecd4c Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Wed, 25 Jun 2025 18:03:44 +0200 Subject: [PATCH] stat page and cache working --- markdown_backend/src/main.rs | 5 ++ markdown_backend/src/markdown.rs | 67 ++++++++++++++++++-- src/app/admin/manage/page.tsx | 10 +++ src/app/admin/manage/rust-status.tsx | 74 ++++++++++++++++++++++ src/app/admin/manage/rust-status/page.tsx | 75 +++++++++++++++++++++++ src/app/api/admin/posts/route.ts | 22 +++++++ 6 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 src/app/admin/manage/rust-status.tsx create mode 100644 src/app/admin/manage/rust-status/page.tsx diff --git a/markdown_backend/src/main.rs b/markdown_backend/src/main.rs index 6e7b80c..8dc14fc 100644 --- a/markdown_backend/src/main.rs +++ b/markdown_backend/src/main.rs @@ -25,6 +25,8 @@ enum Commands { }, /// Watch for changes in the posts directory Watch, + /// Show Rust parser statistics + Rsparseinfo, } fn main() { @@ -65,5 +67,8 @@ fn main() { std::thread::sleep(std::time::Duration::from_secs(60)); } } + Commands::Rsparseinfo => { + println!("{}", markdown::rsparseinfo()); + } } } \ No newline at end of file diff --git a/markdown_backend/src/markdown.rs b/markdown_backend/src/markdown.rs index 15bfcc5..6e3a529 100644 --- a/markdown_backend/src/markdown.rs +++ b/markdown_backend/src/markdown.rs @@ -11,11 +11,14 @@ use ammonia::clean; use slug::slugify; use notify::{RecursiveMode, RecommendedWatcher, Watcher, Config}; use std::sync::mpsc::channel; -use std::time::Duration; +use std::time::{Duration, Instant}; use syntect::highlighting::{ThemeSet, Style}; use syntect::parsing::SyntaxSet; use syntect::html::{highlighted_html_for_string, IncludeBackground}; use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::sync::RwLock; +use serde_json; #[derive(Debug, Deserialize, Clone, serde::Serialize)] pub struct PostFrontmatter { @@ -37,6 +40,19 @@ pub struct Post { pub author: String, } +#[derive(Debug, Clone, serde::Serialize, Default)] +pub struct PostStats { + pub slug: String, + pub cache_hits: u64, + pub cache_misses: u64, + pub last_interpret_time_ms: u128, + pub last_compile_time_ms: u128, +} + +static POST_CACHE: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); +static ALL_POSTS_CACHE: Lazy>>> = Lazy::new(|| RwLock::new(None)); +static POST_STATS: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); + fn get_posts_directory() -> PathBuf { let candidates = [ "./posts", @@ -111,7 +127,27 @@ static AMMONIA: Lazy> = Lazy::new(|| { builder }); +pub fn rsparseinfo() -> String { + let stats = POST_STATS.read().unwrap(); + serde_json::to_string(&stats.values().collect::>()).unwrap_or_else(|_| "[]".to_string()) +} + pub fn get_post_by_slug(slug: &str) -> Result> { + let start = Instant::now(); + let mut stats = POST_STATS.write().unwrap(); + let entry = stats.entry(slug.to_string()).or_insert_with(|| PostStats { + slug: slug.to_string(), + ..Default::default() + }); + // Try cache first + if let Some(post) = POST_CACHE.read().unwrap().get(slug).cloned() { + entry.cache_hits += 1; + entry.last_interpret_time_ms = 0; + entry.last_compile_time_ms = 0; + return Ok(post); + } + entry.cache_misses += 1; + drop(stats); // Release lock before heavy work let posts_dir = get_posts_directory(); let file_path = posts_dir.join(format!("{}.md", slug)); let file_content = fs::read_to_string(&file_path)?; @@ -202,7 +238,9 @@ pub fn get_post_by_slug(slug: &str) -> Result> let sanitized_html = AMMONIA.clean(&html_output).to_string(); - Ok(Post { + let interpret_time = start.elapsed(); + let compile_start = Instant::now(); + let post = Post { slug: slug.to_string(), title: front.title, date: front.date, @@ -211,10 +249,26 @@ pub fn get_post_by_slug(slug: &str) -> Result> content: sanitized_html, created_at: created_at.to_rfc3339(), author: std::env::var("BLOG_OWNER").unwrap_or_else(|_| "Anonymous".to_string()), - }) + }; + let compile_time = compile_start.elapsed(); + // Insert into cache + POST_CACHE.write().unwrap().insert(slug.to_string(), post.clone()); + // Update stats + let mut stats = POST_STATS.write().unwrap(); + let entry = stats.entry(slug.to_string()).or_insert_with(|| PostStats { + slug: slug.to_string(), + ..Default::default() + }); + entry.last_interpret_time_ms = interpret_time.as_millis(); + entry.last_compile_time_ms = compile_time.as_millis(); + Ok(post) } pub fn get_all_posts() -> Result, Box> { + // Try cache first + if let Some(posts) = ALL_POSTS_CACHE.read().unwrap().clone() { + return Ok(posts); + } let posts_dir = get_posts_directory(); let mut posts = Vec::new(); for entry in fs::read_dir(posts_dir)? { @@ -228,6 +282,8 @@ pub fn get_all_posts() -> Result, Box> { } } posts.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + // Cache the result + *ALL_POSTS_CACHE.write().unwrap() = Some(posts.clone()); Ok(posts) } @@ -240,11 +296,13 @@ pub fn watch_posts(on_change: F) -> notify::Result { + // Invalidate caches on any change + POST_CACHE.write().unwrap().clear(); + *ALL_POSTS_CACHE.write().unwrap() = None; on_change(); }, Err(e) => { @@ -254,6 +312,5 @@ pub fn watch_posts(on_change: F) -> notify::Result + + + + + Rust Parser Status +