livekit / server-sdk-go

Client and server SDK for Golang
Apache License 2.0
190 stars 80 forks source link

Bug: "could not find participant" when a participant quickly reconnects #451

Open denis-obukhov opened 4 months ago

denis-obukhov commented 4 months ago

I have an instance of the Go Server SDK that monitors a room. Also, I have iOS client apps that connects to a LiveKit media server.

If an iOS client force disconnects from LiveKit server (like by force killing client app process) and reconnects after a few seconds before LiveKit media server emits a "participant disconnected" message due to timeout then Go LiveKit Server SDK fails with error like: "msg"="could not find participant" "error"=null "participantID"="PA_2rjapiiKp9hi" on media track publication by this iOS client. The error message comes from room.go: handleMediaTrack function.

Probably, it's because of no "participant connected" event is fired when the client reconnects for the second time before the timeout ends.

davidzhao commented 4 months ago

which version of the SDK are you using? Can you share specific steps to reproduce the issue?

denis-obukhov commented 4 months ago

@davidzhao I'm using v2.1.1

Here's code example for Go Server:


package main

import (
    "context"
    "fmt"
    "github.com/livekit/protocol/livekit"
    lksdk "github.com/livekit/server-sdk-go/v2"
    "log"
    "os"
    "os/signal"
    "syscall"
)

var roomClient *lksdk.RoomServiceClient
var liveKitServerURL = "http://127.0.0.1:6880"
var apiKey = "devkey"
var apiSecret = "secret"
var roomName = "test"

func main() {
    roomClient = lksdk.NewRoomServiceClient(liveKitServerURL, apiKey, apiSecret)
    createRoom()
    connectToRoom()

    // This will block the main function indefinitely
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT)
    <-sigChan
}

func createRoom() {
    _, err := roomClient.CreateRoom(context.Background(), &livekit.CreateRoomRequest{
        Name: roomName,
    })
    if err != nil {
        log.Fatalf("❌ Error creating %v room: %v", roomName, err)
        return
    }
    fmt.Println("Room created: ", roomName)
}

func connectToRoom() {
    roomCB := &lksdk.RoomCallback{
        OnParticipantConnected: func(participant *lksdk.RemoteParticipant) {
            fmt.Println("❇️onParticipantConnected: ", participant.Name(), participant.SID(), participant.Identity())
        },
        OnParticipantDisconnected: func(participant *lksdk.RemoteParticipant) {
            fmt.Println("⭕️Participant disconnected: ", participant.Identity())
        },
        ParticipantCallback: lksdk.ParticipantCallback{
            OnTrackPublished: func(publication *lksdk.RemoteTrackPublication, rp *lksdk.RemoteParticipant) {
                fmt.Println("🔔Track published by ", rp.Identity(), rp.SID())
            },
            OnTrackUnpublished: func(publication *lksdk.RemoteTrackPublication, rp *lksdk.RemoteParticipant) {
                fmt.Println("🔕Track unpublished by ", rp.Identity(), rp.SID())
            },
        },
    }

    // Connect to a room
    _, err := lksdk.ConnectToRoom(liveKitServerURL, lksdk.ConnectInfo{
        APIKey:              apiKey,
        APISecret:           apiSecret,
        RoomName:            roomName,
        ParticipantIdentity: "go-server-agent",
    }, roomCB)

    if err != nil {
        log.Fatalf("❌ Error connecting to speaking room: %w", err)
        return
    }
}

I also can provide code for iOS client. But the steps are:

  1. Start Go Server.
  2. Connect to the "test" room from any client (iOS client for my case)
  3. Publish an audio track and stop publishing (turning on and off mic for iOS device)
  4. Force close iOS client app
  5. Relaunch the client
  6. Publish an audio track again
  7. Observe "level"=0 "msg"="could not find participant, deferring track update" "participantID"="PA_PokmPNgaNuvs" message and no "Track published/unpublished" events in Go Server

Here's log I get for the code above:

Room created:  test
❇️onParticipantConnected:   PA_ZbZP56Y9LXdh DenisIphone
🔔Track published by  DenisIphone PA_ZbZP56Y9LXdh
2024/04/25 18:39:50 "level"=0 "msg"="track subscribed" "participant"="DenisIphone" "track"="TR_AMaWYeWpNAXDZK" "kind"="audio"
🔕Track unpublished by  DenisIphone PA_ZbZP56Y9LXdh
// < Here I force killed the client app, relaunched it and published audio again >
2024/04/25 18:40:01 "level"=0 "msg"="could not find participant, deferring track update" "participantID"="PA_Hdxd6K5aDkkq"
2024/04/25 18:40:05 "level"=0 "msg"="participant sid update" "sid-old"="PA_ZbZP56Y9LXdh" "sid-new"="PA_Hdxd6K5aDkkq" "identity"="DenisIphone"
2024/04/25 18:40:05 "level"=0 "msg"="running deferred updates for participant" "participantID"="PA_Hdxd6K5aDkkq" "updates"=1
denis-obukhov commented 4 months ago

I think it could be related to this PR #447 by @dennwc

dennwc commented 4 months ago

As long as it prints running deferred updates for participant, that PR likely works as intended. Without it it will be just participant not found and audio will be lost. Now it at least attempts to re-attach audio.

Probably what's missing is a call to OnTrackPublished when the track reconnects. I need to reproduce it on my side.

denis-obukhov commented 3 months ago

@dennwc Hi! Do you have any progress on this?

dennwc commented 3 months ago

Thank you for the ping, I didn't check it yet. I'll try testing it this week.

denis-obukhov commented 2 months ago

@dennwc Could I help you with something to reproduce the issue?