Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webrtc: set sctp receive buffer size to 100kB #2745

Merged
merged 1 commit into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions p2p/test/transport/rcmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ func TestResourceManagerIsUsed(t *testing.T) {
}
return nil
})
if tc.Name == "WebRTC" {
// webrtc receive buffer is a fix sized buffer allocated up front
connScope.EXPECT().ReserveMemory(gomock.Any(), gomock.Any())
}
connScope.EXPECT().Done().MinTimes(1)

var allStreamsDone sync.WaitGroup
Expand Down
4 changes: 4 additions & 0 deletions p2p/transport/webrtc/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ func (l *listener) setupConnection(
// in a release.
settingEngine.SetReceiveMTU(udpmux.ReceiveBufSize)
settingEngine.DetachDataChannels()
settingEngine.SetSCTPMaxReceiveBufferSize(sctpReceiveBufferSize)
if err := scope.ReserveMemory(sctpReceiveBufferSize, network.ReservationPriorityMedium); err != nil {
return nil, err
}

w, err = newWebRTCConnection(settingEngine, l.config)
if err != nil {
Expand Down
21 changes: 8 additions & 13 deletions p2p/transport/webrtc/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,19 @@ import (
const (
// maxMessageSize is the maximum message size of the Protobuf message we send / receive.
maxMessageSize = 16384
// Pion SCTP association has an internal receive buffer of 1MB (roughly, 1MB per connection).
// We can change this value in the SettingEngine before creating the peerconnection.
// https://github.com/pion/webrtc/blob/v3.1.49/sctptransport.go#L341
maxBufferedAmount = 2 * maxMessageSize
// maxSendBuffer is the maximum data we enqueue on the underlying data channel for writes.
// The underlying SCTP layer has an unbounded buffer for writes. We limit the amount enqueued
// per stream is limited to avoid a single stream monopolizing the entire connection.
maxSendBuffer = 2 * maxMessageSize
// sendBufferLowThreshold is the threshold below which we write more data on the underlying
// data channel. We want a notification as soon as we can write 1 full sized message.
sendBufferLowThreshold = maxSendBuffer - maxMessageSize
// maxTotalControlMessagesSize is the maximum total size of all control messages we will
// write on this stream.
// 4 control messages of size 10 bytes + 10 bytes buffer. This number doesn't need to be
// exact. In the worst case, we enqueue these many bytes more in the webrtc peer connection
// send queue.
maxTotalControlMessagesSize = 50
// bufferedAmountLowThreshold and maxBufferedAmount are bound
// to a stream but congestion control is done on the whole
// SCTP association. This means that a single stream can monopolize
// the complete congestion control window (cwnd) if it does not
// read stream data and it's remote continues to send. We can
// add messages to the send buffer once there is space for 1 full
// sized message.
bufferedAmountLowThreshold = maxBufferedAmount / 2

// Proto overhead assumption is 5 bytes
protoOverhead = 5
Expand Down Expand Up @@ -120,7 +115,7 @@ func newStream(
}
// released when the controlMessageReader goroutine exits
s.controlMessageReaderDone.Add(1)
s.dataChannel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold)
s.dataChannel.SetBufferedAmountLowThreshold(sendBufferLowThreshold)
s.dataChannel.OnBufferedAmountLow(func() {
s.notifyWriteStateChanged()

Expand Down
4 changes: 2 additions & 2 deletions p2p/transport/webrtc/stream_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ func (s *stream) SetWriteDeadline(t time.Time) error {

func (s *stream) availableSendSpace() int {
buffered := int(s.dataChannel.BufferedAmount())
availableSpace := maxBufferedAmount - buffered
availableSpace := maxSendBuffer - buffered
if availableSpace+maxTotalControlMessagesSize < 0 { // this should never happen, but better check
log.Errorw("data channel buffered more data than the maximum amount", "max", maxBufferedAmount, "buffered", buffered)
log.Errorw("data channel buffered more data than the maximum amount", "max", maxSendBuffer, "buffered", buffered)
}
return availableSpace
}
Expand Down
6 changes: 6 additions & 0 deletions p2p/transport/webrtc/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ const (
DefaultDisconnectedTimeout = 20 * time.Second
DefaultFailedTimeout = 30 * time.Second
DefaultKeepaliveTimeout = 15 * time.Second

sctpReceiveBufferSize = 100_000
)

type WebRTCTransport struct {
Expand Down Expand Up @@ -314,6 +316,10 @@ func (t *WebRTCTransport) dial(ctx context.Context, scope network.ConnManagement
// If you run pion on a system with only the loopback interface UP,
// it will not connect to anything.
settingEngine.SetIncludeLoopbackCandidate(true)
settingEngine.SetSCTPMaxReceiveBufferSize(sctpReceiveBufferSize)
if err := scope.ReserveMemory(sctpReceiveBufferSize, network.ReservationPriorityMedium); err != nil {
return nil, err
}

w, err = newWebRTCConnection(settingEngine, t.webrtcConfig)
if err != nil {
Expand Down
Loading