Skip to content

Replace in-memory StreamingProxyActorStore participant tracking with projection-based readmodel #153

@eanzhao

Description

@eanzhao

Problem

StreamingProxyActorStore uses a singleton ConcurrentDictionary to track room participants in-memory. This violates the architecture constraint prohibiting middleware-level in-memory entity state mappings (CLAUDE.md: 中间层状态约束).

Current issues:

  • Drift on restart: participant data is lost when the process restarts, even though the actor's event-sourced state (StreamingProxyGAgentState) has the authoritative participant list
  • No leave tracking from external sources: only the HTTP join endpoint updates the store; GroupChatParticipantLeftEvent from other producers is not reflected
  • Not replay-driven: the store is not rebuilt from committed events, so it can diverge from the actor's authority with no repair path
  • Concurrency risk: ConcurrentDictionary + lock pattern in a singleton, acting as a second source of truth alongside the actor state

Current State

The in-memory store is used by:

  • HandleListParticipantsAsync — returns participant snapshot for REST clients
  • HandleJoinAsync — tracks new participants
  • StreamingProxyNyxParticipantCoordinator.EnsureParticipantsJoinedAsync — checks existing participants before joining Nyx providers

Real-time participant changes are visible via the SSE stream, but snapshot queries (reconnect/refresh flows) depend on the in-memory store.

Proposed Solution

Materialize an actor-scoped current-state readmodel from committed GroupChatParticipantJoined/Left events through the projection pipeline:

  1. Create a StreamingProxyParticipantProjector that consumes committed participant events
  2. Back it with an in-memory document store (dev) or persistent store (prod)
  3. Replace StreamingProxyActorStore.ListParticipants/AddParticipant with readmodel queries
  4. Remove the ConcurrentDictionary-based participant tracking from StreamingProxyActorStore

The room tracking in StreamingProxyActorStore (used only by NyxParticipantCoordinator) can remain as-is or be migrated to IGAgentActorStore in a follow-up.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions