github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/symdb/block_writer_v3.go (about) 1 package symdb 2 3 import ( 4 "fmt" 5 "hash/crc32" 6 "io" 7 "math" 8 "os" 9 "path/filepath" 10 11 "github.com/grafana/pyroscope/pkg/phlaredb/block" 12 v1 "github.com/grafana/pyroscope/pkg/phlaredb/schemas/v1" 13 ) 14 15 type writerV3 struct { 16 config *Config 17 index IndexFile 18 footer Footer 19 files []block.File 20 encodersV3 21 } 22 23 type encodersV3 struct { 24 stringsEncoder *symbolsEncoder[string] 25 mappingsEncoder *symbolsEncoder[v1.InMemoryMapping] 26 functionsEncoder *symbolsEncoder[v1.InMemoryFunction] 27 locationsEncoder *symbolsEncoder[v1.InMemoryLocation] 28 } 29 30 func newWriterV3(c *Config) *writerV3 { 31 return &writerV3{ 32 config: c, 33 index: newIndexFileV3(), 34 footer: newFooterV3(), 35 encodersV3: newEncodersV3(), 36 } 37 } 38 39 func newIndexFileV3() IndexFile { 40 return IndexFile{ 41 Header: IndexHeader{ 42 Magic: symdbMagic, 43 Version: FormatV3, 44 }, 45 } 46 } 47 48 func newFooterV3() Footer { 49 return Footer{ 50 Magic: symdbMagic, 51 Version: FormatV3, 52 } 53 } 54 55 func newEncodersV3() encodersV3 { 56 return encodersV3{ 57 stringsEncoder: newStringsEncoder(), 58 mappingsEncoder: newMappingsEncoder(), 59 functionsEncoder: newFunctionsEncoder(), 60 locationsEncoder: newLocationsEncoder(), 61 } 62 } 63 64 func (w *writerV3) writePartitions(partitions []*PartitionWriter) (err error) { 65 if dst := w.config.Writer; dst != nil { 66 defer func() { 67 _ = w.config.Writer.Close() 68 }() 69 return w.writePartitionsWithWriter(withWriterOffset(dst), partitions) 70 } 71 if err = os.MkdirAll(w.config.Dir, 0o755); err != nil { 72 return fmt.Errorf("failed to create directory %q: %w", w.config.Dir, err) 73 } 74 var f *fileWriter 75 f, err = w.newFile(DefaultFileName) 76 if err != nil { 77 return err 78 } 79 defer func() { 80 _ = f.Close() 81 w.files = []block.File{f.meta()} 82 }() 83 return w.writePartitionsWithWriter(f.w, partitions) 84 } 85 86 func (w *writerV3) writePartitionsWithWriter(f *writerOffset, partitions []*PartitionWriter) (err error) { 87 for _, p := range partitions { 88 if err = writePartitionV3(f, &w.encodersV3, p); err != nil { 89 return fmt.Errorf("failed to write partition: %w", err) 90 } 91 w.index.PartitionHeaders = append(w.index.PartitionHeaders, &p.header) 92 } 93 w.footer.IndexOffset = uint64(f.offset) 94 if _, err = w.index.WriteTo(f); err != nil { 95 return fmt.Errorf("failed to write index: %w", err) 96 } 97 if _, err = f.Write(w.footer.MarshalBinary()); err != nil { 98 return fmt.Errorf("failed to write footer: %w", err) 99 } 100 return nil 101 } 102 103 func (w *writerV3) meta() []block.File { return w.files } 104 105 func (w *writerV3) newFile(path string) (f *fileWriter, err error) { 106 path = filepath.Join(w.config.Dir, path) 107 if f, err = newFileWriter(path); err != nil { 108 return nil, fmt.Errorf("failed to create %q: %w", path, err) 109 } 110 return f, err 111 } 112 113 func writePartitionV3(w *writerOffset, e *encodersV3, p *PartitionWriter) (err error) { 114 if p.header.V3.Strings, err = writeSymbolsBlock(w, p.strings.slice, e.stringsEncoder); err != nil { 115 return err 116 } 117 if p.header.V3.Mappings, err = writeSymbolsBlock(w, p.mappings.slice, e.mappingsEncoder); err != nil { 118 return err 119 } 120 if p.header.V3.Functions, err = writeSymbolsBlock(w, p.functions.slice, e.functionsEncoder); err != nil { 121 return err 122 } 123 if p.header.V3.Locations, err = writeSymbolsBlock(w, p.locations.slice, e.locationsEncoder); err != nil { 124 return err 125 } 126 127 h := StacktraceBlockHeader{ 128 Offset: w.offset, 129 Partition: p.header.Partition, 130 Encoding: StacktraceEncodingGroupVarint, 131 Stacktraces: uint32(len(p.stacktraces.hashToIdx)), 132 StacktraceNodes: p.stacktraces.tree.len(), 133 StacktraceMaxNodes: math.MaxUint32, 134 } 135 crc := crc32.New(castagnoli) 136 if h.Size, err = p.stacktraces.WriteTo(io.MultiWriter(crc, w)); err != nil { 137 return fmt.Errorf("writing stacktrace chunk data: %w", err) 138 } 139 h.CRC = crc.Sum32() 140 p.header.Stacktraces = append(p.header.Stacktraces, h) 141 142 return nil 143 } 144 145 func writeSymbolsBlock[T any](w *writerOffset, s []T, e *symbolsEncoder[T]) (h SymbolsBlockHeader, err error) { 146 h.Offset = uint64(w.offset) 147 crc := crc32.New(castagnoli) 148 mw := io.MultiWriter(crc, w) 149 if err = e.encode(mw, s); err != nil { 150 return h, err 151 } 152 h.Size = uint32(w.offset) - uint32(h.Offset) 153 h.CRC = crc.Sum32() 154 h.Length = uint32(len(s)) 155 h.BlockSize = uint32(e.blockSize) 156 h.BlockHeaderSize = uint16(e.blockEncoder.headerSize()) 157 h.Format = e.blockEncoder.format() 158 return h, nil 159 } 160 161 func WritePartition(p *PartitionWriter, dst io.Writer) error { 162 index := newIndexFileV3() 163 footer := newFooterV3() 164 encoders := newEncodersV3() 165 w := withWriterOffset(dst) 166 167 if err := writePartitionV3(w, &encoders, p); err != nil { 168 return fmt.Errorf("failed to write partition: %w", err) 169 } 170 index.PartitionHeaders = append(index.PartitionHeaders, &p.header) 171 footer.IndexOffset = uint64(w.offset) 172 if _, err := index.WriteTo(w); err != nil { 173 return fmt.Errorf("failed to write index: %w", err) 174 } 175 if _, err := w.Write(footer.MarshalBinary()); err != nil { 176 return fmt.Errorf("failed to write footer: %w", err) 177 } 178 return nil 179 }