Repository navigation
computer: Wake the workspace host by name, not only by id (#243) #244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
aron-cf
wants to merge
1
commit into
main
Choose a base branch
from
fix/proxy-wake-by-name
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@cloudflare/computer": patch | ||
| --- | ||
|
|
||
| `WorkspaceProxy` and `WorkspaceServiceProxy` wake their host Durable Object by name, so `ctx.id.name` survives a wake caused by a container or Dynamic Worker callback. `WorkspaceRef`, `WorkspaceProxyProps` and `WorkspaceServiceProxyProps` accept an optional `name`, filled in from `ctx.id.name` when the backend runs in the host object itself. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { hostDurableObjectId, withHostName } from "./durable-object-name.js"; | ||
|
|
||
| // A namespace whose ids remember how they were made, standing in for | ||
| // DurableObjectNamespace: a named id knows its name, as ctx.id.name | ||
| // does on the instance it wakes. | ||
| class FakeId { | ||
| constructor( | ||
| readonly hex: string, | ||
| readonly name?: string, | ||
| ) {} | ||
| toString(): string { | ||
| return this.hex; | ||
| } | ||
| } | ||
|
|
||
| const ns = { | ||
| idFromName: (name: string) => new FakeId(`hex:${name}`, name), | ||
| idFromString: (id: string) => new FakeId(id), | ||
| }; | ||
|
|
||
| describe("hostDurableObjectId", () => { | ||
| it("addresses by name when the name yields the id", () => { | ||
| const id = hostDurableObjectId(ns, "hex:main", "main"); | ||
| expect(id.toString()).toBe("hex:main"); | ||
| expect(id.name).toBe("main"); | ||
| }); | ||
|
|
||
| it("falls back to the id when the name maps elsewhere", () => { | ||
| const id = hostDurableObjectId(ns, "hex:main", "other"); | ||
| expect(id.toString()).toBe("hex:main"); | ||
| expect(id.name).toBeUndefined(); | ||
| }); | ||
|
|
||
| it("uses the id when there is no name", () => { | ||
| expect(hostDurableObjectId(ns, "hex:main", undefined).name).toBeUndefined(); | ||
| expect(hostDurableObjectId(ns, "hex:main", "").name).toBeUndefined(); | ||
| }); | ||
| }); | ||
|
|
||
| describe("withHostName", () => { | ||
| const ctx = { id: new FakeId("hex:main", "main") }; | ||
|
|
||
| it("adds the name of the object that owns ctx", () => { | ||
| expect(withHostName({ binding: "Agent", id: "hex:main" }, ctx)).toEqual({ | ||
| binding: "Agent", | ||
| id: "hex:main", | ||
| name: "main", | ||
| }); | ||
| }); | ||
|
|
||
| it("leaves a reference to another object alone", () => { | ||
| const ref = { binding: "Agent", id: "hex:other" }; | ||
| expect(withHostName(ref, ctx)).toBe(ref); | ||
| }); | ||
|
|
||
| it("keeps a name the caller supplied", () => { | ||
| const ref = { binding: "Agent", id: "hex:main", name: "explicit" }; | ||
| expect(withHostName(ref, ctx)).toBe(ref); | ||
| }); | ||
|
|
||
| it("leaves the reference alone when the runtime has no name", () => { | ||
| const ref = { binding: "Agent", id: "hex:main" }; | ||
| expect(withHostName(ref, { id: new FakeId("hex:main") })).toBe(ref); | ||
| expect(withHostName(ref, undefined)).toBe(ref); | ||
| expect(withHostName(ref, {})).toBe(ref); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // Keeping a Durable Object's name across proxy wakes. | ||
| // | ||
| // The workspace proxies reach their host Durable Object from props | ||
| // that a container or a Dynamic Worker holds on to. Both outlive the | ||
| // object: after an eviction or a deploy, the proxy is often the first | ||
| // caller, and the instance it wakes is the one every later caller | ||
| // shares. An instance woken through idFromString() has no | ||
| // ctx.id.name for its whole lifetime, and anything that needs the | ||
| // name (agents' Lifecycle, for one) then refuses to start. So the | ||
| // props may carry the name too, and the proxy addresses the object | ||
| // by name whenever that name produces the same id. | ||
|
|
||
| // The part of DurableObjectNamespace the proxies use. Structural, so | ||
| // this module needs no runtime import. | ||
| interface DurableObjectIds<Id> { | ||
| idFromName(name: string): Id; | ||
| idFromString(id: string): Id; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve the id a proxy should wake its host Durable Object with. | ||
| * | ||
| * Prefers `idFromName(name)` when it yields `id`, so the woken | ||
| * instance keeps `ctx.id.name`. A missing name, or one that maps to a | ||
| * different object, falls back to `idFromString(id)`: `id` stays the | ||
| * authority on which object is meant. | ||
| */ | ||
| export function hostDurableObjectId<Id extends { toString(): string }>( | ||
| ns: DurableObjectIds<Id>, | ||
| id: string, | ||
| name: string | undefined, | ||
| ): Id { | ||
| if (name !== undefined && name !== "") { | ||
| const named = ns.idFromName(name); | ||
| if (named.toString() === id) return named; | ||
| } | ||
| return ns.idFromString(id); | ||
| } | ||
|
|
||
| /** | ||
| * Fill in `name` on a reference to the Durable Object that owns `ctx`. | ||
| * | ||
| * Callers usually build the reference from `ctx.id.toString()` alone. | ||
| * When the reference points at this very object and the runtime knows | ||
| * its name, the name is added so the proxy can wake it by name later. | ||
| * A reference to another object, or one that already names itself, is | ||
| * returned unchanged. | ||
| */ | ||
| export function withHostName<Ref extends { id: string; name?: string }>( | ||
| ref: Ref, | ||
| ctx: unknown, | ||
| ): Ref { | ||
| if (ref.name !== undefined) return ref; | ||
| const id = (ctx as { id?: { name?: string; toString(): string } } | undefined)?.id; | ||
| const name = id?.name; | ||
| if (name === undefined || name === "" || id?.toString() !== ref.id) return ref; | ||
| return { ...ref, name }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔍 Automatic name propagation remains untested end-to-end
The tests cover inferred names and name-aware routing separately. No workerd test confirms a backend passes its inferred name to a proxy, leaving the wake fix's wiring unverified.
Was this helpful? React with 👍 or 👎 to provide feedback.