-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cut tagger stream event responses into chunks of 4MB max size each (#…
- Loading branch information
Showing
5 changed files
with
181 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016-present Datadog, Inc. | ||
|
||
package server | ||
|
||
import ( | ||
"google.golang.org/protobuf/proto" | ||
|
||
pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" | ||
) | ||
|
||
// splitBySize splits the given slice into contiguous non-overlapping subslices such that | ||
// the size of each sub-slice is at most maxChunkSize. | ||
// The size of each item is calculated using computeSize | ||
// | ||
// This function assumes that the size of each single item of the initial slice is not larger than maxChunkSize | ||
func splitBySize[T any](slice []T, maxChunkSize int, computeSize func(T) int) [][]T { | ||
|
||
// TODO: return an iter.Seq[[]T] instead of [][]T once we upgrade to golang v1.23 | ||
// returning iter.Seq[[]T] has better performance in terms of memory consumption | ||
var chunks [][]T | ||
currentChunk := []T{} | ||
currentSize := 0 | ||
|
||
for _, item := range slice { | ||
eventSize := computeSize(item) | ||
if currentSize+eventSize > maxChunkSize { | ||
chunks = append(chunks, currentChunk) | ||
currentChunk = []T{} | ||
currentSize = 0 | ||
} | ||
currentChunk = append(currentChunk, item) | ||
currentSize += eventSize | ||
} | ||
if len(currentChunk) > 0 { | ||
chunks = append(chunks, currentChunk) | ||
} | ||
return chunks | ||
} | ||
|
||
// splitEvents splits the array of events to chunks with at most maxChunkSize each | ||
func splitEvents(events []*pb.StreamTagsEvent, maxChunkSize int) [][]*pb.StreamTagsEvent { | ||
return splitBySize( | ||
events, | ||
maxChunkSize, | ||
func(event *pb.StreamTagsEvent) int { return proto.Size(event) }, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed | ||
// under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
// Copyright 2016-present Datadog, Inc. | ||
|
||
package server | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type mockStreamTagsEvent struct { | ||
id int | ||
size int | ||
} | ||
|
||
func TestSplitEvents(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
events []mockStreamTagsEvent | ||
maxChunkSize int | ||
expected [][]mockStreamTagsEvent // Expecting indices of events in chunks for easier comparison | ||
}{ | ||
{ | ||
name: "Empty input", | ||
events: []mockStreamTagsEvent{}, | ||
maxChunkSize: 100, | ||
expected: nil, // No chunks expected | ||
}, | ||
{ | ||
name: "Single event within chunk size", | ||
events: []mockStreamTagsEvent{ | ||
{id: 1, size: 50}, // Mock event with size 50 | ||
}, | ||
maxChunkSize: 100, | ||
expected: [][]mockStreamTagsEvent{ | ||
{ | ||
{id: 1, size: 50}, // One chunk with one event | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Multiple events all fit in one chunk", | ||
events: []mockStreamTagsEvent{ | ||
{id: 1, size: 20}, {id: 2, size: 30}, {id: 3, size: 40}, // Total size = 90 | ||
}, | ||
maxChunkSize: 100, | ||
expected: [][]mockStreamTagsEvent{ | ||
{ | ||
{id: 1, size: 20}, {id: 2, size: 30}, {id: 3, size: 40}, // All events fit in one chunk | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Multiple events require splitting", | ||
events: []mockStreamTagsEvent{ | ||
{id: 1, size: 40}, {id: 2, size: 50}, {id: 3, size: 60}, // Total size = 150 | ||
}, | ||
maxChunkSize: 100, | ||
expected: [][]mockStreamTagsEvent{ | ||
{ | ||
{id: 1, size: 40}, | ||
{id: 2, size: 50}, | ||
}, | ||
{ | ||
{id: 3, size: 60}, | ||
}, // Last event in second chunk | ||
}, | ||
}, | ||
{ | ||
name: "Events fit exactly in chunks", | ||
events: []mockStreamTagsEvent{ | ||
{id: 1, size: 50}, {id: 2, size: 50}, // Total size = 100 | ||
}, | ||
maxChunkSize: 100, | ||
expected: [][]mockStreamTagsEvent{ | ||
{{id: 1, size: 50}, {id: 2, size: 50}}, // Both events fit exactly in one chunk | ||
}, | ||
}, | ||
{ | ||
name: "Event size exactly matches or exceeds chunk size", | ||
events: []mockStreamTagsEvent{ | ||
{id: 1, size: 100}, {id: 2, size: 101}, // One exactly fits, one exceeds | ||
}, | ||
maxChunkSize: 100, | ||
expected: [][]mockStreamTagsEvent{ | ||
{ | ||
{id: 1, size: 100}, | ||
}, | ||
{ | ||
{id: 2, size: 101}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, testCase := range testCases { | ||
t.Run(testCase.name, func(t *testing.T) { | ||
chunks := splitBySize(testCase.events, testCase.maxChunkSize, func(e mockStreamTagsEvent) int { return e.size }) | ||
assert.Equal(t, testCase.expected, chunks) | ||
}) | ||
} | ||
} |