test(db2-ephemeral): gates opt into WO_EPHEMERAL=1 where a durable table is declared; residency section 7
- residency-accept.sh section 7, six checks: the refusal names the class and all three ways forward (exit 2); WO_EPHEMERAL=1 runs from RAM with the boot notice and a write round-trips; WO_EPHEMERAL with WO_DATA refuses; WO_EPHEMERAL=2 refuses naming the accepted value; keys-resident still refuses under the hatch; a plain class (the corpus `methods` fixture) runs with no WO_DATA, rc 0, nothing on stderr - blast radius measured gate by gate — each run without the export first, kept only where the program refused: oop-e2e (fixtures declare tables); db-bench.py's ram/msgrate/growth/randread legs (the durable legs drop it, so a WO_DATA in the caller's shell now refuses loudly instead of silently turning a RAM leg durable); db-actor per run (its restart pair sets WO_DATA); chat (porch's store declares RateLimitCounter default-durable — a library's table binds the consumer); wmux client legs (same image as the server, no WO_DATA; servers and the WO_DATA-carrying r11cli `env -u`) - byte-exact compares (db-actor single-shard, wmux client) drop the one notice line; fibers, subprocess, log-watcher declare no table — untouched - db-bench.py ceiling note: the checked refusal is databasev2 5's now - residency 32/0, oop-e2e 131/0 with this tree Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 4553ca15da235c155b7ad31bbb077c3ad8e88fee)
This commit is contained in:
parent
d38b4f864b
commit
41f48bba3f
5 changed files with 82 additions and 12 deletions
|
|
@ -7,6 +7,10 @@
|
||||||
# drain (close frames, exit 0) — functional legs on BOTH WO_IO backends
|
# drain (close frames, exit 0) — functional legs on BOTH WO_IO backends
|
||||||
# plus an ASan run.
|
# plus an ASan run.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
# databasev2 2 (6a): chat `use`s porch, whose store middleware declares its
|
||||||
|
# tables default-durable (`RateLimitCounter`, ...) — a library-owned durable
|
||||||
|
# table binds every consumer, so this RAM-only gate opts in.
|
||||||
|
export WO_EPHEMERAL=1
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
WOC="$ROOT/compiler/_build/default/bin/woc"
|
WOC="$ROOT/compiler/_build/default/bin/woc"
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,13 @@ else
|
||||||
bad "build" "woc failed"; echo "db-actor-accept: 1 checks, 1 failures"; exit 1
|
bad "build" "woc failed"; echo "db-actor-accept: 1 checks, 1 failures"; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# databasev2 2 (6a): these RAM-only runs opt in with WO_EPHEMERAL=1 (a durable
|
||||||
|
# table refuses to start without WO_DATA); the restart pair below sets WO_DATA
|
||||||
|
# instead, and the two are incompatible, so the hatch is per-run, not exported.
|
||||||
check_set() { # name [env pairs...]
|
check_set() { # name [env pairs...]
|
||||||
local name="$1"; shift
|
local name="$1"; shift
|
||||||
local out
|
local out
|
||||||
out="$(env "$@" timeout 30 "$DIR/target/db-actor" 2>&1)"
|
out="$(env WO_EPHEMERAL=1 "$@" timeout 30 "$DIR/target/db-actor" 2>&1)"
|
||||||
if printf '%s' "$out" | grep -q "writer 1 sees sum" \
|
if printf '%s' "$out" | grep -q "writer 1 sees sum" \
|
||||||
&& printf '%s' "$out" | grep -q "writer 2 sees sum" \
|
&& printf '%s' "$out" | grep -q "writer 2 sees sum" \
|
||||||
&& printf '%s' "$out" | grep -q "^main sees 2 rows, sum 3$" ; then
|
&& printf '%s' "$out" | grep -q "^main sees 2 rows, sum 3$" ; then
|
||||||
|
|
@ -47,7 +50,8 @@ check_set "multi uring" WO_IO=uring
|
||||||
check_set "multi epoll" WO_IO=epoll
|
check_set "multi epoll" WO_IO=epoll
|
||||||
|
|
||||||
# single-shard: byte-exact — the local path is untouched
|
# single-shard: byte-exact — the local path is untouched
|
||||||
sout="$(WO_SHARDS=1 timeout 30 "$DIR/target/db-actor" 2>&1)"
|
# byte-exact on merged stdout+stderr, so the hatch's one boot notice is dropped
|
||||||
|
sout="$(WO_EPHEMERAL=1 WO_SHARDS=1 timeout 30 "$DIR/target/db-actor" 2>&1 | grep -v '^wovm: WO_EPHEMERAL=1')"
|
||||||
want=$'writer 1 sees sum 1\nwriter 2 sees sum 3\nmain sees 2 rows, sum 3'
|
want=$'writer 1 sees sum 1\nwriter 2 sees sum 3\nmain sees 2 rows, sum 3'
|
||||||
if [[ "$sout" == "$want" ]]; then
|
if [[ "$sout" == "$want" ]]; then
|
||||||
ok "single-shard byte-exact"
|
ok "single-shard byte-exact"
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,13 @@ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
BIN = os.path.join(ROOT, "docs/examples/db-bench/target/db-bench")
|
BIN = os.path.join(ROOT, "docs/examples/db-bench/target/db-bench")
|
||||||
# databasev2 2 task 7. Its own program, not a mode in db-bench: declaring a
|
# databasev2 2 task 7. Its own program, not a mode in db-bench: declaring a
|
||||||
# `resident: keys` table is a WHOLE-PROGRAM constraint — the runtime refuses to
|
# `resident: keys` table is a WHOLE-PROGRAM constraint — the runtime refuses to
|
||||||
# start without WO_DATA, for every mode in the module. Putting those classes in
|
# start without WO_DATA, for every mode in the module, and WO_EPHEMERAL=1 does
|
||||||
# db-bench's shared types made growth/ceiling/randread, which deliberately run
|
# not rescue it. Putting those classes in db-bench's shared types made the ram,
|
||||||
# WITHOUT WO_DATA, refuse to start.
|
# msgrate, growth and randread legs, which deliberately run WITHOUT WO_DATA
|
||||||
|
# (ceiling sets one — it measures what survives the kill), refuse to start.
|
||||||
|
# databasev2 2 task 6a: those RAM legs now opt in with WO_EPHEMERAL=1; a
|
||||||
|
# WO_DATA exported in the caller's shell no longer silently turns them durable,
|
||||||
|
# it refuses loudly (WO_EPHEMERAL is incompatible with WO_DATA).
|
||||||
RESID_BIN = os.path.join(ROOT, "docs/examples/residency-bench/target/residency-bench")
|
RESID_BIN = os.path.join(ROOT, "docs/examples/residency-bench/target/residency-bench")
|
||||||
WOC = os.path.join(ROOT, "compiler/_build/default/bin/woc")
|
WOC = os.path.join(ROOT, "compiler/_build/default/bin/woc")
|
||||||
WOVM = os.path.join(ROOT, "runtime/wovm")
|
WOVM = os.path.join(ROOT, "runtime/wovm")
|
||||||
|
|
@ -188,12 +192,13 @@ def campaign():
|
||||||
for flavor in ("ram", "durable"):
|
for flavor in ("ram", "durable"):
|
||||||
for shards in (1, ncores):
|
for shards in (1, ncores):
|
||||||
tag = f"{flavor}.s{'1' if shards == 1 else 'N'}"
|
tag = f"{flavor}.s{'1' if shards == 1 else 'N'}"
|
||||||
env = {"WO_SHARDS": str(shards)}
|
env = {"WO_SHARDS": str(shards), "WO_EPHEMERAL": "1"}
|
||||||
data = None
|
data = None
|
||||||
if flavor == "durable":
|
if flavor == "durable":
|
||||||
data = os.path.join(ROOT, "bench", f"tmp.{os.getpid()}.{tag}")
|
data = os.path.join(ROOT, "bench", f"tmp.{os.getpid()}.{tag}")
|
||||||
os.makedirs(data, exist_ok=True)
|
os.makedirs(data, exist_ok=True)
|
||||||
env["WO_DATA"] = data
|
env["WO_DATA"] = data
|
||||||
|
del env["WO_EPHEMERAL"]
|
||||||
rc, lines, rssg, fdg = run(["all", str(N)], env, 1800, sample_after="write ")
|
rc, lines, rssg, fdg = run(["all", str(N)], env, 1800, sample_after="write ")
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
bad(f"{tag}.all", f"rc={rc} tail={lines[-2:]}")
|
bad(f"{tag}.all", f"rc={rc} tail={lines[-2:]}")
|
||||||
|
|
@ -221,7 +226,8 @@ def campaign():
|
||||||
# raises the cap to the flood size — the measured number keeps
|
# raises the cap to the flood size — the measured number keeps
|
||||||
# iteration 22's semantics exactly.
|
# iteration 22's semantics exactly.
|
||||||
rc, lines, _, _ = run(["msgrate", str(MSG_N)],
|
rc, lines, _, _ = run(["msgrate", str(MSG_N)],
|
||||||
{"WO_SHARDS": str(shards), "WO_MAILBOX": str(MSG_N)}, 300)
|
{"WO_SHARDS": str(shards), "WO_MAILBOX": str(MSG_N),
|
||||||
|
"WO_EPHEMERAL": "1"}, 300)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
bad(tag, f"rc={rc}")
|
bad(tag, f"rc={rc}")
|
||||||
else:
|
else:
|
||||||
|
|
@ -505,7 +511,7 @@ def growth(metrics):
|
||||||
for shape in GROWTH_SHAPES:
|
for shape in GROWTH_SHAPES:
|
||||||
for legname, swap_mb in (("noswap", 0), ("swap", 256)):
|
for legname, swap_mb in (("noswap", 0), ("swap", 256)):
|
||||||
w = cap_wrapper(512, swap_mb)
|
w = cap_wrapper(512, swap_mb)
|
||||||
env = dict(os.environ)
|
env = dict(os.environ); env["WO_EPHEMERAL"] = "1"
|
||||||
argv = w + [BIN, "growth", str(GROWTH_N), shape]
|
argv = w + [BIN, "growth", str(GROWTH_N), shape]
|
||||||
pr = subprocess.run(argv, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
pr = subprocess.run(argv, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
text=True, env=env, timeout=900)
|
text=True, env=env, timeout=900)
|
||||||
|
|
@ -554,9 +560,9 @@ def ceiling(metrics):
|
||||||
malloc succeeds and the process dies TOUCHING the pages, so it never
|
malloc succeeds and the process dies TOUCHING the pages, so it never
|
||||||
gets the chance to report failure. (The VM object arena is the
|
gets the chance to report failure. (The VM object arena is the
|
||||||
opposite: WO_HEAP_MB is checked and traps.) rc is asserted, not
|
opposite: WO_HEAP_MB is checked and traps.) rc is asserted, not
|
||||||
recorded as a metric -- when databasev2 2's byte budget lands this
|
recorded as a metric -- when databasev2 5's byte budget (bounded
|
||||||
should become a checked refusal, and the gate must not fail on that
|
tables) lands this should become a checked refusal, and the gate must
|
||||||
improvement.
|
not fail on that improvement.
|
||||||
|
|
||||||
2. WHAT SURVIVES. With WO_DATA set, replay must yield a contiguous
|
2. WHAT SURVIVES. With WO_DATA set, replay must yield a contiguous
|
||||||
intact prefix: rows 1..M present with the right v, no holes, and not
|
intact prefix: rows 1..M present with the right v, no holes, and not
|
||||||
|
|
@ -772,7 +778,8 @@ def randread(metrics):
|
||||||
w = cap_wrapper(cap_mb, swap_mb)
|
w = cap_wrapper(cap_mb, swap_mb)
|
||||||
pr = subprocess.run(w + [BIN, "randread", str(RAND_N), str(RAND_R)],
|
pr = subprocess.run(w + [BIN, "randread", str(RAND_N), str(RAND_R)],
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
text=True, env=dict(os.environ), timeout=900)
|
text=True, env=dict(os.environ, WO_EPHEMERAL="1"),
|
||||||
|
timeout=900)
|
||||||
lines = pr.stdout.splitlines()
|
lines = pr.stdout.splitlines()
|
||||||
ops, p50, p99, hits, filled = parse_randread(lines)
|
ops, p50, p99, hits, filled = parse_randread(lines)
|
||||||
key = f"randread.{legname}"
|
key = f"randread.{legname}"
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ set -uo pipefail
|
||||||
# narrows determinism to output SETS there); the multi-shard/TSan proofs
|
# narrows determinism to output SETS there); the multi-shard/TSan proofs
|
||||||
# live in the fibers gate, not here.
|
# live in the fibers gate, not here.
|
||||||
export WO_SHARDS=1 # no -e: a failing fixture is handled explicitly, one at a time
|
export WO_SHARDS=1 # no -e: a failing fixture is handled explicitly, one at a time
|
||||||
|
# databasev2 2 (6a): a program with any durable table (the default) refuses to
|
||||||
|
# start without WO_DATA; the corpus is RAM-only by contract, so opt in here.
|
||||||
|
export WO_EPHEMERAL=1
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,58 @@ else
|
||||||
bad "resident:keys refusal fixture compiles" "$(head -1 "$WORK/e")"
|
bad "resident:keys refusal fixture compiles" "$(head -1 "$WORK/e")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ---- 7. a durable table (the default) refuses to run without WO_DATA -------
|
||||||
|
# databasev2 2 task 6a: before this, a durable class with no WO_DATA ran
|
||||||
|
# RAM-only and every write was silently discarded — the exact outcome
|
||||||
|
# `durable: true` promises against. WO_EPHEMERAL=1 is the explicit opt-in to
|
||||||
|
# that RAM-only run; anything else refuses at startup, exit 2, naming the
|
||||||
|
# class and all three ways forward.
|
||||||
|
printf '@table(name: "notes", index: [k])\nclass Notes { k: Text }\nfn main() -> Int { insert Notes { k: "a" }; let n = 0; for x in from r in Notes select r { n = n + 1; } print("notes=${n}"); return 0; }\n' > "$WORK/dur.wo"
|
||||||
|
if "$WOC" --emit "$WORK/dur.wo" -o "$WORK/dur.wob" 2>"$WORK/e"; then
|
||||||
|
# (i) default-durable, no WO_DATA, no WO_EPHEMERAL -> refuse
|
||||||
|
out="$(env -u WO_DATA -u WO_EPHEMERAL "$WOVM" "$WORK/dur.wob" 2>&1)"; rc=$?
|
||||||
|
[[ $rc -eq 2 ]] && grep -q 'Notes' <<<"$out" && grep -q 'WO_DATA=' <<<"$out" \
|
||||||
|
&& grep -q 'WO_EPHEMERAL=1' <<<"$out" && grep -q 'durable: false' <<<"$out" \
|
||||||
|
&& ok "durable table without WO_DATA exits 2, names the class and all three ways forward" \
|
||||||
|
|| bad "durable table without WO_DATA refuses" "exit=$rc got: $out"
|
||||||
|
# (ii) WO_EPHEMERAL=1 -> runs from RAM: boot notice on stderr, a write round-trips
|
||||||
|
out="$(env -u WO_DATA WO_EPHEMERAL=1 "$WOVM" "$WORK/dur.wob" 2>&1)"; rc=$?
|
||||||
|
[[ $rc -eq 0 ]] && grep -q 'WO_EPHEMERAL=1' <<<"$out" && grep -q 'notes=1' <<<"$out" \
|
||||||
|
&& ok "WO_EPHEMERAL=1 runs the durable table from RAM (boot notice, write round-trips)" \
|
||||||
|
|| bad "WO_EPHEMERAL=1 runs from RAM" "exit=$rc got: $out"
|
||||||
|
# (iii) WO_EPHEMERAL together with WO_DATA -> conflict, refuse
|
||||||
|
mkdir -p "$WORK/d7"
|
||||||
|
out="$(WO_DATA="$WORK/d7" WO_EPHEMERAL=1 "$WOVM" "$WORK/dur.wob" 2>&1)"; rc=$?
|
||||||
|
[[ $rc -eq 2 ]] && grep -q 'incompatible with WO_DATA' <<<"$out" \
|
||||||
|
&& ok "WO_EPHEMERAL=1 with WO_DATA set exits 2 and names the conflict" \
|
||||||
|
|| bad "WO_EPHEMERAL + WO_DATA conflict" "exit=$rc got: $out"
|
||||||
|
# (v) the only accepted value is 1
|
||||||
|
out="$(env -u WO_DATA WO_EPHEMERAL=2 "$WOVM" "$WORK/dur.wob" 2>&1)"; rc=$?
|
||||||
|
[[ $rc -eq 2 ]] && grep -q 'WO_EPHEMERAL=1' <<<"$out" \
|
||||||
|
&& ok "WO_EPHEMERAL=2 exits 2 and names the accepted value" \
|
||||||
|
|| bad "WO_EPHEMERAL=2 refused" "exit=$rc got: $out"
|
||||||
|
else
|
||||||
|
bad "durable refusal fixture compiles" "$(head -1 "$WORK/e")"
|
||||||
|
fi
|
||||||
|
# (iv) resident:keys still refuses under WO_EPHEMERAL=1 — the keys loop wins
|
||||||
|
if [[ -f "$WORK/reskeys.wob" ]]; then
|
||||||
|
out="$(env -u WO_DATA WO_EPHEMERAL=1 "$WOVM" "$WORK/reskeys.wob" 2>&1)"; rc=$?
|
||||||
|
[[ $rc -eq 2 ]] && grep -q 'resident: keys' <<<"$out" \
|
||||||
|
&& ok "WO_EPHEMERAL=1 does not rescue resident:keys (exit 2, keys message)" \
|
||||||
|
|| bad "WO_EPHEMERAL=1 vs resident:keys" "exit=$rc got: $out"
|
||||||
|
fi
|
||||||
|
# (vi) a class that is NOT a @table is not a durable table: no WO_DATA, no
|
||||||
|
# WO_EPHEMERAL, rc 0 and nothing on stderr. `durable: true` is a @table
|
||||||
|
# property; the .wob carries the table bit (v8) so the refusal loops can tell.
|
||||||
|
if "$WOC" --emit tests/corpus/run/methods/fixture.wo -o "$WORK/plain.wob" 2>"$WORK/e"; then
|
||||||
|
out="$(env -u WO_DATA -u WO_EPHEMERAL "$WOVM" "$WORK/plain.wob" 2>"$WORK/plain.err")"; rc=$?
|
||||||
|
[[ $rc -eq 0 && "$out" == $'15\n42' ]] && ! grep -q '^wovm:' "$WORK/plain.err" \
|
||||||
|
&& ok "a plain class (no @table) runs without WO_DATA: rc 0, no wovm: line" \
|
||||||
|
|| bad "plain class without WO_DATA" "exit=$rc out: $out err: $(cat "$WORK/plain.err")"
|
||||||
|
else
|
||||||
|
bad "plain-class fixture compiles" "$(head -1 "$WORK/e")"
|
||||||
|
fi
|
||||||
|
|
||||||
# ---- 8. WO_DATA=<file>: the store as one file ------------------------------
|
# ---- 8. WO_DATA=<file>: the store as one file ------------------------------
|
||||||
# databasev2 7: WO_DATA names either a directory (-> <dir>/shard-0.wal, as it
|
# databasev2 7: WO_DATA names either a directory (-> <dir>/shard-0.wal, as it
|
||||||
# always did) or THE log file. Underneath it is the same wo_wal_* path, so
|
# always did) or THE log file. Underneath it is the same wo_wal_* path, so
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue