github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/staging/manager_test.go (about) 1 package staging_test 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 "github.com/treeverse/lakefs/pkg/graveler" 12 "github.com/treeverse/lakefs/pkg/graveler/staging" 13 "github.com/treeverse/lakefs/pkg/kv" 14 "github.com/treeverse/lakefs/pkg/kv/kvtest" 15 "github.com/treeverse/lakefs/pkg/testutil" 16 "go.uber.org/ratelimit" 17 ) 18 19 func newTestStagingManager(t *testing.T) (context.Context, graveler.StagingManager) { 20 t.Helper() 21 ctx := context.Background() 22 store := kvtest.GetStore(ctx, t) 23 return ctx, staging.NewManager(ctx, store, kv.NewStoreLimiter(store, ratelimit.NewUnlimited()), false, nil) 24 } 25 26 func TestUpdate(t *testing.T) { 27 ctx, s := newTestStagingManager(t) 28 29 key := "a/b/c/my-key-1234" 30 testVal := newTestValue("identity1", "value1") 31 testVal2 := newTestValue("identity2", "value2") 32 // update missing key 33 err := s.Update(ctx, "t1", []byte(key), func(value *graveler.Value) (*graveler.Value, error) { 34 require.Nil(t, value) 35 return testVal, nil 36 }) 37 require.NoError(t, err) 38 39 // update existing key 40 err = s.Update(ctx, "t1", []byte(key), func(value *graveler.Value) (*graveler.Value, error) { 41 require.Equal(t, testVal, value) 42 return testVal2, nil 43 }) 44 require.NoError(t, err) 45 46 // get value 47 val, err := s.Get(ctx, "t1", []byte(key)) 48 require.NoError(t, err) 49 require.Equal(t, testVal2, val) 50 51 // update with error 52 err = s.Update(ctx, "t1", []byte(key), func(value *graveler.Value) (*graveler.Value, error) { 53 require.Equal(t, testVal2, value) 54 return testVal, graveler.ErrNotUnique 55 }) 56 require.ErrorIs(t, err, graveler.ErrNotUnique) 57 58 // get value - see it didn't update after an error 59 val, err = s.Get(ctx, "t1", []byte(key)) 60 require.NoError(t, err) 61 require.Equal(t, testVal2, val) 62 63 // update without set new value 64 err = s.Update(ctx, "t1", []byte(key), func(value *graveler.Value) (*graveler.Value, error) { 65 require.Equal(t, testVal2, value) 66 return nil, graveler.ErrSkipValueUpdate 67 }) 68 require.NoError(t, err) 69 // verify that we didn't update the value 70 val, err = s.Get(ctx, "t1", []byte(key)) 71 require.NoError(t, err) 72 require.Equal(t, testVal2, val) 73 } 74 75 func TestSetGet(t *testing.T) { 76 ctx, s := newTestStagingManager(t) 77 _, err := s.Get(ctx, "t1", []byte("a/b/c/")) 78 if !errors.Is(err, graveler.ErrNotFound) { 79 t.Fatalf("error different than expected. expected=%v, got=%v", graveler.ErrNotFound, err) 80 } 81 value := newTestValue("identity1", "value1") 82 err = s.Set(ctx, "t1", []byte("a/b/c/"), value, false) 83 testutil.Must(t, err) 84 e, err := s.Get(ctx, "t1", []byte("a/b/c/")) 85 testutil.Must(t, err) 86 if string(e.Identity) != "identity1" { 87 t.Errorf("got wrong value. expected=%s, got=%s", "identity1", string(e.Identity)) 88 } 89 } 90 91 func TestMultiToken(t *testing.T) { 92 ctx, s := newTestStagingManager(t) 93 _, err := s.Get(ctx, "t1", []byte("a/b/c/")) 94 if !errors.Is(err, graveler.ErrNotFound) { 95 t.Fatalf("error different than expected. expected=%v, got=%v", graveler.ErrNotFound, err) 96 } 97 err = s.Set(ctx, "t1", []byte("a/b/c/"), newTestValue("identity1", "value1"), false) 98 testutil.Must(t, err) 99 e, err := s.Get(ctx, "t1", []byte("a/b/c/")) 100 testutil.Must(t, err) 101 if string(e.Identity) != "identity1" { 102 t.Errorf("got wrong identity. expected=%s, got=%s", "identity1", string(e.Identity)) 103 } 104 err = s.Set(ctx, "t2", []byte("a/b/c/"), newTestValue("identity2", "value2"), false) 105 testutil.Must(t, err) 106 e, err = s.Get(ctx, "t1", []byte("a/b/c/")) 107 testutil.Must(t, err) 108 if string(e.Identity) != "identity1" { 109 t.Errorf("got wrong value identity. expected=%s, got=%s", "identity1", string(e.Identity)) 110 } 111 e, err = s.Get(ctx, "t2", []byte("a/b/c/")) 112 testutil.Must(t, err) 113 if string(e.Identity) != "identity2" { 114 t.Errorf("got wrong value identity. expected=%s, got=%s", "identity2", string(e.Identity)) 115 t.Errorf("got wrong value identity. expected=%s, got=%s", "identity2", string(e.Identity)) 116 } 117 } 118 119 func TestDrop(t *testing.T) { 120 ctx, s := newTestStagingManager(t) 121 numOfValues := 1400 122 setupDrop(ctx, t, numOfValues, s) 123 err := s.Drop(ctx, "t1") 124 testutil.Must(t, err) 125 v, err := s.Get(ctx, "t1", []byte("key0000")) 126 if !errors.Is(err, graveler.ErrNotFound) { 127 t.Fatalf("after dropping staging area, expected ErrNotFound in Get. got err=%v, got value=%v", err, v) 128 } 129 it := s.List(ctx, "t1", 0) 130 if it.Next() { 131 t.Fatal("expected staging area with token t1 to be empty, got non-empty iterator") 132 } 133 it.Close() 134 it = s.List(ctx, "t2", 0) 135 count := 0 136 for it.Next() { 137 if string(it.Value().Data) != fmt.Sprintf("value%d", count) { 138 t.Fatalf("unexpected value returned from List at index %d. expected=%s, got=%s", count, fmt.Sprintf("value%d", count), string(it.Value().Data)) 139 } 140 count++ 141 } 142 it.Close() 143 if count != numOfValues { 144 t.Errorf("got unexpected number of results. expected=%d, got=%d", numOfValues, count) 145 } 146 } 147 148 func setupDrop(ctx context.Context, t *testing.T, numOfValues int, s graveler.StagingManager) { 149 for i := 0; i < numOfValues; i++ { 150 err := s.Set(ctx, "t1", []byte(fmt.Sprintf("key%04d", i)), newTestValue(fmt.Sprintf("identity%d", i), fmt.Sprintf("value%d", i)), false) 151 testutil.Must(t, err) 152 err = s.Set(ctx, "t2", []byte(fmt.Sprintf("key%04d", i)), newTestValue(fmt.Sprintf("identity%d", i), fmt.Sprintf("value%d", i)), false) 153 testutil.Must(t, err) 154 } 155 } 156 157 func TestDropAsync(t *testing.T) { 158 ctx := context.Background() 159 store := kvtest.GetStore(ctx, t) 160 ch := make(chan bool) 161 s := staging.NewManager(ctx, store, kv.NewStoreLimiter(store, ratelimit.NewUnlimited()), false, nil) 162 s.OnCleanup(func() { 163 close(ch) 164 }) 165 166 numOfValues := 1400 167 setupDrop(ctx, t, numOfValues, s) 168 169 err := s.DropAsync(ctx, "t1") 170 require.NoError(t, err) 171 172 // wait for async cleanup to end 173 <-ch 174 it := s.List(ctx, "t1", 0) 175 if it.Next() { 176 t.Fatal("expected staging area with token t1 to be empty, got non-empty iterator") 177 } 178 it.Close() 179 } 180 181 func TestDropByPrefix(t *testing.T) { 182 ctx, s := newTestStagingManager(t) 183 numOfValues := 2400 184 setupDrop(ctx, t, numOfValues, s) 185 186 err := s.DropByPrefix(ctx, "t1", []byte("key1")) 187 testutil.Must(t, err) 188 v, err := s.Get(ctx, "t1", []byte("key1000")) 189 if !errors.Is(err, graveler.ErrNotFound) { 190 // key1000 starts with the deleted prefix - should have been deleted 191 t.Fatalf("after dropping staging area, expected ErrNotFound in Get. got err=%v, got value=%s", err, v) 192 } 193 _, err = s.Get(ctx, "t1", []byte("key0000")) 194 // key0000 does not start with the deleted prefix - should be returned 195 testutil.Must(t, err) 196 it := s.List(ctx, "t1", 0) 197 count := 0 198 for it.Next() { 199 count++ 200 } 201 it.Close() 202 if count != numOfValues-1000 { 203 t.Errorf("got unexpected number of results after drop. expected=%d, got=%d", numOfValues-1000, count) 204 } 205 it = s.List(ctx, "t2", 0) 206 count = 0 207 for it.Next() { 208 count++ 209 } 210 it.Close() 211 if count != numOfValues { 212 t.Errorf("got unexpected number of results. expected=%d, got=%d", numOfValues, count) 213 } 214 } 215 216 func TestDropPrefixBytes(t *testing.T) { 217 ctx, s := newTestStagingManager(t) 218 tests := map[string]struct { 219 keys []graveler.Key 220 prefix graveler.Key 221 expectedLengthAfterDrop int 222 }{ 223 "prefix with all bytes=MaxUint8": { 224 keys: []graveler.Key{{255, 255, 254, 254}, {255, 255, 254, 255}, {255, 255, 255, 253}, {255, 255, 255, 254}, {255, 255, 255, 255}}, 225 prefix: graveler.Key{255, 255, 255}, 226 expectedLengthAfterDrop: 2, 227 }, 228 "all zero prefix": { 229 keys: []graveler.Key{{0, 0, 0, 0}, {0, 0, 0, 255}, {0, 0, 1, 0}, {0, 0, 1, 1}}, 230 prefix: graveler.Key{0, 0, 0}, 231 expectedLengthAfterDrop: 2, 232 }, 233 "prefix common to all keys": { 234 keys: []graveler.Key{{0, 0, 0, 0}, {0, 0, 0, 255}, {0, 0, 1, 0}, {0, 0, 1, 1}}, 235 prefix: graveler.Key{0, 0}, 236 expectedLengthAfterDrop: 0, 237 }, 238 "axUint8 in keys - prefix length 1": { 239 keys: []graveler.Key{{1, 0, 0, 0}, {1, 0, 0, 255}, {1, 0, 255, 255}, {1, 255, 255, 255}}, 240 prefix: graveler.Key{1}, 241 expectedLengthAfterDrop: 0, 242 }, 243 "MaxUint8 in keys - prefix length 2": { 244 keys: []graveler.Key{{1, 0, 0, 0}, {1, 0, 0, 255}, {1, 0, 255, 255}, {1, 255, 255, 255}}, 245 prefix: graveler.Key{1, 0}, 246 expectedLengthAfterDrop: 1, 247 }, 248 "MaxUint8 in keys - prefix length 3": { 249 keys: []graveler.Key{{1, 0, 0, 0}, {1, 0, 0, 255}, {1, 0, 255, 255}, {1, 255, 255, 255}}, 250 prefix: graveler.Key{1, 0, 0}, 251 expectedLengthAfterDrop: 2, 252 }, 253 "MaxUint8 in keys - prefix length 4": { 254 keys: []graveler.Key{{1, 0, 0, 0}, {1, 0, 0, 255}, {1, 0, 255, 255}, {1, 255, 255, 255}}, 255 prefix: graveler.Key{1, 0, 0, 0}, 256 expectedLengthAfterDrop: 3, 257 }, 258 "multi-length keys - prefix length 3": { 259 keys: []graveler.Key{{1, 0}, {1, 1}, {1, 0, 1}, {1, 1, 1}, {1, 1, 1, 255}, {1, 1, 255, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 255, 1, 1, 1, 1, 1, 1, 1, 1}}, 260 prefix: graveler.Key{1, 1, 1}, 261 expectedLengthAfterDrop: 4, 262 }, 263 "multi-length keys - prefix length 4": { 264 keys: []graveler.Key{{1, 0}, {1, 1}, {1, 0, 1}, {1, 1, 1}, {1, 1, 1, 255}, {1, 1, 255, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 255, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 255, 255, 255, 255}}, 265 prefix: graveler.Key{1, 1, 1, 1}, 266 expectedLengthAfterDrop: 7, 267 }, 268 "empty prefix": { 269 keys: []graveler.Key{{1, 0}, {1, 1}, {1, 0, 1}, {1, 1, 1}, {1, 1, 1, 255}, {1, 1, 255, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 255, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 255, 255, 255, 255}, {2, 0}}, 270 prefix: graveler.Key{}, 271 expectedLengthAfterDrop: 0, 272 }, 273 "multi-length keys - prefix with MaxUint 8": { 274 keys: []graveler.Key{{1, 0}, {1, 1}, {1, 0, 1}, {1, 1, 1}, {1, 1, 1, 255}, {1, 1, 255, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 255, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 255, 255, 255, 255}, {2, 0}}, 275 prefix: graveler.Key{0, 255, 255, 255}, 276 expectedLengthAfterDrop: 10, 277 }, 278 "multi-length keys - prefix with MaxUint 8 - prefix length 2": { 279 keys: []graveler.Key{{1, 0}, {1, 1}, {1, 0, 1}, {1, 1, 1}, {1, 1, 1, 255}, {1, 1, 255, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 255, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 255, 255, 255, 255}, {2, 0}}, 280 prefix: graveler.Key{1, 255}, 281 expectedLengthAfterDrop: 10, 282 }, 283 "multi-length keys - prefix with MaxUint 8 - prefix length 3": { 284 keys: []graveler.Key{{1, 254, 255, 255}, {1, 255}, {1, 255, 255}, {1, 255, 255, 255}, {2, 255}}, 285 prefix: graveler.Key{1, 255, 255}, 286 expectedLengthAfterDrop: 3, 287 }, 288 } 289 for name, tst := range tests { 290 st := graveler.StagingToken(fmt.Sprintf("t_%s", name)) 291 t.Run(name, func(t *testing.T) { 292 for _, k := range tst.keys { 293 err := s.Set(ctx, st, k, &graveler.Value{ 294 Identity: []byte{0, 0, 0, 0, 0, 0}, 295 Data: []byte{0, 0, 0, 0, 0, 0}, 296 }, false) 297 testutil.Must(t, err) 298 } 299 err := s.DropByPrefix(ctx, st, tst.prefix) 300 testutil.Must(t, err) 301 it := s.List(ctx, st, 0) 302 testutil.Must(t, err) 303 count := 0 304 for it.Next() { 305 count++ 306 } 307 if count != tst.expectedLengthAfterDrop { 308 t.Fatalf("unexpected number of values after drop. expected=%d, got=%d", tst.expectedLengthAfterDrop, count) 309 } 310 if it.Err() != nil { 311 t.Fatalf("got unexpected error: %v", it.Err()) 312 } 313 it.Close() 314 }) 315 } 316 } 317 318 func TestList(t *testing.T) { 319 ctx, s := newTestStagingManager(t) 320 for _, numOfValues := range []int{1, 100, 1000, 1500, 2500} { 321 token := graveler.StagingToken(fmt.Sprintf("t_%d", numOfValues)) 322 for i := 0; i < numOfValues; i++ { 323 err := s.Set(ctx, token, []byte(fmt.Sprintf("key%04d", i)), newTestValue(fmt.Sprintf("identity%d", i), fmt.Sprintf("value%d", i)), false) 324 testutil.Must(t, err) 325 } 326 res := make([]*graveler.ValueRecord, 0, numOfValues) 327 it := s.List(ctx, token, 0) 328 for it.Next() { 329 res = append(res, it.Value()) 330 } 331 if it.Err() != nil { 332 t.Fatalf("got unexpected error from list: %v", it.Err()) 333 } 334 it.Close() 335 if len(res) != numOfValues { 336 t.Errorf("got unexpected number of results. expected=%d, got=%d", numOfValues, len(res)) 337 } 338 for i, e := range res { 339 if !bytes.Equal(e.Key, []byte(fmt.Sprintf("key%04d", i))) { 340 t.Fatalf("got unexpected key from List at index %d: expected: key%04d, got: %s", i, i, string(e.Key)) 341 } 342 if string(e.Data) != fmt.Sprintf("value%d", i) { 343 t.Fatalf("unexpected value returned from List at index %d. expected=%s, got=%s", i, fmt.Sprintf("value%d", i), string(e.Data)) 344 } 345 } 346 } 347 } 348 349 func TestSeek(t *testing.T) { 350 ctx, s := newTestStagingManager(t) 351 numOfValues := 100 352 for i := 0; i < numOfValues; i++ { 353 err := s.Set(ctx, "t1", []byte(fmt.Sprintf("key%04d", i)), newTestValue("identity1", "value1"), false) 354 testutil.Must(t, err) 355 } 356 it := s.List(ctx, "t1", 0) 357 defer it.Close() 358 if it.SeekGE([]byte("key0050")); !it.Next() { 359 t.Fatal("iterator seek expected to return true, got false") 360 } 361 expected := "key0050" 362 if !bytes.Equal(it.Value().Key, []byte("key0050")) { 363 t.Fatalf("got unexpected key after iterator seek. expected=%s, got=%s", expected, string(it.Value().Key)) 364 } 365 if !it.Next() { 366 t.Fatal("iterator next expected to return true, got false") 367 } 368 expected = "key0051" 369 if !bytes.Equal(it.Value().Key, []byte(expected)) { 370 t.Fatalf("got unexpected key after iterator seek. expected=%s, got=%s", expected, string(it.Value().Key)) 371 } 372 if it.SeekGE([]byte("key1000")); it.Next() { 373 t.Fatal("iterator seek expected to return false, got true") 374 } 375 if it.SeekGE([]byte("key0060a")); !it.Next() { 376 t.Fatal("iterator seek expected to return true, got false") 377 } 378 expected = "key0061" 379 if !bytes.Equal(it.Value().Key, []byte(expected)) { 380 t.Fatalf("got unexpected key after iterator seek. expected=%s, got=%s", expected, string(it.Value().Key)) 381 } 382 if !it.Next() { 383 t.Fatal("iterator next expected to return true, got false") 384 } 385 } 386 387 func TestNilValue(t *testing.T) { 388 ctx, s := newTestStagingManager(t) 389 err := s.Set(ctx, "t1", []byte("key1"), nil, false) 390 testutil.Must(t, err) 391 err = s.Set(ctx, "t1", []byte("key2"), newTestValue("identity2", "value2"), false) 392 testutil.Must(t, err) 393 e, err := s.Get(ctx, "t1", []byte("key1")) 394 testutil.Must(t, err) 395 if e != nil { 396 t.Errorf("got unexpected value. expected=nil, got=%s", e) 397 } 398 it := s.List(ctx, "t1", 0) 399 testutil.Must(t, err) 400 defer it.Close() 401 if !it.Next() { 402 t.Fatalf("expected to get key from list") 403 } 404 405 expected := "key1" 406 if !bytes.Equal(it.Value().Key, []byte(expected)) { 407 t.Errorf("got unexpected key. expected=key1, got=%s", it.Value().Key) 408 } 409 if it.Value().Value != nil { 410 t.Errorf("got unexpected value. expected=nil, got=%s", it.Value().Value) 411 } 412 413 if !it.Next() { 414 t.Fatalf("expected to get key from list") 415 } 416 e = it.Value().Value 417 if string(e.Identity) != "identity2" { 418 t.Errorf("got wrong identity. expected=%s, got=%s", "identity2", string(e.Identity)) 419 } 420 } 421 422 func TestNilIdentity(t *testing.T) { 423 ctx, s := newTestStagingManager(t) 424 err := s.Set(ctx, "t1", []byte("key1"), newTestValue("identity1", "value1"), false) 425 testutil.Must(t, err) 426 err = s.Set(ctx, "t1", []byte("key1"), &graveler.Value{ 427 Identity: nil, 428 Data: []byte("value1"), 429 }, false) 430 if !errors.Is(err, graveler.ErrInvalidValue) { 431 t.Fatalf("got unexpected error. expected=%v, got=%v", graveler.ErrInvalidValue, err) 432 } 433 e, err := s.Get(ctx, "t1", []byte("key1")) 434 testutil.Must(t, err) 435 if string(e.Identity) != "identity1" { 436 t.Errorf("got wrong identity. expected=%s, got=%s", "identity1", string(e.Identity)) 437 } 438 } 439 440 func TestDeleteAndTombstone(t *testing.T) { 441 ctx, s := newTestStagingManager(t) 442 _, err := s.Get(ctx, "t1", []byte("key1")) 443 if !errors.Is(err, graveler.ErrNotFound) { 444 t.Fatalf("error different than expected. expected=%v, got=%v", graveler.ErrNotFound, err) 445 } 446 tombstoneValues := []*graveler.Value{ 447 { 448 Identity: []byte("identity1"), 449 Data: make([]byte, 0), 450 }, 451 { 452 Identity: []byte("identity1"), 453 Data: nil, 454 }, 455 } 456 for _, val := range tombstoneValues { 457 err = s.Set(ctx, "t1", []byte("key1"), val, false) 458 testutil.Must(t, err) 459 e, err := s.Get(ctx, "t1", []byte("key1")) 460 testutil.Must(t, err) 461 if len(e.Data) != 0 { 462 t.Fatalf("expected empty data, got: %v", e.Data) 463 } 464 if string(e.Identity) != "identity1" { 465 t.Fatalf("got unexpected value identity. expected=%s, got=%s", "identity1", string(e.Identity)) 466 } 467 it := s.List(ctx, "t1", 0) 468 testutil.Must(t, err) 469 if !it.Next() { 470 t.Fatalf("expected to get key from list") 471 } 472 if it.Err() != nil { 473 t.Fatalf("unexpected error from iterator: %v", it.Err()) 474 } 475 if len(it.Value().Value.Data) != 0 { 476 t.Fatalf("expected empty value data from iterator, got: %v", it.Value().Value.Data) 477 } 478 it.Close() 479 } 480 err = s.Set(ctx, "t1", []byte("key1"), newTestValue("identity3", "value3"), false) 481 testutil.Must(t, err) 482 e, err := s.Get(ctx, "t1", []byte("key1")) 483 testutil.Must(t, err) 484 if string(e.Identity) != "identity3" { 485 t.Fatalf("got unexpected value identity. expected=%s, got=%s", "identity3", string(e.Identity)) 486 } 487 err = s.DropKey(ctx, "t1", []byte("key1")) 488 testutil.Must(t, err) 489 _, err = s.Get(ctx, "t1", []byte("key1")) 490 if !errors.Is(err, graveler.ErrNotFound) { 491 t.Fatalf("error different than expected. expected=%v, got=%v", graveler.ErrNotFound, err) 492 } 493 } 494 495 func newTestValue(identity, data string) *graveler.Value { 496 return &graveler.Value{ 497 Identity: []byte(identity), 498 Data: []byte(data), 499 } 500 }