feat(dashboard): mirror events via SessionEventDistributor subscriber (fixes dark feed without gRPC client)
This commit is contained in:
@@ -2,7 +2,6 @@ using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.MxGateway.Server.Configuration;
|
||||
using ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs;
|
||||
using ZB.MOM.WW.MxGateway.Server.Metrics;
|
||||
using ZB.MOM.WW.MxGateway.Server.Sessions;
|
||||
using ZB.MOM.WW.MxGateway.Server.Workers;
|
||||
@@ -12,9 +11,7 @@ namespace ZB.MOM.WW.MxGateway.Server.Grpc;
|
||||
public sealed class EventStreamService(
|
||||
ISessionManager sessionManager,
|
||||
IOptions<GatewayOptions> options,
|
||||
GatewayMetrics metrics,
|
||||
IDashboardEventBroadcaster dashboardEventBroadcaster,
|
||||
ILogger<EventStreamService> logger) : IEventStreamService
|
||||
GatewayMetrics metrics) : IEventStreamService
|
||||
{
|
||||
/// <summary>
|
||||
/// Streams events from a session to the client asynchronously.
|
||||
@@ -26,8 +23,19 @@ public sealed class EventStreamService(
|
||||
/// <see cref="SessionEventDistributor"/> pump. The pump owns the single drain of
|
||||
/// the worker event stream and the worker→public mapping (mirroring the former
|
||||
/// <c>ProduceEventsAsync</c>); this loop is the per-subscriber boundary that
|
||||
/// applies the per-RPC filter (<c>AfterWorkerSequence</c>), the dashboard mirror,
|
||||
/// queue-depth metrics, and the backpressure/overflow policy.
|
||||
/// applies the per-RPC filter (<c>AfterWorkerSequence</c>), queue-depth metrics,
|
||||
/// and the backpressure/overflow policy.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Task 6 moved the dashboard mirror OFF this per-RPC loop. The dashboard is now a
|
||||
/// first-class internal subscriber on the session's
|
||||
/// <see cref="SessionEventDistributor"/> (see <c>GatewaySession.StartDashboardMirror</c>),
|
||||
/// so it receives session events even when no gRPC client is streaming. This loop no
|
||||
/// longer mirrors to the dashboard. One deliberate consequence: the dashboard now sees
|
||||
/// RAW session events, not the per-gRPC-subscriber <c>AfterWorkerSequence</c>-filtered
|
||||
/// view this loop applies — the dashboard is a separate LDAP-authenticated monitoring
|
||||
/// view that should see the session's full event activity (per-session dashboard ACL is
|
||||
/// the separate Task 18).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Overflow handling (Task 5): the distributor's per-subscriber channel is bounded
|
||||
@@ -97,34 +105,12 @@ public sealed class EventStreamService(
|
||||
|
||||
// Per-RPC filter stays at the subscriber boundary: each request may resume
|
||||
// from a different AfterWorkerSequence, so the shared pump fans raw events and
|
||||
// this loop drops the ones at or below the caller's watermark. Filtered events
|
||||
// are not mirrored to the dashboard, matching the pre-Task-4 ordering where
|
||||
// the skip ran before the dashboard Publish.
|
||||
// this loop drops the ones at or below the caller's watermark.
|
||||
if (mxEvent.WorkerSequence <= afterWorkerSequence)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mirror the event to the dashboard EventsHub group for this session.
|
||||
// Fire-and-forget — broadcast errors must not affect the source gRPC stream.
|
||||
// Server-041: IDashboardEventBroadcaster documents Publish as never-throw,
|
||||
// but we enforce that at the seam too so a future implementation that adds
|
||||
// synchronous validation or a serializer hop cannot fault this loop and end
|
||||
// the client's gRPC stream. (Task 6 will move this tap onto its own
|
||||
// distributor subscriber; for Task 4 it coexists here, firing once per
|
||||
// delivered event for the single subscriber exactly as before.)
|
||||
try
|
||||
{
|
||||
dashboardEventBroadcaster.Publish(session.SessionId, mxEvent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogDebug(
|
||||
ex,
|
||||
"Dashboard event mirror threw for session {SessionId}; continuing.",
|
||||
session.SessionId);
|
||||
}
|
||||
|
||||
// Queue-depth gauge tracks events the pump has fanned into this subscriber's
|
||||
// channel but the client has not yet consumed — the same "buffered, not yet
|
||||
// delivered" quantity the pre-Task-4 per-RPC channel reported. The bounded
|
||||
|
||||
Reference in New Issue
Block a user