Skip to content

fix(service): reopen a routing key whose session was deleted - #1160

Merged
edwin-zvs merged 1 commit into
mainfrom
fix/service-recreate-deleted-session
Aug 2, 2026
Merged

fix(service): reopen a routing key whose session was deleted#1160
edwin-zvs merged 1 commit into
mainfrom
fix/service-recreate-deleted-session

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

The bug

Delete a session that a service created under session-key routing, and that routing key is bricked. Every later message from the channel with the same key fails instead of starting a new conversation.

ServiceIngress::submit_tracked resolved the key to a persisted session id and went straight to manager.detail(&id).await?. For a deleted session that returns Err("session not found: <id>"), and the ? propagated it out of the delivery. Nothing anywhere prunes the routing map — ingress.rs only ever inserts, and session deletion has no hook into the service supervisor — so the dangling entry survived, and so did the failure. The only repair was hand-editing <data_dir>/services/<service>.json.

single routing had the identical bug: it resolves through the same keyed path under the fixed key __single__, so deleting the shared session broke the channel for every caller. per-event never routes by key and was unaffected.

The fix

Resolve the key through a liveness probe, in a new live_session_for_key. An entry whose session is gone is dropped and the delivery falls through to the normal creation path, so the key opens a fresh session and keeps working. Both the canonical entry and any legacy bare-key duplicate are cleared, along with the ownership record, so a deleted id cannot later expose an unrelated session through the channel. Pruning happens under the state lock the creation path already holds, so it stays atomic against a concurrent delivery on the same key.

Liveness is get_entry — registry membership — not the full detail read. A transient transcript or storage error is not evidence of deletion and must not abandon a live conversation, so that case still fails the delivery and keeps the entry.

Tests

Four unit tests in crates/daemon/src/service/ingress.rs, covering the keyed case, the single fixed key, the legacy bare-key entry, and an unrouted key. Verified non-vacuous: with the probe forced to the old always-reuse behavior, the three deletion tests fail and the rest pass.

Full workspace suite is green. pty_render::tests::smith_tool_expand_collapse_rebuilds_only_retained_suffix failed on two loaded runs and passed on four unloaded ones — it asserts expand_us < 80_000 in wall-clock microseconds, so it is load-sensitive and unrelated to this diff (it also fails/passes the same way with the diff stashed).

Spec

specs/0177-service-routing-keys-outlive-their-sessions.md records the rule: the routing table is a cache, not a guarantee, and a dangling entry means "unrouted", not "broken".

A service's routing table maps a routing key to the session serving it,
but nothing prunes that table when a session is deleted — the operator
can delete a routed session from any client, and the service is never
told. The delivery path then resolved the key to a session id that no
longer existed and propagated the resulting "session not found" error,
leaving the stale entry in place. That bricked the key permanently: every
later delivery failed the same way, and the only repair was hand-editing
the service's state file.

Resolve the key through a liveness probe instead. A routing entry whose
session is gone is dropped, and the delivery falls through to the normal
creation path, so the key opens a fresh session and keeps working. Both
the canonical entry and any legacy bare-key duplicate are cleared, along
with the ownership record, so a deleted id cannot later expose an
unrelated session through the channel.

This covers `single` routing as well as `session-key`: both resolve
through the same keyed path, `single` under a fixed key. `per-event`
never routed by key and was unaffected.

Membership in the session registry is probed rather than a full session
read, so a transient transcript or storage error is not mistaken for a
deletion and does not abandon a live conversation.
@edwin-zvs
edwin-zvs merged commit 794149e into main Aug 2, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the fix/service-recreate-deleted-session branch August 2, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant