diff --git a/packages/partyserver/src/index.ts b/packages/partyserver/src/index.ts index aa3ea328..4882dcdb 100644 --- a/packages/partyserver/src/index.ts +++ b/packages/partyserver/src/index.ts @@ -641,6 +641,8 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam } this.#_name = name; + await this.ctx.storage.put("__partyserver_name", name); + if (this.#status !== "started") { await this.#initialize(); } @@ -793,6 +795,12 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam } async alarm(): Promise { + if (!this.#_name) { + const stored = await this.ctx.storage.get("__partyserver_name"); + if (stored) { + this.#_name = stored; + } + } if (this.#status !== "started") { // This means the server "woke up" after hibernation // so we need to hydrate it again diff --git a/packages/partyserver/src/tests/index.test.ts b/packages/partyserver/src/tests/index.test.ts index 141e041a..39b6ed4c 100644 --- a/packages/partyserver/src/tests/index.test.ts +++ b/packages/partyserver/src/tests/index.test.ts @@ -532,6 +532,37 @@ describe("Alarm (initialize without redundant blockConcurrencyWhile)", () => { }); }); +describe("Alarm name recovery from storage", () => { + it("this.name is available in onAlarm after cold wake", async () => { + const id = env.AlarmNameServer.idFromName("alarm-name-test"); + const stub = env.AlarmNameServer.get(id); + + // Seed storage directly, bypassing Server.fetch()/setName() so #_name stays unset. + const res = await stub.fetch( + new Request( + "http://example.com/?seed=1&name=alarm-name-test" + ) + ); + expect(await res.text()).toEqual("seeded"); + + const ran = await runDurableObjectAlarm(stub); + expect(ran).toBe(true); + + const stateRes = await stub.fetch( + new Request("http://example.com/", { + headers: { "x-partykit-room": "alarm-name-test" } + }) + ); + const state = (await stateRes.json()) as { + alarmName: string | null; + nameWasCold: boolean; + }; + + expect(state.nameWasCold).toBe(true); + expect(state.alarmName).toEqual("alarm-name-test"); + }); +}); + describe("CORS", () => { it("returns CORS headers on OPTIONS preflight for matched routes", async () => { const ctx = createExecutionContext(); diff --git a/packages/partyserver/src/tests/worker.ts b/packages/partyserver/src/tests/worker.ts index 1928a9bf..7e3786f4 100644 --- a/packages/partyserver/src/tests/worker.ts +++ b/packages/partyserver/src/tests/worker.ts @@ -23,6 +23,7 @@ export type Env = { HibernatingNameInMessage: DurableObjectNamespace; TagsServer: DurableObjectNamespace; TagsServerInMemory: DurableObjectNamespace; + AlarmNameServer: DurableObjectNamespace; }; export class Stateful extends Server { @@ -352,6 +353,52 @@ export class TagsServerInMemory extends Server { } } +/** + * Tests that this.name is recovered from storage when alarm() fires without a prior HTTP request in the wake cycle. + */ +export class AlarmNameServer extends Server { + static options = { + hibernate: true + }; + + alarmName: string | null = null; + nameWasCold = false; + + // Bypass Server.fetch() for the seed request so setName() is never called. + // This simulates a DO that was previously named, went to sleep, and wakes + // up cold for an alarm — #_name is unset, only storage has the name. + async fetch(request: Request): Promise { + const url = new URL(request.url); + if (url.searchParams.get("seed")) { + const name = url.searchParams.get("name")!; + await this.ctx.storage.put("__partyserver_name", name); + await this.ctx.storage.setAlarm(Date.now() + 60_000); + return new Response("seeded"); + } + return super.fetch(request); + } + + async alarm() { + try { + this.name; + } catch { + this.nameWasCold = true; + } + await super.alarm(); + } + + onAlarm() { + this.alarmName = this.name; + } + + async onRequest(): Promise { + return Response.json({ + alarmName: this.alarmName, + nameWasCold: this.nameWasCold + }); + } +} + export class CorsServer extends Server { onRequest(): Response | Promise { return Response.json({ cors: true }); diff --git a/packages/partyserver/src/tests/wrangler.jsonc b/packages/partyserver/src/tests/wrangler.jsonc index 6e1e8c41..07eb812a 100644 --- a/packages/partyserver/src/tests/wrangler.jsonc +++ b/packages/partyserver/src/tests/wrangler.jsonc @@ -66,6 +66,10 @@ { "name": "TagsServerInMemory", "class_name": "TagsServerInMemory" + }, + { + "name": "AlarmNameServer", + "class_name": "AlarmNameServer" } ] }, @@ -86,7 +90,8 @@ "FailingOnStartServer", "HibernatingNameInMessage", "TagsServer", - "TagsServerInMemory" + "TagsServerInMemory", + "AlarmNameServer" ] } ]