Skip to content

Commit

Permalink
Add ability to get selected candidate pair stats (#735)
Browse files Browse the repository at this point in the history
It is useful to have stats from just the selected pair as a lightweight
option where a lot of agents are running, for example, an SFU.

lint

Switch udp_mux_test to use sha256 instead of sha1 (#733)

Minor change to this test to stop using sha1 and remove the linter
exceptions.

Co-authored-by: Daniel Kessler <[email protected]>

Update module golang.org/x/net to v0.29.0

Generated by renovateBot

Update module github.com/pion/dtls/v3 to v3.0.3

Generated by renovateBot
  • Loading branch information
boks1971 authored Oct 7, 2024
1 parent 410d6ec commit 854fdfd
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
50 changes: 50 additions & 0 deletions agent_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,56 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats {
return res
}

// GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair.
// Returns false if there is no selected pair
func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) {
isAvailable := false
var res CandidatePairStats
err := a.loop.Run(a.loop, func(_ context.Context) {
sp := a.getSelectedPair()
if sp == nil {
return
}

isAvailable = true
res = CandidatePairStats{
Timestamp: time.Now(),
LocalCandidateID: sp.Local.ID(),
RemoteCandidateID: sp.Remote.ID(),
State: sp.state,
Nominated: sp.nominated,
// PacketsSent uint32
// PacketsReceived uint32
// BytesSent uint64
// BytesReceived uint64
// LastPacketSentTimestamp time.Time
// LastPacketReceivedTimestamp time.Time
// FirstRequestTimestamp time.Time
// LastRequestTimestamp time.Time
// LastResponseTimestamp time.Time
TotalRoundTripTime: sp.TotalRoundTripTime(),
CurrentRoundTripTime: sp.CurrentRoundTripTime(),
// AvailableOutgoingBitrate float64
// AvailableIncomingBitrate float64
// CircuitBreakerTriggerCount uint32
// RequestsReceived uint64
// RequestsSent uint64
ResponsesReceived: sp.ResponsesReceived(),
// ResponsesSent uint64
// RetransmissionsReceived uint64
// RetransmissionsSent uint64
// ConsentRequestsSent uint64
// ConsentExpiredTimestamp time.Time
}
})
if err != nil {
a.log.Errorf("Failed to get selected candidate pair stats: %v", err)
return CandidatePairStats{}, false
}

return res, isAvailable
}

// GetLocalCandidatesStats returns a list of local candidates stats
func (a *Agent) GetLocalCandidatesStats() []CandidateStats {
var res []CandidateStats
Expand Down
85 changes: 84 additions & 1 deletion agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ func TestInvalidGather(t *testing.T) {
})
}

func TestCandidatePairStats(t *testing.T) {
func TestCandidatePairsStats(t *testing.T) {
defer test.CheckRoutines(t)()

// Avoid deadlocks?
Expand Down Expand Up @@ -789,6 +789,89 @@ func TestCandidatePairStats(t *testing.T) {
}
}

func TestSelectedCandidatePairStats(t *testing.T) {
defer test.CheckRoutines(t)()

// Avoid deadlocks?
defer test.TimeOut(1 * time.Second).Stop()

a, err := NewAgent(&AgentConfig{})
if err != nil {
t.Fatalf("Failed to create agent: %s", err)
}
defer func() {
require.NoError(t, a.Close())
}()

hostConfig := &CandidateHostConfig{
Network: "udp",
Address: "192.168.1.1",
Port: 19216,
Component: 1,
}
hostLocal, err := NewCandidateHost(hostConfig)
if err != nil {
t.Fatalf("Failed to construct local host candidate: %s", err)
}

srflxConfig := &CandidateServerReflexiveConfig{
Network: "udp",
Address: "10.10.10.2",
Port: 19218,
Component: 1,
RelAddr: "4.3.2.1",
RelPort: 43212,
}
srflxRemote, err := NewCandidateServerReflexive(srflxConfig)
if err != nil {
t.Fatalf("Failed to construct remote srflx candidate: %s", err)
}

// no selected pair, should return not available
_, ok := a.GetSelectedCandidatePairStats()
require.False(t, ok)

// add pair and populate some RTT stats
p := a.findPair(hostLocal, srflxRemote)
if p == nil {
a.addPair(hostLocal, srflxRemote)
p = a.findPair(hostLocal, srflxRemote)
}
for i := 0; i < 10; i++ {
p.UpdateRoundTripTime(time.Duration(i+1) * time.Second)
}

// set the pair as selected
a.setSelectedPair(p)

stats, ok := a.GetSelectedCandidatePairStats()
require.True(t, ok)

if stats.LocalCandidateID != hostLocal.ID() {
t.Fatal("invalid local candidate id")
}
if stats.RemoteCandidateID != srflxRemote.ID() {
t.Fatal("invalid remote candidate id")
}

expectedCurrentRoundTripTime := time.Duration(10) * time.Second
if stats.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() {
t.Fatalf("expected current round trip time to be %f, it is %f instead",
expectedCurrentRoundTripTime.Seconds(), stats.CurrentRoundTripTime)
}

expectedTotalRoundTripTime := time.Duration(55) * time.Second
if stats.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() {
t.Fatalf("expected total round trip time to be %f, it is %f instead",
expectedTotalRoundTripTime.Seconds(), stats.TotalRoundTripTime)
}

if stats.ResponsesReceived != 10 {
t.Fatalf("expected responses received to be 10, it is %d instead",
stats.ResponsesReceived)
}
}

func TestLocalCandidateStats(t *testing.T) {
defer test.CheckRoutines(t)()

Expand Down

0 comments on commit 854fdfd

Please sign in to comment.