mirror of
https://github.com/livekit/livekit.git
synced 2026-11-04 13:28:42 +00:00
* rtc: measure subscribe pacing delay in shadow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * prometheus: raise the subscribe pace shadow top bucket to 120 s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: pace video subscribes per node in shadow, record the wait only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: subscribe pacer doc wording, make the wiring test independent of timing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: sample shadow pacing at arrival, skip one-shot, keep sub-millisecond waits Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: take the shadow pacing token before setup, in arrival order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: build shadow pacing buckets in one pass Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
// Copyright 2026 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package rtc
|
|
|
|
import (
|
|
"slices"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/livekit/protocol/logger"
|
|
)
|
|
|
|
// SubscribePacer measures, in shadow, how long video subscribes on this node would wait
|
|
// if they were paced at each of a set of rates. It never delays a subscribe.
|
|
//
|
|
// Each rate is a token bucket that refills at that many subscribes per second and holds
|
|
// one second of tokens, so a burst of up to one second's worth goes at once and the rest
|
|
// waits in line.
|
|
type SubscribePacer struct {
|
|
lock sync.Mutex
|
|
buckets []subscribePaceBucket
|
|
}
|
|
|
|
type subscribePaceBucket struct {
|
|
rateLabel string
|
|
interval time.Duration // time to refill one token
|
|
burst time.Duration // how far ahead of now tokens may be taken without waiting
|
|
nextAt time.Time // when the next token becomes free, if every earlier subscribe had waited for its token
|
|
}
|
|
|
|
// NewSubscribePacer returns nil when no rates are given, which turns the shadow off.
|
|
func NewSubscribePacer(rates []float64) *SubscribePacer {
|
|
rates = slices.Clone(rates)
|
|
slices.Sort(rates)
|
|
|
|
var buckets []subscribePaceBucket
|
|
var labels []string
|
|
for i, rate := range rates {
|
|
if rate <= 0 || (i > 0 && rate == rates[i-1]) {
|
|
continue
|
|
}
|
|
|
|
interval := time.Duration(float64(time.Second) / rate)
|
|
label := strconv.FormatFloat(rate, 'f', -1, 64)
|
|
buckets = append(buckets, subscribePaceBucket{
|
|
rateLabel: label,
|
|
interval: interval,
|
|
burst: max(time.Second-interval, 0),
|
|
})
|
|
labels = append(labels, label)
|
|
}
|
|
if len(buckets) == 0 {
|
|
return nil
|
|
}
|
|
|
|
logger.Infow("subscribe pacing shadow enabled", "rates", labels)
|
|
return &SubscribePacer{buckets: buckets}
|
|
}
|
|
|
|
// Delays takes a token from every bucket for a subscribe that starts at now and returns,
|
|
// keyed by rate, how long that subscribe would have waited for it.
|
|
func (p *SubscribePacer) Delays(now time.Time) map[string]time.Duration {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
p.lock.Lock()
|
|
defer p.lock.Unlock()
|
|
|
|
delays := make(map[string]time.Duration, len(p.buckets))
|
|
for i := range p.buckets {
|
|
b := &p.buckets[i]
|
|
takeAt := b.nextAt
|
|
if takeAt.Before(now) {
|
|
takeAt = now
|
|
}
|
|
delays[b.rateLabel] = max(takeAt.Sub(now)-b.burst, 0)
|
|
b.nextAt = takeAt.Add(b.interval)
|
|
}
|
|
return delays
|
|
}
|