Precompile native modules off request path
This commit is contained in:
@@ -178,6 +178,8 @@ 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 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
|
and data bodies into every new worker. A missing/stale/invalid serialized module
|
||||||
still reads, validates, compiles, and republishes the complete wasm artifact.
|
still reads, validates, compiles, and republishes the complete wasm artifact.
|
||||||
|
The proactive compiler also creates that serialization immediately after source
|
||||||
|
compilation, keeping first-worker native compilation off the request path.
|
||||||
Cold module compilation and deserialization are host work, so `load_unit()`
|
Cold module compilation and deserialization are host work, so `load_unit()`
|
||||||
refreshes the epoch deadline before its first guest call. Otherwise a component
|
refreshes the epoch deadline before its first guest call. Otherwise a component
|
||||||
whose compilation outlasted the guest CPU budget would immediately trap in the
|
whose compilation outlasted the guest CPU budget would immediately trap in the
|
||||||
|
|||||||
@@ -181,9 +181,26 @@ wait "$rebuild_lock_pid"
|
|||||||
# parent CLI request must wait and render the new child, never its stale wasm.
|
# parent CLI request must wait and render the new child, never its stale wasm.
|
||||||
printf '%s\n' 'COMPONENT(Request& context) { print("nested-component-marker-a"); }' >"$source_dir/nested-child.uce"
|
printf '%s\n' 'COMPONENT(Request& context) { print("nested-component-marker-a"); }' >"$source_dir/nested-child.uce"
|
||||||
printf '%s\n' \
|
printf '%s\n' \
|
||||||
'CLI(Request& context) { DValue props; print(component("nested-child", props, context)); }' >"$source_dir/nested-parent.uce"
|
'CLI(Request& context) { DValue props; print(component("nested-child", props, context)); }' \
|
||||||
|
'RENDER(Request& context) { DValue props; print(component("nested-child", props, context)); }' >"$source_dir/nested-parent.uce"
|
||||||
assert_marker nested-parent nested-component-marker-a
|
assert_marker nested-parent nested-component-marker-a
|
||||||
nested_child_wasm="$cache_dir/nested-child.uce.wasm"
|
nested_child_wasm="$cache_dir/nested-child.uce.wasm"
|
||||||
|
|
||||||
|
# A proactive source rebuild must publish the native serialized module before
|
||||||
|
# any request worker has to load the changed unit.
|
||||||
|
nested_parent_cwasm="$cache_dir/nested-parent.uce.cwasm"
|
||||||
|
rm -f "$nested_parent_cwasm"
|
||||||
|
printf '%s\n' \
|
||||||
|
'CLI(Request& context) { DValue props; print(component("nested-child", props, context)); /* proactive-serialization-v2 */ }' \
|
||||||
|
'RENDER(Request& context) { DValue props; print(component("nested-child", props, context)); /* proactive-serialization-v2 */ }' >"$source_dir/nested-parent.uce"
|
||||||
|
curl -fsS -H "Host: $http_host" "http://127.0.0.1/$test_name/nested-parent.uce" >/dev/null
|
||||||
|
deadline=$((SECONDS + 10))
|
||||||
|
while [[ ! -s "$nested_parent_cwasm" && $SECONDS -lt $deadline ]]; do sleep 0.1; done
|
||||||
|
if [[ ! -s "$nested_parent_cwasm" ]]; then
|
||||||
|
echo "proactive compiler did not serialize rebuilt module" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
(
|
(
|
||||||
exec 8>"$nested_child_wasm.lock"
|
exec 8>"$nested_child_wasm.lock"
|
||||||
flock 8
|
flock 8
|
||||||
|
|||||||
+19
-7
@@ -1266,7 +1266,8 @@ void run_proactive_compiler()
|
|||||||
bool source_missing = false;
|
bool source_missing = false;
|
||||||
auto retry_it = retry_after.find(file_name);
|
auto retry_it = retry_after.find(file_name);
|
||||||
bool retry_allowed = (retry_it == retry_after.end() || time_precise() >= retry_it->second);
|
bool retry_allowed = (retry_it == retry_after.end() || time_precise() >= retry_it->second);
|
||||||
if(compiler_unit_needs_recompile(&background_context, file_name, &source_missing) && retry_allowed)
|
bool needs_compile = compiler_unit_needs_recompile(&background_context, file_name, &source_missing);
|
||||||
|
if(needs_compile && retry_allowed)
|
||||||
proactive_compile_queue_push(compile_queue, file_name);
|
proactive_compile_queue_push(compile_queue, file_name);
|
||||||
if(source_missing)
|
if(source_missing)
|
||||||
{
|
{
|
||||||
@@ -1291,14 +1292,15 @@ void run_proactive_compiler()
|
|||||||
auto retry_it = retry_after.find(file_name);
|
auto retry_it = retry_after.find(file_name);
|
||||||
if(retry_it != retry_after.end() && time_precise() < retry_it->second)
|
if(retry_it != retry_after.end() && time_precise() < retry_it->second)
|
||||||
continue;
|
continue;
|
||||||
|
bool failed = false;
|
||||||
|
String wasm_path = server_state.config["BIN_DIRECTORY"] + file_name + ".wasm";
|
||||||
if(compiler_unit_needs_recompile(&background_context, file_name, &source_missing))
|
if(compiler_unit_needs_recompile(&background_context, file_name, &source_missing))
|
||||||
{
|
{
|
||||||
printf("(i) proactive compile %s\n", file_name.c_str());
|
printf("(i) proactive compile %s\n", file_name.c_str());
|
||||||
auto su = get_shared_unit(&background_context, file_name);
|
auto su = get_shared_unit(&background_context, file_name);
|
||||||
if(su && su->compiler_messages == "")
|
failed = !su || su->compiler_messages != "";
|
||||||
retry_after.erase(file_name);
|
if(su)
|
||||||
else
|
wasm_path = su->wasm_name;
|
||||||
retry_after[file_name] = time_precise() + failure_retry_interval;
|
|
||||||
}
|
}
|
||||||
else if(source_missing)
|
else if(source_missing)
|
||||||
{
|
{
|
||||||
@@ -1306,10 +1308,20 @@ void run_proactive_compiler()
|
|||||||
compiler_untrack_known_unit(&background_context, file_name);
|
compiler_untrack_known_unit(&background_context, file_name);
|
||||||
retry_after.erase(file_name);
|
retry_after.erase(file_name);
|
||||||
}
|
}
|
||||||
else
|
if(!source_missing && !failed && wasm_serialized_module_needs_refresh(wasm_path))
|
||||||
{
|
{
|
||||||
retry_after.erase(file_name);
|
printf("(i) proactive serialize %s\n", file_name.c_str());
|
||||||
|
String serialize_error = wasm_serialize_module_artifact(wasm_path);
|
||||||
|
if(serialize_error != "")
|
||||||
|
{
|
||||||
|
printf("(!) proactive serialize failed for %s: %s\n", file_name.c_str(), serialize_error.c_str());
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(!source_missing && failed)
|
||||||
|
retry_after[file_name] = time_precise() + failure_retry_interval;
|
||||||
|
else if(!source_missing)
|
||||||
|
retry_after.erase(file_name);
|
||||||
background_context.session.clear();
|
background_context.session.clear();
|
||||||
background_context.session_loaded_hash = "";
|
background_context.session_loaded_hash = "";
|
||||||
clear_shared_unit_cache(server_state);
|
clear_shared_unit_cache(server_state);
|
||||||
|
|||||||
@@ -93,6 +93,16 @@ String wasm_backend_start(Request& request)
|
|||||||
return(wasm_backend_ensure_started(&request));
|
return(wasm_backend_ensure_started(&request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wasm_serialized_module_needs_refresh(const String& wasm_path)
|
||||||
|
{
|
||||||
|
return(WasmWorker::serialized_module_needs_refresh(wasm_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
String wasm_serialize_module_artifact(const String& wasm_path)
|
||||||
|
{
|
||||||
|
return(WasmWorker::serialize_module_artifact(wasm_path));
|
||||||
|
}
|
||||||
|
|
||||||
static bool wasm_artifact_exists(Request* context, const String& entry_unit)
|
static bool wasm_artifact_exists(Request* context, const String& entry_unit)
|
||||||
{
|
{
|
||||||
if(entry_unit == "")
|
if(entry_unit == "")
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ bool wasm_backend_should_handle(Request& request, const String& entry_unit);
|
|||||||
// requests. Returns "" on success or the retained backend initialization error.
|
// requests. Returns "" on success or the retained backend initialization error.
|
||||||
String wasm_backend_start(Request& request);
|
String wasm_backend_start(Request& request);
|
||||||
|
|
||||||
|
// Proactive compiler hooks for keeping Wasmtime's native serialization newer
|
||||||
|
// than a compiled unit artifact before any request worker loads it.
|
||||||
|
bool wasm_serialized_module_needs_refresh(const String& wasm_path);
|
||||||
|
String wasm_serialize_module_artifact(const String& wasm_path);
|
||||||
|
|
||||||
// Serve a request through a wasm workspace by invoking a named unit handler —
|
// Serve a request through a wasm workspace by invoking a named unit handler —
|
||||||
// "render", "cli", "websocket", "serve_http", "serve_http:named" — and populate
|
// "render", "cli", "websocket", "serve_http", "serve_http:named" — and populate
|
||||||
// the native Request. Returns "" on success or a collapsed error. The handler is
|
// the native Request. Returns "" on success or a collapsed error. The handler is
|
||||||
|
|||||||
@@ -1049,6 +1049,36 @@ public:
|
|||||||
return(unit);
|
return(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool serialized_module_needs_refresh(const String& wasm_path)
|
||||||
|
{
|
||||||
|
struct stat wasm_stat;
|
||||||
|
struct stat cached_stat;
|
||||||
|
String cached_path = cached_wasm_path(wasm_path);
|
||||||
|
if(stat(wasm_path.c_str(), &wasm_stat) != 0)
|
||||||
|
return(false);
|
||||||
|
if(stat(cached_path.c_str(), &cached_stat) != 0)
|
||||||
|
return(true);
|
||||||
|
return(cached_stat.st_mtim.tv_sec < wasm_stat.st_mtim.tv_sec ||
|
||||||
|
(cached_stat.st_mtim.tv_sec == wasm_stat.st_mtim.tv_sec && cached_stat.st_mtim.tv_nsec <= wasm_stat.st_mtim.tv_nsec));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String serialize_module_artifact(const String& wasm_path)
|
||||||
|
{
|
||||||
|
if(!serialized_module_needs_refresh(wasm_path))
|
||||||
|
return("");
|
||||||
|
std::vector<u8> bytes;
|
||||||
|
if(!wasm_read_file(wasm_path, bytes))
|
||||||
|
return("cannot read " + wasm_path);
|
||||||
|
wasmtime::Engine engine = make_engine();
|
||||||
|
String error;
|
||||||
|
auto module = compile_and_cache_module(engine, cached_wasm_path(wasm_path), bytes, error);
|
||||||
|
if(!module)
|
||||||
|
return(error);
|
||||||
|
if(serialized_module_needs_refresh(wasm_path))
|
||||||
|
return("cannot publish serialized module for " + wasm_path);
|
||||||
|
return("");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static String cached_wasm_path(const String& wasm_path)
|
static String cached_wasm_path(const String& wasm_path)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user