From f745ce9413b3101177e34f3a0ec67c7681ab4474 Mon Sep 17 00:00:00 2001 From: udo Date: Sat, 18 Jul 2026 06:16:49 +0000 Subject: [PATCH] Deduplicate entry freshness filesystem work --- docs/wasm-runtime-architecture.md | 3 +++ scripts/test_dependency_invalidation.sh | 10 ++++++++++ src/lib/compiler.cpp | 24 ++++++++++++++++++------ src/wasm/backend.cpp | 2 +- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/wasm-runtime-architecture.md b/docs/wasm-runtime-architecture.md index 05edefd..b594bbc 100644 --- a/docs/wasm-runtime-architecture.md +++ b/docs/wasm-runtime-architecture.md @@ -209,6 +209,9 @@ The same snapshot divides pre-dispatch WASM readiness into entry normalization, mutation freshness, artifact stat, complete dependency freshness, and worker availability. `ready_check_count` distinguishes the warm one-check path from an on-demand compile and recheck; repeated snapshot reads retain the initial values. +Freshness still stats every distinct source on every entry check. Exact repeated +load paths are deduplicated before canonicalization, while distinct aliases are +resolved independently so symlink retargets remain immediately visible. When a current serialized module exists, the worker scans wasm section headers and reads only `dylink.0` and `uce.abi`; it does not fault the multi-megabyte code and data bodies into every new worker. A missing/stale/invalid serialized module diff --git a/scripts/test_dependency_invalidation.sh b/scripts/test_dependency_invalidation.sh index f13da44..dde483d 100755 --- a/scripts/test_dependency_invalidation.sh +++ b/scripts/test_dependency_invalidation.sh @@ -83,6 +83,16 @@ assert_marker symlink-parent symlink-marker-a ln -sfn "symlink-target-b.uce" "$source_dir/symlink-child.uce" assert_marker symlink-parent symlink-marker-b +# A repeated exact transitive load is signature-deduplicated before path +# canonicalization, but the shared dependency must still invalidate the parent. +printf '%s\n' '#ifndef UCE_DIAMOND_COMMON' '#define UCE_DIAMOND_COMMON' 'String diamond_marker() { return("diamond-a"); }' '#endif' >"$source_dir/diamond-common.uce" +printf '%s\n' '#load "diamond-common.uce"' >"$source_dir/diamond-left.uce" +printf '%s\n' '#load "diamond-common.uce"' >"$source_dir/diamond-right.uce" +printf '%s\n' '#load "diamond-left.uce"' '#load "diamond-right.uce"' 'CLI(Request& context) { print(diamond_marker()); }' >"$source_dir/diamond-parent.uce" +assert_marker diamond-parent diamond-a +sed -i 's/diamond-a/diamond-b/' "$source_dir/diamond-common.uce" +assert_marker diamond-parent diamond-b + # HTTP entry units can resolve route/components dynamically. A changed dynamic # component must enter the demand-priority queue just like a changed entry unit; # otherwise a common dependency rebuild can leave the requested page stale for diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index 19cfc3e..b43fff2 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -216,6 +216,11 @@ UnitSourceSignatureEntry compiler_unit_source_entry(String file_name, bool allow void compiler_append_unit_source_signature(String file_name, std::set& visited, String& signature, bool allow_recent_stat) { + // The signature already deduplicates canonical files. Skip repeated exact + // load paths before resolving them again; distinct aliases are still + // canonicalized so symlink retargets remain visible on the next check. + if(visited.find(file_name) != visited.end()) + return; String normalized = file_name; UnitSourceSignatureEntry entry; bool recent = allow_recent_stat && compiler_recent_unit_source_entry(normalized, entry); @@ -226,7 +231,11 @@ void compiler_append_unit_source_signature(String file_name, std::set& v normalized = file_name; } if(visited.find(normalized) != visited.end()) + { + visited.insert(file_name); return; + } + visited.insert(file_name); visited.insert(normalized); if(!recent) @@ -244,9 +253,9 @@ String compiler_unit_source_signature(String file_name, bool allow_recent_stat = return(gen_sha1(signature)); } -String compiler_unit_input_signature(Request* context, SharedUnit* su, bool allow_recent_source_stat = false) +String compiler_unit_input_signature(Request* context, SharedUnit* su, bool allow_recent_source_stat = false, bool source_exists_known = false) { - if(!context || !su || !file_exists(su->file_name)) + if(!context || !su || (!source_exists_known && !file_exists(su->file_name))) return(""); String setup_template = context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"]; @@ -509,9 +518,12 @@ bool compiler_has_known_unit_cached(Request* context, String file_name) SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, SharedUnit* su, bool allow_recent_source_stat = false) { SharedUnitFilesystemState state; - state.source_exists = file_exists(su->file_name); - if(state.source_exists) - state.source_time = file_mtime(su->file_name); + struct stat source_stat; + if(stat(su->file_name.c_str(), &source_stat) == 0) + { + state.source_exists = true; + state.source_time = source_stat.st_mtime; + } state.setup_template_time = file_mtime( context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"] @@ -519,7 +531,7 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share state.compiler_abi_time = compiler_runtime_abi_time(context); state.metadata_time = file_mtime(su->meta_file_name); state.compile_output_time = file_mtime(su->compile_output_file_name); - state.current_input_signature = compiler_unit_input_signature(context, su, allow_recent_source_stat); + state.current_input_signature = compiler_unit_input_signature(context, su, allow_recent_source_stat, state.source_exists); state.metadata_exists = (state.metadata_time != 0); state.compile_output_exists = (state.compile_output_time != 0); if(state.metadata_exists) diff --git a/src/wasm/backend.cpp b/src/wasm/backend.cpp index 38b3eaf..257d577 100644 --- a/src/wasm/backend.cpp +++ b/src/wasm/backend.cpp @@ -129,7 +129,7 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit) // metadata mismatches, which can leave stale wasm with old imports. bool source_missing = false; phase_started = time_precise(); - if(compiler_unit_needs_recompile(context, entry_unit, &source_missing)) + if(compiler_unit_needs_recompile(context, entry_unit, &source_missing, false, true)) { context->stats.wasm_ready_freshness_us += (u64)((time_precise() - phase_started) * 1000000.0); compiler_prioritize_unit(context, entry_unit);