Skip to content
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
11 changes: 7 additions & 4 deletions core/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,11 @@ func HashRequestOptions(opts *tangopb.RequestOptions) string {
// for typical target rates.
const cancelCheckInterval = 4096

// ResultToGetTargetGraphResponse converts a Result to a GetTargetGraphResponse
func ResultToGetTargetGraphResponse(ctx context.Context, result targethasher.Result) ([]*tangopb.GetTargetGraphResponse, error) {
// ResultToGetTargetGraphResponse converts a Result to a GetTargetGraphResponse.
// targetChunkSize controls how many OptimizedTarget entries per stream message.
// metadataMapChunkSize controls how many entries per metadata map chunk.
// TODO: move this function to internal/mapper
func ResultToGetTargetGraphResponse(ctx context.Context, result targethasher.Result, targetChunkSize, metadataMapChunkSize int) ([]*tangopb.GetTargetGraphResponse, error) {
Comment thread
yushan8 marked this conversation as resolved.
// Map target names to ids. This list is topologically sorted, so the ids are stable.
// IDs start at 1 — 0 is reserved as the proto3 "unset" sentinel so consumers using
// encoding/json (which honors `omitempty` on int32 fields) never silently lose a target.
Expand Down Expand Up @@ -224,14 +227,14 @@ func ResultToGetTargetGraphResponse(ctx context.Context, result targethasher.Res
attrStrValIDToVal := attrStrValMapper.Invert()

// chunk targets into multiple messages for streaming
responses := chunkTargets(optimizedTargets, DefaultTargetChunkSize)
responses := chunkTargets(optimizedTargets, targetChunkSize)
for _, meta := range ChunkMetadata(
targetIDToName,
ruleTypeIDToName,
tagIDToName,
attrNameIDToName,
attrStrValIDToVal,
DefaultMetadataMapChunkSize,
metadataMapChunkSize,
) {
responses = append(responses, &tangopb.GetTargetGraphResponse{
Item: &tangopb.GetTargetGraphResponse_Metadata{Metadata: meta},
Expand Down
50 changes: 39 additions & 11 deletions core/common/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ func TestChunkTargets(t *testing.T) {
func TestResultToGetTargetGraphResponse_Chunking(t *testing.T) {
t.Parallel()

// 500 targets with DefaultTargetChunkSize=250 → 2 target chunks + 1 metadata = 3 responses
numTargets := 500
result := targethasher.Result{
TargetNames: make([]string, numTargets),
Expand All @@ -198,16 +197,45 @@ func TestResultToGetTargetGraphResponse_Chunking(t *testing.T) {
result.Targets[name] = &targethasher.Target{Name: name, Hash: []byte{0}, RuleType: "go_library"}
}

responses, err := ResultToGetTargetGraphResponse(context.Background(), result)
require.NoError(t, err)

// 2 target chunks + 1 metadata chunk (500 targets well under DefaultMetadataMapChunkSize)
require.Len(t, responses, 3)
tests := []struct {
name string
targetChunkSize int
metadataMapChunkSize int
wantTargetChunks int
}{
{
name: "250 per chunk",
targetChunkSize: 250,
metadataMapChunkSize: 50_000,
wantTargetChunks: 2,
},
{
name: "100 per chunk",
targetChunkSize: 100,
metadataMapChunkSize: 50_000,
wantTargetChunks: 5,
},
{
name: "all in one chunk",
targetChunkSize: 1000,
metadataMapChunkSize: 50_000,
wantTargetChunks: 1,
},
}

for _, resp := range responses[:2] {
_, ok := resp.Item.(*pb.GetTargetGraphResponse_Targets)
assert.True(t, ok, "expected Targets chunk")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
responses, err := ResultToGetTargetGraphResponse(context.Background(), result, tt.targetChunkSize, tt.metadataMapChunkSize)
require.NoError(t, err)

var targetChunks int
for _, resp := range responses {
if _, ok := resp.Item.(*pb.GetTargetGraphResponse_Targets); ok {
targetChunks++
}
}
assert.Equal(t, tt.wantTargetChunks, targetChunks)
})
}
_, ok := responses[2].Item.(*pb.GetTargetGraphResponse_Metadata)
assert.True(t, ok, "last response should be Metadata")
}
5 changes: 4 additions & 1 deletion example/cmd/query-bench/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ func run() error {
totalDuration += elapsed
fmt.Printf("run %d: targethasher: %v (%d targets)\n", i+1, elapsed.Round(time.Millisecond), len(targethasherResult.TargetNames))
start = time.Now()
response, err := common.ResultToGetTargetGraphResponse(ctx, targethasherResult) // also print the time to convert to GetTargetGraphResponse format
response, err := common.ResultToGetTargetGraphResponse(
ctx, targethasherResult,
common.DefaultTargetChunkSize, common.DefaultMetadataMapChunkSize,
)
if err != nil {
return fmt.Errorf("run %d: converting to GetTargetGraphResponse: %w", i+1, err)
}
Expand Down
5 changes: 4 additions & 1 deletion orchestrator/native_orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,10 @@ func (b *nativeOrchestrator) GetTargetGraph(ctx context.Context, param GetTarget
if err != nil {
return nil, fmt.Errorf("compute target graph: %w", err)
}
responses, err := common.ResultToGetTargetGraphResponse(ctx, result)
responses, err := common.ResultToGetTargetGraphResponse(ctx, result,
b.config.Service.Chunking.TargetChunkSize,
b.config.Service.Chunking.MetadataMapChunkSize,
)
if err != nil {
return nil, fmt.Errorf("convert target graph to response: %w", err)
}
Expand Down
4 changes: 4 additions & 0 deletions orchestrator/testdata/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ repository:

service:
worker_pool_size: 3
chunking:
target_chunk_size: 250
changed_target_chunk_size: 125
metadata_map_chunk_size: 50000
Loading