Bound request-time Wasm compilation
This commit is contained in:
+69
-16
@@ -6,6 +6,7 @@
|
||||
// Minimal FastCGI client: connection brokers forward to a clean-engine worker.
|
||||
#include "lib/fcgi_forward.h"
|
||||
#include <csetjmp>
|
||||
#include <chrono>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -43,6 +44,16 @@ static volatile sig_atomic_t request_fault_frame_count = 0;
|
||||
|
||||
void close_inherited_server_sockets();
|
||||
u64 request_seed_from_time(f64 time_value);
|
||||
|
||||
using WasmInvocationClock = std::chrono::steady_clock;
|
||||
u64 wasm_invocation_remaining_ms(WasmInvocationClock::time_point deadline)
|
||||
{
|
||||
auto now = WasmInvocationClock::now();
|
||||
if(now >= deadline)
|
||||
return(0);
|
||||
return((u64)std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now).count());
|
||||
}
|
||||
|
||||
void prepare_request_body_maps(Request& request);
|
||||
|
||||
Request* set_active_request(Request& request)
|
||||
@@ -73,7 +84,8 @@ void clear_request_output(Request& request)
|
||||
request.ob_start();
|
||||
}
|
||||
|
||||
bool render_wasm_error_page(Request& request, String config_key, s32 status_code, String status_reason, DValue error_info)
|
||||
bool render_wasm_error_page(Request& request, String config_key, s32 status_code, String status_reason, DValue error_info,
|
||||
WasmInvocationClock::time_point invocation_deadline, u64 invocation_budget_ms)
|
||||
{
|
||||
if(!(request.params["REQUEST_METHOD"] != "" && request.ob && request.ob_stack.size() > 0))
|
||||
return(false);
|
||||
@@ -93,13 +105,22 @@ bool render_wasm_error_page(Request& request, String config_key, s32 status_code
|
||||
request.header["Content-Type"] = first(request.server->config["CONTENT_TYPE"], "text/html; charset=utf-8");
|
||||
|
||||
String unit = compiler_normalize_unit_path(&request, unit_file);
|
||||
bool compile_timed_out = false;
|
||||
if(!wasm_backend_should_handle(request, unit))
|
||||
get_shared_unit(&request, unit);
|
||||
{
|
||||
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
|
||||
if(remaining_ms == 0)
|
||||
compile_timed_out = true;
|
||||
else
|
||||
get_shared_unit_bounded(&request, unit, remaining_ms, &compile_timed_out);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
String wasm_error = "";
|
||||
if(wasm_backend_should_handle(request, unit))
|
||||
wasm_error = wasm_backend_serve(request, unit, "render");
|
||||
if(compile_timed_out)
|
||||
wasm_error = "UCE_INVOCATION_TIMEOUT: wasm invocation exceeded " + std::to_string(invocation_budget_ms) + " ms";
|
||||
else if(wasm_backend_should_handle(request, unit))
|
||||
wasm_error = wasm_backend_serve(request, unit, "render", wasm_invocation_remaining_ms(invocation_deadline));
|
||||
else
|
||||
wasm_error = "error page wasm unit unavailable after compile: " + unit_file;
|
||||
String html = ob_get_close();
|
||||
@@ -122,7 +143,8 @@ bool render_wasm_error_page(Request& request, String config_key, s32 status_code
|
||||
return(true);
|
||||
}
|
||||
|
||||
void render_request_failure(Request& request, String title, String details, String trace, int status_code = 500)
|
||||
void render_request_failure(Request& request, String title, String details, String trace,
|
||||
WasmInvocationClock::time_point invocation_deadline, u64 invocation_budget_ms, int status_code = 500)
|
||||
{
|
||||
request.response_code = request_status_line(request, status_code, "Internal Server Error");
|
||||
request.header.clear();
|
||||
@@ -149,7 +171,8 @@ void render_request_failure(Request& request, String title, String details, Stri
|
||||
error_info["signal_name"] = signal_name((int)request_fault_signal);
|
||||
}
|
||||
error_info["trace"] = trace;
|
||||
if(render_wasm_error_page(request, "page_runtime_error", status_code, "Internal Server Error", error_info))
|
||||
if(render_wasm_error_page(request, "page_runtime_error", status_code, "Internal Server Error", error_info,
|
||||
invocation_deadline, invocation_budget_ms))
|
||||
{
|
||||
request.err = "UCE runtime error: " + title + (details != "" ? " (" + details + ")" : "");
|
||||
restore_active_request(previous_context);
|
||||
@@ -388,6 +411,8 @@ int handle_cli_complete(FastCGIRequest& request)
|
||||
request.random_index = 0;
|
||||
request.random_seed = request_seed_from_time(request.stats.time_start);
|
||||
request.ob_start();
|
||||
u64 invocation_budget_ms = wasm_backend_invocation_timeout_ms(request);
|
||||
auto invocation_deadline = WasmInvocationClock::now() + std::chrono::milliseconds(invocation_budget_ms);
|
||||
|
||||
String method = trim(request.params["REQUEST_METHOD"]);
|
||||
String command = trim(first(request.params["DOCUMENT_URI"], request.params["REQUEST_URI"]));
|
||||
@@ -441,15 +466,26 @@ int handle_cli_complete(FastCGIRequest& request)
|
||||
// been removed, so a unit that still cannot be served by wasm is a
|
||||
// request failure instead of a fallback path.
|
||||
SharedUnit* cli_compile_state = 0;
|
||||
bool cli_compile_timed_out = false;
|
||||
bool cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
|
||||
if(!cli_wasm_ready)
|
||||
{
|
||||
cli_compile_state = get_shared_unit(&request, cli_unit);
|
||||
cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
|
||||
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
|
||||
if(remaining_ms == 0)
|
||||
cli_compile_timed_out = true;
|
||||
else
|
||||
cli_compile_state = get_shared_unit_bounded(&request, cli_unit, remaining_ms, &cli_compile_timed_out);
|
||||
if(!cli_compile_timed_out)
|
||||
cli_wasm_ready = wasm_backend_should_handle(request, cli_unit);
|
||||
}
|
||||
if(cli_wasm_ready)
|
||||
if(cli_compile_timed_out)
|
||||
{
|
||||
String wasm_error = wasm_backend_serve(request, cli_unit, "cli");
|
||||
request.set_status(500, "Internal Server Error");
|
||||
print("UCE_INVOCATION_TIMEOUT: wasm invocation exceeded ", invocation_budget_ms, " ms\n");
|
||||
}
|
||||
else if(cli_wasm_ready)
|
||||
{
|
||||
String wasm_error = wasm_backend_serve(request, cli_unit, "cli", wasm_invocation_remaining_ms(invocation_deadline));
|
||||
if(wasm_error != "")
|
||||
{
|
||||
request.set_status(500, "Internal Server Error");
|
||||
@@ -476,11 +512,13 @@ int handle_cli_complete(FastCGIRequest& request)
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
render_request_failure(request, "uncaught exception during CLI request", e.what(), backtrace_capture(32, 1), 500);
|
||||
render_request_failure(request, "uncaught exception during CLI request", e.what(), backtrace_capture(32, 1),
|
||||
invocation_deadline, invocation_budget_ms, 500);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
render_request_failure(request, "unknown uncaught exception during CLI request", "", backtrace_capture(32, 1), 500);
|
||||
render_request_failure(request, "unknown uncaught exception during CLI request", "", backtrace_capture(32, 1),
|
||||
invocation_deadline, invocation_budget_ms, 500);
|
||||
}
|
||||
|
||||
for(auto &f : request.uploaded_files)
|
||||
@@ -522,6 +560,8 @@ int handle_complete(FastCGIRequest& request) {
|
||||
request.random_index = 0;
|
||||
request.random_seed = request_seed_from_time(request.stats.time_start);
|
||||
request.ob_start();
|
||||
u64 invocation_budget_ms = wasm_backend_invocation_timeout_ms(request);
|
||||
auto invocation_deadline = WasmInvocationClock::now() + std::chrono::milliseconds(invocation_budget_ms);
|
||||
request_fault_request = &request;
|
||||
request_fault_active = 1;
|
||||
request_fault_signal = 0;
|
||||
@@ -557,7 +597,7 @@ int handle_complete(FastCGIRequest& request) {
|
||||
request_fault_active = 0;
|
||||
restore_request_fault_handlers();
|
||||
request.stats.wasm_backend_started = time_precise();
|
||||
String wasm_error = wasm_backend_serve(request, entry_unit, handler);
|
||||
String wasm_error = wasm_backend_serve(request, entry_unit, handler, wasm_invocation_remaining_ms(invocation_deadline));
|
||||
request.stats.wasm_backend_finished = time_precise();
|
||||
install_request_fault_handlers();
|
||||
request_fault_active = 1;
|
||||
@@ -605,13 +645,25 @@ int handle_complete(FastCGIRequest& request) {
|
||||
// recheck. Native execution has been removed, so a unit that still cannot
|
||||
// be served by wasm becomes a clean 500 request failure.
|
||||
SharedUnit* entry_compile_state = 0;
|
||||
bool entry_compile_timed_out = false;
|
||||
auto wasm_ready = [&](const String& unit) -> bool {
|
||||
if(wasm_backend_should_handle(request, unit))
|
||||
return(true);
|
||||
entry_compile_state = get_shared_unit(&request, unit);
|
||||
return(wasm_backend_should_handle(request, unit));
|
||||
u64 remaining_ms = wasm_invocation_remaining_ms(invocation_deadline);
|
||||
if(remaining_ms == 0)
|
||||
entry_compile_timed_out = true;
|
||||
else
|
||||
entry_compile_state = get_shared_unit_bounded(&request, unit, remaining_ms, &entry_compile_timed_out);
|
||||
return(!entry_compile_timed_out && wasm_backend_should_handle(request, unit));
|
||||
};
|
||||
auto fail_wasm_unavailable = [&](const String& handler) {
|
||||
if(entry_compile_timed_out)
|
||||
{
|
||||
failure_title = "wasm invocation timeout";
|
||||
failure_details = "";
|
||||
failure_trace = "UCE_INVOCATION_TIMEOUT: wasm invocation exceeded " + std::to_string(invocation_budget_ms) + " ms";
|
||||
return;
|
||||
}
|
||||
failure_title = "wasm unit unavailable after compile";
|
||||
String compile_error = entry_compile_state ? first(entry_compile_state->compiler_messages, entry_compile_state->compile_error_status) : String("");
|
||||
failure_details = compile_error != "" ? compile_error : handler + " handler could not be served by wasm";
|
||||
@@ -691,7 +743,8 @@ int handle_complete(FastCGIRequest& request) {
|
||||
restore_request_fault_handlers();
|
||||
|
||||
if(failure_title != "")
|
||||
render_request_failure(request, failure_title, failure_details, failure_trace, 500);
|
||||
render_request_failure(request, failure_title, failure_details, failure_trace,
|
||||
invocation_deadline, invocation_budget_ms, 500);
|
||||
|
||||
for( auto &f : request.uploaded_files)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user