moq: impose maximum size on pending reordered bytes (#6112)

Decrease the maximum memory that clients can take by imposing a maximum
size of 100MB on the pending reordered bytes.
This commit is contained in:
Alessandro Ros
2026-08-18 12:49:38 +02:00
committed by GitHub
parent c59aac9c02
commit 31a054b79d
4 changed files with 72 additions and 19 deletions
+24 -2
View File
@@ -12,12 +12,22 @@ import (
// Reorderer is a subgroup reorderer.
type Reorderer struct {
MaxReordered int
MaxPendingBytes int
Parent logger.Writer
initialized bool
mu sync.Mutex
curGroupID uint64
pending map[uint64]*subgroup.SubGroup
pendingBytes int
}
func subGroupPayloadSize(sg *subgroup.SubGroup) int {
n := 0
for _, obj := range sg.Objects {
n += len(obj.Payload)
}
return n
}
// Initialize initializes the reorderer.
@@ -45,7 +55,11 @@ func (r *Reorderer) Push(sg *subgroup.SubGroup) ([]*subgroup.SubGroup, error) {
return []*subgroup.SubGroup{sg}, nil
default:
if prev, ok := r.pending[sg.Header.GroupID]; ok {
r.pendingBytes -= subGroupPayloadSize(prev)
}
r.pending[sg.Header.GroupID] = sg
r.pendingBytes += subGroupPayloadSize(sg)
diff := sg.Header.GroupID - r.curGroupID
@@ -56,11 +70,17 @@ func (r *Reorderer) Push(sg *subgroup.SubGroup) ([]*subgroup.SubGroup, error) {
}
}
if countInRange == diff {
switch {
case countInRange == diff:
return r.flushUpTo(sg.Header.GroupID), nil
} else if len(r.pending) > r.MaxReordered {
case len(r.pending) > r.MaxReordered:
r.Parent.Log(logger.Warn, "too many reordered subgroups, flushing")
return r.flushUpTo(sg.Header.GroupID), nil
case r.pendingBytes > r.MaxPendingBytes:
r.Parent.Log(logger.Warn, "too many reordered bytes, flushing")
return r.flushUpTo(sg.Header.GroupID), nil
}
}
@@ -79,6 +99,7 @@ func (r *Reorderer) flushUpTo(maxGroupID uint64) []*subgroup.SubGroup {
out := make([]*subgroup.SubGroup, 0, len(ids))
for _, id := range ids {
out = append(out, r.pending[id])
r.pendingBytes -= subGroupPayloadSize(r.pending[id])
delete(r.pending, id)
}
@@ -90,6 +111,7 @@ func (r *Reorderer) flushUpTo(maxGroupID uint64) []*subgroup.SubGroup {
break
}
out = append(out, next)
r.pendingBytes -= subGroupPayloadSize(next)
delete(r.pending, r.curGroupID+1)
r.curGroupID++
}
@@ -20,8 +20,17 @@ func makeSG(groupID uint64) *subgroup.SubGroup {
}
}
func makeSGWithPayload(groupID uint64, size int) *subgroup.SubGroup {
return &subgroup.SubGroup{
Header: subgroup.Header{GroupID: groupID},
Objects: []subgroup.Object{{
Payload: make([]byte, size),
}},
}
}
func TestReordererFirstPush(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -31,7 +40,7 @@ func TestReordererFirstPush(t *testing.T) {
}
func TestReordererInOrder(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -51,7 +60,7 @@ func TestReordererInOrder(t *testing.T) {
}
func TestReordererStale(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg5 := makeSG(5)
@@ -71,7 +80,7 @@ func TestReordererStale(t *testing.T) {
}
func TestReordererOutOfOrderPending(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -86,7 +95,7 @@ func TestReordererOutOfOrderPending(t *testing.T) {
}
func TestReordererOutOfOrderFilled(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -109,7 +118,7 @@ func TestReordererOutOfOrderFilled(t *testing.T) {
}
func TestReordererDrainAfterFill(t *testing.T) { //nolint:dupl
r := &reorderer.Reorderer{MaxReordered: 5, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 5, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -136,7 +145,7 @@ func TestReordererDrainAfterFill(t *testing.T) { //nolint:dupl
}
func TestReordererMaxReordered(t *testing.T) { //nolint:dupl
r := &reorderer.Reorderer{MaxReordered: 2, Parent: nopLogger{}}
r := &reorderer.Reorderer{MaxReordered: 2, MaxPendingBytes: 100000, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
@@ -163,3 +172,23 @@ func TestReordererMaxReordered(t *testing.T) { //nolint:dupl
require.NoError(t, err)
require.Equal(t, []*subgroup.SubGroup{sg2, sg4, sg5}, out)
}
func TestReordererMaxPendingBytes(t *testing.T) {
r := &reorderer.Reorderer{MaxReordered: 10, MaxPendingBytes: 100, Parent: nopLogger{}}
r.Initialize()
sg0 := makeSG(0)
out, err := r.Push(sg0)
require.NoError(t, err)
require.Equal(t, []*subgroup.SubGroup{sg0}, out)
sg2 := makeSGWithPayload(2, 60)
out, err = r.Push(sg2)
require.NoError(t, err)
require.Nil(t, out)
sg4 := makeSGWithPayload(4, 60)
out, err = r.Push(sg4)
require.NoError(t, err)
require.Equal(t, []*subgroup.SubGroup{sg2, sg4}, out)
}
+1
View File
@@ -16,6 +16,7 @@ type inboundTrack struct {
func (t *inboundTrack) initialize() {
t.reorderer = &reorderer.Reorderer{
MaxReordered: maxReorderedSubGroups,
MaxPendingBytes: maxReorderedPendingBytes,
Parent: t.parent,
}
t.reorderer.Initialize()
+1
View File
@@ -35,6 +35,7 @@ import (
const (
maxReorderedSubGroups = 50
maxReorderedPendingBytes = 100 * 1024 * 1024
maxCatalogTracks = 50
)