Skip to content

alts: increase write record size max to 1MB #8512

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion cmd/protoc-gen-go-grpc/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/cmd/protoc-gen-go-grpc

go 1.23.0
go 1.24.0

require (
google.golang.org/grpc v1.70.0
Expand Down
32 changes: 0 additions & 32 deletions credentials/alts/internal/conn/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
package conn

import (
"encoding/binary"
"errors"
"fmt"
)

const (
Expand All @@ -48,33 +46,3 @@ func SliceForAppend(in []byte, n int) (head, tail []byte) {
tail = head[len(in):]
return head, tail
}

// ParseFramedMsg parse the provided buffer and returns a frame of the format
// msgLength+msg and any remaining bytes in that buffer.
func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) {
// If the size field is not complete, return the provided buffer as
// remaining buffer.
length, sufficientBytes := parseMessageLength(b)
if !sufficientBytes {
return nil, b, nil
}
if length > maxLen {
return nil, nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen)
}
if len(b) < int(length)+4 { // account for the first 4 msg length bytes.
// Frame is not complete yet.
return nil, b, nil
}
return b[:MsgLenFieldSize+length], b[MsgLenFieldSize+length:], nil
}

// parseMessageLength returns the message length based on frame header. It also
// returns a boolean indicating if the buffer contains sufficient bytes to parse
// the length header. If there are insufficient bytes, (0, false) is returned.
func parseMessageLength(b []byte) (uint32, bool) {
if len(b) < MsgLenFieldSize {
return 0, false
}
msgLenField := b[:MsgLenFieldSize]
return binary.LittleEndian.Uint32(msgLenField), true
}
54 changes: 40 additions & 14 deletions credentials/alts/internal/conn/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,12 @@ const (
MsgLenFieldSize = 4
// The byte size of the message type field of a framed message.
msgTypeFieldSize = 4
// The bytes size limit for a ALTS record message.
// The bytes size limit for an ALTS record message.
altsRecordLengthLimit = 1024 * 1024 // 1 MiB
// The default bytes size of a ALTS record message.
altsRecordDefaultLength = 4 * 1024 // 4KiB
// Message type value included in ALTS record framing.
altsRecordMsgType = uint32(0x06)
// The initial write buffer size.
altsWriteBufferInitialSize = 32 * 1024 // 32KiB
// The maximum write buffer size. This *must* be multiple of
// altsRecordDefaultLength.
altsWriteBufferMaxSize = 512 * 1024 // 512KiB
// The initial buffer used to read from the network.
altsReadBufferInitialSize = 32 * 1024 // 32KiB
)
Expand Down Expand Up @@ -116,7 +111,7 @@ func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, prot
return nil, fmt.Errorf("protocol %q: %v", recordProtocol, err)
}
overhead := MsgLenFieldSize + msgTypeFieldSize + crypto.EncryptionOverhead()
payloadLengthLimit := altsRecordDefaultLength - overhead
payloadLengthLimit := altsRecordLengthLimit - overhead
// We pre-allocate protected to be of size 32KB during initialization.
// We increase the size of the buffer by the required amount if it can't
// hold a complete encrypted record.
Expand Down Expand Up @@ -144,7 +139,7 @@ func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, prot
func (p *conn) Read(b []byte) (n int, err error) {
if len(p.buf) == 0 {
var framedMsg []byte
framedMsg, p.nextFrame, err = ParseFramedMsg(p.nextFrame, altsRecordLengthLimit)
framedMsg, err = p.parseFramedMsg(p.nextFrame, altsRecordLengthLimit)
if err != nil {
return n, err
}
Expand Down Expand Up @@ -184,7 +179,7 @@ func (p *conn) Read(b []byte) (n int, err error) {
return 0, err
}
p.protected = p.protected[:len(p.protected)+n]
framedMsg, p.nextFrame, err = ParseFramedMsg(p.protected, altsRecordLengthLimit)
framedMsg, err = p.parseFramedMsg(p.protected, altsRecordLengthLimit)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -225,6 +220,38 @@ func (p *conn) Read(b []byte) (n int, err error) {
return n, nil
}

// parseFramedMsg parses the provided buffer and returns a frame of the format
// msgLength+msg iff a full frame is available.
func (p *conn) parseFramedMsg(b []byte, maxLen uint32) ([]byte, error) {
// If the size field is not complete, return the provided buffer as
// remaining buffer.
p.nextFrame = b
length, sufficientBytes := parseMessageLength(b)
if !sufficientBytes {
return nil, nil
}
if length > maxLen {
return nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen)
}
if len(b) < int(length)+4 { // account for the first 4 msg length bytes.
// Frame is not complete yet.
return nil, nil
}
p.nextFrame = b[MsgLenFieldSize+length:]
return b[:MsgLenFieldSize+length], nil
}

// parseMessageLength returns the message length based on frame header. It also
// returns a boolean indicating if the buffer contains sufficient bytes to parse
// the length header. If there are insufficient bytes, (0, false) is returned.
func parseMessageLength(b []byte) (uint32, bool) {
if len(b) < MsgLenFieldSize {
return 0, false
}
msgLenField := b[:MsgLenFieldSize]
return binary.LittleEndian.Uint32(msgLenField), true
}

// Write encrypts, frames, and writes bytes from b to the underlying connection.
func (p *conn) Write(b []byte) (n int, err error) {
n = len(b)
Expand All @@ -233,10 +260,9 @@ func (p *conn) Write(b []byte) (n int, err error) {
size := len(b) + numOfFrames*p.overhead
// If writeBuf is too small, increase its size up to the maximum size.
partialBSize := len(b)
if size > altsWriteBufferMaxSize {
size = altsWriteBufferMaxSize
const numOfFramesInMaxWriteBuf = altsWriteBufferMaxSize / altsRecordDefaultLength
partialBSize = numOfFramesInMaxWriteBuf * p.payloadLengthLimit
if size > altsRecordLengthLimit {
size = altsRecordLengthLimit
partialBSize = p.payloadLengthLimit
}
if len(p.writeBuf) < size {
p.writeBuf = make([]byte, size)
Expand Down Expand Up @@ -282,7 +308,7 @@ func (p *conn) Write(b []byte) (n int, err error) {
// written. This means we need to remove header,
// encryption overheads, and any partially-written
// frame data.
numOfWrittenFrames := int(math.Floor(float64(nn) / float64(altsRecordDefaultLength)))
numOfWrittenFrames := int(math.Floor(float64(nn) / float64(altsRecordLengthLimit)))
return partialBStart + numOfWrittenFrames*p.payloadLengthLimit, err
}
}
Expand Down
6 changes: 3 additions & 3 deletions credentials/alts/internal/conn/record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ func (s) TestSmallReadBuffer(t *testing.T) {
func testLargeMsg(t *testing.T, rp string) {
clientConn, serverConn := newConnPair(rp, nil, nil)
// msgLen is such that the length in the framing is larger than the
// default size of one frame.
msgLen := altsRecordDefaultLength - msgTypeFieldSize - clientConn.crypto.EncryptionOverhead() + 1
// max size of one frame.
msgLen := altsRecordLengthLimit - msgTypeFieldSize - clientConn.crypto.EncryptionOverhead() + 1
msg := make([]byte, msgLen)
if n, err := clientConn.Write(msg); n != len(msg) || err != nil {
t.Fatalf("Write() = %v, %v; want %v, <nil>", n, err, len(msg))
Expand Down Expand Up @@ -292,7 +292,7 @@ func testWriteLargeData(t *testing.T, rp string) {
clientConn, serverConn := newConnPair(rp, nil, nil)
// Message size is intentionally chosen to not be multiple of
// payloadLengthLimit.
msgSize := altsWriteBufferMaxSize + (100 * 1024)
msgSize := altsRecordLengthLimit + (100 * 1024)
clientMsg := make([]byte, msgSize)
for i := 0; i < msgSize; i++ {
clientMsg[i] = 0xAA
Expand Down
2 changes: 1 addition & 1 deletion examples/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/examples

go 1.23.0
go 1.24.0

require (
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443
Expand Down
2 changes: 1 addition & 1 deletion gcp/observability/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/gcp/observability

go 1.23.0
go 1.24.0

require (
cloud.google.com/go/logging v1.13.0
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc

go 1.23.0
go 1.24.0

require (
github.com/cespare/xxhash/v2 v2.3.0
Expand Down
2 changes: 1 addition & 1 deletion interop/observability/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/interop/observability

go 1.23.0
go 1.24.0

require (
google.golang.org/grpc v1.73.0
Expand Down
2 changes: 1 addition & 1 deletion interop/xds/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/interop/xds

go 1.23.0
go 1.24.0

replace google.golang.org/grpc => ../..

Expand Down
2 changes: 1 addition & 1 deletion scripts/vet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ for MOD_FILE in $(find . -name 'go.mod'); do
gofmt -s -d -l . 2>&1 | fail_on_output
goimports -l . 2>&1 | not grep -vE "\.pb\.go"

go mod tidy -compat=1.23
go mod tidy -compat=1.24
git status --porcelain 2>&1 | fail_on_output || \
(git status; git --no-pager diff; exit 1)

Expand Down
2 changes: 1 addition & 1 deletion security/advancedtls/examples/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/security/advancedtls/examples

go 1.23.0
go 1.24.0

require (
google.golang.org/grpc v1.73.0
Expand Down
2 changes: 1 addition & 1 deletion security/advancedtls/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/security/advancedtls

go 1.23.0
go 1.24.0

require (
github.com/google/go-cmp v0.7.0
Expand Down
2 changes: 1 addition & 1 deletion stats/opencensus/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/stats/opencensus

go 1.23.0
go 1.24.0

require (
github.com/google/go-cmp v0.7.0
Expand Down
2 changes: 1 addition & 1 deletion test/tools/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module google.golang.org/grpc/test/tools

go 1.23.0
go 1.24.0

require (
github.com/client9/misspell v0.3.4
Expand Down
Loading