github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/meta_range_writer.go (about) 1 package committed 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "sort" 9 10 "github.com/treeverse/lakefs/pkg/graveler" 11 "github.com/treeverse/lakefs/pkg/logging" 12 ) 13 14 type GeneralMetaRangeWriter struct { 15 ctx context.Context 16 metadata graveler.Metadata 17 params *Params // for breaking ranges 18 namespace Namespace 19 metaRangeManager RangeManager 20 rangeManager RangeManager 21 rangeWriter RangeWriter // writer for the current range 22 lastKey Key 23 batchWriteCloser BatchWriterCloser 24 ranges []Range 25 } 26 27 const ( 28 MetadataTypeKey = "type" 29 MetadataRangesType = "ranges" 30 MetadataMetarangesType = "metaranges" 31 ) 32 33 var ( 34 ErrUnsortedKeys = errors.New("keys should be written in ascending order") 35 ErrNilValue = errors.New("record value should not be nil") 36 ) 37 38 func NewGeneralMetaRangeWriter(ctx context.Context, rangeManager, metaRangeManager RangeManager, params *Params, namespace Namespace, md graveler.Metadata) *GeneralMetaRangeWriter { 39 return &GeneralMetaRangeWriter{ 40 ctx: ctx, 41 metadata: md, 42 rangeManager: rangeManager, 43 metaRangeManager: metaRangeManager, 44 batchWriteCloser: NewBatchCloser(params.MaxUploaders), 45 params: params, 46 namespace: namespace, 47 } 48 } 49 50 // WriteRecord writes a record to the current range, decides if should close range 51 func (w *GeneralMetaRangeWriter) WriteRecord(record graveler.ValueRecord) error { 52 if w.lastKey != nil && bytes.Compare(record.Key, w.lastKey) <= 0 { 53 return ErrUnsortedKeys 54 } 55 if record.Value == nil { 56 return ErrNilValue 57 } 58 59 var err error 60 if w.rangeWriter == nil { 61 w.rangeWriter, err = w.rangeManager.GetWriter(w.ctx, w.namespace, w.metadata) 62 if err != nil { 63 return fmt.Errorf("get range writer: %w", err) 64 } 65 w.rangeWriter.SetMetadata(MetadataTypeKey, MetadataRangesType) 66 } 67 68 v, err := MarshalValue(record.Value) 69 if err != nil { 70 return err 71 } 72 err = w.rangeWriter.WriteRecord(Record{Key: Key(record.Key), Value: v}) 73 if err != nil { 74 return fmt.Errorf("write record to range: %w", err) 75 } 76 w.lastKey = Key(record.Key.Copy()) 77 if w.shouldBreakAtKey(record.Key) { 78 return w.closeCurrentRange() 79 } 80 return nil 81 } 82 83 func (w *GeneralMetaRangeWriter) closeCurrentRange() error { 84 if w.rangeWriter == nil { 85 return nil 86 } 87 if err := w.batchWriteCloser.CloseWriterAsync(w.rangeWriter); err != nil { 88 return fmt.Errorf("write range: %w", err) 89 } 90 w.rangeWriter = nil 91 return nil 92 } 93 94 func (w *GeneralMetaRangeWriter) getBatchedRanges() ([]Range, error) { 95 wr, err := w.batchWriteCloser.Wait() 96 if err != nil { 97 return nil, fmt.Errorf("batch write closer wait: %w", err) 98 } 99 ranges := make([]Range, len(wr)) 100 for i, r := range wr { 101 ranges[i] = Range{ 102 ID: r.RangeID, 103 MinKey: r.First, 104 MaxKey: r.Last, 105 EstimatedSize: r.EstimatedRangeSizeBytes, 106 Count: int64(r.Count), 107 } 108 } 109 return ranges, nil 110 } 111 112 func (w *GeneralMetaRangeWriter) WriteRange(rng Range) error { 113 if w.lastKey != nil && bytes.Compare(rng.MinKey, w.lastKey) <= 0 { 114 return ErrUnsortedKeys 115 } 116 if err := w.closeCurrentRange(); err != nil { 117 return err 118 } 119 w.lastKey = make(Key, len(rng.MaxKey)) 120 copy(w.lastKey, rng.MaxKey) 121 w.ranges = append(w.ranges, rng) 122 return nil 123 } 124 125 func (w *GeneralMetaRangeWriter) Close(ctx context.Context) (*graveler.MetaRangeID, error) { 126 if err := w.closeCurrentRange(); err != nil { 127 return nil, err 128 } 129 ranges, err := w.getBatchedRanges() 130 if err != nil { 131 return nil, err 132 } 133 ranges = append(ranges, w.ranges...) 134 sort.Slice(ranges, func(i, j int) bool { 135 return bytes.Compare(ranges[i].MaxKey, ranges[j].MaxKey) < 0 136 }) 137 w.ranges = ranges 138 return w.writeRangesToMetaRange(ctx) 139 } 140 141 // shouldBreakAtKey returns true if should break range after the given key 142 func (w *GeneralMetaRangeWriter) shouldBreakAtKey(key graveler.Key) bool { 143 return w.rangeWriter.ShouldBreakAtKey(key, w.params) 144 } 145 146 // writeRangesToMetaRange writes all ranges to a MetaRange and returns the MetaRangeID 147 func (w *GeneralMetaRangeWriter) writeRangesToMetaRange(ctx context.Context) (*graveler.MetaRangeID, error) { 148 metaRangeWriter, err := w.metaRangeManager.GetWriter(w.ctx, w.namespace, w.metadata) 149 if err != nil { 150 return nil, fmt.Errorf("failed creating metarange writer: %w", err) 151 } 152 153 // write user provided metadata, if any 154 for k, v := range w.metadata { 155 metaRangeWriter.SetMetadata(k, v) 156 } 157 // set type 158 metaRangeWriter.SetMetadata(MetadataTypeKey, MetadataMetarangesType) 159 160 defer func() { 161 if abortErr := metaRangeWriter.Abort(); abortErr != nil { 162 logging.FromContext(ctx).WithError(err).WithField("namespace", w.namespace).Error("failed aborting metarange writer") 163 } 164 }() 165 for _, p := range w.ranges { 166 rangeValue, err := rangeToValue(p) 167 if err != nil { 168 return nil, err 169 } 170 if err := metaRangeWriter.WriteRecord(Record{Key: p.MaxKey, Value: rangeValue}); err != nil { 171 return nil, fmt.Errorf("failed writing range to metarange writer: %w", err) 172 } 173 } 174 wr, err := metaRangeWriter.Close() 175 if err != nil { 176 return nil, fmt.Errorf("failed closing metarange writer: %w", err) 177 } 178 metaRangeID := graveler.MetaRangeID(wr.RangeID) 179 return &metaRangeID, nil 180 } 181 182 func (w *GeneralMetaRangeWriter) Abort() error { 183 if w.rangeWriter == nil { 184 return nil 185 } 186 return w.rangeWriter.Abort() 187 }