Cache positive entry freshness checks
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user