fix: persist Server.name to durable storage for alarm/cold-start hydration#337
Merged
threepointone merged 1 commit intomainfrom Feb 22, 2026
Merged
fix: persist Server.name to durable storage for alarm/cold-start hydration#337threepointone merged 1 commit intomainfrom
threepointone merged 1 commit intomainfrom
Conversation
Persist the Durable Object server name so it survives cold starts and is available inside onStart/onAlarm and scheduled callbacks. Introduces a NAME_STORAGE_KEY constant and has setName write the name to storage; fetch(), alarms and ws handlers hydrate the name from storage via a new #ensureInitialized() helper. Adds tests and two test DOs (AlarmNameServer, NoNameServer) plus wrangler test config updates; updates package peer/dev dependency ranges for partyserver and bumps partysocket. Adds a changeset entry and enables an experimental changesets option.
🦋 Changeset detectedLatest commit: c7eda2e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
hono-party
partyfn
partyserver
partysocket
partysub
partysync
partytracks
partywhen
y-partyserver
commit: |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
this.namethrows when accessed insideonAlarm()or scheduled callbacks because alarms are the only Durable Object entry point that never receives an HTTP request — and partyserver's name was only set from thex-partykit-roomrequest header.This PR persists the name to
ctx.storage.kv(synchronous DO storage) onsetName()and hydrates it lazily in thenamegetter. This makesthis.namework in every context — alarms, WebSocket hibernation wake-ups, directstub.fetch()without the header, and any future entry point.Fixes cloudflare/agents#933. Supersedes #331 with a more comprehensive approach.
What changed
Core fix: name persistence (
packages/partyserver/src/index.ts)setName()now persists the name toctx.storage.kv.put("__ps_name", name)synchronously alongside setting#_name.namegetter hydrates fromctx.storage.kv.get("__ps_name")before throwing, so the name is available on any cold start where it was previously persisted.#_longErrorAboutNameThrown— the dedup flag is no longer needed since the getter now falls back to storage before erroring.Cleanup: consolidated initialization
#initialize()→#ensureInitialized()with an earlyif (this.#status === "started") returnguard, making it idempotent. Eliminated 4 separateif (this.#status !== "started")checks acrossfetch(),setName(),alarm(), and the WS handlers.Cleanup: decoupled name from WS handlers
webSocketMessage/Close/Errorno longer callsetName(connection.server). They call#ensureInitialized()instead. The name is handled by the getter's storage hydration, not by reading it from the per-connection WebSocket attachment. This resolves the// TODO: this shouldn't be asynccomments on all three handlers.Cleanup:
fetch()storage fallbackfetch()now tries storage before requiring the header. On cold start, if the name was previously persisted, thex-partykit-roomheader is no longer required. The header is only needed on first-ever contact with a DO. This means directstub.fetch()calls work for previously-initialized DOs.Dependency range widening
partyserverdependency ranges inhono-party,partysub,partysync,y-partyserver, andpartywhenfrom^0.2.0(which means>=0.2.0 <0.3.0in 0.x semver) to>=0.2.0 <1.0.0so a minor bump doesn't cascade into major bumps on all dependents.onlyUpdatePeerDependentsWhenOutOfRangeto changeset config to prevent unnecessary major cascades for 0.x packages.How it works
Comparison with #331
await ctx.storage.put/get(async)ctx.storage.kv.put/get(sync)alarm()namegetter — all entry pointsfetch()headersetName(connection.server))#ensureInitialized()if (#status)checks#ensureInitialized()Test plan
8 new tests added (40 total, up from 32):
persists name to storage when setName is called— basic write paththis.name is available inside onAlarm after normal setup— warm alarmhydrates name from storage on cold alarm wake (bypassing setName)— seeds storage directly without calling setName, proving the getter's KV read is the recovery mechanismthis.name is available inside onStart during alarm wake— critical for Agent's MCP restorationfetch() hydrates name from storage without requiring the header— direct stub.fetch works for returning DOssetName is idempotent for the same value— no throw on repeated callsthrows when name was never set and is not in storage— brand-new DOs still error correctlygetServerByName persists the name for future access— name set via routing is available on subsequent headerless fetchesAll 40 tests pass.
Made with Cursor