fix: ensure num_participants is accurate in webhook events (#4265) (#4422)

* fix: ensure num_participants is accurate in webhook events (#4265)

  Three fixes for stale/incorrect num_participants in webhook payloads:

  1. Move participant map insertion before MarkDirty in join path so
     updateProto() counts the new participant.
  2. Use fresh room.ToProto() for participant_joined webhook instead of
     a stale snapshot captured at session start.
  3. Remove direct NumParticipants-- in leave path (inconsistent with
     updateProto's IsDependent check), force immediate proto update,
     and wait for completion before triggering onClose callbacks.

* fix: use ToProtoConsistent for webhook events instead of forcing immediate updates
This commit is contained in:
Onyeka Obi
2026-04-12 18:26:14 -07:00
committed by GitHub
parent c91e79af35
commit cdb0769c38
2 changed files with 12 additions and 7 deletions

View File

@@ -323,6 +323,14 @@ func (r *Room) ToProto() *livekit.Room {
return r.protoProxy.Get()
}
// ToProtoConsistent returns a room proto with participant counts computed
// directly from the current participants map, bypassing the batched proto
// proxy. Use this when an accurate num_participants is required immediately,
// e.g. for webhook or telemetry events.
func (r *Room) ToProtoConsistent() *livekit.Room {
return r.updateProto()
}
func (r *Room) Name() livekit.RoomName {
return livekit.RoomName(r.protoRoom.Name)
}
@@ -1403,10 +1411,6 @@ func (r *Room) RemoveParticipant(
delete(r.participantRequestSources, identity)
delete(r.hasPublished, identity)
delete(r.agentParticpants, identity)
if !p.Hidden() {
r.protoRoom.NumParticipants--
}
immediateChange := false
if p.IsRecorder() {
activeRecording := false

View File

@@ -569,7 +569,8 @@ func (r *RoomManager) StartSession(
persistRoomForParticipantCount(room.ToProto())
clientMeta := &livekit.AnalyticsClientMeta{Region: r.currentNode.Region(), Node: string(r.currentNode.NodeID())}
r.telemetry.ParticipantJoined(ctx, protoRoom, participant.ToProto(), pi.Client, clientMeta, true, participant.TelemetryGuard())
// Use a consistent room proto so num_participants reflects the newly joined participant
r.telemetry.ParticipantJoined(ctx, room.ToProtoConsistent(), participant.ToProto(), pi.Client, clientMeta, true, participant.TelemetryGuard())
participant.AddOnClose(types.ParticipantCloseKeyNormal, func(p types.LocalParticipant) {
participantServerClosers.Close()
@@ -577,8 +578,8 @@ func (r *RoomManager) StartSession(
pLogger.Errorw("could not delete participant", err)
}
// update room store with new numParticipants
proto := room.ToProto()
// use consistent proto so num_participants is accurate for webhook
proto := room.ToProtoConsistent()
persistRoomForParticipantCount(proto)
r.telemetry.ParticipantLeft(ctx, proto, p.ToProto(), true, participant.TelemetryGuard())
})