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  }