github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/merge_test.go (about) 1 package committed_test 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "strings" 9 "testing" 10 11 "github.com/golang/mock/gomock" 12 "github.com/stretchr/testify/assert" 13 "github.com/treeverse/lakefs/pkg/graveler" 14 "github.com/treeverse/lakefs/pkg/graveler/committed" 15 "github.com/treeverse/lakefs/pkg/graveler/committed/mock" 16 "github.com/treeverse/lakefs/pkg/graveler/testutil" 17 ) 18 19 type actionType string 20 21 const ( 22 actionTypeWriteRange actionType = "write-range" 23 actionTypeWriteRecord actionType = "write-record" 24 ) 25 26 type writeAction struct { 27 action actionType 28 rng committed.Range 29 key string 30 identity string 31 } 32 33 type recordMatcher struct { 34 key graveler.Key 35 identity []byte 36 } 37 38 func newRecordMatcher(key string, identity string) recordMatcher { 39 return recordMatcher{ 40 key: graveler.Key(key), 41 identity: []byte(identity), 42 } 43 } 44 45 func (m recordMatcher) Matches(x interface{}) bool { 46 record := x.(graveler.ValueRecord) 47 return bytes.Equal(m.key, record.Key) && bytes.Equal(m.identity, record.Identity) 48 } 49 50 func (m recordMatcher) String() string { 51 return fmt.Sprintf("is equal to key:%s, identity:%s", m.key, m.identity) 52 } 53 54 type testValueRecord struct { 55 key string 56 identity string 57 } 58 59 type testRange struct { 60 rng committed.Range 61 records []testValueRecord 62 } 63 64 type testRunResult struct { 65 mergeStrategies []graveler.MergeStrategy 66 expectedActions []writeAction 67 expectedErr error 68 } 69 70 type testMetaRange struct { 71 ranges []testRange 72 } 73 74 func (t *testMetaRange) GetMetaRangeID() graveler.MetaRangeID { 75 var sb strings.Builder 76 for _, rng := range t.ranges { 77 sb.WriteString(string(rng.rng.ID)) 78 sb.WriteString("#") 79 } 80 return graveler.MetaRangeID(sb.String()) 81 } 82 83 func newTestMetaRange(ranges []testRange) *testMetaRange { 84 return &testMetaRange{ranges: ranges} 85 } 86 87 type testCase struct { 88 baseRange *testMetaRange 89 sourceRange *testMetaRange 90 destRange *testMetaRange 91 expectedResult []testRunResult 92 } 93 94 type testCases map[string]testCase 95 96 func createIter(tr *testMetaRange) committed.Iterator { 97 iter := testutil.NewFakeIterator() 98 for _, rng := range tr.ranges { 99 addRange := rng.rng 100 iter.AddRange(&addRange) 101 for _, record := range rng.records { 102 iter.AddValueRecords(&graveler.ValueRecord{ 103 Key: graveler.Key(record.key), 104 Value: &graveler.Value{ 105 Identity: []byte(record.identity), 106 Data: nil, 107 }, 108 }) 109 } 110 } 111 return iter 112 } 113 114 func Test_merge(t *testing.T) { 115 tests := testCases{ 116 "dest range added before": { 117 baseRange: newTestMetaRange([]testRange{ 118 { 119 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 120 records: nil, 121 }, {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 122 {"k11", "base:k11"}, {"k12", "base:k12"}, 123 }}, 124 }), 125 sourceRange: newTestMetaRange([]testRange{{ 126 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 127 records: nil, 128 }}), 129 destRange: newTestMetaRange([]testRange{ 130 {rng: committed.Range{ID: "dest:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 131 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 132 }), 133 expectedResult: []testRunResult{{ 134 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 135 expectedActions: []writeAction{ 136 { 137 action: actionTypeWriteRange, 138 rng: committed.Range{ID: "dest:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}, 139 }, 140 { 141 action: actionTypeWriteRange, 142 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 143 }, 144 }, 145 expectedErr: nil, 146 }}, 147 }, 148 "source range added before": { 149 baseRange: newTestMetaRange([]testRange{ 150 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 151 {rng: committed.Range{ID: "base:k7-k8", MinKey: committed.Key("k7"), MaxKey: committed.Key("k8"), Count: 2, EstimatedSize: 1024}}, 152 }), 153 sourceRange: newTestMetaRange([]testRange{ 154 {rng: committed.Range{ID: "source:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1024}}, 155 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 156 }), 157 destRange: newTestMetaRange([]testRange{ 158 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 159 }), 160 expectedResult: []testRunResult{{ 161 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 162 expectedActions: []writeAction{ 163 { 164 action: actionTypeWriteRange, 165 rng: committed.Range{ID: "source:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1024}, 166 }, 167 { 168 action: actionTypeWriteRange, 169 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}, 170 }, 171 }, 172 }}, 173 }, 174 "source range removed before": { 175 baseRange: newTestMetaRange([]testRange{ 176 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1024}}, 177 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 178 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 179 {"k11", "base:k11"}, {"k12", "base:k12"}, 180 }}, 181 }), 182 sourceRange: newTestMetaRange([]testRange{ 183 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 184 }), 185 destRange: newTestMetaRange([]testRange{ 186 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1024}}, 187 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}}, 188 }), 189 expectedResult: []testRunResult{{ 190 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 191 expectedActions: []writeAction{ 192 { 193 action: actionTypeWriteRange, 194 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1024}, 195 }, 196 }, 197 }}, 198 }, 199 "source range inner change": { 200 baseRange: newTestMetaRange([]testRange{ 201 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 202 {"k1", "base:k1"}, {"k3", "base:k3"}, 203 }}, 204 {rng: committed.Range{ID: "base:k4", MinKey: committed.Key("k4"), EstimatedSize: 1024}, records: []testValueRecord{ 205 {"k4", "base:k4"}, 206 }}, 207 }), 208 sourceRange: newTestMetaRange([]testRange{ 209 {rng: committed.Range{ID: "source:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 210 {"k1", "base:k1"}, {"k2", "source:k2"}, {"k3", "source:k3"}, 211 }}, 212 }), 213 destRange: newTestMetaRange([]testRange{ 214 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 215 {"k1", "base:k1"}, 216 {"k3", "base:k3"}, 217 }}, 218 }), 219 expectedResult: []testRunResult{{ 220 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 221 expectedActions: []writeAction{ 222 { 223 action: actionTypeWriteRange, 224 rng: committed.Range{ID: "source:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, 225 }, 226 }, 227 }}, 228 }, 229 "dest range inner change": { 230 baseRange: newTestMetaRange([]testRange{ 231 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 232 {"k1", "base:k1"}, 233 {"k3", "base:k3"}, 234 }}, 235 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 236 {"k11", "base:k11"}, {"k12", "base:k12"}, 237 }}, 238 }), 239 sourceRange: newTestMetaRange([]testRange{ 240 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 241 {"k1", "base:k1"}, 242 {"k3", "base:k3"}, 243 }}, 244 }), 245 destRange: newTestMetaRange([]testRange{ 246 {rng: committed.Range{ID: "dest:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, records: []testValueRecord{ 247 {"k1", "base:k1"}, 248 {"k2", "dest:k2"}, 249 {"k3", "dest:k3"}, 250 }}, 251 }), 252 expectedResult: []testRunResult{{ 253 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 254 expectedActions: []writeAction{ 255 { 256 action: actionTypeWriteRange, 257 rng: committed.Range{ID: "dest:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), EstimatedSize: 1024}, 258 }, 259 }, 260 expectedErr: nil, 261 }}, 262 }, 263 "source range append after": { 264 baseRange: newTestMetaRange([]testRange{ 265 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), Count: 2, EstimatedSize: 1024}, records: []testValueRecord{ 266 {"k1", "base:k1"}, 267 {"k3", "base:k3"}, 268 }}, 269 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 270 {"k11", "base:k11"}, {"k12", "base:k12"}, 271 }}, 272 }), 273 sourceRange: newTestMetaRange([]testRange{ 274 {rng: committed.Range{ID: "source:k1-k5", MinKey: committed.Key("k1"), MaxKey: committed.Key("k5"), Count: 4, EstimatedSize: 1024}, records: []testValueRecord{ 275 {"k1", "base:k1"}, 276 {"k3", "base:k3"}, 277 {"k4", "source:k4"}, 278 {"k5", "source:k5"}, 279 }}, 280 }), 281 destRange: newTestMetaRange([]testRange{ 282 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3"), Count: 2, EstimatedSize: 1024}, records: []testValueRecord{ 283 {"k1", "base:k1"}, 284 {"k3", "base:k3"}, 285 }}, 286 }), 287 expectedResult: []testRunResult{ 288 { 289 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 290 expectedActions: []writeAction{ 291 { 292 action: actionTypeWriteRecord, 293 key: "k1", 294 identity: "base:k1", 295 }, 296 { 297 action: actionTypeWriteRecord, 298 key: "k3", 299 identity: "base:k3", 300 }, 301 { 302 action: actionTypeWriteRecord, 303 key: "k4", 304 identity: "source:k4", 305 }, 306 { 307 action: actionTypeWriteRecord, 308 key: "k5", 309 identity: "source:k5", 310 }, 311 }, 312 }, 313 }, 314 }, 315 "source range append and remove after": { 316 baseRange: newTestMetaRange([]testRange{ 317 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3")}, records: []testValueRecord{ 318 {"k1", "base:k1"}, {"k3", "base:k3"}, 319 }}, 320 { 321 rng: committed.Range{ID: "base:k4-k6", MinKey: committed.Key("k4"), MaxKey: committed.Key("k6")}, records: []testValueRecord{ 322 {"k4", "base:k4"}, {"k6", "base:k6"}, 323 }, 324 }, 325 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 326 {"k11", "base:k11"}, {"k12", "base:k12"}, 327 }}, 328 }), 329 sourceRange: newTestMetaRange([]testRange{ 330 {rng: committed.Range{ID: "source:k1-k5", MinKey: committed.Key("k1"), MaxKey: committed.Key("k5")}, records: []testValueRecord{ 331 {"k1", "base:k1"}, {"k2", "source:k2"}, {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "source:k5"}, 332 }}, 333 }), 334 destRange: newTestMetaRange([]testRange{ 335 {rng: committed.Range{ID: "base:k1-k3", MinKey: committed.Key("k1"), MaxKey: committed.Key("k3")}, records: []testValueRecord{ 336 {"k1", "base:k1"}, {"k3", "base:k3"}, 337 }}, 338 { 339 rng: committed.Range{ID: "base:k4-k6", MinKey: committed.Key("k4"), MaxKey: committed.Key("k6")}, records: []testValueRecord{ 340 {"k4", "base:k4"}, {"k6", "base:k6"}, 341 }, 342 }, 343 }), 344 expectedResult: []testRunResult{{ 345 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 346 expectedActions: []writeAction{ 347 { 348 action: actionTypeWriteRecord, 349 key: "k1", 350 identity: "base:k1", 351 }, 352 { 353 action: actionTypeWriteRecord, 354 key: "k2", 355 identity: "source:k2", 356 }, 357 { 358 action: actionTypeWriteRecord, 359 key: "k3", 360 identity: "base:k3", 361 }, 362 { 363 action: actionTypeWriteRecord, 364 key: "k4", 365 identity: "base:k4", 366 }, 367 { 368 action: actionTypeWriteRecord, 369 key: "k5", 370 identity: "source:k5", 371 }, 372 }, 373 expectedErr: nil, 374 }}, 375 }, 376 "source range - overlapping ranges": { 377 baseRange: newTestMetaRange([]testRange{ 378 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 379 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 380 }}, 381 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 382 {"k11", "base:k11"}, {"k12", "base:k12"}, 383 }}, 384 }), 385 sourceRange: newTestMetaRange([]testRange{ 386 {rng: committed.Range{ID: "source:k1-k10", MinKey: committed.Key("k1"), MaxKey: committed.Key("k10"), Count: 6, EstimatedSize: 66666}, records: []testValueRecord{ 387 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k4", "source:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, {"k10", "source:k10"}, 388 }}, 389 }), 390 destRange: newTestMetaRange([]testRange{ 391 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 392 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 393 }}, 394 }), 395 expectedResult: []testRunResult{{ 396 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 397 expectedActions: []writeAction{ 398 {action: actionTypeWriteRecord, key: "k1", identity: "base:k1"}, 399 {action: actionTypeWriteRecord, key: "k3", identity: "base:k3"}, 400 {action: actionTypeWriteRecord, key: "k4", identity: "source:k4"}, 401 {action: actionTypeWriteRecord, key: "k5", identity: "base:k5"}, 402 {action: actionTypeWriteRecord, key: "k6", identity: "base:k6"}, 403 {action: actionTypeWriteRecord, key: "k10", identity: "source:k10"}, 404 }, 405 }}, 406 }, 407 "dest range - overlapping ranges": { 408 baseRange: newTestMetaRange([]testRange{ 409 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 410 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 411 }}, 412 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 413 {"k11", "base:k11"}, {"k12", "base:k12"}, 414 }}, 415 }), 416 sourceRange: newTestMetaRange([]testRange{ 417 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 418 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 419 }}, 420 }), 421 destRange: newTestMetaRange([]testRange{ 422 {rng: committed.Range{ID: "dest:k1-k10", MinKey: committed.Key("k1"), MaxKey: committed.Key("k10"), Count: 6, EstimatedSize: 66666}, records: []testValueRecord{ 423 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k4", "dest:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, {"k10", "dest:k10"}, 424 }}, 425 }), 426 expectedResult: []testRunResult{{ 427 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 428 expectedActions: []writeAction{ 429 {action: actionTypeWriteRecord, key: "k1", identity: "base:k1"}, 430 {action: actionTypeWriteRecord, key: "k3", identity: "base:k3"}, 431 {action: actionTypeWriteRecord, key: "k4", identity: "dest:k4"}, 432 {action: actionTypeWriteRecord, key: "k5", identity: "base:k5"}, 433 {action: actionTypeWriteRecord, key: "k6", identity: "base:k6"}, 434 {action: actionTypeWriteRecord, key: "k10", identity: "dest:k10"}, 435 }, 436 expectedErr: nil, 437 }}, 438 }, 439 "source - remove at end of range": { 440 baseRange: newTestMetaRange([]testRange{ 441 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 442 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 443 }}, 444 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 445 {"k11", "base:k11"}, {"k12", "base:k12"}, 446 }}, 447 }), 448 sourceRange: newTestMetaRange([]testRange{ 449 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 4444}, records: []testValueRecord{ 450 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 451 }}, 452 }), 453 destRange: newTestMetaRange([]testRange{ 454 {rng: committed.Range{ID: "dest:k1-k10", MinKey: committed.Key("k1"), MaxKey: committed.Key("k10"), Count: 6, EstimatedSize: 66666}, records: []testValueRecord{ 455 {"k1", "base:k1"}, {"k3", "base:k3"}, {"k4", "dest:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, {"k10", "dest:k10"}, 456 }}, 457 }), 458 expectedResult: []testRunResult{{ 459 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 460 expectedActions: []writeAction{ 461 {action: actionTypeWriteRecord, key: "k1", identity: "base:k1"}, 462 {action: actionTypeWriteRecord, key: "k3", identity: "base:k3"}, 463 {action: actionTypeWriteRecord, key: "k4", identity: "dest:k4"}, 464 {action: actionTypeWriteRecord, key: "k5", identity: "base:k5"}, 465 {action: actionTypeWriteRecord, key: "k6", identity: "base:k6"}, 466 {action: actionTypeWriteRecord, key: "k10", identity: "dest:k10"}, 467 }, 468 expectedErr: nil, 469 }}, 470 }, 471 "both added key to range": { 472 baseRange: newTestMetaRange([]testRange{ 473 {rng: committed.Range{ID: "base:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6")}, records: []testValueRecord{ 474 {"k1", "base:k1"}, 475 {"k6", "base:k6"}, 476 }}, 477 }), 478 sourceRange: newTestMetaRange([]testRange{ 479 {rng: committed.Range{ID: "source:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6")}, records: []testValueRecord{ 480 {"k1", "base:k1"}, 481 {"k2", "source:k2"}, 482 {"k6", "base:k6"}, 483 }}, 484 }), 485 destRange: newTestMetaRange([]testRange{ 486 {rng: committed.Range{ID: "dest:k1-k6", MinKey: committed.Key("k1"), MaxKey: committed.Key("k6")}, records: []testValueRecord{ 487 {"k1", "base:k1"}, 488 {"k3", "dest:k3"}, 489 {"k6", "base:k6"}, 490 }}, 491 }), 492 expectedResult: []testRunResult{{ 493 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 494 expectedActions: []writeAction{ 495 { 496 action: actionTypeWriteRecord, 497 key: "k1", 498 identity: "base:k1", 499 }, 500 { 501 action: actionTypeWriteRecord, 502 key: "k2", 503 identity: "source:k2", 504 }, 505 { 506 action: actionTypeWriteRecord, 507 key: "k3", 508 identity: "dest:k3", 509 }, 510 { 511 action: actionTypeWriteRecord, 512 key: "k6", 513 identity: "base:k6", 514 }, 515 }, 516 }}, 517 }, 518 "source range removed": { 519 baseRange: newTestMetaRange([]testRange{ 520 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 521 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 522 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 523 {"k11", "base:k11"}, {"k12", "base:k12"}, 524 }}, 525 }), 526 sourceRange: newTestMetaRange([]testRange{{ 527 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 528 records: nil, 529 }}), 530 destRange: newTestMetaRange([]testRange{ 531 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 532 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 533 }), 534 expectedResult: []testRunResult{{ 535 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 536 expectedActions: []writeAction{ 537 { 538 action: actionTypeWriteRange, 539 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 540 }, 541 }, 542 expectedErr: nil, 543 }}, 544 }, 545 "dest range removed": { 546 baseRange: newTestMetaRange([]testRange{ 547 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 548 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 549 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 550 {"k11", "base:k11"}, {"k12", "base:k12"}, 551 }}, 552 }), 553 sourceRange: newTestMetaRange([]testRange{ 554 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 555 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 556 }), 557 destRange: newTestMetaRange([]testRange{ 558 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 559 }), 560 expectedResult: []testRunResult{{ 561 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 562 expectedActions: []writeAction{ 563 { 564 action: actionTypeWriteRange, 565 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 566 }, 567 }, 568 expectedErr: nil, 569 }}, 570 }, 571 "source key removed from range - same bounds": { 572 baseRange: newTestMetaRange([]testRange{ 573 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 574 { 575 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 576 records: []testValueRecord{ 577 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 578 }, 579 }, 580 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 581 {"k11", "base:k11"}, {"k12", "base:k12"}, 582 }}, 583 }), 584 sourceRange: newTestMetaRange([]testRange{ 585 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 586 { 587 rng: committed.Range{ID: "source:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 588 records: []testValueRecord{ 589 {"k3", "base:k3"}, {"k6", "base:k6"}, 590 }, 591 }, 592 }), 593 destRange: newTestMetaRange([]testRange{ 594 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 595 { 596 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 597 records: []testValueRecord{ 598 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 599 }, 600 }, 601 }), 602 expectedResult: []testRunResult{{ 603 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 604 expectedActions: []writeAction{ 605 {action: actionTypeWriteRange, rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 606 {action: actionTypeWriteRange, rng: committed.Range{ID: "source:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 607 }, 608 }}, 609 }, 610 "source key removed from range": { 611 baseRange: newTestMetaRange([]testRange{ 612 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 613 { 614 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 615 records: []testValueRecord{ 616 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 617 }, 618 }, 619 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 620 {"k11", "base:k11"}, {"k12", "base:k12"}, 621 }}, 622 }), 623 sourceRange: newTestMetaRange([]testRange{ 624 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 625 { 626 rng: committed.Range{ID: "source:k3-k5", MinKey: committed.Key("k3"), MaxKey: committed.Key("k5"), Count: 2, EstimatedSize: 1234}, 627 records: []testValueRecord{ 628 {"k3", "base:k3"}, {"k5", "base:k5"}, 629 }, 630 }, 631 }), 632 destRange: newTestMetaRange([]testRange{ 633 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 634 { 635 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 636 records: []testValueRecord{ 637 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 638 }, 639 }, 640 }), 641 expectedResult: []testRunResult{{ 642 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 643 expectedActions: []writeAction{ 644 {action: actionTypeWriteRange, rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 645 {action: actionTypeWriteRecord, key: "k3", identity: "base:k3"}, 646 {action: actionTypeWriteRecord, key: "k5", identity: "base:k5"}, 647 }, 648 }}, 649 }, 650 "dest key removed from range": { 651 baseRange: newTestMetaRange([]testRange{ 652 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 653 { 654 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 655 records: []testValueRecord{ 656 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 657 }, 658 }, 659 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 660 {"k11", "base:k11"}, {"k12", "base:k12"}, 661 }}, 662 }), 663 sourceRange: newTestMetaRange([]testRange{ 664 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 665 { 666 rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 4, EstimatedSize: 1234}, 667 records: []testValueRecord{ 668 {"k3", "base:k3"}, {"k4", "base:k4"}, {"k5", "base:k5"}, {"k6", "base:k6"}, 669 }, 670 }, 671 }), 672 destRange: newTestMetaRange([]testRange{ 673 {rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 674 { 675 rng: committed.Range{ID: "dest:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}, 676 records: []testValueRecord{ 677 {"k3", "base:k3"}, {"k6", "base:k6"}, 678 }, 679 }, 680 }), 681 expectedResult: []testRunResult{{ 682 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 683 expectedActions: []writeAction{ 684 {action: actionTypeWriteRange, rng: committed.Range{ID: "base:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 685 {action: actionTypeWriteRange, rng: committed.Range{ID: "dest:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 686 }, 687 expectedErr: nil, 688 }}, 689 }, 690 "empty source and base": { 691 baseRange: newTestMetaRange([]testRange{}), 692 sourceRange: newTestMetaRange([]testRange{}), 693 destRange: newTestMetaRange([]testRange{ 694 {rng: committed.Range{ID: "dest:k1-k2", MinKey: committed.Key("k1"), MaxKey: committed.Key("k2"), Count: 2, EstimatedSize: 1234}}, 695 {rng: committed.Range{ID: "base:k3-k6", MinKey: committed.Key("k3"), MaxKey: committed.Key("k6"), Count: 2, EstimatedSize: 1234}}, 696 }), 697 expectedResult: []testRunResult{{ 698 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 699 expectedActions: []writeAction{}, 700 expectedErr: graveler.ErrNoChanges, 701 }}, 702 }, 703 "dest removed range and added range after source removed range edges": { 704 baseRange: newTestMetaRange([]testRange{ 705 { 706 rng: committed.Range{ID: "base:k1-k5", MinKey: committed.Key("k1"), MaxKey: committed.Key("k5"), Count: 5, EstimatedSize: 1234}, 707 records: []testValueRecord{ 708 {"k1", "base:k1"}, 709 {"k2", "base:k2"}, 710 {"k3", "base:k3"}, 711 {"k4", "base:k4"}, 712 {"k5", "base:k5"}, 713 }, 714 }, 715 }), 716 sourceRange: newTestMetaRange([]testRange{ 717 { 718 rng: committed.Range{ID: "source:k3-k4", MinKey: committed.Key("k3"), MaxKey: committed.Key("k4"), Count: 2, EstimatedSize: 1234}, 719 records: []testValueRecord{ 720 {"k3", "base:k3"}, {"k4", "base:k4"}, 721 }, 722 }, 723 }), 724 destRange: newTestMetaRange([]testRange{ 725 { 726 rng: committed.Range{ID: "dest:k6-k7", MinKey: committed.Key("k6"), MaxKey: committed.Key("k7"), Count: 2, EstimatedSize: 1234}, 727 records: []testValueRecord{ 728 {"k6", "dest:k6"}, {"k7", "dest:k7"}, 729 }, 730 }, 731 }), 732 expectedResult: []testRunResult{{ 733 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 734 expectedActions: []writeAction{ 735 {action: actionTypeWriteRecord, key: "k6", identity: "dest:k6"}, 736 {action: actionTypeWriteRecord, key: "k7", identity: "dest:k7"}, 737 }, 738 expectedErr: nil, 739 }}, 740 }, 741 "no changes": { 742 baseRange: newTestMetaRange([]testRange{ 743 { 744 rng: committed.Range{ID: "base:k3-k4", MinKey: committed.Key("k3"), MaxKey: committed.Key("k4"), Count: 2, EstimatedSize: 1234}, 745 records: []testValueRecord{ 746 {"k3", "base:k3"}, {"k4", "base:k4"}, 747 }, 748 }, 749 }), 750 sourceRange: newTestMetaRange([]testRange{ 751 { 752 rng: committed.Range{ID: "base:k3-k4", MinKey: committed.Key("k3"), MaxKey: committed.Key("k4"), Count: 2, EstimatedSize: 1234}, 753 records: []testValueRecord{ 754 {"k3", "base:k3"}, {"k4", "base:k4"}, 755 }, 756 }, 757 }), 758 destRange: newTestMetaRange([]testRange{ 759 { 760 rng: committed.Range{ID: "base:k3-k4", MinKey: committed.Key("k3"), MaxKey: committed.Key("k4"), Count: 2, EstimatedSize: 1234}, 761 records: []testValueRecord{ 762 {"k3", "base:k3"}, {"k4", "base:k4"}, 763 }, 764 }, 765 }), 766 expectedResult: []testRunResult{{ 767 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 768 expectedActions: []writeAction{}, 769 expectedErr: graveler.ErrNoChanges, 770 }}, 771 }, 772 "source and dest changed record identity": { 773 baseRange: newTestMetaRange([]testRange{ 774 { 775 rng: committed.Range{ID: "base:b-c", MinKey: committed.Key("b"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 776 records: []testValueRecord{{key: "b", identity: "b"}, {key: "c", identity: "c"}}, 777 }, 778 { 779 rng: committed.Range{ID: "base:d-e", MinKey: committed.Key("d"), MaxKey: committed.Key("e"), Count: 2, EstimatedSize: 1024}, 780 records: []testValueRecord{{key: "d", identity: "d"}, {key: "e", identity: "e"}}, 781 }, 782 }), 783 sourceRange: newTestMetaRange([]testRange{ 784 { 785 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 786 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "d", identity: "d"}}, 787 }, 788 { 789 rng: committed.Range{ID: "source:e", MinKey: committed.Key("e"), MaxKey: committed.Key("e"), Count: 2, EstimatedSize: 1024}, 790 records: []testValueRecord{{key: "e", identity: "e1"}}, 791 }, 792 }), 793 destRange: newTestMetaRange([]testRange{ 794 { 795 rng: committed.Range{ID: "base:b-c", MinKey: committed.Key("b"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 796 records: []testValueRecord{{key: "b", identity: "b"}, {key: "c", identity: "c"}}, 797 }, 798 { 799 rng: committed.Range{ID: "dest:d-e", MinKey: committed.Key("d"), MaxKey: committed.Key("e"), Count: 2, EstimatedSize: 1024}, 800 records: []testValueRecord{{key: "d", identity: "d1"}, {key: "e", identity: "e"}}, 801 }, 802 }), 803 expectedResult: []testRunResult{{ 804 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 805 expectedActions: []writeAction{ 806 { 807 action: actionTypeWriteRecord, 808 key: "a", 809 identity: "a", 810 }, 811 { 812 action: actionTypeWriteRecord, 813 key: "b", 814 identity: "b", 815 }, 816 { 817 action: actionTypeWriteRecord, 818 key: "d", 819 identity: "d1", 820 }, 821 { 822 action: actionTypeWriteRecord, 823 key: "e", 824 identity: "e1", 825 }, 826 }, 827 expectedErr: nil, 828 }}, 829 }, 830 "dest removed all source added": { 831 baseRange: newTestMetaRange([]testRange{ 832 { 833 rng: committed.Range{ID: "base:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 834 records: []testValueRecord{{key: "a", identity: "a"}, {key: "d", identity: "d"}}, 835 }, 836 }), 837 sourceRange: newTestMetaRange([]testRange{ 838 { 839 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 4, EstimatedSize: 1024}, 840 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 841 }, 842 }), 843 destRange: newTestMetaRange([]testRange{}), 844 expectedResult: []testRunResult{{ 845 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 846 expectedActions: []writeAction{ 847 { 848 action: actionTypeWriteRecord, 849 key: "b", 850 identity: "b", 851 }, 852 { 853 action: actionTypeWriteRecord, 854 key: "c", 855 identity: "c", 856 }, 857 }, 858 expectedErr: nil, 859 }}, 860 }, 861 "same identity different key": { 862 baseRange: newTestMetaRange([]testRange{ 863 { 864 rng: committed.Range{ID: "base:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 865 records: []testValueRecord{{key: "a", identity: "a"}, {key: "d", identity: "d"}}, 866 }, 867 }), 868 sourceRange: newTestMetaRange([]testRange{ 869 { 870 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 4, EstimatedSize: 1024}, 871 records: []testValueRecord{{key: "a", identity: "a"}, {key: "a1", identity: "a"}, {key: "c", identity: "c"}}, 872 }, 873 }), 874 destRange: newTestMetaRange([]testRange{ 875 { 876 rng: committed.Range{ID: "dest:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 877 records: []testValueRecord{{key: "a", identity: "a"}, {key: "d", identity: "d"}}, 878 }, 879 }), 880 expectedResult: []testRunResult{{ 881 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 882 expectedActions: []writeAction{ 883 { 884 action: actionTypeWriteRecord, 885 key: "a", 886 identity: "a", 887 }, 888 { 889 action: actionTypeWriteRecord, 890 key: "a1", 891 identity: "a", 892 }, 893 { 894 action: actionTypeWriteRecord, 895 key: "c", 896 identity: "c", 897 }, 898 }, 899 expectedErr: nil, 900 }}, 901 }, 902 "dest removed all source range before base": { 903 baseRange: newTestMetaRange([]testRange{ 904 { 905 rng: committed.Range{ID: "base:c-d", MinKey: committed.Key("c"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 906 records: []testValueRecord{{key: "c", identity: "c"}, {key: "d", identity: "d"}}, 907 }, 908 }), 909 sourceRange: newTestMetaRange([]testRange{ 910 { 911 rng: committed.Range{ID: "source:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 912 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}}, 913 }, 914 }), 915 destRange: newTestMetaRange([]testRange{}), 916 expectedResult: []testRunResult{{ 917 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 918 expectedActions: []writeAction{ 919 { 920 action: actionTypeWriteRange, 921 rng: committed.Range{ID: "source:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 922 }, 923 }, 924 expectedErr: nil, 925 }}, 926 }, 927 "dest removed all different key different identity": { 928 baseRange: newTestMetaRange([]testRange{ 929 { 930 rng: committed.Range{ID: "base:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 931 records: []testValueRecord{{key: "a", identity: "a"}, {key: "a2", identity: "a2"}, {key: "b", identity: "b"}}, 932 }, 933 }), 934 sourceRange: newTestMetaRange([]testRange{ 935 { 936 rng: committed.Range{ID: "source:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 937 records: []testValueRecord{{key: "a", identity: "a"}, {key: "a1", identity: "a1"}, {key: "b", identity: "b"}}, 938 }, 939 }), 940 destRange: newTestMetaRange([]testRange{}), 941 expectedResult: []testRunResult{{ 942 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 943 expectedActions: []writeAction{ 944 { 945 action: actionTypeWriteRecord, 946 key: "a1", 947 identity: "a1", 948 }, 949 }, 950 expectedErr: nil, 951 }}, 952 }, 953 "dest removed all base and source same identity": { 954 baseRange: newTestMetaRange([]testRange{ 955 { 956 rng: committed.Range{ID: "base:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 957 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}}, 958 }, 959 { 960 rng: committed.Range{ID: "base:d-f", MinKey: committed.Key("d"), MaxKey: committed.Key("f"), Count: 2, EstimatedSize: 1024}, 961 records: []testValueRecord{{key: "d", identity: "d"}, {key: "f", identity: "f"}}, 962 }, 963 }), 964 sourceRange: newTestMetaRange([]testRange{ 965 { 966 rng: committed.Range{ID: "base:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 967 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}}, 968 }, 969 { 970 rng: committed.Range{ID: "source:c-e", MinKey: committed.Key("c"), MaxKey: committed.Key("e"), Count: 2, EstimatedSize: 1024}, 971 records: []testValueRecord{{key: "c", identity: "c"}, {key: "e", identity: "e"}}, 972 }, 973 }), 974 destRange: newTestMetaRange([]testRange{}), 975 expectedResult: []testRunResult{{ 976 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 977 expectedActions: []writeAction{ 978 { 979 action: actionTypeWriteRecord, 980 key: "c", 981 identity: "c", 982 }, 983 { 984 action: actionTypeWriteRecord, 985 key: "e", 986 identity: "e", 987 }, 988 }, 989 expectedErr: nil, 990 }}, 991 }, 992 "source key before dest range": { 993 baseRange: newTestMetaRange([]testRange{ 994 { 995 rng: committed.Range{ID: "base:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 996 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 997 }, 998 }), 999 sourceRange: newTestMetaRange([]testRange{ 1000 { 1001 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1002 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 1003 }, 1004 }), 1005 destRange: newTestMetaRange([]testRange{ 1006 { 1007 rng: committed.Range{ID: "dest:b-c", MinKey: committed.Key("b"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1008 records: []testValueRecord{{key: "b", identity: "b"}, {key: "c", identity: "c"}}, 1009 }, 1010 { 1011 rng: committed.Range{ID: "dest:e-f", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2, EstimatedSize: 1024}, 1012 records: []testValueRecord{{key: "e", identity: "e"}, {key: "f", identity: "f"}}, 1013 }, 1014 }), 1015 expectedResult: []testRunResult{{ 1016 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1017 expectedActions: []writeAction{ 1018 { 1019 action: actionTypeWriteRecord, 1020 key: "b", 1021 identity: "b", 1022 }, 1023 { 1024 action: actionTypeWriteRecord, 1025 key: "c", 1026 identity: "c", 1027 }, 1028 { 1029 action: actionTypeWriteRange, 1030 rng: committed.Range{ID: "dest:e-f", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2, EstimatedSize: 1024}, 1031 }, 1032 }, 1033 expectedErr: nil, 1034 }}, 1035 }, 1036 "dest key before source range": { 1037 baseRange: newTestMetaRange([]testRange{ 1038 { 1039 rng: committed.Range{ID: "base:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1040 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 1041 }, 1042 }), 1043 sourceRange: newTestMetaRange([]testRange{ 1044 { 1045 rng: committed.Range{ID: "source:b-c", MinKey: committed.Key("b"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1046 records: []testValueRecord{{key: "b", identity: "b"}, {key: "c", identity: "c"}}, 1047 }, 1048 { 1049 rng: committed.Range{ID: "source:e-f", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2, EstimatedSize: 1024}, 1050 records: []testValueRecord{{key: "e", identity: "e"}, {key: "f", identity: "f"}}, 1051 }, 1052 }), 1053 destRange: newTestMetaRange([]testRange{ 1054 { 1055 rng: committed.Range{ID: "dest:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1056 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 1057 }, 1058 }), 1059 expectedResult: []testRunResult{{ 1060 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1061 expectedActions: []writeAction{ 1062 { 1063 action: actionTypeWriteRecord, 1064 key: "b", 1065 identity: "b", 1066 }, 1067 { 1068 action: actionTypeWriteRecord, 1069 key: "c", 1070 identity: "c", 1071 }, 1072 { 1073 action: actionTypeWriteRange, 1074 rng: committed.Range{ID: "source:e-f", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2, EstimatedSize: 1024}, 1075 }, 1076 }, 1077 expectedErr: nil, 1078 }}, 1079 }, 1080 "dest range before source key": { 1081 baseRange: newTestMetaRange([]testRange{ 1082 { 1083 rng: committed.Range{ID: "base:c-c", MinKey: committed.Key("c"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1084 records: []testValueRecord{{key: "c", identity: "c"}}, 1085 }, 1086 }), 1087 sourceRange: newTestMetaRange([]testRange{ 1088 { 1089 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1090 records: []testValueRecord{{key: "a", identity: "a"}, {key: "d", identity: "d"}}, 1091 }, 1092 }), 1093 destRange: newTestMetaRange([]testRange{ 1094 { 1095 rng: committed.Range{ID: "dest:a-a", MinKey: committed.Key("a"), MaxKey: committed.Key("a"), Count: 2, EstimatedSize: 1024}, 1096 records: []testValueRecord{{key: "a", identity: "a"}}, 1097 }, 1098 { 1099 rng: committed.Range{ID: "dest:b-b", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1100 records: []testValueRecord{{key: "b", identity: "b"}}, 1101 }, 1102 { 1103 rng: committed.Range{ID: "base:c-c", MinKey: committed.Key("c"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1104 records: []testValueRecord{{key: "c", identity: "c"}}, 1105 }, 1106 }), 1107 expectedResult: []testRunResult{{ 1108 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1109 expectedActions: []writeAction{ 1110 { 1111 action: actionTypeWriteRecord, 1112 key: "a", 1113 identity: "a", 1114 }, 1115 { 1116 action: actionTypeWriteRange, 1117 rng: committed.Range{ID: "dest:b-b", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1118 }, 1119 { 1120 action: actionTypeWriteRecord, 1121 key: "d", 1122 identity: "d", 1123 }, 1124 }, 1125 expectedErr: nil, 1126 }}, 1127 }, 1128 "source range before dest key": { 1129 baseRange: newTestMetaRange([]testRange{ 1130 { 1131 rng: committed.Range{ID: "base:c-c", MinKey: committed.Key("c"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1132 records: []testValueRecord{{key: "c", identity: "c"}}, 1133 }, 1134 }), 1135 sourceRange: newTestMetaRange([]testRange{ 1136 { 1137 rng: committed.Range{ID: "source:a-a", MinKey: committed.Key("a"), MaxKey: committed.Key("a"), Count: 2, EstimatedSize: 1024}, 1138 records: []testValueRecord{{key: "a", identity: "a"}}, 1139 }, 1140 { 1141 rng: committed.Range{ID: "source:b-b", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1142 records: []testValueRecord{{key: "b", identity: "b"}}, 1143 }, 1144 { 1145 rng: committed.Range{ID: "base:c-c", MinKey: committed.Key("c"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 1024}, 1146 records: []testValueRecord{{key: "c", identity: "c"}}, 1147 }, 1148 }), 1149 destRange: newTestMetaRange([]testRange{ 1150 { 1151 rng: committed.Range{ID: "dest:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1152 records: []testValueRecord{{key: "a", identity: "a"}, {key: "d", identity: "d"}}, 1153 }, 1154 }), 1155 expectedResult: []testRunResult{{ 1156 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1157 expectedActions: []writeAction{ 1158 { 1159 action: actionTypeWriteRecord, 1160 key: "a", 1161 identity: "a", 1162 }, 1163 { 1164 action: actionTypeWriteRange, 1165 rng: committed.Range{ID: "source:b-b", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1166 }, 1167 { 1168 action: actionTypeWriteRecord, 1169 key: "d", 1170 identity: "d", 1171 }, 1172 }, 1173 expectedErr: nil, 1174 }}, 1175 }, 1176 "dest and base are the same": { 1177 baseRange: newTestMetaRange([]testRange{ 1178 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1179 {"k11", "base:k11"}, {"k12", "base:k12"}, 1180 }}, 1181 }), 1182 sourceRange: newTestMetaRange([]testRange{ 1183 {rng: committed.Range{ID: "source:k13-k14", MinKey: committed.Key("k13"), MaxKey: committed.Key("k14"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1184 {"k13", "base:k13"}, {"k14", "base:k14"}, 1185 }}, 1186 }), 1187 destRange: newTestMetaRange([]testRange{ 1188 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1189 {"k11", "base:k11"}, {"k12", "base:k12"}, 1190 }}, 1191 }), 1192 expectedResult: []testRunResult{{ 1193 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1194 expectedActions: []writeAction{}, 1195 }}, 1196 }, 1197 "source and dest are the same": { 1198 baseRange: newTestMetaRange([]testRange{ 1199 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1200 {"k11", "base:k11"}, {"k12", "base:k12"}, 1201 }}, 1202 }), 1203 sourceRange: newTestMetaRange([]testRange{ 1204 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1205 {"k11", "base:k11"}, {"k12", "base:k12"}, 1206 }}, 1207 }), 1208 destRange: newTestMetaRange([]testRange{ 1209 {rng: committed.Range{ID: "base:k11-k12", MinKey: committed.Key("k11"), MaxKey: committed.Key("k12"), Count: 2, EstimatedSize: 4444}, records: []testValueRecord{ 1210 {"k11", "base:k11"}, {"k12", "base:k12"}, 1211 }}, 1212 }), 1213 expectedResult: []testRunResult{{ 1214 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone, graveler.MergeStrategyDest, graveler.MergeStrategySrc}, 1215 expectedActions: []writeAction{}, 1216 expectedErr: graveler.ErrNoChanges, 1217 }}, 1218 }, 1219 } 1220 1221 runMergeTests(tests, t) 1222 } 1223 1224 func TestMergeStrategies(t *testing.T) { 1225 tests := testCases{ 1226 // Base branch has a 2 records range, with the keys 'a' and 'b'. Source branch changes the value on key 'a' and leaves key 'b' unchanged 1227 // Dest branch deletes both entries, creating a conflict on entry 'a' 1228 // As per merge strategies, the expected outcomes are: 1229 // - No strategy - a conflict 1230 // - Dest strategy - favors dest branch, so both records are deleted, 'ignoring' the value modification on record 'a' 1231 // - Source strategy - favors source branch, so record 'a', with the modified value, is written, ignoring its deletion on dest. Record b 1232 // is still deleted as there is no conflict involving it 1233 "dest removed all same key different identity": { 1234 baseRange: newTestMetaRange([]testRange{ 1235 { 1236 rng: committed.Range{ID: "base:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1237 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}}, 1238 }, 1239 }), 1240 sourceRange: newTestMetaRange([]testRange{ 1241 { 1242 rng: committed.Range{ID: "source:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1243 records: []testValueRecord{{key: "a", identity: "a1"}, {key: "b", identity: "b"}}, 1244 }, 1245 }), 1246 destRange: newTestMetaRange([]testRange{}), 1247 expectedResult: []testRunResult{ 1248 { 1249 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1250 expectedActions: []writeAction{}, 1251 expectedErr: graveler.ErrConflictFound, 1252 }, 1253 { 1254 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1255 expectedActions: []writeAction{}, 1256 expectedErr: nil, 1257 }, 1258 { 1259 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1260 expectedActions: []writeAction{ 1261 { 1262 action: actionTypeWriteRecord, 1263 key: "a", 1264 identity: "a1", 1265 }, 1266 }, 1267 expectedErr: nil, 1268 }, 1269 }, 1270 }, 1271 // Base branch has a 5 records range, with the keys 'k1' through 'k5'. Both source and dest branches modify this range by deleting and 1272 // changing this range. Both branches delete records 'k1', 'k2' and 'k5' and while dest branch also deletes entries 'k3' and 'k4', source 1273 // branch leaves record 'k3' unchanged and changes the value on record 'k4', creating a conflict 1274 // Dest branch also adds 2 new records, 'k6' and 'k7', which should not create a conflict 1275 // As per merge strategies, the expected outcomes are: 1276 // - No strategy - a conflict 1277 // - Dest strategy - favors dest branch, so records 'k1' through 'k5' are deleted, 'ignoring' the value modification on record 'k4'. 1278 // - Source strategy - favors source branch, so record 'k4', with the modified value, is written, ignoring its deletion on dest. The rest 1279 // of record 'k1', 'k2' 'k3' and 'k5' are still deleted as there is no conflict involving them. Same goes for 'k6' and 'k7' which are 1280 // written on both 'dest-wins' and 'source-wins' strategies 1281 "source and dest change same range conflict": { 1282 baseRange: newTestMetaRange([]testRange{ 1283 { 1284 rng: committed.Range{ID: "base:k1-k5", MinKey: committed.Key("k1"), MaxKey: committed.Key("k5"), Count: 5, EstimatedSize: 1234}, 1285 records: []testValueRecord{ 1286 {"k1", "base:k1"}, 1287 {"k2", "base:k2"}, 1288 {"k3", "base:k3"}, 1289 {"k4", "base:k4"}, 1290 {"k5", "base:k5"}, 1291 }, 1292 }, 1293 }), 1294 sourceRange: newTestMetaRange([]testRange{ 1295 { 1296 rng: committed.Range{ID: "source:k3-k4", MinKey: committed.Key("k3"), MaxKey: committed.Key("k4"), Count: 2, EstimatedSize: 1234}, 1297 records: []testValueRecord{ 1298 {"k3", "base:k3"}, {"k4", "source:k4"}, 1299 }, 1300 }, 1301 }), 1302 destRange: newTestMetaRange([]testRange{ 1303 { 1304 rng: committed.Range{ID: "dest:k6-k7", MinKey: committed.Key("k6"), MaxKey: committed.Key("k7"), Count: 2, EstimatedSize: 1234}, 1305 records: []testValueRecord{ 1306 {"k6", "dest:k6"}, {"k7", "dest:k7"}, 1307 }, 1308 }, 1309 }), 1310 expectedResult: []testRunResult{ 1311 { 1312 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1313 expectedActions: []writeAction{}, 1314 expectedErr: graveler.ErrConflictFound, 1315 }, 1316 { 1317 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1318 expectedActions: []writeAction{ 1319 { 1320 action: actionTypeWriteRecord, 1321 key: "k6", 1322 identity: "dest:k6", 1323 }, 1324 { 1325 action: actionTypeWriteRecord, 1326 key: "k7", 1327 identity: "dest:k7", 1328 }, 1329 }, 1330 expectedErr: nil, 1331 }, 1332 { 1333 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1334 expectedActions: []writeAction{ 1335 { 1336 action: actionTypeWriteRecord, 1337 key: "k4", 1338 identity: "source:k4", 1339 }, 1340 { 1341 action: actionTypeWriteRecord, 1342 key: "k6", 1343 identity: "dest:k6", 1344 }, 1345 { 1346 action: actionTypeWriteRecord, 1347 key: "k7", 1348 identity: "dest:k7", 1349 }, 1350 }, 1351 expectedErr: nil, 1352 }, 1353 }, 1354 }, 1355 // Base branch has a 2 records range, with the keys 'a' and 'b'. Source branch deletes both records and adds 2 new records - 'c' and 'd' 1356 // Dest branch changes the value on key 'a', creating a conflict with the deleted 'a' record on source, and leaves key 'b' unchanged 1357 // As per merge strategies, the expected outcomes are: 1358 // - No strategy - a conflict 1359 // - Dest strategy - favors dest branch, so record 'a', with the modified value, is written, ignoring its deletion on source 1360 // - Source strategy - favors source branch, so record 'a' is deleted, ignoring its value change on branch dest 1361 // Record 'b' is deleted for both strategies as there is no conflict involving it. Same goes for records 'c' and 'd' that are written, 1362 // regardless of merge strategy 1363 "dest range before source": { 1364 baseRange: newTestMetaRange([]testRange{ 1365 { 1366 rng: committed.Range{ID: "base:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1367 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}}, 1368 }, 1369 }), 1370 sourceRange: newTestMetaRange([]testRange{ 1371 { 1372 rng: committed.Range{ID: "source:c-d", MinKey: committed.Key("c"), MaxKey: committed.Key("d"), Count: 2, EstimatedSize: 1024}, 1373 records: []testValueRecord{{key: "c", identity: "c"}, {key: "d", identity: "d"}}, 1374 }, 1375 }), 1376 destRange: newTestMetaRange([]testRange{ 1377 { 1378 rng: committed.Range{ID: "dest:a-b", MinKey: committed.Key("a"), MaxKey: committed.Key("b"), Count: 2, EstimatedSize: 1024}, 1379 records: []testValueRecord{{key: "a", identity: "a1"}, {key: "b", identity: "b"}}, 1380 }, 1381 }), 1382 expectedResult: []testRunResult{ 1383 { 1384 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1385 expectedActions: []writeAction{}, 1386 expectedErr: graveler.ErrConflictFound, 1387 }, 1388 { 1389 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1390 expectedActions: []writeAction{ 1391 { 1392 action: actionTypeWriteRecord, 1393 key: "a", 1394 identity: "a1", 1395 }, 1396 { 1397 action: actionTypeWriteRecord, 1398 key: "c", 1399 identity: "c", 1400 }, 1401 { 1402 action: actionTypeWriteRecord, 1403 key: "d", 1404 identity: "d", 1405 }, 1406 }, 1407 expectedErr: nil, 1408 }, 1409 { 1410 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1411 expectedActions: []writeAction{ 1412 { 1413 action: actionTypeWriteRecord, 1414 key: "c", 1415 identity: "c", 1416 }, 1417 { 1418 action: actionTypeWriteRecord, 1419 key: "d", 1420 identity: "d", 1421 }, 1422 }, 1423 expectedErr: nil, 1424 }, 1425 }, 1426 }, 1427 "source change and dest delete same entry": { 1428 baseRange: newTestMetaRange([]testRange{{ 1429 rng: committed.Range{ID: "base:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 333}, 1430 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}}, 1431 }}), 1432 sourceRange: newTestMetaRange([]testRange{{ 1433 rng: committed.Range{ID: "source:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 123}, 1434 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b1"}, {key: "c", identity: "c"}}, 1435 }}), 1436 destRange: newTestMetaRange([]testRange{{ 1437 rng: committed.Range{ID: "dest:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 321}, 1438 records: []testValueRecord{{key: "a", identity: "a"}, {key: "c", identity: "c"}}, 1439 }}), 1440 expectedResult: []testRunResult{ 1441 { 1442 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1443 expectedActions: []writeAction{ 1444 { 1445 action: actionTypeWriteRecord, 1446 key: "a", 1447 identity: "a", 1448 }, 1449 }, 1450 expectedErr: graveler.ErrConflictFound, 1451 }, 1452 { 1453 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1454 expectedActions: []writeAction{ 1455 { 1456 action: actionTypeWriteRecord, 1457 key: "a", 1458 identity: "a", 1459 }, 1460 { 1461 action: actionTypeWriteRecord, 1462 key: "c", 1463 identity: "c", 1464 }, 1465 }, 1466 expectedErr: nil, 1467 }, 1468 { 1469 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1470 expectedActions: []writeAction{ 1471 { 1472 action: actionTypeWriteRecord, 1473 key: "a", 1474 identity: "a", 1475 }, 1476 { 1477 action: actionTypeWriteRecord, 1478 key: "b", 1479 identity: "b1", 1480 }, 1481 { 1482 action: actionTypeWriteRecord, 1483 key: "c", 1484 identity: "c", 1485 }, 1486 }, 1487 expectedErr: nil, 1488 }, 1489 }, 1490 }, 1491 1492 "source delete and dest change same entry": { 1493 baseRange: newTestMetaRange([]testRange{{ 1494 rng: committed.Range{ID: "base:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 333}, 1495 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}}, 1496 }}), 1497 sourceRange: newTestMetaRange([]testRange{{ 1498 rng: committed.Range{ID: "source:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 2, EstimatedSize: 123}, 1499 records: []testValueRecord{{key: "a", identity: "a"}, {key: "c", identity: "c"}}, 1500 }}), 1501 destRange: newTestMetaRange([]testRange{{ 1502 rng: committed.Range{ID: "dest:a-c", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 321}, 1503 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b1"}, {key: "c", identity: "c"}}, 1504 }}), 1505 expectedResult: []testRunResult{ 1506 { 1507 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1508 expectedActions: []writeAction{ 1509 { 1510 action: actionTypeWriteRecord, 1511 key: "a", 1512 identity: "a", 1513 }, 1514 }, 1515 expectedErr: graveler.ErrConflictFound, 1516 }, 1517 { 1518 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1519 expectedActions: []writeAction{ 1520 { 1521 action: actionTypeWriteRecord, 1522 key: "a", 1523 identity: "a", 1524 }, 1525 { 1526 action: actionTypeWriteRecord, 1527 key: "b", 1528 identity: "b1", 1529 }, 1530 { 1531 action: actionTypeWriteRecord, 1532 key: "c", 1533 identity: "c", 1534 }, 1535 }, 1536 expectedErr: nil, 1537 }, 1538 { 1539 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1540 expectedActions: []writeAction{ 1541 { 1542 action: actionTypeWriteRecord, 1543 key: "a", 1544 identity: "a", 1545 }, 1546 { 1547 action: actionTypeWriteRecord, 1548 key: "c", 1549 identity: "c", 1550 }, 1551 }, 1552 expectedErr: nil, 1553 }, 1554 }, 1555 }, 1556 1557 "source and dest delete from same range": { 1558 baseRange: newTestMetaRange([]testRange{{ 1559 rng: committed.Range{ID: "base:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 4, EstimatedSize: 4444}, 1560 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}, {key: "d", identity: "d"}}, 1561 }}), 1562 sourceRange: newTestMetaRange([]testRange{{ 1563 rng: committed.Range{ID: "source:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 3, EstimatedSize: 1234}, 1564 records: []testValueRecord{{key: "a", identity: "a"}, {key: "c", identity: "c1"}, {key: "d", identity: "d"}}, 1565 }}), 1566 destRange: newTestMetaRange([]testRange{{ 1567 rng: committed.Range{ID: "dest:a-d", MinKey: committed.Key("a"), MaxKey: committed.Key("d"), Count: 3, EstimatedSize: 4321}, 1568 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b1"}, {key: "d", identity: "d"}}, 1569 }}), 1570 expectedResult: []testRunResult{ 1571 { 1572 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1573 expectedActions: []writeAction{ 1574 { 1575 action: actionTypeWriteRecord, 1576 key: "a", 1577 identity: "a", 1578 }, 1579 }, 1580 expectedErr: graveler.ErrConflictFound, 1581 }, 1582 { 1583 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1584 expectedActions: []writeAction{ 1585 { 1586 action: actionTypeWriteRecord, 1587 key: "a", 1588 identity: "a", 1589 }, 1590 { 1591 action: actionTypeWriteRecord, 1592 key: "b", 1593 identity: "b1", 1594 }, 1595 { 1596 action: actionTypeWriteRecord, 1597 key: "d", 1598 identity: "d", 1599 }, 1600 }, 1601 expectedErr: nil, 1602 }, 1603 { 1604 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1605 expectedActions: []writeAction{ 1606 { 1607 action: actionTypeWriteRecord, 1608 key: "a", 1609 identity: "a", 1610 }, 1611 { 1612 action: actionTypeWriteRecord, 1613 key: "c", 1614 identity: "c1", 1615 }, 1616 { 1617 action: actionTypeWriteRecord, 1618 key: "d", 1619 identity: "d", 1620 }, 1621 }, 1622 expectedErr: nil, 1623 }, 1624 }, 1625 }, 1626 "source and dest change same entry": { 1627 baseRange: newTestMetaRange([]testRange{{ 1628 rng: committed.Range{ID: "base:a-a", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 1234}, 1629 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b"}, {key: "c", identity: "c"}}, 1630 }}), 1631 sourceRange: newTestMetaRange([]testRange{{ 1632 rng: committed.Range{ID: "source:a-a", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 1234}, 1633 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b1"}, {key: "c", identity: "c"}}, 1634 }}), 1635 destRange: newTestMetaRange([]testRange{{ 1636 rng: committed.Range{ID: "dest:a-a", MinKey: committed.Key("a"), MaxKey: committed.Key("c"), Count: 3, EstimatedSize: 1234}, 1637 records: []testValueRecord{{key: "a", identity: "a"}, {key: "b", identity: "b2"}, {key: "c", identity: "c"}}, 1638 }}), 1639 expectedResult: []testRunResult{ 1640 { 1641 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyNone}, 1642 expectedActions: []writeAction{ 1643 { 1644 action: actionTypeWriteRecord, 1645 key: "a", 1646 identity: "a", 1647 }, 1648 }, 1649 expectedErr: graveler.ErrConflictFound, 1650 }, 1651 { 1652 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategyDest}, 1653 expectedActions: []writeAction{ 1654 { 1655 action: actionTypeWriteRecord, 1656 key: "a", 1657 identity: "a", 1658 }, 1659 { 1660 action: actionTypeWriteRecord, 1661 key: "b", 1662 identity: "b2", 1663 }, 1664 { 1665 action: actionTypeWriteRecord, 1666 key: "c", 1667 identity: "c", 1668 }, 1669 }, 1670 expectedErr: nil, 1671 }, 1672 { 1673 mergeStrategies: []graveler.MergeStrategy{graveler.MergeStrategySrc}, 1674 expectedActions: []writeAction{ 1675 { 1676 action: actionTypeWriteRecord, 1677 key: "a", 1678 identity: "a", 1679 }, 1680 { 1681 action: actionTypeWriteRecord, 1682 key: "b", 1683 identity: "b1", 1684 }, 1685 { 1686 action: actionTypeWriteRecord, 1687 key: "c", 1688 identity: "c", 1689 }, 1690 }, 1691 expectedErr: nil, 1692 }, 1693 }, 1694 }, 1695 } 1696 1697 runMergeTests(tests, t) 1698 } 1699 1700 func runMergeTests(tests testCases, t *testing.T) { 1701 for name, tst := range tests { 1702 for _, expectedResult := range tst.expectedResult { 1703 for _, mergeStrategy := range expectedResult.mergeStrategies { 1704 t.Run(name, func(t *testing.T) { 1705 ctrl := gomock.NewController(t) 1706 defer ctrl.Finish() 1707 ctx := context.Background() 1708 writer := mock.NewMockMetaRangeWriter(ctrl) 1709 for _, action := range expectedResult.expectedActions { 1710 switch action.action { 1711 case actionTypeWriteRecord: 1712 writer.EXPECT().WriteRecord(newRecordMatcher(action.key, action.identity)) 1713 case actionTypeWriteRange: 1714 writer.EXPECT().WriteRange(gomock.Eq(action.rng)) 1715 } 1716 } 1717 metaRangeManager := mock.NewMockMetaRangeManager(ctrl) 1718 metaRangeManager.EXPECT().NewWriter(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(writer) 1719 sourceMetaRangeID := tst.sourceRange.GetMetaRangeID() 1720 destMetaRangeID := tst.destRange.GetMetaRangeID() 1721 baseMetaRangeID := tst.baseRange.GetMetaRangeID() 1722 metaRangeManager.EXPECT().NewMetaRangeIterator(gomock.Any(), gomock.Any(), baseMetaRangeID).AnyTimes().Return(createIter(tst.baseRange), nil) 1723 metaRangeManager.EXPECT().NewMetaRangeIterator(gomock.Any(), gomock.Any(), sourceMetaRangeID).AnyTimes().Return(createIter(tst.sourceRange), nil) 1724 metaRangeManager.EXPECT().NewMetaRangeIterator(gomock.Any(), gomock.Any(), destMetaRangeID).AnyTimes().Return(createIter(tst.destRange), nil) 1725 1726 rangeManager := mock.NewMockRangeManager(ctrl) 1727 1728 writer.EXPECT().Abort().AnyTimes() 1729 metaRangeId := graveler.MetaRangeID("merge") 1730 writer.EXPECT().Close(gomock.Any()).Return(&metaRangeId, nil).AnyTimes() 1731 committedManager := committed.NewCommittedManager(metaRangeManager, rangeManager, params) 1732 _, err := committedManager.Merge(ctx, "ns", destMetaRangeID, sourceMetaRangeID, baseMetaRangeID, mergeStrategy) 1733 if !errors.Is(err, expectedResult.expectedErr) { 1734 t.Fatalf("Merge error='%v', expected='%v'", err, expectedResult.expectedErr) 1735 } 1736 }) 1737 } 1738 } 1739 } 1740 } 1741 1742 func TestMergeCancelContext(t *testing.T) { 1743 ctrl := gomock.NewController(t) 1744 defer ctrl.Finish() 1745 1746 t.Run("source", func(t *testing.T) { 1747 base := testutil.NewFakeIterator(). 1748 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 1}). 1749 AddValueRecords(makeV("b", "dest:b")) 1750 source := testutil.NewFakeIterator() 1751 destination := testutil.NewFakeIterator(). 1752 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 1}). 1753 AddValueRecords(makeV("b", "dest:b")) 1754 writer := mock.NewMockMetaRangeWriter(ctrl) 1755 ctx, cancel := context.WithCancel(context.Background()) 1756 cancel() 1757 err := committed.Merge(ctx, writer, base, source, destination, graveler.MergeStrategyNone) 1758 assert.True(t, errors.Is(err, context.Canceled), "context canceled error") 1759 }) 1760 1761 t.Run("destination", func(t *testing.T) { 1762 base := testutil.NewFakeIterator(). 1763 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("a"), MaxKey: committed.Key("cz"), Count: 2}). 1764 AddValueRecords(makeV("a", "base:a"), makeV("c", "base:c")) 1765 source := testutil.NewFakeIterator(). 1766 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("a"), MaxKey: committed.Key("cz"), Count: 2}). 1767 AddValueRecords(makeV("a", "base:a"), makeV("c", "base:c")) 1768 destination := testutil.NewFakeIterator() 1769 writer := mock.NewMockMetaRangeWriter(ctrl) 1770 ctx, cancel := context.WithCancel(context.Background()) 1771 cancel() 1772 err := committed.Merge(ctx, writer, base, source, destination, graveler.MergeStrategyNone) 1773 assert.True(t, errors.Is(err, context.Canceled), "context canceled error") 1774 }) 1775 1776 t.Run("source_and_destination", func(t *testing.T) { 1777 base := testutil.NewFakeIterator(). 1778 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("a"), MaxKey: committed.Key("cz"), Count: 3}). 1779 AddValueRecords(makeV("a", "base:a"), makeV("c", "base:c")) 1780 source := testutil.NewFakeIterator(). 1781 AddRange(&committed.Range{ID: "two", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2}). 1782 AddValueRecords(makeV("e", "source:e"), makeV("f", "source:f")) 1783 destination := testutil.NewFakeIterator(). 1784 AddRange(&committed.Range{ID: "three", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 1}). 1785 AddValueRecords(makeV("b", "dest:b")) 1786 writer := mock.NewMockMetaRangeWriter(ctrl) 1787 ctx, cancel := context.WithCancel(context.Background()) 1788 cancel() 1789 err := committed.Merge(ctx, writer, base, source, destination, graveler.MergeStrategyNone) 1790 assert.True(t, errors.Is(err, context.Canceled), "context canceled error") 1791 }) 1792 1793 t.Run("base", func(t *testing.T) { 1794 base := testutil.NewFakeIterator() 1795 source := testutil.NewFakeIterator(). 1796 AddRange(&committed.Range{ID: "two", MinKey: committed.Key("e"), MaxKey: committed.Key("f"), Count: 2}). 1797 AddValueRecords(makeV("e", "source:e"), makeV("f", "source:f")) 1798 destination := testutil.NewFakeIterator(). 1799 AddRange(&committed.Range{ID: "one", MinKey: committed.Key("b"), MaxKey: committed.Key("b"), Count: 1}). 1800 AddValueRecords(makeV("b", "dest:b")) 1801 writer := mock.NewMockMetaRangeWriter(ctrl) 1802 ctx, cancel := context.WithCancel(context.Background()) 1803 cancel() 1804 err := committed.Merge(ctx, writer, base, source, destination, graveler.MergeStrategyNone) 1805 assert.True(t, errors.Is(err, context.Canceled), "context canceled error") 1806 }) 1807 }