Skip to content

Single-actor-per-pid check for ReplicatedEventSourcedActor #58

@pathosDev

Description

@pathosDev

`ReplicatedEventSourcedActor` assumes it's the single writer for its `persistenceId` on the local node — that's how `_appendOne` can read `highestSeq` and `append` in one mailbox tick without optimistic-concurrency races. Across nodes (different replicas), parallel writes are the whole point and they're handled correctly via vector clocks.

But within ONE node, two actors with the same `persistenceId` violate the invariant:

```ts
system.actorOf(Props.create(() => new MyReplicated('counter-1')), 'a');
system.actorOf(Props.create(() => new MyReplicated('counter-1')), 'b'); // same pid!
```

Both share the same journal and the same in-memory replicaId. Their `_appendOne` calls race; the second one fails with `JournalConcurrencyError` and silently drops the write. In-memory state on each actor diverges.

Fix: detect the second `actorOf` for the same `persistenceId` and either:

A. Throw at preStart — fail fast. User has to choose a different pid or share a single ref.
B. Warn + log — let it through but flag the misconfiguration.

Recommendation: A. This is a correctness invariant, not a soft preference; failing loudly avoids silent data divergence.

Implementation sketch:

The `ReplicatedEventSourcedActor` base class registers itself with a per-system `Map<persistenceId, ActorRef>` on `preStart` and unregisters on `postStop`. Second registration → throw.

Components:

File Task
`src/persistence/ReplicatedEventSourcedActor.ts` Add the registry + check; throw `Error('duplicate ReplicatedEventSourcedActor for persistenceId X')`.
`tests/unit/persistence/replicated/SingleWriter.test.ts` (new) Spawn two with the same pid → second throws.

Estimate: 1-2 days.

Verification:

  • Unit test: `actorOf` of a second instance with the same pid throws synchronously (before the actor even starts processing messages).
  • Existing tests stay green — they don't double-spawn.

Out of scope:

  • Multi-replica coordination (the cluster-wide pattern IS multi-writer; that's not what this issue addresses).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: lowNice-to-have / niche / demand-driven

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions