Skip to content

Commit df2411f

Browse files
committed
fixup test
1 parent 10fc527 commit df2411f

5 files changed

Lines changed: 83 additions & 68 deletions

File tree

test/tests/panic.spec.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,23 @@ describe("Panic Hook with WASM Reinitialization", () => {
9090
expect(responses[2].status).toBe(200);
9191
}
9292

93+
// Test 4: JS error recovery - counter should NOT reset after a JS error
94+
{
95+
const uniqueId = `JSERR_${Date.now()}_${Math.random()}`;
96+
97+
const resp1 = await mf.dispatchFetch(`${mfUrl}durable/${uniqueId}`);
98+
const match1 = (await resp1.text()).match(/unstored_count: (\d+)/);
99+
const count1 = match1 ? parseInt(match1[1]) : 0;
100+
101+
const jsErrorResp = await mf.dispatchFetch(`${mfUrl}test-js-error`);
102+
expect(jsErrorResp.status).toBe(500);
103+
104+
const resp2 = await mf.dispatchFetch(`${mfUrl}durable/${uniqueId}`);
105+
const match2 = (await resp2.text()).match(/unstored_count: (\d+)/);
106+
const count2 = match2 ? parseInt(match2[1]) : 0;
107+
expect(count2).toBe(count1 + 1);
108+
}
109+
93110
} else {
94111
// ===== PANIC=ABORT MODE TESTS (default) =====
95112
// In this mode, panics cause "Workers runtime canceled" and WASM reinitializes.
@@ -163,54 +180,38 @@ describe("Panic Hook with WASM Reinitialization", () => {
163180
expect(recoveryResp.status).toBe(200);
164181
}
165182
}
183+
}
166184

167-
// explicit abort() recovery test
168-
{
169-
await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
170-
const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
171-
expect(await resp.text()).toContain("unstored_count:");
172-
173-
const abortResp = await mf.dispatchFetch(`${mfUrl}test-abort`);
174-
expect(abortResp.status).toBe(500);
175-
176-
const abortText = await abortResp.text();
177-
expect(abortText).toContain("Workers runtime canceled");
178-
179-
const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
180-
expect(await normalResp.text()).toContain("unstored_count: 1");
181-
}
182-
183-
// JS error recovery test
184-
{
185-
await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
186-
const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
187-
expect(await resp.text()).toContain("unstored_count:");
185+
// // explicit abort() recovery test
186+
// {
187+
// await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
188+
// const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
189+
// expect(await resp.text()).toContain("unstored_count:");
188190

189-
const jsErrorResp = await mf.dispatchFetch(`${mfUrl}test-js-error`);
190-
expect(jsErrorResp.status).toBe(500);
191+
// const abortResp = await mf.dispatchFetch(`${mfUrl}test-abort`);
192+
// expect(abortResp.status).toBe(500);
191193

192-
const jsErrorText = await jsErrorResp.text();
193-
expect(jsErrorText).toContain("Workers runtime canceled");
194+
// const abortText = await abortResp.text();
195+
// expect(abortText).toContain("Workers runtime canceled");
194196

195-
const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
196-
expect(await normalResp.text()).toContain("unstored_count: 1");
197-
}
197+
// const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
198+
// expect(await normalResp.text()).toContain("unstored_count: 1");
199+
// }
198200

199-
// out of memory recovery test
200-
{
201-
await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
202-
const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
203-
expect(await resp.text()).toContain("unstored_count:");
201+
// out of memory recovery test
202+
{
203+
await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
204+
const resp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
205+
expect(await resp.text()).toContain("unstored_count:");
204206

205-
const oomResp = await mf.dispatchFetch(`${mfUrl}test-oom`);
206-
expect(oomResp.status).toBe(500);
207+
const oomResp = await mf.dispatchFetch(`${mfUrl}test-oom`);
208+
expect(oomResp.status).toBe(500);
207209

208-
const oomText = await oomResp.text();
209-
expect(oomText).toContain("Workers runtime canceled");
210+
const oomText = await oomResp.text();
211+
expect(oomText).toContain("Workers runtime canceled");
210212

211-
const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
212-
expect(await normalResp.text()).toContain("unstored_count: 1");
213-
}
213+
const normalResp = await mf.dispatchFetch(`${mfUrl}durable/COUNTER`);
214+
expect(await normalResp.text()).toContain("unstored_count: 1");
214215
}
215216
}, 20_000);
216217
});

test/wrangler.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ binding = 'DELETE_BUCKET'
6969
bucket_name = 'delete-bucket'
7070
preview_bucket_name = 'delete-bucket'
7171

72-
[build]
73-
command = "WASM_BINDGEN_BIN=../wasm-bindgen/target/debug/wasm-bindgen ../target/debug/worker-build --release"
72+
# [build]
73+
# command = "WASM_BINDGEN_BIN=../wasm-bindgen/target/debug/wasm-bindgen ../target/debug/worker-build --release"
7474

7575
[[migrations]]
7676
tag = "v1"

worker-build/src/build/mod.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -563,12 +563,6 @@ pub fn wasm_bindgen_build(
563563
cmd.arg(arg);
564564
}
565565

566-
// When building with panic=unwind, the wasm-bindgen Rust runtime emits
567-
// an `__instance_terminated` global in the Wasm binary. The CLI's
568-
// catch_handler transform auto-detects this and generates Wasm-level
569-
// catch wrappers that handle abort detection and reinit, allowing the
570-
// JS shim to be drastically simplified.
571-
572566
utils::run(cmd, "wasm-bindgen").context("Running the wasm-bindgen CLI")?;
573567
Ok(())
574568
}

worker-build/src/js/shim-unwind.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import * as exports from "./index.js";
1919

2020
Error.stackTraceLimit = 100;
2121

22+
if (!console.createTask) console.createTask = () => ({ run: (fn) => fn() });
23+
2224
// Shared state object from the worker crate's inline_js snippet. The object
2325
// reference is grabbed once here (single Wasm hop at module load); after that
2426
// every read of `reinitState.id` is a plain JS property access — no Wasm

worker/src/reinit_support.rs

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,55 @@
55
//! Wasm instance become stale. The JS shim needs a way to detect this so it
66
//! can re-construct Durable Object instances transparently.
77
//!
8-
//! This module maintains a generation counter in a shared JS object (via
9-
//! `inline_js`) that persists across Wasm resets. A `set_on_reinit` hook bumps
8+
//! This module maintains a generation counter in an object stored on
9+
//! `globalThis` that persists across Wasm resets. A `set_on_reinit` hook bumps
1010
//! the counter each time a fresh instance is created. The shim grabs a
1111
//! reference to the shared object once at module load time, then compares its
1212
//! `.id` property (a pure JS property read — no Wasm hop) at each method call.
1313
1414
use wasm_bindgen::prelude::*;
1515

1616
// ---------------------------------------------------------------------------
17-
// JS-side persistent state object. The inline snippet lives outside the Wasm
18-
// instance so its state survives `__wbg_reset_state`. The shim holds a
19-
// reference to the same object and reads `.id` directly — zero Wasm overhead
20-
// on the hot path.
17+
// JS-side persistent state object. Stored on `globalThis` so it survives
18+
// `__wbg_reset_state` without needing an `inline_js` snippet (which causes
19+
// esbuild duplicate-key warnings when wasm-bindgen emits the module map).
2120
// ---------------------------------------------------------------------------
2221

23-
#[wasm_bindgen(inline_js = "
24-
const state = { id: 0 };
25-
export function bumpInstanceId() { state.id++; }
26-
export function getState() { return state; }
27-
")]
28-
extern "C" {
29-
#[wasm_bindgen(js_name = "bumpInstanceId")]
30-
fn bump_instance_id();
22+
const STATE_KEY: &str = "__worker_reinit_state";
3123

32-
#[wasm_bindgen(js_name = "getState")]
33-
fn get_state() -> JsValue;
24+
fn get_or_create_state() -> JsValue {
25+
let global = js_sys::global();
26+
let key = JsValue::from_str(STATE_KEY);
27+
let existing = js_sys::Reflect::get(&global, &key).unwrap_or(JsValue::UNDEFINED);
28+
if !existing.is_undefined() {
29+
return existing;
30+
}
31+
let obj = js_sys::Object::new();
32+
js_sys::Reflect::set(&obj, &JsValue::from_str("id"), &JsValue::from(0_u32)).expect("set id");
33+
js_sys::Reflect::set(&global, &key, &obj).expect("set state on globalThis");
34+
obj.into()
35+
}
36+
37+
fn bump_instance_id() {
38+
let state = get_or_create_state();
39+
let current = js_sys::Reflect::get(&state, &JsValue::from_str("id"))
40+
.ok()
41+
.and_then(|v| v.as_f64())
42+
.unwrap_or(0.0) as u32;
43+
js_sys::Reflect::set(
44+
&state,
45+
&JsValue::from_str("id"),
46+
&JsValue::from(current + 1),
47+
)
48+
.expect("bump id");
3449
}
3550

3651
// ---------------------------------------------------------------------------
3752
// Reinit hook — called by wasm-bindgen after each fresh instance is created.
3853
// ---------------------------------------------------------------------------
3954

4055
fn on_reinit() {
56+
web_sys::console::log_1(&"[workers-rs] reinit".into());
4157
bump_instance_id();
4258
}
4359

@@ -50,6 +66,7 @@ fn ensure_initialized() {
5066
use std::sync::Once;
5167
static REGISTER: Once = Once::new();
5268
REGISTER.call_once(|| {
69+
web_sys::console::log_1(&"[workers-rs] registering reinit hook".into());
5370
wasm_bindgen::handler::set_on_reinit(on_reinit);
5471
});
5572
}
@@ -82,10 +99,11 @@ pub fn init() {
8299
// Export for the JS shim.
83100
// ---------------------------------------------------------------------------
84101

85-
/// Returns the shared state object `{ id: number }`. The shim calls this once
86-
/// at module load time and then reads `state.id` directly on every DO method
87-
/// call — a plain JS property access with no Wasm boundary crossing.
102+
/// Returns the shared state object `{ id: number }` from `globalThis`. The
103+
/// shim calls this once at module load time and then reads `state.id` directly
104+
/// on every DO method call — a plain JS property access with no Wasm boundary
105+
/// crossing.
88106
#[wasm_bindgen(js_name = "__worker_reinit_state")]
89107
pub fn reinit_state() -> JsValue {
90-
get_state()
108+
get_or_create_state()
91109
}

0 commit comments

Comments
 (0)