Skip to content

[Test] A restarted actor at a reused path is never tested against a Terminated queued for the previous incarnation #1380

Description

@pathosDev

Use case

src/internal/ActorCell.ts:83-90 documents the hazard precisely, in the code, as the reason watch bookkeeping is keyed the way it is:

ActorPath.toString() is the canonical address and deliberately omits the uid — location transparency depends on it staying stable across a restart. Watch bookkeeping needs the opposite: a name that is re-spawned (a restarted parent recreating a named child, a router pool rebuilding its routees) must be a different subject, or the previous incarnation's pending Terminated is delivered against its successor and the successor is never registered at all, because the address is already in the map.

So the mechanism exists and the reasoning is written down. What is missing is a test that binds it.

tests/unit/internal/WatchProvenance.test.ts has three cases, and all three are about a fabricated Terminated — one arriving from a party that was never watched. That is the forgery case. It is not the collision case, where the Terminated is entirely legitimate but belongs to a dead incarnation of a path that has since been reused.

The distinction matters because the two are fixed by different code. Forgery is rejected by provenance checking; collision is prevented by the uid keying the comment above describes. Removing the uid from the watch key would leave all three provenance tests green.

The scenario is worth constructing because it is a genuine race and not a synthetic one: a supervisor restarts a named child, and the Terminated for the old instance is still in flight when the new one registers.

Proposed shape

  • Construct the collision directly: watch a named child, cause a restart that re-creates it under the same name, and arrange for the old incarnation's Terminated to be delivered after the new one is registered.
  • Assert the new incarnation does not receive it, and that its own watch registration survives.
  • Assert the reverse too — that the watcher of the old incarnation is notified correctly, so the fix is not "drop everything ambiguous".
  • Prove the test discriminates: remove the uid from the watch key and confirm this test goes red while the three provenance tests stay green. That two-sided result is the evidence, and it is worth recording on the issue.

Acceptance

  • A test constructs a Terminated for a previous incarnation of a reused path.
  • It asserts the successor neither receives it nor loses its own registration.
  • Neutralising the uid distinction makes this test fail and leaves the provenance tests green — recorded in the issue.

Verification status

Confirmed by reading. The comment is quoted verbatim from src/internal/ActorCell.ts:83-90. tests/unit/internal/WatchProvenance.test.ts was read in full; its three test names are "a fabricated Terminated is not delivered and does not retire the watch", "a fabricated Terminated is dead-lettered rather than silently consumed", and "a fabricated Terminated does not consume a watchWith substitution". None constructs a legitimate Terminated for a superseded incarnation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions