Cache immutable Wasm server configuration encoding

This commit is contained in:
udo
2026-07-18 03:43:44 +00:00
parent a043fb8fc7
commit 516ed94032
6 changed files with 134 additions and 23 deletions
+81
View File
@@ -1175,6 +1175,87 @@ bool ucb_decode(const String& encoded, DValue& out, String* error_out)
return(false);
}
#ifdef __UCE_WASM_CORE__
static bool ucb_decode_flat_string_map(const String& encoded, StringMap& out, String* error_out)
{
String error;
StringMap decoded;
size_t offset = 0;
auto fail = [&](String message) {
error = message;
return(false);
};
if(encoded.size() < 5 || encoded.compare(0, 4, UCEB_MAGIC) != 0)
fail("missing UCEB magic header");
else if((u8)encoded[4] != UCEB_VERSION)
fail("unsupported UCEB version");
else
{
offset = 5;
if(encoded.size() - offset < 2)
fail("unexpected end of UCEB2 string map");
else
{
u8 flags = (u8)encoded[offset++];
char type = encoded[offset++];
u64 scalar_len = 0, child_count = 0;
if(flags != 0 || type != 'M')
fail("UCEB2 string map root must be a map");
else if(!ucb_read_varint(encoded, offset, scalar_len) || scalar_len != 0)
fail("UCEB2 string map root must have no scalar");
else if(!ucb_read_varint(encoded, offset, child_count))
fail("invalid UCEB2 string map child count");
else
{
for(u64 i = 0; error == "" && i < child_count; i++)
{
u64 key_len = 0, value_len = 0, value_children = 0;
if(!ucb_read_varint(encoded, offset, key_len) || key_len > encoded.size() - offset)
{
fail("invalid UCEB2 string map key");
break;
}
String key(encoded.data() + offset, (size_t)key_len);
offset += (size_t)key_len;
if(encoded.size() - offset < 2)
{
fail("unexpected end of UCEB2 string map value");
break;
}
u8 value_flags = (u8)encoded[offset++];
char value_type = encoded[offset++];
if(value_flags != 0 || value_type != 'S' || !ucb_read_varint(encoded, offset, value_len) || value_len > encoded.size() - offset)
{
fail("UCEB2 string map value must be a string scalar");
break;
}
String value(encoded.data() + offset, (size_t)value_len);
offset += (size_t)value_len;
if(!ucb_read_varint(encoded, offset, value_children) || value_children != 0)
{
fail("UCEB2 string map value cannot have children");
break;
}
decoded[std::move(key)] = std::move(value);
}
if(error == "" && offset != encoded.size())
fail("trailing bytes after UCEB2 string map");
}
}
}
if(error != "")
{
if(error_out)
*error_out = error;
return(false);
}
out = std::move(decoded);
if(error_out)
*error_out = "";
return(true);
}
#endif
DValue ucb_decode(const String& encoded)
{
DValue out;
+17 -5
View File
@@ -68,10 +68,10 @@ static String wasm_backend_ensure_started(Request* context)
}
g_wasm_worker = new WasmWorker(wc);
// Server configuration is finalized before render workers start. Retain one
// native tree so per-request UCEB encoding can reference it without rebuilding
// dozens of identical nodes; the guest still decodes it into fresh state.
// Server configuration is finalized before render workers start. Encode it
// once; each fresh guest decodes the cached flat map into its own Server.
g_wasm_worker->server_config_context = cfg;
g_wasm_worker->server_config_encoded = ucb_encode(g_wasm_worker->server_config_context);
g_wasm_init_error = g_wasm_worker->init();
if(g_wasm_init_error == "")
g_wasm_init_error = wasm_worker_prepare(*g_wasm_worker);
@@ -159,8 +159,6 @@ String wasm_backend_serve(Request& request, const String& entry_unit, const Stri
for(auto& entry : source)
ctx[key][entry.first] = entry.second;
};
if(request.server)
ctx["server_config"].set_reference(&g_wasm_worker->server_config_context);
copy_map(request.params, "params");
copy_map(request.get, "get");
copy_map(request.post, "post");
@@ -240,7 +238,21 @@ String wasm_backend_serve(Request& request, const String& entry_unit, const Stri
if(to_bool(request.server->config["WASM_BACKEND_VERBOSE"], false))
{
request.header["X-UCE-Backend"] = "wasm";
request.header["X-UCE-Wasm-Workspace-Setup-Us"] = std::to_string(response.workspace_setup_us);
request.header["X-UCE-Wasm-Workspace-Birth-Us"] = std::to_string(response.workspace_birth_us);
request.header["X-UCE-Wasm-Birth-Policy-Us"] = std::to_string(response.birth_policy_us);
request.header["X-UCE-Wasm-Birth-Import-Us"] = std::to_string(response.birth_import_us);
request.header["X-UCE-Wasm-Birth-Instantiate-Us"] = std::to_string(response.birth_instantiate_us);
request.header["X-UCE-Wasm-Birth-Exports-Us"] = std::to_string(response.birth_exports_us);
request.header["X-UCE-Wasm-Birth-Initialize-Us"] = std::to_string(response.birth_initialize_us);
request.header["X-UCE-Wasm-Context-Apply-Us"] = std::to_string(response.context_apply_us);
request.header["X-UCE-Wasm-Context-Bytes"] = std::to_string(response.context_bytes);
request.header["X-UCE-Wasm-Server-Config-Bytes"] = std::to_string(response.server_config_bytes);
request.header["X-UCE-Wasm-Context-Encode-Us"] = std::to_string(response.context_encode_us);
request.header["X-UCE-Wasm-Context-Allocate-Us"] = std::to_string(response.context_allocate_us);
request.header["X-UCE-Wasm-Context-Write-Us"] = std::to_string(response.context_write_us);
request.header["X-UCE-Wasm-Context-Guest-Apply-Us"] = std::to_string(response.context_guest_apply_us);
request.header["X-UCE-Wasm-Context-Free-Us"] = std::to_string(response.context_free_us);
request.header["X-UCE-Wasm-Workspace-Complete-Us"] = std::to_string(response.workspace_complete_us);
request.header["X-UCE-Wasm-Entry-Invoke-Us"] = std::to_string(response.entry_invoke_us);
request.header["X-UCE-Wasm-Entry-Load-Us"] = std::to_string(response.entry_load_us);
+12 -6
View File
@@ -7,6 +7,7 @@
#define __UCE_WASM_CORE__ 1
#include "../lib/uce_lib.cpp"
#include "../lib/mysql-connector.h"
#include "../lib/sqlite-connector.h"
@@ -799,19 +800,26 @@ void uce_wasm_core_reset_request()
wasm_component_paths.clear();
}
// Host pushes the UCEB2-encoded request context into a guest buffer
// (uce_alloc) and applies it here; mirrors the native param population.
int uce_wasm_apply_context(const char* buf, size_t len)
// Host pushes the worker-cached immutable configuration followed by the
// dynamic UCEB2 request context into one guest buffer.
int uce_wasm_apply_context(const char* config_buf, size_t config_len, const char* context_buf, size_t context_len)
{
if(context == 0)
uce_wasm_core_init();
StringMap decoded_config;
DValue decoded;
String error;
if(!ucb_decode(String(buf, len), decoded, &error))
if(!ucb_decode_flat_string_map(String(config_buf, config_len), decoded_config, &error))
{
uce_host_log(3, error.data(), error.size());
return(1);
}
if(!ucb_decode(String(context_buf, context_len), decoded, &error))
{
uce_host_log(3, error.data(), error.size());
return(2);
}
wasm_server.config = std::move(decoded_config);
wasm_request.call = std::move(decoded);
DValue& applied = wasm_request.call;
auto apply_map = [](DValue* source, StringMap& dest) {
@@ -821,8 +829,6 @@ int uce_wasm_apply_context(const char* buf, size_t len)
dest[key] = item.to_string();
});
};
if(applied.key("server_config"))
apply_map(applied.key("server_config"), wasm_server.config);
apply_map(applied.key("params"), wasm_request.params);
wasm_request.response_code = wasm_request.params["GATEWAY_INTERFACE"] != "" ?
"Status: 200 OK" : "HTTP/1.1 200 OK";
+11 -4
View File
@@ -187,6 +187,7 @@ struct WasmRequestProfile
u64 context_apply_us = 0;
u64 context_apply_cpu_us = 0;
u64 context_bytes = 0;
u64 server_config_bytes = 0;
u64 context_encode_us = 0;
u64 context_allocate_us = 0;
u64 context_write_us = 0;
@@ -978,6 +979,7 @@ public:
std::optional<wasmtime::Linker> core_linker;
std::unique_ptr<wasmtime_instance_pre_t, WasmInstancePreDeleter> core_instance_pre;
DValue server_config_context;
String server_config_encoded;
struct CoreImport
{
String module;
@@ -1532,21 +1534,25 @@ public:
return(elapsed);
};
String encoded = ucb_encode(context_tree);
context_bytes = encoded.size();
server_config_bytes = worker.server_config_encoded.size();
context_bytes = server_config_bytes + encoded.size();
context_encode_us = phase_us();
int32_t guest_ptr = 0;
String error = call_core("uce_alloc", { (int32_t)encoded.size() }, &guest_ptr);
String error = call_core("uce_alloc", { (int32_t)context_bytes }, &guest_ptr);
context_allocate_us = phase_us();
if(error != "")
return(error);
if(guest_ptr == 0)
return("guest uce_alloc failed for context buffer");
error = guest_write((u32)guest_ptr, encoded);
error = guest_write((u32)guest_ptr, worker.server_config_encoded);
if(error == "")
error = guest_write((u32)guest_ptr + (u32)server_config_bytes, encoded);
context_write_us = phase_us();
if(error != "")
return(error);
int32_t rc = 0;
error = call_core("uce_wasm_apply_context", { guest_ptr, (int32_t)encoded.size() }, &rc);
error = call_core("uce_wasm_apply_context", { guest_ptr, (int32_t)server_config_bytes,
guest_ptr + (int32_t)server_config_bytes, (int32_t)encoded.size() }, &rc);
context_guest_apply_us = phase_us();
if(error != "")
return(error);
@@ -2592,6 +2598,7 @@ private:
response["context_apply_us"] = (f64)self->context_apply_us;
response["context_apply_cpu_us"] = (f64)self->context_apply_cpu_us;
response["context_bytes"] = (f64)self->context_bytes;
response["server_config_bytes"] = (f64)self->server_config_bytes;
response["context_encode_us"] = (f64)self->context_encode_us;
response["context_allocate_us"] = (f64)self->context_allocate_us;
response["context_write_us"] = (f64)self->context_write_us;