Files
traces/cmd/tempo-cli/cmd-convert-parquet-3to4.go
Zach LeslieandGitHub a9f63d073b Replace TenantIndex, BlockMeta and CompactedBlockMeta with proto near-equivalents (#4072)
* Update proto for v1 backend

* Functioning blocklist with new package

* Functioning blocklist with existing package

* Generate proto to existing package

* Add wrapper around google UUID for proto marshaling

* Update blocklist for wrapper type

* Test WriteTenantIndex

* Implement json for uuid

* Almost functional backend tests

* Passing backend tests

* Improve UUID coverage and utility

* TempoDB updates for UUID and struct type changes

* Update method style for uuid.New()

* Update method style for uuid.MustParse()

* Add uuid.From helper

* Update modules for UUID and proto struct types

* Update poller integration test for UUID

* Update tempo-cli for UUID and proto struct type changes

* Lint and clean up

* Fix serverless

* Fix cli

* Fix cli

* Add fixture tests for roundtrip assurance between versions

* Clean up notes from proto definition

* Update backend/test

* Be more specific about unmarshal UUID

* Include event for compacted block

* Use better json tag for compacted meta

* Implement UnmarshalJson on CompcatedBlockMeta for embedded struct

* Test for CompactedMeta marshal/unmarshal

* Drop benchmark index

* Clean commented code

* Extend mocks to capture multiple writes

* Update backend/test fixtures

* Write both proto and json to the backend for all three files in all backends

* Fix lint

* Back UUID with google.UUID to avoid struct

* Relocate pkg/uuid to tempodb/backend

* Re-plumb UUID through tempodb

* Re-plumb UUID through cmd

* Re-plumb UUID through modules

* Re-plumb UUID through integration/

* Drop package spec from tempo.proto

* Improve encoding marshal performance

* Drop block proto and tenantindex json

* Drop additional proto include

* Implement zstd tenant index compression

* Add note about use of json in the dedicatedcolumns

* Fix ingester local block

* Spell

* Revert mock update

* Drop json fallback from tenantindex read

* Replace single-tenant test fixtures

* Update fixture replacement test

* Improve error handling and fix test

* Extend test for tenantindex round-trip

* Small fixes

* Revert "Drop json fallback from tenantindex read"

This reverts commit 834ff5b85d8a8a4d7eb560fbb67ba8f3a5a3959a.

* Add back mocks to capture multiple backend writes

* Add back proto & json writitng/reading for tenantindex

* Change TotalObjects type from int32 -> int64

The original change was int -> int32, which on 64bit machines is a
downgrade that I overlooked.  Raising here to int64 to maintain the
size.

* Store write buffer at object path in mocks to improve readability in tests

* Avoid allocation in interface validation

* Update note to include intern

* Pluralize metas and compacted_metas fields

* Fallback to json only on ErrDoesNotExist

* Lint

* Drop debug

* Revert "Pluralize metas and compacted_metas fields"

This reverts commit 46a8c36e37ac33e1ffc104287d898930e02cdd97.

* Import original tenantindex and test contents for backwards compatibility

* Update changelog
2024-10-02 18:40:59 +00:00

185 lines
4.3 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"github.com/parquet-go/parquet-go"
"github.com/grafana/tempo/pkg/tempopb"
"github.com/grafana/tempo/pkg/traceql"
"github.com/grafana/tempo/tempodb/backend"
"github.com/grafana/tempo/tempodb/backend/local"
"github.com/grafana/tempo/tempodb/encoding/common"
"github.com/grafana/tempo/tempodb/encoding/vparquet3"
"github.com/grafana/tempo/tempodb/encoding/vparquet4"
)
type convertParquet3to4 struct {
In string `arg:"" help:"The input parquet block to read from."`
Out string `arg:"" help:"The output folder to write block to." default:"./out" optional:""`
DedicatedColumns []string `arg:"" help:"List of dedicated columns to convert. Overwrites existing dedicated columns" optional:""`
}
func (cmd *convertParquet3to4) Run() error {
cmd.In = getPathToBlockDir(cmd.In)
// open the input parquet file
in, pf, err := openParquetFile(cmd.In)
if err != nil {
return err
}
defer in.Close()
// open the input metadata file
meta, err := readBlockMeta(cmd.In)
if err != nil {
return err
}
// create output block
outR, outW, _, err := local.New(&local.Config{
Path: cmd.Out,
})
if err != nil {
return err
}
// calculate dedicated columns
var dedicatedCols backend.DedicatedColumns
if len(cmd.DedicatedColumns) > 0 {
dedicatedCols = make(backend.DedicatedColumns, 0, len(cmd.DedicatedColumns))
for _, col := range cmd.DedicatedColumns {
att, err := traceql.ParseIdentifier(col)
if err != nil {
return err
}
scope := backend.DedicatedColumnScopeSpan
if att.Scope == traceql.AttributeScopeResource {
scope = backend.DedicatedColumnScopeResource
}
fmt.Printf("add dedicated column scope=%s name=%s\n", scope, att.Name)
dedicatedCols = append(dedicatedCols, backend.DedicatedColumn{
Scope: scope,
Name: att.Name,
Type: backend.DedicatedColumnTypeString,
})
}
} else {
dedicatedCols = meta.DedicatedColumns
}
// copy block
blockCfg := &common.BlockConfig{
BloomFP: 0.99,
BloomShardSizeBytes: 1024 * 1024,
Version: vparquet4.VersionString,
RowGroupSizeBytes: 100 * 1024 * 1024,
}
newMeta := *meta
newMeta.Version = vparquet4.VersionString
newMeta.DedicatedColumns = dedicatedCols
// create iterator over in file
iter := &parquetIterator3{
r: parquet.NewGenericReader[*vparquet3.Trace](pf),
m: meta,
}
fmt.Printf("Creating vParquet4 block in %s\n", filepath.Join(cmd.Out, meta.TenantID, newMeta.BlockID.String()))
fmt.Printf("Converting rows 0 to %d\n", pf.NumRows())
outMeta, err := vparquet4.CreateBlock(context.Background(), blockCfg, &newMeta, iter, backend.NewReader(outR), backend.NewWriter(outW))
if err != nil {
return err
}
fmt.Printf("Successfully created block with size=%d and footerSize=%d\n", outMeta.Size_, outMeta.FooterSize)
return nil
}
func getPathToBlockDir(path string) string {
if filepath.Base(path) == "data.parquet" {
return filepath.Dir(path)
}
return path
}
func openParquetFile(blockPath string) (*os.File, *parquet.File, error) {
inFile := filepath.Join(blockPath, "data.parquet")
in, err := os.Open(inFile)
if err != nil {
return nil, nil, err
}
inStat, err := in.Stat()
if err != nil {
return nil, nil, err
}
pf, err := parquet.OpenFile(in, inStat.Size())
if err != nil {
return nil, nil, err
}
return in, pf, nil
}
func readBlockMeta(blockPath string) (*backend.BlockMeta, error) {
metaFile := filepath.Join(blockPath, "meta.json")
inMeta, err := os.Open(metaFile)
if err != nil {
return nil, err
}
defer inMeta.Close()
var meta backend.BlockMeta
err = json.NewDecoder(inMeta).Decode(&meta)
if err != nil {
return nil, err
}
return &meta, nil
}
type parquetIterator3 struct {
r *parquet.GenericReader[*vparquet3.Trace]
m *backend.BlockMeta
i int
}
func (i *parquetIterator3) Next(_ context.Context) (common.ID, *tempopb.Trace, error) {
traces := []*vparquet3.Trace{{}}
i.i++
if i.i%1000 == 0 {
fmt.Println(i.i)
}
_, err := i.r.Read(traces)
if errors.Is(err, io.EOF) {
return nil, nil, io.EOF
}
if err != nil {
return nil, nil, err
}
pqTrace := traces[0]
pbTrace := vparquet3.ParquetTraceToTempopbTrace(i.m, pqTrace)
return pqTrace.TraceID, pbTrace, nil
}
func (i *parquetIterator3) Close() {
_ = i.r.Close()
}