fix: still working on rendering speed

This commit is contained in:
2026-07-24 07:10:57 +02:00
parent 473c48937d
commit d9085cf681
7 changed files with 176 additions and 123 deletions

View File

@@ -337,7 +337,11 @@ impl TaskManager {
impl Default for TaskManager { impl Default for TaskManager {
fn default() -> Self { fn default() -> Self {
Self::new(3) Self::new(
std::thread::available_parallelism()
.map(usize::from)
.unwrap_or(1),
)
} }
} }
@@ -358,6 +362,19 @@ mod tests {
assert_eq!(mgr.status(id), Some(TaskStatus::Running)); assert_eq!(mgr.status(id), Some(TaskStatus::Running));
} }
#[test]
fn default_uses_every_online_worker_like_bds2() {
let mgr = TaskManager::default();
let expected = std::thread::available_parallelism()
.map(usize::from)
.unwrap_or(1);
for index in 0..expected {
mgr.submit(&format!("task {index}"));
}
assert_eq!(mgr.running_count(), expected);
}
#[test] #[test]
fn max_concurrent_enforced() { fn max_concurrent_enforced() {
let mgr = TaskManager::new(3); let mgr = TaskManager::new(3);

View File

@@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex, OnceLock};
use liquid::ParserBuilder; use liquid::ParserBuilder;
use liquid::partials::{EagerCompiler, InMemorySource}; use liquid::partials::{EagerCompiler, InMemorySource};
use liquid_core::model::ScalarCow; use liquid_core::model::{Object, ScalarCow, ValueCow};
use liquid_core::parser::FilterArguments; use liquid_core::parser::FilterArguments;
use liquid_core::{ use liquid_core::{
Display_filter, Expression, Filter, FilterParameters, FilterReflection, FromFilterParameters, Display_filter, Expression, Filter, FilterParameters, FilterReflection, FromFilterParameters,
@@ -62,6 +62,21 @@ impl LiquidRenderer {
let globals = liquid::to_object(context)?; let globals = liquid::to_object(context)?;
Ok(template.0.render(&globals)?) Ok(template.0.render(&globals)?)
} }
pub(crate) fn render_with_base<T: Serialize>(
&self,
template: &CompiledLiquidTemplate,
base: &Object,
context: &T,
) -> Result<String, RenderError> {
let page = liquid::to_object(context)?;
let globals = base
.iter()
.chain(page.iter())
.map(|(key, value)| (key.to_string(), ValueCow::from(value)))
.collect::<HashMap<_, _>>();
Ok(template.0.render(&globals)?)
}
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@@ -261,15 +276,10 @@ impl Filter for MarkdownFilter {
.and_then(|value| value.as_scalar()) .and_then(|value| value.as_scalar())
.map(|scalar| scalar.to_kstr().to_string()) .map(|scalar| scalar.to_kstr().to_string())
.unwrap_or_default(); .unwrap_or_default();
let macro_context = MacroRenderContext {
roots: collect_macro_roots(runtime),
post_id: post_id.clone(),
host: Arc::clone(&self.host),
};
let key = MarkdownCacheKey { let key = MarkdownCacheKey {
content_hash: content_hash(markdown.as_bytes()), content_hash: content_hash(markdown.as_bytes()),
language, language,
post_id, post_id: post_id.clone(),
}; };
let entry = { let entry = {
let mut cache = self.cache.lock().unwrap_or_else(|error| error.into_inner()); let mut cache = self.cache.lock().unwrap_or_else(|error| error.into_inner());
@@ -278,8 +288,17 @@ impl Filter for MarkdownFilter {
Ok(Value::scalar( Ok(Value::scalar(
entry entry
.get_or_init(|| { .get_or_init(|| {
let expanded = expand_builtin_macros(markdown.as_str(), &macro_context); let rendered = if markdown.contains("[[") {
let rendered = render_markdown_to_html(&expanded); let macro_context = MacroRenderContext {
roots: collect_macro_roots(runtime),
post_id,
host: Arc::clone(&self.host),
};
let expanded = expand_builtin_macros(markdown.as_str(), &macro_context);
render_markdown_to_html(&expanded)
} else {
render_markdown_to_html(markdown.as_str())
};
rewrite_rendered_html_urls(&rendered, &rewrite_context) rewrite_rendered_html_urls(&rendered, &rewrite_context)
}) })
.clone(), .clone(),

View File

@@ -84,7 +84,7 @@ struct TemplateBundle {
struct RenderPostRecord { struct RenderPostRecord {
post: Post, post: Post,
source_post_id: String, source_post_id: String,
body_markdown: String, body_markdown: Arc<str>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -118,13 +118,9 @@ struct LinkContext {
pub struct SiteRenderContext { pub struct SiteRenderContext {
metadata: ProjectMetadata, metadata: ProjectMetadata,
is_preview: bool,
main_language: String, main_language: String,
tags: Vec<Tag>, tags: Vec<Tag>,
category_settings: HashMap<String, CategorySettings>, category_settings: HashMap<String, CategorySettings>,
canonical_media_map: HashMap<String, String>,
project_media: Vec<Value>,
project_tags: Vec<Value>,
render_categories: HashMap<String, RenderCategorySettings>, render_categories: HashMap<String, RenderCategorySettings>,
bundle: TemplateBundle, bundle: TemplateBundle,
languages: Vec<LanguageRenderContext>, languages: Vec<LanguageRenderContext>,
@@ -149,9 +145,9 @@ struct LanguageRenderContext {
linked_media_by_post_id: HashMap<String, Vec<Value>>, linked_media_by_post_id: HashMap<String, Vec<Value>>,
post_data_json_by_id: HashMap<String, Value>, post_data_json_by_id: HashMap<String, Value>,
menu_items: Vec<Value>, menu_items: Vec<Value>,
canonical_post_path_by_slug: HashMap<String, String>,
taxonomy: TaxonomyContext, taxonomy: TaxonomyContext,
links: LinkContext, links: LinkContext,
liquid_globals: liquid::Object,
} }
pub fn build_site_render_artifacts( pub fn build_site_render_artifacts(
@@ -191,7 +187,7 @@ pub fn build_site_route_manifest(
.map(|post| RenderPostRecord { .map(|post| RenderPostRecord {
post: post.clone(), post: post.clone(),
source_post_id: post.id.clone(), source_post_id: post.id.clone(),
body_markdown: String::new(), body_markdown: Arc::from(""),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let list_posts = filter_posts_for_lists(&posts, &category_settings); let list_posts = filter_posts_for_lists(&posts, &category_settings);
@@ -400,6 +396,21 @@ pub fn prepare_site_render_context(
canonical_post_path_by_slug(&posts, &language, &main_language); canonical_post_path_by_slug(&posts, &language, &main_language);
let taxonomy = build_taxonomy_context(&posts, &tags); let taxonomy = build_taxonomy_context(&posts, &tags);
let links = build_link_context(&posts, &post_links, &language, &main_language); let links = build_link_context(&posts, &post_links, &language, &main_language);
let liquid_globals = liquid::to_object(&json!({
"language": language,
"language_prefix": language_prefix(&language, &main_language),
"main_language": main_language,
"is_preview": is_preview,
"macro_scripts": bundle.macro_scripts,
"macro_templates": bundle.macro_templates,
"pico_stylesheet_href": pico_stylesheet_href(metadata),
"menu_items": menu_items,
"canonical_post_path_by_slug": canonical_post_path_by_slug,
"canonical_media_path_by_source_path": canonical_media_map,
"project": { "media": project_media },
"Tags": project_tags,
"tag_color_by_name": taxonomy.tag_colors,
}))?;
language_contexts.push(LanguageRenderContext { language_contexts.push(LanguageRenderContext {
language, language,
posts, posts,
@@ -407,21 +418,17 @@ pub fn prepare_site_render_context(
linked_media_by_post_id, linked_media_by_post_id,
post_data_json_by_id, post_data_json_by_id,
menu_items, menu_items,
canonical_post_path_by_slug,
taxonomy, taxonomy,
links, links,
liquid_globals,
}); });
} }
Ok(SiteRenderContext { Ok(SiteRenderContext {
metadata: metadata.clone(), metadata: metadata.clone(),
is_preview,
main_language, main_language,
tags, tags,
category_settings, category_settings,
canonical_media_map,
project_media,
project_tags,
render_categories, render_categories,
bundle, bundle,
languages: language_contexts, languages: language_contexts,
@@ -467,15 +474,10 @@ pub fn build_site_render_artifacts_from_context(
metadata, metadata,
language, language,
&context.category_settings, &context.category_settings,
&language_context.menu_items,
&language_context.canonical_post_path_by_slug,
&language_context.taxonomy, &language_context.taxonomy,
&language_context.post_data_json_by_id, &language_context.post_data_json_by_id,
&context.canonical_media_map,
&context.project_media,
&context.project_tags,
&context.bundle, &context.bundle,
context.is_preview, &language_context.liquid_globals,
) )
.map(|html| { .map(|html| {
on_page_rendered(&route.url_path); on_page_rendered(&route.url_path);
@@ -556,21 +558,15 @@ pub fn build_site_render_artifacts_from_context(
render_post_route( render_post_route(
metadata, metadata,
language, language,
main_language,
record, record,
&context.tags, &context.tags,
&context.render_categories, &context.render_categories,
&language_context.linked_media_by_post_id, &language_context.linked_media_by_post_id,
&language_context.links, &language_context.links,
&language_context.canonical_post_path_by_slug,
&language_context.menu_items,
&language_context.taxonomy, &language_context.taxonomy,
&language_context.post_data_json_by_id, &language_context.post_data_json_by_id,
&context.canonical_media_map,
&context.project_media,
&context.project_tags,
&context.bundle, &context.bundle,
context.is_preview, &language_context.liquid_globals,
) )
.map(|html| { .map(|html| {
on_page_rendered(url_path); on_page_rendered(url_path);
@@ -811,7 +807,7 @@ fn load_language_posts(
Some(PostLanguageVariant::Base) => posts.push(RenderPostRecord { Some(PostLanguageVariant::Base) => posts.push(RenderPostRecord {
post: post.clone(), post: post.clone(),
source_post_id: post.id.clone(), source_post_id: post.id.clone(),
body_markdown: body.clone(), body_markdown: Arc::from(body.as_str()),
}), }),
Some(PostLanguageVariant::Translation) => { Some(PostLanguageVariant::Translation) => {
let Some(translation) = translation else { let Some(translation) = translation else {
@@ -837,7 +833,7 @@ fn load_language_posts(
posts.push(RenderPostRecord { posts.push(RenderPostRecord {
post: translated_post, post: translated_post,
source_post_id: post.id.clone(), source_post_id: post.id.clone(),
body_markdown: translated_body, body_markdown: Arc::from(translated_body),
}); });
} }
None => {} None => {}
@@ -1141,35 +1137,22 @@ fn render_list_route(
metadata: &ProjectMetadata, metadata: &ProjectMetadata,
language: &str, language: &str,
category_settings: &HashMap<String, CategorySettings>, category_settings: &HashMap<String, CategorySettings>,
menu_items: &[Value],
canonical_post_path_by_slug: &HashMap<String, String>,
taxonomy: &TaxonomyContext, taxonomy: &TaxonomyContext,
post_data_json_by_id: &HashMap<String, Value>, post_data_json_by_id: &HashMap<String, Value>,
canonical_media_path_by_source_path: &HashMap<String, String>,
project_media: &[Value],
project_tags: &[Value],
bundle: &TemplateBundle, bundle: &TemplateBundle,
is_preview: bool, liquid_globals: &liquid::Object,
) -> Result<String, Box<dyn Error + Send + Sync>> { ) -> Result<String, Box<dyn Error + Send + Sync>> {
let main_language = main_language(metadata); let post_data_json_by_id = page_post_data(&route.posts, post_data_json_by_id);
let list_template = route let list_template = route
.list_template_slug .list_template_slug
.as_deref() .as_deref()
.and_then(|slug| bundle.list_template_by_slug.get(slug)) .and_then(|slug| bundle.list_template_by_slug.get(slug))
.unwrap_or(&bundle.default_list_template); .unwrap_or(&bundle.default_list_template);
let context = json!({ let context = json!({
"language": language,
"language_prefix": language_prefix(language, main_language),
"main_language": main_language,
"is_preview": is_preview,
"macro_scripts": bundle.macro_scripts,
"macro_templates": bundle.macro_templates,
"html_theme_attribute": serde_json::Value::Null, "html_theme_attribute": serde_json::Value::Null,
"page_title": route.page_title, "page_title": route.page_title,
"pico_stylesheet_href": pico_stylesheet_href(metadata),
"blog_languages": build_list_blog_languages(metadata, language, &route.url_path), "blog_languages": build_list_blog_languages(metadata, language, &route.url_path),
"alternate_links": build_alternate_list_links(metadata, &route.url_path), "alternate_links": build_alternate_list_links(metadata, &route.url_path),
"menu_items": menu_items,
"calendar_initial_year": route.posts.first().map(|post| calendar_initial_parts(&post.post).0).unwrap_or(1970), "calendar_initial_year": route.posts.first().map(|post| calendar_initial_parts(&post.post).0).unwrap_or(1970),
"calendar_initial_month": route.posts.first().map(|post| calendar_initial_parts(&post.post).1).unwrap_or(1), "calendar_initial_month": route.posts.first().map(|post| calendar_initial_parts(&post.post).1).unwrap_or(1),
"archive_context": route.archive_context, "archive_context": route.archive_context,
@@ -1188,41 +1171,32 @@ fn render_list_route(
"total_pages": route.total_pages, "total_pages": route.total_pages,
"total_items": route.total_items, "total_items": route.total_items,
"items_per_page": route.items_per_page, "items_per_page": route.items_per_page,
"canonical_post_path_by_slug": canonical_post_path_by_slug,
"canonical_media_path_by_source_path": canonical_media_path_by_source_path,
"post_data_json_by_id": post_data_json_by_id, "post_data_json_by_id": post_data_json_by_id,
"project": { "media": project_media },
"Tags": project_tags,
"post_categories": taxonomy.categories, "post_categories": taxonomy.categories,
"post_tags": taxonomy.tags, "post_tags": taxonomy.tags,
"tag_color_by_name": taxonomy.tag_colors,
"backlinks": Vec::<Value>::new(), "backlinks": Vec::<Value>::new(),
"not_found_message": serde_json::Value::Null, "not_found_message": serde_json::Value::Null,
"not_found_back_label": serde_json::Value::Null, "not_found_back_label": serde_json::Value::Null,
}); });
Ok(bundle.renderer.render(list_template, &context)?) Ok(bundle
.renderer
.render_with_base(list_template, liquid_globals, &context)?)
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn render_post_route( fn render_post_route(
metadata: &ProjectMetadata, metadata: &ProjectMetadata,
language: &str, language: &str,
main_language: &str,
record: &RenderPostRecord, record: &RenderPostRecord,
tags: &[Tag], tags: &[Tag],
render_categories: &HashMap<String, RenderCategorySettings>, render_categories: &HashMap<String, RenderCategorySettings>,
linked_media_by_post_id: &HashMap<String, Vec<Value>>, linked_media_by_post_id: &HashMap<String, Vec<Value>>,
links: &LinkContext, links: &LinkContext,
canonical_post_path_by_slug: &HashMap<String, String>,
menu_items: &[Value],
taxonomy: &TaxonomyContext, taxonomy: &TaxonomyContext,
post_data_json_by_id: &HashMap<String, Value>, post_data_json_by_id: &HashMap<String, Value>,
canonical_media_path_by_source_path: &HashMap<String, String>,
project_media: &[Value],
project_tags: &[Value],
bundle: &TemplateBundle, bundle: &TemplateBundle,
is_preview: bool, liquid_globals: &liquid::Object,
) -> Result<String, Box<dyn Error + Send + Sync>> { ) -> Result<String, Box<dyn Error + Send + Sync>> {
let resolved = resolve_post_template(RenderTemplateLookup { let resolved = resolve_post_template(RenderTemplateLookup {
post: &record.post, post: &record.post,
@@ -1257,32 +1231,20 @@ fn render_post_route(
.get(&record.source_post_id) .get(&record.source_post_id)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
let post_data_json_by_id = page_post_data(std::slice::from_ref(record), post_data_json_by_id);
let context = json!({ let context = json!({
"language": language,
"language_prefix": language_prefix(language, main_language),
"main_language": main_language,
"is_preview": is_preview,
"macro_scripts": bundle.macro_scripts,
"macro_templates": bundle.macro_templates,
"page_title": record.post.title, "page_title": record.post.title,
"pico_stylesheet_href": pico_stylesheet_href(metadata),
"html_theme_attribute": serde_json::Value::Null, "html_theme_attribute": serde_json::Value::Null,
"alternate_links": build_alternate_post_links(&record.post, metadata), "alternate_links": build_alternate_post_links(&record.post, metadata),
"blog_languages": build_post_blog_languages(&record.post, metadata, language), "blog_languages": build_post_blog_languages(&record.post, metadata, language),
"menu_items": menu_items,
"calendar_initial_year": calendar_initial_parts(&record.post).0, "calendar_initial_year": calendar_initial_parts(&record.post).0,
"calendar_initial_month": calendar_initial_parts(&record.post).1, "calendar_initial_month": calendar_initial_parts(&record.post).1,
"post": post_context(&record.post, &record.body_markdown, linked_media, outgoing_link_context, incoming_link_context), "post": post_context(&record.post, record.body_markdown.as_ref(), linked_media, outgoing_link_context, incoming_link_context),
"post_categories": taxonomy_items_for_categories(&record.post.categories, taxonomy), "post_categories": taxonomy_items_for_categories(&record.post.categories, taxonomy),
"post_tags": taxonomy_items_for_tags(&record.post.tags, taxonomy, tags), "post_tags": taxonomy_items_for_tags(&record.post.tags, taxonomy, tags),
"tag_color_by_name": taxonomy.tag_colors,
"backlinks": backlinks, "backlinks": backlinks,
"canonical_post_path_by_slug": canonical_post_path_by_slug,
"canonical_media_path_by_source_path": canonical_media_path_by_source_path,
"post_data_json_by_id": post_data_json_by_id, "post_data_json_by_id": post_data_json_by_id,
"project": { "media": project_media },
"Tags": project_tags,
"day_blocks": Vec::<Value>::new(), "day_blocks": Vec::<Value>::new(),
"archive_context": serde_json::Value::Null, "archive_context": serde_json::Value::Null,
"show_archive_range_heading": false, "show_archive_range_heading": false,
@@ -1299,7 +1261,9 @@ fn render_post_route(
"not_found_back_label": serde_json::Value::Null, "not_found_back_label": serde_json::Value::Null,
}); });
Ok(bundle.renderer.render(&template, &context)?) Ok(bundle
.renderer
.render_with_base(&template, liquid_globals, &context)?)
} }
fn render_not_found_route( fn render_not_found_route(
@@ -1521,7 +1485,7 @@ fn resolve_list_content(
if show_title && !excerpt.is_empty() { if show_title && !excerpt.is_empty() {
record.post.excerpt.clone().unwrap_or_default() record.post.excerpt.clone().unwrap_or_default()
} else { } else {
record.body_markdown.clone() record.body_markdown.to_string()
} }
} }
@@ -1622,6 +1586,20 @@ fn build_post_data_json_by_id(
.collect() .collect()
} }
fn page_post_data<'a>(
posts: &'a [RenderPostRecord],
post_data_json_by_id: &'a HashMap<String, Value>,
) -> HashMap<&'a str, &'a Value> {
posts
.iter()
.filter_map(|record| {
post_data_json_by_id
.get(&record.post.id)
.map(|value| (record.post.id.as_str(), value))
})
.collect()
}
fn build_published_tag_counts(posts: &[(Post, String)], tags: &[Tag]) -> Vec<Value> { fn build_published_tag_counts(posts: &[(Post, String)], tags: &[Tag]) -> Vec<Value> {
let mut counts = HashMap::<String, usize>::new(); let mut counts = HashMap::<String, usize>::new();
for (post, _) in posts { for (post, _) in posts {

View File

@@ -480,6 +480,50 @@ fn section_generation_reports_its_urls_and_defers_pagefind() {
assert!(!dir.path().join(old_fragment).exists()); assert!(!dir.path().join(old_fragment).exists());
} }
#[test]
fn list_pages_receive_only_their_page_post_data_like_bds2() {
let (db, dir) = setup();
insert_template(
db.conn(),
&make_list_template("list", "POST_DATA={{ post_data_json_by_id | size }}"),
)
.unwrap();
let mut metadata = make_metadata();
metadata.max_posts_per_page = 1;
let posts = vec![
PublishedPostSource {
post: make_post("hello", 1_710_000_000_000),
body_markdown: "Hello".into(),
},
PublishedPostSource {
post: make_post("next", 1_710_086_400_000),
body_markdown: "Next".into(),
},
];
render_site_section_with_progress(
db.conn(),
dir.path(),
"p1",
&metadata,
&posts,
GenerationSection::Core,
|_, _, _| {},
|_| {},
|| false,
)
.unwrap();
assert_eq!(
std::fs::read_to_string(dir.path().join("index.html")).unwrap(),
"POST_DATA=1"
);
assert_eq!(
std::fs::read_to_string(dir.path().join("page/2/index.html")).unwrap(),
"POST_DATA=1"
);
}
#[test] #[test]
fn section_generation_reports_while_html_is_rendered_before_files_are_written() { fn section_generation_reports_while_html_is_rendered_before_files_are_written() {
let (db, dir) = setup(); let (db, dir) = setup();

View File

@@ -11210,14 +11210,21 @@ mod tests {
.iter() .iter()
.all(|task| task.group_name.as_deref() == Some("Render Site")) .all(|task| task.group_name.as_deref() == Some("Render Site"))
); );
assert_eq!( let running = std::thread::available_parallelism()
snapshots .map(usize::from)
.unwrap_or(1)
.min(snapshots.len());
assert!(
snapshots[..running]
.iter() .iter()
.map(|task| task.status.clone()) .all(|task| task.status == Running)
.collect::<Vec<_>>(),
vec![Running, Running, Running, Pending, Pending]
); );
assert!(snapshots[..3].iter().all(|task| { assert!(
snapshots[running..]
.iter()
.all(|task| task.status == Pending)
);
assert!(snapshots[..running].iter().all(|task| {
task.progress.is_some() task.progress.is_some()
&& task && task
.message .message

View File

@@ -149,7 +149,6 @@ impl BdsApp {
); );
let mut render_task_ids = Vec::new(); let mut render_task_ids = Vec::new();
let mut tasks = Vec::new(); let mut tasks = Vec::new();
let prepared_generation = Arc::new(std::sync::OnceLock::new());
for section in sections { for section in sections {
let label = t(self.ui_locale, generation_section_label_key(section)); let label = t(self.ui_locale, generation_section_label_key(section));
@@ -174,7 +173,6 @@ impl BdsApp {
let task_data_dir = data_dir.clone(); let task_data_dir = data_dir.clone();
let task_group_id = group_id.clone(); let task_group_id = group_id.clone();
let task_validation = validation.clone(); let task_validation = validation.clone();
let task_prepared_generation = Arc::clone(&prepared_generation);
let locale = self.ui_locale; let locale = self.ui_locale;
tasks.push(Task::perform( tasks.push(Task::perform(
async move { async move {
@@ -187,7 +185,6 @@ impl BdsApp {
task_id, task_id,
section, section,
task_validation, task_validation,
task_prepared_generation,
force, force,
locale, locale,
page_work, page_work,
@@ -497,9 +494,6 @@ fn run_site_generation_section(
task_id: TaskId, task_id: TaskId,
section: engine::generation::GenerationSection, section: engine::generation::GenerationSection,
validation: Option<engine::validate_site::SiteValidationReport>, validation: Option<engine::validate_site::SiteValidationReport>,
prepared_generation: Arc<
std::sync::OnceLock<Result<Arc<engine::generation::PreparedSiteGeneration>, String>>,
>,
force: bool, force: bool,
locale: UiLocale, locale: UiLocale,
expected_pages: usize, expected_pages: usize,
@@ -517,33 +511,26 @@ fn run_site_generation_section(
)), )),
); );
let db = Database::open(&db_path).map_err(|error| error.to_string())?; let db = Database::open(&db_path).map_err(|error| error.to_string())?;
let prepared = prepared_generation let metadata = engine::meta::read_project_json(&data_dir).map_err(|error| error.to_string())?;
.get_or_init(|| { let posts = bds_core::db::queries::post::list_posts_by_project(db.conn(), &project_id)
let metadata = .map_err(|error| error.to_string())?;
engine::meta::read_project_json(&data_dir).map_err(|error| error.to_string())?; let sources = posts
let posts = bds_core::db::queries::post::list_posts_by_project(db.conn(), &project_id) .into_iter()
.map_err(|error| error.to_string())?; .filter(engine::generation::has_published_snapshot)
let sources = posts .map(|post| engine::generation::load_published_post_source(&data_dir, post))
.into_iter() .collect::<Result<Vec<_>, _>>()
.filter(engine::generation::has_published_snapshot) .map_err(|error| error.to_string())?
.map(|post| engine::generation::load_published_post_source(&data_dir, post)) .into_iter()
.collect::<Result<Vec<_>, _>>() .flatten()
.map_err(|error| error.to_string())? .collect::<Vec<_>>();
.into_iter() let prepared = engine::generation::prepare_site_generation(
.flatten() db.conn(),
.collect::<Vec<_>>(); &data_dir,
engine::generation::prepare_site_generation( &project_id,
db.conn(), &metadata,
&data_dir, &sources,
&project_id, )
&metadata, .map_err(|error| error.to_string())?;
&sources,
)
.map(Arc::new)
.map_err(|error| error.to_string())
})
.as_ref()
.map_err(Clone::clone)?;
let output_dir = data_dir.join("html"); let output_dir = data_dir.join("html");
std::fs::create_dir_all(&output_dir).map_err(|error| error.to_string())?; std::fs::create_dir_all(&output_dir).map_err(|error| error.to_string())?;
let render_manager = Arc::clone(&task_manager); let render_manager = Arc::clone(&task_manager);
@@ -578,7 +565,7 @@ fn run_site_generation_section(
db.conn(), db.conn(),
&output_dir, &output_dir,
&project_id, &project_id,
prepared, &prepared,
&validation, &validation,
section, section,
on_page, on_page,
@@ -589,7 +576,7 @@ fn run_site_generation_section(
db.conn(), db.conn(),
&output_dir, &output_dir,
&project_id, &project_id,
prepared, &prepared,
section, section,
force, force,
&on_rendered, &on_rendered,

View File

@@ -54,7 +54,8 @@ surface TaskSurface {
} }
config { config {
max_concurrent: Integer = 3 -- Runtime default: all online CPU workers, matching bDS2.
max_concurrent: Integer
progress_throttle: Duration = 250.milliseconds progress_throttle: Duration = 250.milliseconds
finished_task_ttl: Duration = 1.hour finished_task_ttl: Duration = 1.hour
recent_finished_limit: Integer = 10 recent_finished_limit: Integer = 10