Cache positive entry freshness checks

This commit is contained in:
udo
2026-07-19 04:24:45 +00:00
parent d95fb38183
commit 4f010d37a0
8 changed files with 332 additions and 11 deletions
+3
View File
@@ -320,8 +320,11 @@ struct Request {
u64 wasm_ready_mutation_check_us = 0;
u64 wasm_ready_artifact_stat_us = 0;
u64 wasm_ready_freshness_us = 0;
u64 wasm_ready_source_generation_us = 0;
u64 wasm_ready_freshness_full_check_us = 0;
u64 wasm_ready_worker_us = 0;
u32 wasm_ready_check_count = 0;
u32 wasm_ready_freshness_cache_hit_count = 0;
u64 mem_high = 0;
u64 mem_alloc = 0;
u32 invoke_count = 0;
+120 -2
View File
@@ -24,6 +24,58 @@ static std::atomic<bool> g_wasm_epoch_running(false);
static String g_wasm_init_error;
static bool g_wasm_init_attempted = false;
struct WasmEntryArtifactIdentity
{
dev_t device = 0;
ino_t inode = 0;
mode_t mode = 0;
off_t size = 0;
timespec modified = {};
timespec changed = {};
};
struct WasmEntryFreshnessState
{
std::chrono::steady_clock::time_point checked_at;
String source_generation;
WasmEntryArtifactIdentity wasm;
WasmEntryArtifactIdentity metadata;
WasmEntryArtifactIdentity setup_template;
};
static std::mutex g_wasm_entry_freshness_mutex;
static std::map<String, WasmEntryFreshnessState> g_wasm_entry_freshness;
static constexpr u64 WASM_ENTRY_FRESHNESS_CACHE_MAX = 4096;
static constexpr auto WASM_ENTRY_FRESHNESS_TTL = std::chrono::seconds(10);
static WasmEntryArtifactIdentity wasm_entry_artifact_identity(const struct stat& info)
{
return(WasmEntryArtifactIdentity{ info.st_dev, info.st_ino, info.st_mode, info.st_size, info.st_mtim, info.st_ctim });
}
static bool wasm_entry_artifact_identity_matches(const WasmEntryArtifactIdentity& expected, const struct stat& actual)
{
return(
expected.device == actual.st_dev && expected.inode == actual.st_ino && expected.mode == actual.st_mode && expected.size == actual.st_size &&
expected.modified.tv_sec == actual.st_mtim.tv_sec && expected.modified.tv_nsec == actual.st_mtim.tv_nsec &&
expected.changed.tv_sec == actual.st_ctim.tv_sec && expected.changed.tv_nsec == actual.st_ctim.tv_nsec
);
}
static bool wasm_entry_cache_allowed(Request* context)
{
if(!compiler_request_can_serve_stale_artifact(context))
return(false);
String method = to_upper(trim(context->params["REQUEST_METHOD"]));
return(method == "GET" || method == "HEAD" || method == "OPTIONS");
}
static void wasm_entry_freshness_forget(const String& entry_unit)
{
std::lock_guard<std::mutex> lock(g_wasm_entry_freshness_mutex);
g_wasm_entry_freshness.erase(entry_unit);
}
bool wasm_backend_configured(Request* context)
{
return(context && context->server);
@@ -144,6 +196,41 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
return(false);
}
context->stats.wasm_ready_artifact_stat_us += (u64)((time_precise() - phase_started) * 1000000.0);
f64 freshness_started = time_precise();
bool cache_allowed = wasm_entry_cache_allowed(context);
String metadata_path = compiler_unit_bin_directory(context) + entry_unit + ".meta.txt";
String setup_template_path = path_join(context->server->config["COMPILER_SYS_PATH"], context->server->config["SETUP_TEMPLATE"]);
String source_generation;
struct stat metadata_st;
struct stat setup_template_st;
bool metadata_exists = false;
bool setup_template_exists = false;
auto now = std::chrono::steady_clock::now();
if(cache_allowed)
{
phase_started = time_precise();
source_generation = compiler_source_generation(context);
context->stats.wasm_ready_source_generation_us += (u64)((time_precise() - phase_started) * 1000000.0);
metadata_exists = stat(metadata_path.c_str(), &metadata_st) == 0 && S_ISREG(metadata_st.st_mode);
setup_template_exists = stat(setup_template_path.c_str(), &setup_template_st) == 0 && S_ISREG(setup_template_st.st_mode);
if(source_generation != "" && metadata_exists && setup_template_exists)
{
std::lock_guard<std::mutex> lock(g_wasm_entry_freshness_mutex);
auto cached = g_wasm_entry_freshness.find(entry_unit);
if(cached != g_wasm_entry_freshness.end() && now - cached->second.checked_at < WASM_ENTRY_FRESHNESS_TTL &&
cached->second.source_generation == source_generation &&
wasm_entry_artifact_identity_matches(cached->second.wasm, wasm_st) &&
wasm_entry_artifact_identity_matches(cached->second.metadata, metadata_st) &&
wasm_entry_artifact_identity_matches(cached->second.setup_template, setup_template_st))
{
context->stats.wasm_ready_freshness_cache_hit_count++;
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - freshness_started) * 1000000.0);
return(true);
}
if(cached != g_wasm_entry_freshness.end())
g_wasm_entry_freshness.erase(cached);
}
}
// Require the artifact to satisfy the full compiler freshness check. Source
// mtime alone misses runtime/unit ABI changes, setup-template changes, and
// metadata mismatches, which can leave stale wasm with old imports.
@@ -151,13 +238,44 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
phase_started = time_precise();
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);
u64 full_freshness_us = (u64)((time_precise() - phase_started) * 1000000.0);
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - freshness_started) * 1000000.0);
context->stats.wasm_ready_freshness_full_check_us += full_freshness_us;
wasm_entry_freshness_forget(entry_unit);
compiler_prioritize_unit(context, entry_unit);
return(compiler_unit_can_serve_stale_artifact(context, entry_unit));
}
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - phase_started) * 1000000.0);
u64 full_freshness_us = (u64)((time_precise() - phase_started) * 1000000.0);
context->stats.wasm_ready_freshness_full_check_us += full_freshness_us;
if(source_missing)
{
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - freshness_started) * 1000000.0);
wasm_entry_freshness_forget(entry_unit);
return(false);
}
if(cache_allowed && source_generation != "")
{
struct stat final_wasm_st;
struct stat final_metadata_st;
struct stat final_setup_template_st;
phase_started = time_precise();
String final_generation = compiler_source_generation(context);
context->stats.wasm_ready_source_generation_us += (u64)((time_precise() - phase_started) * 1000000.0);
if(final_generation == source_generation && stat(wasm_path.c_str(), &final_wasm_st) == 0 && S_ISREG(final_wasm_st.st_mode) &&
stat(metadata_path.c_str(), &final_metadata_st) == 0 && S_ISREG(final_metadata_st.st_mode) &&
stat(setup_template_path.c_str(), &final_setup_template_st) == 0 && S_ISREG(final_setup_template_st.st_mode))
{
std::lock_guard<std::mutex> lock(g_wasm_entry_freshness_mutex);
if(g_wasm_entry_freshness.size() >= WASM_ENTRY_FRESHNESS_CACHE_MAX)
g_wasm_entry_freshness.clear();
g_wasm_entry_freshness[entry_unit] = {
std::chrono::steady_clock::now(), final_generation,
wasm_entry_artifact_identity(final_wasm_st), wasm_entry_artifact_identity(final_metadata_st),
wasm_entry_artifact_identity(final_setup_template_st)
};
}
}
context->stats.wasm_ready_freshness_us += (u64)((time_precise() - freshness_started) * 1000000.0);
return(true);
}
+13 -1
View File
@@ -1499,8 +1499,11 @@ public:
u64 ready_mutation_check_us = 0;
u64 ready_artifact_stat_us = 0;
u64 ready_freshness_us = 0;
u64 ready_source_generation_us = 0;
u64 ready_freshness_full_check_us = 0;
u64 ready_worker_us = 0;
u32 ready_check_count = 0;
u32 ready_freshness_cache_hit_count = 0;
f64 workspace_wall_start = 0;
f64 workspace_cpu_start = 0;
struct rusage thread_runtime_start = {};
@@ -1608,7 +1611,8 @@ public:
void set_perf_snapshot(u64 worker_pid, u64 parent_pid, u64 request_count,
f64 time_init, f64 time_params, f64 time_input, f64 time_start,
u64 ready_normalize_us, u64 ready_mutation_check_us, u64 ready_artifact_stat_us,
u64 ready_freshness_us, u64 ready_worker_us, u32 ready_check_count,
u64 ready_freshness_us, u64 ready_source_generation_us, u64 ready_freshness_full_check_us,
u64 ready_worker_us, u32 ready_check_count, u32 ready_freshness_cache_hit_count,
f64 workspace_wall_start, f64 workspace_cpu_start)
{
request_perf.worker_pid = worker_pid;
@@ -1622,8 +1626,11 @@ public:
request_perf.ready_mutation_check_us = ready_mutation_check_us;
request_perf.ready_artifact_stat_us = ready_artifact_stat_us;
request_perf.ready_freshness_us = ready_freshness_us;
request_perf.ready_source_generation_us = ready_source_generation_us;
request_perf.ready_freshness_full_check_us = ready_freshness_full_check_us;
request_perf.ready_worker_us = ready_worker_us;
request_perf.ready_check_count = ready_check_count;
request_perf.ready_freshness_cache_hit_count = ready_freshness_cache_hit_count;
request_perf.workspace_wall_start = workspace_wall_start;
request_perf.workspace_cpu_start = workspace_cpu_start;
request_perf.active = true;
@@ -3042,8 +3049,11 @@ private:
response["ready_mutation_check_us"] = (f64)self->request_perf.ready_mutation_check_us;
response["ready_artifact_stat_us"] = (f64)self->request_perf.ready_artifact_stat_us;
response["ready_freshness_us"] = (f64)self->request_perf.ready_freshness_us;
response["ready_source_generation_us"] = (f64)self->request_perf.ready_source_generation_us;
response["ready_freshness_full_check_us"] = (f64)self->request_perf.ready_freshness_full_check_us;
response["ready_worker_us"] = (f64)self->request_perf.ready_worker_us;
response["ready_check_count"] = (f64)self->request_perf.ready_check_count;
response["ready_freshness_cache_hit_count"] = (f64)self->request_perf.ready_freshness_cache_hit_count;
if(self->request_perf.time_start > 0 && self->request_perf.time_init > 0)
response["accept_us"] = (f64)((self->request_perf.time_start - self->request_perf.time_init) * 1000000.0);
if(self->request_perf.time_params > 0 && self->request_perf.time_init > 0)
@@ -4308,7 +4318,9 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const Request& request
request.stats.time_init, request.stats.time_params, request.stats.time_input, request.stats.time_start,
request.stats.wasm_ready_normalize_us, request.stats.wasm_ready_mutation_check_us,
request.stats.wasm_ready_artifact_stat_us, request.stats.wasm_ready_freshness_us,
request.stats.wasm_ready_source_generation_us, request.stats.wasm_ready_freshness_full_check_us,
request.stats.wasm_ready_worker_us, request.stats.wasm_ready_check_count,
request.stats.wasm_ready_freshness_cache_hit_count,
serve_started, cpu_started);
workspace.request_perf.thread_runtime_start = thread_runtime_start;
workspace.request_perf.thread_cpu_start = thread_cpu_start;