github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/merge.go (about)

     1  package committed
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  
     8  	"github.com/treeverse/lakefs/pkg/graveler"
     9  	"github.com/treeverse/lakefs/pkg/logging"
    10  )
    11  
    12  type merger struct {
    13  	ctx    context.Context
    14  	logger logging.Logger
    15  
    16  	writer               MetaRangeWriter
    17  	base                 Iterator
    18  	source               Iterator
    19  	dest                 Iterator
    20  	haveSource, haveDest bool
    21  	strategy             graveler.MergeStrategy
    22  }
    23  
    24  // getNextGEKey moves base iterator from its current position to the next greater equal value
    25  func (m *merger) getNextGEKey(key graveler.Key) (*graveler.ValueRecord, error) {
    26  	baseValue, _ := m.base.Value()
    27  	if baseValue != nil && bytes.Compare(key, baseValue.Key) <= 0 {
    28  		return baseValue, nil
    29  	}
    30  
    31  	for {
    32  		_, baseRange := m.base.Value()
    33  		if baseRange != nil && bytes.Compare(baseRange.MaxKey, key) >= 0 {
    34  			for {
    35  				baseValue, innerRange := m.base.Value()
    36  				if baseValue != nil && bytes.Compare(key, baseValue.Key) <= 0 {
    37  					return baseValue, nil
    38  				}
    39  				if !m.base.Next() || innerRange.ID != baseRange.ID {
    40  					break
    41  				}
    42  			}
    43  		} else if !m.base.NextRange() {
    44  			break
    45  		}
    46  	}
    47  	if err := m.base.Err(); err != nil {
    48  		return nil, err
    49  	}
    50  	return nil, nil
    51  }
    52  
    53  // getNextOverlappingFromBase moves base iterator from its current position to the next range overlapping with rangeToOverlap
    54  func (m *merger) getNextOverlappingFromBase(rangeToOverlap *Range) (*Range, error) {
    55  	for {
    56  		_, baseRange := m.base.Value()
    57  		if baseRange != nil && bytes.Compare(baseRange.MaxKey, rangeToOverlap.MinKey) >= 0 {
    58  			if bytes.Compare(baseRange.MinKey, rangeToOverlap.MaxKey) > 0 {
    59  				return nil, nil
    60  			}
    61  			return baseRange, nil
    62  		}
    63  		if !m.base.NextRange() {
    64  			break
    65  		}
    66  	}
    67  	return nil, m.base.Err()
    68  }
    69  
    70  // writeRange writes Range using writer
    71  func (m *merger) writeRange(writeRange *Range) error {
    72  	if m.logger.IsTracing() {
    73  		m.logger.WithFields(logging.Fields{
    74  			"from": string(writeRange.MinKey),
    75  			"to":   string(writeRange.MaxKey),
    76  			"ID":   writeRange.ID,
    77  		}).Trace("copy entire range")
    78  	}
    79  	if err := m.writer.WriteRange(*writeRange); err != nil {
    80  		return fmt.Errorf("copy range %s: %w", writeRange.ID, err)
    81  	}
    82  	return nil
    83  }
    84  
    85  // writeRecord writes graveler.ValueRecord using writer
    86  func (m *merger) writeRecord(writeValue *graveler.ValueRecord) error {
    87  	if m.logger.IsTracing() {
    88  		m.logger.WithFields(logging.Fields{
    89  			"key": string(writeValue.Key),
    90  			"ID":  string(writeValue.Identity),
    91  		}).Trace("write record")
    92  	}
    93  	if err := m.writer.WriteRecord(*writeValue); err != nil {
    94  		return fmt.Errorf("write record: %w", err)
    95  	}
    96  	return nil
    97  }
    98  
    99  func (m *merger) destBeforeSource(destValue *graveler.ValueRecord) error {
   100  	baseValue, err := m.getNextGEKey(destValue.Key)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	if baseValue != nil && bytes.Equal(destValue.Identity, baseValue.Identity) && bytes.Equal(destValue.Key, baseValue.Key) { // source deleted this record
   105  		m.haveDest = m.dest.Next()
   106  	} else {
   107  		if baseValue != nil && bytes.Equal(destValue.Key, baseValue.Key) { // deleted by source changed by dest
   108  			switch m.strategy {
   109  			case graveler.MergeStrategyDest:
   110  				break
   111  			case graveler.MergeStrategySrc:
   112  				m.haveDest = m.dest.Next()
   113  				return nil
   114  			default: // graveler.MergeStrategyNone
   115  				return graveler.ErrConflictFound
   116  			}
   117  		}
   118  		// dest added this record
   119  		err := m.writeRecord(destValue)
   120  		if err != nil {
   121  			return fmt.Errorf("write dest record: %w", err)
   122  		}
   123  		m.haveDest = m.dest.Next()
   124  	}
   125  	return nil
   126  }
   127  
   128  func (m *merger) sourceBeforeDest(sourceValue *graveler.ValueRecord) error {
   129  	baseValue, err := m.getNextGEKey(sourceValue.Key)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	if baseValue != nil && bytes.Equal(sourceValue.Identity, baseValue.Identity) && bytes.Equal(sourceValue.Key, baseValue.Key) { // dest deleted this record
   134  		m.haveSource = m.source.Next()
   135  	} else {
   136  		if baseValue != nil && bytes.Equal(sourceValue.Key, baseValue.Key) { // deleted by dest and changed by source
   137  			switch m.strategy {
   138  			case graveler.MergeStrategyDest:
   139  				m.haveSource = m.source.Next()
   140  				return nil
   141  			case graveler.MergeStrategySrc:
   142  				break
   143  			default: // graveler.MergeStrategyNone
   144  				return graveler.ErrConflictFound
   145  			}
   146  		}
   147  		// source added this record
   148  		err := m.writeRecord(sourceValue)
   149  		if err != nil {
   150  			return fmt.Errorf("write source record: %w", err)
   151  		}
   152  		m.haveSource = m.source.Next()
   153  	}
   154  	return nil
   155  }
   156  
   157  // handleAll handles the case where only one Iterator from source or dest remains
   158  // Since the iterator can be for either the source ot the dest range, the function
   159  // receives a graveler.MergeStrategy parameter - strategyToInclude - to indicate
   160  // which strategy favors the given range. In case of a conflict, the configured m.strategy
   161  // is compared to the given strategyToInclude, and if they match - the conflict will
   162  // be resolved by taking the value from the given range. If not and the configured
   163  // m.strategy is other than MergeStrategyNone, the record is ignored. If m.strategy is
   164  // MergeStrategyNone - a conflict will be reported
   165  func (m *merger) handleAll(iter Iterator, strategyToInclude graveler.MergeStrategy) error {
   166  	for {
   167  		select {
   168  		case <-m.ctx.Done():
   169  			return m.ctx.Err()
   170  		default:
   171  		}
   172  		iterValue, iterRange := iter.Value()
   173  		if iterValue == nil {
   174  			baseRange, err := m.getNextOverlappingFromBase(iterRange)
   175  			if err != nil {
   176  				return fmt.Errorf("base range GE: %w", err)
   177  			}
   178  			if baseRange == nil || baseRange.ID == iterRange.ID {
   179  				if baseRange == nil {
   180  					if err := m.writeRange(iterRange); err != nil {
   181  						return err
   182  					}
   183  				}
   184  				if !iter.NextRange() {
   185  					break
   186  				}
   187  			} else if !iter.Next() { // need to enter this range
   188  				break
   189  			}
   190  		} else {
   191  			baseValue, err := m.getNextGEKey(iterValue.Key)
   192  			if err != nil {
   193  				return fmt.Errorf("base value GE: %w", err)
   194  			}
   195  			if baseValue == nil || !bytes.Equal(baseValue.Identity, iterValue.Identity) {
   196  				shouldWriteRecord := true
   197  				if baseValue != nil && bytes.Equal(baseValue.Key, iterValue.Key) { // deleted by one changed by iter
   198  					if m.strategy == graveler.MergeStrategyNone { // conflict is only reported if no strategy is selected
   199  						return graveler.ErrConflictFound
   200  					}
   201  					// In case of conflict, if the strategy favors the given iter we
   202  					// still want to write the record. Otherwise, it will be ignored.
   203  					if m.strategy != strategyToInclude {
   204  						shouldWriteRecord = false
   205  					}
   206  				}
   207  				if shouldWriteRecord {
   208  					if err := m.writeRecord(iterValue); err != nil {
   209  						return err
   210  					}
   211  				}
   212  			}
   213  			if !iter.Next() {
   214  				break
   215  			}
   216  		}
   217  	}
   218  	return iter.Err()
   219  }
   220  
   221  // handleBothRanges handles the case where both source and dest iterators are at the header of a range
   222  func (m *merger) handleBothRanges(sourceRange *Range, destRange *Range) error {
   223  	switch {
   224  	case sourceRange.ID == destRange.ID: // range hasn't changed or both added the same range
   225  		err := m.writeRange(sourceRange)
   226  		if err != nil {
   227  			return err
   228  		}
   229  		m.haveSource = m.source.NextRange()
   230  		m.haveDest = m.dest.NextRange()
   231  
   232  	case sourceRange.EqualBounds(destRange):
   233  		baseRange, err := m.getNextOverlappingFromBase(sourceRange)
   234  		if err != nil {
   235  			return err
   236  		}
   237  		if baseRange != nil && (sourceRange.ID == baseRange.ID || destRange.ID == baseRange.ID) {
   238  			if sourceRange.ID == baseRange.ID { // dest added changes
   239  				err = m.writeRange(destRange)
   240  			} else {
   241  				err = m.writeRange(sourceRange) // source added changes
   242  			}
   243  			if err != nil {
   244  				return err
   245  			}
   246  			m.haveSource = m.source.NextRange()
   247  			m.haveDest = m.dest.NextRange()
   248  		} else { // enter both ranges
   249  			m.haveSource = m.source.Next()
   250  			m.haveDest = m.dest.Next()
   251  		}
   252  
   253  	case sourceRange.BeforeRange(destRange):
   254  		baseRange, err := m.getNextOverlappingFromBase(sourceRange)
   255  		if err != nil {
   256  			return fmt.Errorf("base range GE: %w", err)
   257  		}
   258  		if baseRange == nil { // source added this range
   259  			err = m.writeRange(sourceRange)
   260  			if err != nil {
   261  				return err
   262  			}
   263  			m.haveSource = m.source.NextRange()
   264  			return nil
   265  		}
   266  		if sourceRange.ID == baseRange.ID { // dest deleted this range
   267  			m.haveSource = m.source.NextRange()
   268  			return nil
   269  		}
   270  		// both changed this range
   271  		m.haveSource = m.source.Next()
   272  		m.haveDest = m.dest.Next()
   273  
   274  	case destRange.BeforeRange(sourceRange) && m.validWritingRange(m.dest):
   275  		baseRange, err := m.getNextOverlappingFromBase(destRange)
   276  		if err != nil {
   277  			return fmt.Errorf("base range GE: %w", err)
   278  		}
   279  		if baseRange == nil { // dest added this range
   280  			err = m.writeRange(destRange)
   281  			if err != nil {
   282  				return err
   283  			}
   284  			m.haveDest = m.dest.NextRange()
   285  			return nil
   286  		}
   287  		if destRange.ID == baseRange.ID { // source deleted this range
   288  			m.haveDest = m.dest.NextRange()
   289  			return nil
   290  		}
   291  		// both changed this range
   292  		m.haveSource = m.source.Next()
   293  		m.haveDest = m.dest.Next()
   294  
   295  	default: // ranges overlapping
   296  		m.haveSource = m.source.Next()
   297  		m.haveDest = m.dest.Next()
   298  	}
   299  	return nil
   300  }
   301  
   302  func (m *merger) handleConflict(sourceValue *graveler.ValueRecord, destValue *graveler.ValueRecord) error {
   303  	switch m.strategy {
   304  	case graveler.MergeStrategyDest:
   305  		err := m.writeRecord(destValue)
   306  		if err != nil {
   307  			return fmt.Errorf("write record: %w", err)
   308  		}
   309  	case graveler.MergeStrategySrc:
   310  		err := m.writeRecord(sourceValue)
   311  		if err != nil {
   312  			return fmt.Errorf("write record: %w", err)
   313  		}
   314  	default: // graveler.MergeStrategyNone
   315  		return graveler.ErrConflictFound
   316  	}
   317  	m.haveSource = m.source.Next()
   318  	m.haveDest = m.dest.Next()
   319  	return nil
   320  }
   321  
   322  // handleBothKeys handles the case where both source and dest iterators are inside range
   323  func (m *merger) handleBothKeys(sourceValue *graveler.ValueRecord, destValue *graveler.ValueRecord) error {
   324  	c := bytes.Compare(sourceValue.Key, destValue.Key)
   325  	switch {
   326  	case c < 0: // source before dest
   327  		return m.sourceBeforeDest(sourceValue)
   328  	case c > 0: // dest before source
   329  		return m.destBeforeSource(destValue)
   330  
   331  	default: // identical keys
   332  		baseValue, err := m.getNextGEKey(destValue.Key)
   333  		if err != nil {
   334  			return err
   335  		}
   336  		if !bytes.Equal(sourceValue.Identity, destValue.Identity) {
   337  			if baseValue != nil {
   338  				switch {
   339  				case bytes.Equal(sourceValue.Identity, baseValue.Identity):
   340  					err = m.writeRecord(destValue)
   341  				case bytes.Equal(destValue.Identity, baseValue.Identity):
   342  					err = m.writeRecord(sourceValue)
   343  				default: // both changed the same key
   344  					return m.handleConflict(sourceValue, destValue)
   345  				}
   346  				if err != nil {
   347  					return fmt.Errorf("write record: %w", err)
   348  				}
   349  				m.haveSource = m.source.Next()
   350  				m.haveDest = m.dest.Next()
   351  				return nil
   352  			} else { // both added the same key with different identity
   353  				return m.handleConflict(sourceValue, destValue)
   354  			}
   355  		}
   356  		// record hasn't changed or both added the same record
   357  		err = m.writeRecord(sourceValue)
   358  		if err != nil {
   359  			return fmt.Errorf("write record: %w", err)
   360  		}
   361  		m.haveSource = m.source.Next()
   362  		m.haveDest = m.dest.Next()
   363  	}
   364  	return nil
   365  }
   366  
   367  // handleDestRangeSourceKey handles the case where source Iterator inside range and dest Iterator at the header of a range
   368  func (m *merger) handleDestRangeSourceKey(destRange *Range, sourceValue *graveler.ValueRecord) error {
   369  	if bytes.Compare(destRange.MinKey, sourceValue.Key) > 0 { // source before dest range
   370  		return m.sourceBeforeDest(sourceValue)
   371  	}
   372  
   373  	if bytes.Compare(destRange.MaxKey, sourceValue.Key) < 0 &&
   374  		m.validWritingRange(m.dest) { // dest range before source
   375  		baseRange, err := m.getNextOverlappingFromBase(destRange)
   376  		if err != nil {
   377  			return fmt.Errorf("base range GE: %w", err)
   378  		}
   379  		if baseRange == nil {
   380  			err = m.writeRange(destRange)
   381  			if err != nil {
   382  				return err
   383  			}
   384  			m.haveDest = m.dest.NextRange()
   385  			return nil
   386  		}
   387  		if destRange.ID == baseRange.ID { // source deleted this range
   388  			m.haveDest = m.dest.NextRange()
   389  			return nil
   390  		}
   391  	}
   392  	// dest is at start of range which we need to scan, enter it
   393  	m.haveDest = m.dest.Next()
   394  	return nil
   395  }
   396  
   397  // handleSourceRangeDestKey handles the case where dest Iterator inside range and source Iterator at the header of a range
   398  func (m *merger) handleSourceRangeDestKey(sourceRange *Range, destValue *graveler.ValueRecord) error {
   399  	if bytes.Compare(sourceRange.MinKey, destValue.Key) > 0 { // dest before source range
   400  		return m.destBeforeSource(destValue)
   401  	}
   402  
   403  	if bytes.Compare(sourceRange.MaxKey, destValue.Key) < 0 { // source range before dest
   404  		baseRange, err := m.getNextOverlappingFromBase(sourceRange)
   405  		if err != nil {
   406  			return fmt.Errorf("base range GE: %w", err)
   407  		}
   408  		if baseRange == nil {
   409  			err = m.writeRange(sourceRange)
   410  			if err != nil {
   411  				return err
   412  			}
   413  			m.haveSource = m.source.NextRange()
   414  			return nil
   415  		}
   416  		if sourceRange.ID == baseRange.ID { // dest deleted this range
   417  			m.haveSource = m.source.NextRange()
   418  			return nil
   419  		}
   420  	}
   421  	// source is at start of range which we need to scan, enter it
   422  	m.haveSource = m.source.Next()
   423  	return nil
   424  }
   425  
   426  func (m *merger) merge() error {
   427  	m.haveSource, m.haveDest, _ = m.source.Next(), m.dest.Next(), m.base.Next()
   428  	if err := m.source.Err(); err != nil {
   429  		return err
   430  	}
   431  	if err := m.dest.Err(); err != nil {
   432  		return err
   433  	}
   434  	if err := m.base.Err(); err != nil {
   435  		return err
   436  	}
   437  
   438  	for m.haveSource && m.haveDest {
   439  		select {
   440  		case <-m.ctx.Done():
   441  			return m.ctx.Err()
   442  		default:
   443  		}
   444  		sourceValue, sourceRange := m.source.Value()
   445  		destValue, destRange := m.dest.Value()
   446  		var err error
   447  		switch {
   448  		case sourceValue == nil && destValue == nil:
   449  			err = m.handleBothRanges(sourceRange, destRange)
   450  		case destValue == nil && sourceValue != nil:
   451  			err = m.handleDestRangeSourceKey(destRange, sourceValue)
   452  		case sourceValue == nil && destValue != nil:
   453  			err = m.handleSourceRangeDestKey(sourceRange, destValue)
   454  		default:
   455  			err = m.handleBothKeys(sourceValue, destValue)
   456  		}
   457  		if err != nil {
   458  			return err
   459  		}
   460  		if err = m.source.Err(); err != nil {
   461  			return err
   462  		}
   463  		if err = m.dest.Err(); err != nil {
   464  			return err
   465  		}
   466  		if err = m.base.Err(); err != nil {
   467  			return err
   468  		}
   469  	}
   470  
   471  	if m.haveSource {
   472  		if err := m.handleAll(m.source, graveler.MergeStrategySrc); err != nil {
   473  			return err
   474  		}
   475  	}
   476  	if m.haveDest {
   477  		if err := m.handleAll(m.dest, graveler.MergeStrategyDest); err != nil {
   478  			return err
   479  		}
   480  	}
   481  	return nil
   482  }
   483  
   484  func (m *merger) validWritingRange(it Iterator) bool {
   485  	switch v := it.(type) {
   486  	case ImportIterator:
   487  		return !v.IsCurrentPrefixIncludedInRange()
   488  	default:
   489  		return true
   490  	}
   491  }
   492  
   493  func Merge(ctx context.Context, writer MetaRangeWriter, base Iterator, source Iterator, destination Iterator, strategy graveler.MergeStrategy) error {
   494  	m := merger{
   495  		ctx:      ctx,
   496  		logger:   logging.FromContext(ctx),
   497  		writer:   writer,
   498  		base:     base,
   499  		source:   source,
   500  		dest:     destination,
   501  		strategy: strategy,
   502  	}
   503  	return m.merge()
   504  }