mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* 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
160 lines
4.4 KiB
Go
160 lines
4.4 KiB
Go
package vparquet3
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"io"
|
|
|
|
"go.uber.org/atomic"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/grafana/tempo/pkg/cache"
|
|
"github.com/grafana/tempo/tempodb/backend"
|
|
)
|
|
|
|
type cacheReaderAt interface {
|
|
ReadAtWithCache([]byte, int64, cache.Role) (int, error)
|
|
}
|
|
|
|
// BackendReaderAt is used to track backend requests and present a io.ReaderAt interface backed
|
|
// by a backend.Reader
|
|
type BackendReaderAt struct {
|
|
ctx context.Context
|
|
r backend.Reader
|
|
name string
|
|
meta *backend.BlockMeta
|
|
|
|
bytesRead atomic.Uint64
|
|
}
|
|
|
|
var _ cacheReaderAt = (*BackendReaderAt)(nil)
|
|
|
|
func NewBackendReaderAt(ctx context.Context, r backend.Reader, name string, meta *backend.BlockMeta) *BackendReaderAt {
|
|
return &BackendReaderAt{ctx, r, name, meta, atomic.Uint64{}}
|
|
}
|
|
|
|
func (b *BackendReaderAt) ReadAt(p []byte, off int64) (int, error) {
|
|
return b.ReadAtWithCache(p, off, cache.RoleNone)
|
|
}
|
|
|
|
func (b *BackendReaderAt) ReadAtWithCache(p []byte, off int64, role cache.Role) (int, error) {
|
|
err := b.r.ReadRange(b.ctx, b.name, (uuid.UUID)(b.meta.BlockID), b.meta.TenantID, uint64(off), p, &backend.CacheInfo{
|
|
Role: role,
|
|
Meta: b.meta,
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b.bytesRead.Add(uint64(len(p)))
|
|
return len(p), nil
|
|
}
|
|
|
|
func (b *BackendReaderAt) BytesRead() uint64 {
|
|
return b.bytesRead.Load()
|
|
}
|
|
|
|
type cachedObjectRecord struct {
|
|
length int64
|
|
role cache.Role
|
|
}
|
|
|
|
// cachedReaderAt is used to route specific reads to the caching layer. this must be passed directly into
|
|
// the parquet.File so thet Set*Section() methods get called.
|
|
type cachedReaderAt struct {
|
|
r cacheReaderAt
|
|
cachedObjects map[int64]cachedObjectRecord // storing offsets and length of objects we want to cache
|
|
|
|
readerSize int64
|
|
footerSize uint32
|
|
|
|
maxPageSize int
|
|
}
|
|
|
|
var (
|
|
_ cacheReaderAt = (*cachedReaderAt)(nil)
|
|
_ io.ReaderAt = (*cachedReaderAt)(nil)
|
|
)
|
|
|
|
func newCachedReaderAt(r cacheReaderAt, maxPageSize int, size int64, footerSize uint32) *cachedReaderAt {
|
|
return &cachedReaderAt{r, map[int64]cachedObjectRecord{}, size, footerSize, maxPageSize}
|
|
}
|
|
|
|
// called by parquet-go in OpenFile() to set offset and length of footer section
|
|
func (r *cachedReaderAt) SetFooterSection(offset, length int64) {
|
|
r.cachedObjects[offset] = cachedObjectRecord{length, cache.RoleParquetFooter}
|
|
}
|
|
|
|
// called by parquet-go in OpenFile() to set offset and length of column indexes
|
|
func (r *cachedReaderAt) SetColumnIndexSection(offset, length int64) {
|
|
r.cachedObjects[offset] = cachedObjectRecord{length, cache.RoleParquetColumnIdx}
|
|
}
|
|
|
|
// called by parquet-go in OpenFile() to set offset and length of offset index section
|
|
func (r *cachedReaderAt) SetOffsetIndexSection(offset, length int64) {
|
|
r.cachedObjects[offset] = cachedObjectRecord{length, cache.RoleParquetOffsetIdx}
|
|
}
|
|
|
|
func (r *cachedReaderAt) ReadAt(p []byte, off int64) (int, error) {
|
|
if len(p) == 4 && off == 0 {
|
|
// Magic header
|
|
return copy(p, []byte("PAR1")), nil
|
|
}
|
|
|
|
if len(p) == 8 && off == r.readerSize-8 && r.footerSize > 0 /* not present in previous block metas */ {
|
|
// Magic footer
|
|
binary.LittleEndian.PutUint32(p, r.footerSize)
|
|
copy(p[4:8], []byte("PAR1"))
|
|
return 8, nil
|
|
}
|
|
|
|
// check if the offset and length is stored as a special object
|
|
rec, ok := r.cachedObjects[off]
|
|
if ok && rec.length == int64(len(p)) {
|
|
return r.r.ReadAtWithCache(p, off, rec.role)
|
|
}
|
|
|
|
if len(p) <= r.maxPageSize {
|
|
return r.r.ReadAtWithCache(p, off, cache.RoleParquetPage)
|
|
}
|
|
|
|
return r.r.ReadAtWithCache(p, off, cache.RoleNone)
|
|
}
|
|
|
|
func (r *cachedReaderAt) ReadAtWithCache(p []byte, off int64, role cache.Role) (int, error) {
|
|
return r.r.ReadAtWithCache(p, off, role)
|
|
}
|
|
|
|
// walReaderAt is wrapper over io.ReaderAt, and is used to measure the total bytes read when searching walBlock.
|
|
type walReaderAt struct {
|
|
ctx context.Context
|
|
r io.ReaderAt
|
|
bytesRead atomic.Uint64
|
|
}
|
|
|
|
var _ io.ReaderAt = (*walReaderAt)(nil)
|
|
|
|
func newWalReaderAt(ctx context.Context, r io.ReaderAt) *walReaderAt {
|
|
return &walReaderAt{ctx, r, atomic.Uint64{}}
|
|
}
|
|
|
|
func (wr *walReaderAt) ReadAt(p []byte, off int64) (int, error) {
|
|
if err := wr.ctx.Err(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// parquet-go will call ReadAt when reading data from a parquet file.
|
|
n, err := wr.r.ReadAt(p, off)
|
|
// ReadAt can read less than len(p) bytes in some cases
|
|
wr.bytesRead.Add(uint64(n))
|
|
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (wr *walReaderAt) BytesRead() uint64 {
|
|
return wr.bytesRead.Load()
|
|
}
|