github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/compare_iterator.go (about) 1 package committed 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 8 "github.com/treeverse/lakefs/pkg/graveler" 9 ) 10 11 type compareIterator struct { 12 ctx context.Context 13 errorOnConflict bool 14 diffIt DiffIterator 15 val *graveler.Diff 16 rng *RangeDiff 17 base Iterator 18 err error 19 } 20 21 var ErrUnsupportedRangeDiffType = errors.New("range diff type unsupported - supports only added and removed") 22 23 // NewCompareIterator accepts an iterator describing a diff from the merge destination to the source. 24 // It returns a DiffIterator with the changes to perform on the destination branch, in order to merge the source into it, 25 // relative to base as the merge base. 26 // When reaching a conflict, the returned Diff will be of type graveler.DiffTypeConflict. 27 func NewCompareIterator(ctx context.Context, diffDestToSource DiffIterator, base Iterator) *compareIterator { 28 return &compareIterator{ 29 ctx: ctx, 30 diffIt: diffDestToSource, 31 base: base, 32 errorOnConflict: false, 33 } 34 } 35 36 // baseGE returns value ( and its range) from base iterator, which is greater or equal than the given key 37 func (d *compareIterator) baseGE(key graveler.Key) (*graveler.ValueRecord, *Range, error) { 38 d.base.SeekGE(key) 39 if !d.base.Next() { 40 return nil, nil, d.err 41 } 42 baseValue, baseRange := d.base.Value() 43 return baseValue, baseRange, nil 44 } 45 46 func (d *compareIterator) valueFromBase(key graveler.Key) (*graveler.ValueRecord, error) { 47 d.base.SeekGE(key) 48 var val *graveler.ValueRecord 49 for d.base.Next() && val == nil { 50 val, _ = d.base.Value() 51 } 52 if err := d.base.Err(); err != nil { 53 return nil, err 54 } 55 if val == nil || !bytes.Equal(val.Key, key) { 56 return nil, nil 57 } 58 return val, nil 59 } 60 61 func (d *compareIterator) handleConflict() bool { 62 if d.errorOnConflict { 63 d.err = graveler.ErrConflictFound 64 return false 65 } 66 val, rng := d.diffIt.Value() 67 if val != nil { 68 d.val = val.Copy() 69 } 70 d.setRangeDiff(rng) 71 d.val.Type = graveler.DiffTypeConflict 72 return true 73 } 74 75 func (d *compareIterator) setRangeDiff(r *RangeDiff) { 76 if r != nil { 77 d.rng = r.Copy() 78 return 79 } 80 d.rng = nil 81 } 82 83 // stepNext is called after diffIt Next or NextRange and iterates over diff iterator until the compare iterator has a value 84 func (d *compareIterator) stepNext() bool { 85 for { 86 select { 87 case <-d.ctx.Done(): 88 d.err = d.ctx.Err() 89 return false 90 default: 91 } 92 var hasNext bool 93 var done bool 94 val, _ := d.diffIt.Value() 95 if val == nil { 96 // range header 97 hasNext, done = d.stepRange() 98 } else { 99 hasNext, done = d.stepValue() 100 } 101 if done { 102 return hasNext 103 } 104 if !hasNext { 105 break 106 } 107 } 108 if d.err != nil { 109 d.err = d.diffIt.Err() 110 } 111 return false 112 } 113 114 // stepValue moves one step according to current value 115 // returns hasMore if iterator has more, and done if the step is over (got to a value, end of iterator, or error) 116 func (d *compareIterator) stepValue() (hasNext, done bool) { 117 val, rngDiff := d.diffIt.Value() 118 key := val.Key 119 typ := val.Type 120 baseVal, err := d.valueFromBase(key) 121 if err != nil { 122 d.err = err 123 return false, true 124 } 125 switch typ { 126 case graveler.DiffTypeAdded: 127 // exists on source, but not on dest 128 if baseVal == nil { 129 // added only on source 130 d.val = val.Copy() 131 d.setRangeDiff(rngDiff) 132 return true, true 133 } 134 if !bytes.Equal(baseVal.Identity, val.Value.Identity) { 135 // removed on dest, but changed on source 136 return d.handleConflict(), true 137 } 138 case graveler.DiffTypeChanged: 139 if baseVal == nil { 140 // added on dest and source, with different identities 141 return d.handleConflict(), true 142 } 143 if bytes.Equal(baseVal.Identity, val.Value.Identity) { 144 // changed on dest, but not on source 145 return d.diffIt.Next(), false 146 } 147 if !bytes.Equal(baseVal.Identity, val.LeftIdentity) { 148 // changed on dest and source, to different identities 149 return d.handleConflict(), true 150 } 151 // changed only on source 152 d.val = val.Copy() 153 d.setRangeDiff(rngDiff) 154 return true, true 155 case graveler.DiffTypeRemoved: 156 // exists on dest, but not on source 157 if baseVal != nil { 158 if bytes.Equal(baseVal.Identity, val.LeftIdentity) { 159 // removed on source, not changed on dest 160 d.val = val.Copy() 161 d.setRangeDiff(rngDiff) 162 return true, true 163 } 164 // changed on dest, removed on source 165 return d.handleConflict(), true 166 } 167 // added on dest, but not on source - next value 168 } 169 return d.diffIt.Next(), false 170 } 171 172 // stepRange moves one step according to current range 173 // returns hasMore if iterator has more, and done if the step is over (got to a value, end of iterator, or error) 174 func (d *compareIterator) stepRange() (hasMore bool, done bool) { 175 _, rngDiff := d.diffIt.Value() 176 typ := rngDiff.Type 177 rng := rngDiff.Range 178 leftID := rngDiff.LeftIdentity 179 baseValue, baseRange, err := d.baseGE(graveler.Key(rng.MinKey)) 180 if err != nil { 181 d.err = err 182 return false, true 183 } 184 switch typ { 185 case graveler.DiffTypeAdded: 186 // exists on source, but not on dest 187 if baseRange != nil && rng.ID == baseRange.ID { 188 // removed only on dest -> skip range 189 return d.diffIt.NextRange(), false 190 } 191 if baseValue == nil || bytes.Compare(rng.MaxKey, baseValue.Key) < 0 { 192 // added only on source 193 d.setRangeDiff(rngDiff) 194 d.val = nil 195 return true, true 196 } 197 // overlapping base and diff, must step into range 198 return d.diffIt.Next(), false 199 case graveler.DiffTypeRemoved: 200 if baseRange != nil && rng.ID == baseRange.ID { 201 // removed on source, not changed on dest 202 d.setRangeDiff(rngDiff) 203 d.val = nil 204 return true, true 205 } 206 if (baseRange != nil && bytes.Compare(rng.MaxKey, baseRange.MinKey) < 0) || baseValue == nil || bytes.Compare(rng.MaxKey, baseValue.Key) < 0 { 207 // added on dest, but not on source, skip range 208 return d.diffIt.NextRange(), false 209 } 210 211 // overlapping base and diff, must step into range 212 return d.diffIt.Next(), false 213 214 case graveler.DiffTypeChanged: 215 if baseRange != nil && leftID == baseRange.ID { 216 // changed only in source 217 d.setRangeDiff(rngDiff) 218 d.val = nil 219 return true, true 220 } 221 if baseRange != nil && bytes.Compare(rng.MaxKey, baseRange.MinKey) < 0 { 222 // conflict, added on dest and source 223 return d.handleConflict(), true 224 } 225 if baseRange != nil && rng.ID == baseRange.ID { 226 // changed on dest, but not on source, skip range 227 return d.diffIt.NextRange(), false 228 } 229 return d.diffIt.Next(), false 230 default: 231 d.err = ErrUnsupportedRangeDiffType 232 } 233 return false, true 234 } 235 236 func (d *compareIterator) Next() bool { 237 if d.diffIt.Next() { 238 return d.stepNext() 239 } 240 d.err = d.diffIt.Err() 241 return false 242 } 243 244 func (d *compareIterator) NextRange() bool { 245 if !d.diffIt.NextRange() { 246 d.err = d.diffIt.Err() 247 return false 248 } 249 return d.stepNext() 250 } 251 252 func (d *compareIterator) SeekGE(id graveler.Key) { 253 d.val = nil 254 d.err = nil 255 d.diffIt.SeekGE(id) 256 } 257 258 func (d *compareIterator) Value() (*graveler.Diff, *RangeDiff) { 259 if d.err != nil { 260 return nil, nil 261 } 262 return d.val, d.rng 263 } 264 265 func (d *compareIterator) Err() error { 266 return d.err 267 } 268 269 func (d *compareIterator) Close() { 270 d.diffIt.Close() 271 d.base.Close() 272 }