Refresh guest deadline after MySQL hostcalls
This commit is contained in:
@@ -407,6 +407,10 @@ header free-functions are `inline`. The wasm backend exposes only declarations
|
|||||||
caller props are restored. Its warm-request ceiling guards the request-scope
|
caller props are restored. Its warm-request ceiling guards the request-scope
|
||||||
invariant that entering a nested component swaps the active prop trees in
|
invariant that entering a nested component swaps the active prop trees in
|
||||||
constant time instead of deep-copying the outer tree for every child.
|
constant time instead of deep-copying the outer tree for every child.
|
||||||
|
`scripts/test_mysql_epoch_refresh.sh` spends more than half of the configured
|
||||||
|
guest epoch on each side of a real MySQL query. It proves the blocking
|
||||||
|
database hostcall refreshes the guest deadline so cumulative request work is
|
||||||
|
allowed while a CPU loop without intervening host work still traps.
|
||||||
|
|
||||||
- **Log timeliness**: the base process line-buffers stdout before forking
|
- **Log timeliness**: the base process line-buffers stdout before forking
|
||||||
workers, the proactive compiler, and the WebSocket broker. This keeps each
|
workers, the proactive compiler, and the WebSocket broker. This keeps each
|
||||||
|
|||||||
@@ -70,5 +70,6 @@ if [[ "$action" == "run" ]]; then
|
|||||||
scripts/test_cold_component_deadline.sh
|
scripts/test_cold_component_deadline.sh
|
||||||
scripts/test_nested_component_props.sh
|
scripts/test_nested_component_props.sh
|
||||||
scripts/test_password_hashing.sh
|
scripts/test_password_hashing.sh
|
||||||
|
scripts/test_mysql_epoch_refresh.sh
|
||||||
scripts/test_log_timeliness.sh
|
scripts/test_log_timeliness.sh
|
||||||
fi
|
fi
|
||||||
|
|||||||
Executable
+70
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
test_name="mysql-epoch-refresh-test-$$"
|
||||||
|
site_directory="${UCE_TEST_SITE_DIRECTORY:-site}"
|
||||||
|
settings_file="${UCE_SETTINGS_FILE:-/etc/uce/settings.cfg}"
|
||||||
|
if [[ -z "${UCE_TEST_SITE_DIRECTORY:-}" && -r "$settings_file" ]]; then
|
||||||
|
configured_site_directory=$(awk -F= '/^[[:space:]]*SITE_DIRECTORY[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' "$settings_file")
|
||||||
|
if [[ -n "${configured_site_directory:-}" ]]; then
|
||||||
|
site_directory="$configured_site_directory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
source_dir="$site_directory/$test_name"
|
||||||
|
bin_directory="${BIN_DIRECTORY:-}"
|
||||||
|
if [[ -z "$bin_directory" && -r "$settings_file" ]]; then
|
||||||
|
bin_directory=$(awk -F= '/^[[:space:]]*BIN_DIRECTORY[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' "$settings_file")
|
||||||
|
fi
|
||||||
|
bin_directory="${bin_directory:-/tmp/uce/work}"
|
||||||
|
ticks=$(awk -F= '/^[[:space:]]*WASM_EPOCH_DEADLINE_TICKS[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' "$settings_file" 2>/dev/null || true)
|
||||||
|
period_ms=$(awk -F= '/^[[:space:]]*WASM_EPOCH_PERIOD_MS[[:space:]]*=/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit}' "$settings_file" 2>/dev/null || true)
|
||||||
|
ticks="${ticks:-200}"
|
||||||
|
period_ms="${period_ms:-50}"
|
||||||
|
segment_seconds=$(awk -v ticks="$ticks" -v period="$period_ms" 'BEGIN { printf "%.6f", ticks * period * 0.00055 }')
|
||||||
|
cache_dir=""
|
||||||
|
test_user="uce_epoch_$$"
|
||||||
|
test_password=$(printf '%s' "$test_name-$(date +%s%N)" | sha256sum | cut -c1-32)
|
||||||
|
test_user_created=false
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ "$test_user_created" == true ]]; then
|
||||||
|
mariadb -e "DROP USER IF EXISTS '$test_user'@'127.0.0.1'" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
rm -rf "$source_dir"
|
||||||
|
if [[ -n "$cache_dir" ]]; then
|
||||||
|
rm -rf "$cache_dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
mkdir -p "$source_dir"
|
||||||
|
cache_dir="$bin_directory$(realpath "$source_dir")"
|
||||||
|
mariadb -e "DROP USER IF EXISTS '$test_user'@'127.0.0.1'; CREATE USER '$test_user'@'127.0.0.1' IDENTIFIED BY '$test_password'"
|
||||||
|
test_user_created=true
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
'void mysql_epoch_burn(f64 seconds) {' \
|
||||||
|
' f64 deadline = time_precise() + seconds;' \
|
||||||
|
' u64 spins = 0; while(time_precise() < deadline) spins++;' \
|
||||||
|
'}' \
|
||||||
|
'CLI(Request& context) {' \
|
||||||
|
" MySQL* db = mysql_connect(\"127.0.0.1\", \"$test_user\", \"$test_password\");" \
|
||||||
|
' if(db == 0 || mysql_error(db) != "") { print("mysql-connect-failed:", db == 0 ? "null" : mysql_error(db)); return; }' \
|
||||||
|
" mysql_epoch_burn($segment_seconds);" \
|
||||||
|
' mysql_query(db, "SELECT 1 AS value");' \
|
||||||
|
" mysql_epoch_burn($segment_seconds);" \
|
||||||
|
' mysql_query(db, "SELECT 2 AS value");' \
|
||||||
|
' if(mysql_error(db) == "") print("mysql-epoch-refresh-ok"); else print("mysql-query-failed:", mysql_error(db));' \
|
||||||
|
' mysql_disconnect(db);' \
|
||||||
|
'}' >"$source_dir/test.uce"
|
||||||
|
|
||||||
|
output=$(scripts/uce-cli "/$test_name/test.uce" 2>&1) || {
|
||||||
|
echo "MySQL hostcall did not refresh the guest epoch deadline: $output" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if [[ "$output" != "mysql-epoch-refresh-ok" ]]; then
|
||||||
|
echo "MySQL epoch refresh failed: $output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "MySQL epoch refresh passed with ${segment_seconds}s guest segments"
|
||||||
+2
-1
@@ -2416,7 +2416,7 @@ private:
|
|||||||
return(std::monostate());
|
return(std::monostate());
|
||||||
}));
|
}));
|
||||||
if(mod == "env" && name == "uce_host_mysql")
|
if(mod == "env" && name == "uce_host_mysql")
|
||||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||||
String encoded;
|
String encoded;
|
||||||
self->hostcall_read(args[0].i32(), args[1].i32(), encoded);
|
self->hostcall_read(args[0].i32(), args[1].i32(), encoded);
|
||||||
u32 cap = (u32)args[3].i32();
|
u32 cap = (u32)args[3].i32();
|
||||||
@@ -2499,6 +2499,7 @@ private:
|
|||||||
}
|
}
|
||||||
if(buf != 0 && cap >= out.size())
|
if(buf != 0 && cap >= out.size())
|
||||||
self->hostcall_write(buf, out);
|
self->hostcall_write(buf, out);
|
||||||
|
caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
|
||||||
results[0] = Val((int32_t)out.size());
|
results[0] = Val((int32_t)out.size());
|
||||||
return(std::monostate());
|
return(std::monostate());
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user