openvidu/openvidu-dashboard/openspec/changes/writer-review-fixes/design.md

4.3 KiB

Design

Context

  • Ingress state: IOInfoService.UpdateIngressState (pkg/service/ioservice_ingress.go) stores the state the ingress service sends and emits IngressStarted, IngressUpdated or IngressEnded with it. On a local deployment the buffering and publishing states arrive with an empty room_id, and only the ending state names the room (RM_rgFu9jsQne28), although the ingress participant joined that room 40 ms before the publishing state.
  • Ingress projection: the writer's projection/ingress.go lifts roomSid from state.room_id, and adds the session to the room's internal.ingressSessionIds only when it is set.
  • Ingress store: IOInfoService holds an IngressStore, which getIngressStore resolves to the *RedisStore, also the ObjectStore that loads rooms by name. Ingress needs Redis.
  • Egress history: projection/egress.go adds { status, at } to statusHistory with $setUnion, which drops only identical entries: two active updates 13 ms apart, one per output, both stay.
  • Browser vocabulary: projection/vocabulary.go maps an empty browser to other, through browserFamily().

Goals / Non-Goals

Goals:

  • A publishing ingress session is linked to, and counted by, its room with no new projection path.
  • The contract stays additive.

Non-Goals:

  • Changing how the ingress service reports its state (upstream LiveKit ingress).

Decisions

D1. The server names the room of an ingress state

In an OPENVIDU block of UpdateIngressState, when req.State.RoomId is empty and the ingress has a room name, the server loads that room through the store and sets the room's SID on the state it emits. A missing or failing load leaves it empty, logged at debug. The block runs before the telemetry calls, so the webhook and the analytics event carry the same state, and the stored state is updated with it too.

The store is reached through a type assertion of s.is to the room loader that *RedisStore implements, so the wire graph is unchanged.

Rejected: linking the session in the writer when the ingress participant joins. That event has no resource_id, so the room could not count the session until it ends, and the room's counters would still miss a live ingress.

D2. One status history entry per egress status

The statusHistory update keeps, for the incoming status, the entry with the earliest at: it drops any stored entry of the same status whose at is later, adds the incoming entry when no entry of that status is left, sorts by at and keeps the last 50. Egress statuses only move forward, so a repeated status is never a new step.

Rejected: comparing with the last entry only, which depends on the order events arrive in.

D3. none in the browser vocabulary

browserFamily("") returns none, and BrowserFamily gains NONE: 'none', in the contract that the validator and the writer share.

Rejected: leaving other for both, which hides SDKs without a browser among unknown browsers.

Data model

Collection Change
ingress_sessions roomSid set from the first publishing state, instead of the ending one.
rooms internal.ingressSessionIds, and the counters it feeds, set as soon as the session publishes.
egresses statusHistory with one entry per status.
analytics_5min, analytics_hourly, analytics_daily participants.byBrowser.none.
  • No index change.
  • No schema version bump: the v2 schema has not shipped.
  • Legacy migration: the legacy events carry no ingress state of this kind and no output updates, so the mapping is unchanged. Legacy sessions without a browser stay under other.

Performance budget

One LoadRoom (a Redis HGET) per ingress state update without a room id: a handful per ingress session. The writer budgets are unchanged; the batch benchmark covers the egress update.

Risks / Trade-offs

  • [The room is recreated under the same name while the ingress publishes] → The state names the room active at the time of the update, the one the ingress participant is in.
  • [The webhooks of those state updates now carry room_id] → An addition, matching what the ending state already sends.

Migration Plan

Ships with the 3.10 server, and the dashboard typings ship with it through the contract. ui-review-fixes labels the new key.