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 }