github.com/weaviate/weaviate@v1.24.6/usecases/replica/finder_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package replica 13 14 import ( 15 "context" 16 "testing" 17 18 "github.com/go-openapi/strfmt" 19 "github.com/stretchr/testify/assert" 20 "github.com/weaviate/weaviate/entities/additional" 21 "github.com/weaviate/weaviate/entities/models" 22 "github.com/weaviate/weaviate/entities/search" 23 "github.com/weaviate/weaviate/entities/storobj" 24 "github.com/weaviate/weaviate/usecases/objects" 25 ) 26 27 func object(id strfmt.UUID, lastTime int64) *storobj.Object { 28 return &storobj.Object{ 29 Object: models.Object{ 30 ID: id, 31 LastUpdateTimeUnix: lastTime, 32 }, 33 } 34 } 35 36 func replica(id strfmt.UUID, lastTime int64, deleted bool) objects.Replica { 37 x := objects.Replica{ 38 Deleted: deleted, 39 Object: &storobj.Object{ 40 Object: models.Object{ 41 ID: id, 42 LastUpdateTimeUnix: lastTime, 43 }, 44 }, 45 } 46 if !x.Deleted { 47 x.ID = id 48 } 49 return x 50 } 51 52 func TestFinderReplicaNotFound(t *testing.T) { 53 var ( 54 f = newFakeFactory("C1", "S", []string{}) 55 ctx = context.Background() 56 finder = f.newFinder("A") 57 ) 58 _, err := finder.GetOne(ctx, "ONE", "S", "id", nil, additional.Properties{}) 59 assert.ErrorIs(t, err, errReplicas) 60 f.assertLogErrorContains(t, errNoReplicaFound.Error()) 61 62 _, err = finder.Exists(ctx, "ONE", "S", "id") 63 assert.ErrorIs(t, err, errReplicas) 64 f.assertLogErrorContains(t, errNoReplicaFound.Error()) 65 66 finder.CheckConsistency(ctx, All, []*storobj.Object{objectEx("1", 1, "S", "N")}) 67 f.assertLogErrorContains(t, errReplicas.Error()) 68 } 69 70 func TestFinderNodeObject(t *testing.T) { 71 var ( 72 id = strfmt.UUID("123") 73 cls = "C1" 74 shard = "SH1" 75 nodes = []string{"A", "B", "C"} 76 ctx = context.Background() 77 r = objects.Replica{ID: id, Object: object(id, 3)} 78 adds = additional.Properties{} 79 proj = search.SelectProperties{} 80 ) 81 82 t.Run("Unresolved", func(t *testing.T) { 83 f := newFakeFactory("C1", shard, nodes) 84 finder := f.newFinder("A") 85 _, err := finder.NodeObject(ctx, "N", "S", "id", nil, additional.Properties{}) 86 assert.Contains(t, err.Error(), "N") 87 }) 88 89 t.Run("Success", func(t *testing.T) { 90 f := newFakeFactory("C1", shard, nodes) 91 finder := f.newFinder("A") 92 for _, n := range nodes { 93 f.RClient.On("FetchObject", anyVal, n, cls, shard, id, proj, adds).Return(r, nil) 94 } 95 got, err := finder.NodeObject(ctx, nodes[0], shard, id, proj, adds) 96 assert.Nil(t, err) 97 assert.Equal(t, r.Object, got) 98 }) 99 } 100 101 func TestFinderGetOneWithConsistencyLevelALL(t *testing.T) { 102 var ( 103 id = strfmt.UUID("123") 104 cls = "C1" 105 shard = "SH1" 106 nodes = []string{"A", "B", "C"} 107 ctx = context.Background() 108 adds = additional.Properties{} 109 proj = search.SelectProperties{} 110 nilObject *storobj.Object 111 emptyItem = objects.Replica{} 112 ) 113 114 t.Run("AllButOne", func(t *testing.T) { 115 var ( 116 f = newFakeFactory("C1", shard, nodes) 117 finder = f.newFinder("A") 118 digestIDs = []strfmt.UUID{id} 119 item = objects.Replica{ID: id, Object: object(id, 3)} 120 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 121 ) 122 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(item, nil) 123 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, errAny) 124 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 125 126 got, err := finder.GetOne(ctx, All, shard, id, proj, adds) 127 128 assert.ErrorIs(t, err, errRead) 129 f.assertLogErrorContains(t, errAny.Error()) 130 131 assert.Equal(t, nilObject, got) 132 }) 133 134 t.Run("Success", func(t *testing.T) { 135 var ( 136 f = newFakeFactory("C1", shard, nodes) 137 finder = f.newFinder("A") 138 digestIDs = []strfmt.UUID{id} 139 item = objects.Replica{ID: id, Object: object(id, 3)} 140 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 141 ) 142 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(item, nil) 143 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 144 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 145 146 got, err := finder.GetOne(ctx, All, shard, id, proj, adds) 147 assert.Nil(t, err) 148 assert.Equal(t, item.Object, got) 149 }) 150 151 t.Run("NotFound", func(t *testing.T) { 152 var ( 153 f = newFakeFactory("C1", shard, nodes) 154 finder = f.newFinder("A") 155 digestIDs = []strfmt.UUID{id} 156 // obj = object(id, 3) 157 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 0}} 158 ) 159 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(emptyItem, nil) 160 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 161 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 162 163 got, err := finder.GetOne(ctx, All, shard, id, proj, adds) 164 assert.Nil(t, err) 165 assert.Equal(t, nilObject, got) 166 }) 167 } 168 169 func TestFinderGetOneWithConsistencyLevelQuorum(t *testing.T) { 170 var ( 171 id = strfmt.UUID("123") 172 cls = "C1" 173 shard = "SH1" 174 nodes = []string{"A", "B", "C"} 175 ctx = context.Background() 176 adds = additional.Properties{} 177 proj = search.SelectProperties{} 178 nilObject *storobj.Object 179 emptyItem = objects.Replica{} 180 ) 181 182 t.Run("AllButOne", func(t *testing.T) { 183 var ( 184 f = newFakeFactory("C1", shard, nodes) 185 finder = f.newFinder("A") 186 digestIDs = []strfmt.UUID{id} 187 item = objects.Replica{ID: id, Object: object(id, 3)} 188 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 189 ) 190 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(item, nil) 191 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, errAny) 192 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, errAny) 193 194 got, err := finder.GetOne(ctx, Quorum, shard, id, proj, adds) 195 assert.ErrorIs(t, err, errRead) 196 f.assertLogErrorContains(t, errAny.Error()) 197 assert.Equal(t, nilObject, got) 198 }) 199 200 t.Run("Success", func(t *testing.T) { 201 var ( 202 f = newFakeFactory("C1", shard, nodes) 203 finder = f.newFinder("A") 204 digestIDs = []strfmt.UUID{id} 205 item = objects.Replica{ID: id, Object: object(id, 3)} 206 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 207 ) 208 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(item, nil) 209 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, errAny) 210 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 211 212 got, err := finder.GetOne(ctx, Quorum, shard, id, proj, adds) 213 assert.Nil(t, err) 214 assert.Equal(t, item.Object, got) 215 }) 216 217 t.Run("NotFound", func(t *testing.T) { 218 var ( 219 f = newFakeFactory("C1", shard, nodes) 220 finder = f.newFinder("A") 221 digestIDs = []strfmt.UUID{id} 222 // obj = object(id, 3) 223 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 0}} 224 ) 225 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(emptyItem, nil) 226 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 227 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, errAny) 228 229 got, err := finder.GetOne(ctx, Quorum, shard, id, proj, adds) 230 assert.Nil(t, err) 231 assert.Equal(t, nilObject, got) 232 }) 233 } 234 235 func TestFinderGetOneWithConsistencyLevelOne(t *testing.T) { 236 var ( 237 id = strfmt.UUID("123") 238 cls = "C1" 239 shard = "SH1" 240 nodes = []string{"A", "B", "C"} 241 ctx = context.Background() 242 adds = additional.Properties{} 243 proj = search.SelectProperties{} 244 nilObject *storobj.Object 245 emptyItem = objects.Replica{} 246 ) 247 248 t.Run("None", func(t *testing.T) { 249 var ( 250 f = newFakeFactory("C1", shard, nodes) 251 finder = f.newFinder("A") 252 // obj = objects.Replica{ID: id, Object: object(id, 3) 253 ) 254 for _, n := range nodes { 255 f.RClient.On("FetchObject", anyVal, n, cls, shard, id, proj, adds).Return(emptyItem, errAny) 256 } 257 258 got, err := finder.GetOne(ctx, One, shard, id, proj, adds) 259 assert.ErrorIs(t, err, errRead) 260 f.assertLogErrorContains(t, errAny.Error()) 261 assert.Equal(t, nilObject, got) 262 }) 263 264 t.Run("Success", func(t *testing.T) { 265 var ( 266 f = newFakeFactory("C1", shard, nodes) 267 finder = f.newFinder(nodes[2]) 268 item = objects.Replica{ID: id, Object: object(id, 3)} 269 ) 270 f.RClient.On("FetchObject", anyVal, nodes[2], cls, shard, id, proj, adds).Return(item, nil) 271 got, err := finder.GetOne(ctx, One, shard, id, proj, adds) 272 assert.Nil(t, err) 273 assert.Equal(t, item.Object, got) 274 }) 275 276 t.Run("NotFound", func(t *testing.T) { 277 var ( 278 f = newFakeFactory("C1", shard, nodes) 279 finder = f.newFinder("A") 280 ) 281 f.RClient.On("FetchObject", anyVal, nodes[0], cls, shard, id, proj, adds).Return(emptyItem, nil) 282 283 got, err := finder.GetOne(ctx, One, shard, id, proj, adds) 284 assert.Nil(t, err) 285 assert.Equal(t, nilObject, got) 286 }) 287 } 288 289 func TestFinderExistsWithConsistencyLevelALL(t *testing.T) { 290 var ( 291 id = strfmt.UUID("123") 292 cls = "C1" 293 shard = "SH1" 294 nodes = []string{"A", "B", "C"} 295 ctx = context.Background() 296 nilReply = []RepairResponse(nil) 297 ) 298 299 t.Run("None", func(t *testing.T) { 300 var ( 301 f = newFakeFactory("C1", shard, nodes) 302 finder = f.newFinder("A") 303 digestIDs = []strfmt.UUID{id} 304 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 305 ) 306 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 307 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(nilReply, errAny) 308 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 309 310 got, err := finder.Exists(ctx, All, shard, id) 311 assert.ErrorIs(t, err, errRead) 312 f.assertLogErrorContains(t, errAny.Error()) 313 assert.Equal(t, false, got) 314 }) 315 316 t.Run("Success", func(t *testing.T) { 317 var ( 318 f = newFakeFactory("C1", shard, nodes) 319 finder = f.newFinder("A") 320 digestIDs = []strfmt.UUID{id} 321 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 322 ) 323 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 324 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 325 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 326 327 got, err := finder.Exists(ctx, All, shard, id) 328 assert.Nil(t, err) 329 assert.Equal(t, true, got) 330 }) 331 332 t.Run("NotFound", func(t *testing.T) { 333 var ( 334 f = newFakeFactory("C1", shard, nodes) 335 finder = f.newFinder("A") 336 digestIDs = []strfmt.UUID{id} 337 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 0, Deleted: true}} 338 ) 339 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 340 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 341 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, nil) 342 343 got, err := finder.Exists(ctx, All, shard, id) 344 assert.Nil(t, err) 345 assert.Equal(t, false, got) 346 }) 347 } 348 349 func TestFinderExistsWithConsistencyLevelQuorum(t *testing.T) { 350 var ( 351 id = strfmt.UUID("123") 352 cls = "C1" 353 shard = "SH1" 354 nodes = []string{"A", "B", "C"} 355 ctx = context.Background() 356 nilReply = []RepairResponse(nil) 357 ) 358 359 t.Run("AllButOne", func(t *testing.T) { 360 var ( 361 f = newFakeFactory("C1", shard, nodes) 362 finder = f.newFinder("A") 363 digestIDs = []strfmt.UUID{id} 364 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 365 ) 366 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 367 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(nilReply, errAny) 368 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, errAny) 369 370 got, err := finder.Exists(ctx, Quorum, shard, id) 371 assert.ErrorIs(t, err, errRead) 372 f.assertLogErrorContains(t, errAny.Error()) 373 assert.Equal(t, false, got) 374 }) 375 376 t.Run("Success", func(t *testing.T) { 377 var ( 378 f = newFakeFactory("C1", shard, nodes) 379 finder = f.newFinder("A") 380 digestIDs = []strfmt.UUID{id} 381 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 382 ) 383 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 384 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 385 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, errAny) 386 387 got, err := finder.Exists(ctx, Quorum, shard, id) 388 assert.Nil(t, err) 389 assert.Equal(t, true, got) 390 }) 391 392 t.Run("NotFound", func(t *testing.T) { 393 var ( 394 f = newFakeFactory("C1", shard, nodes) 395 finder = f.newFinder("A") 396 digestIDs = []strfmt.UUID{id} 397 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 0, Deleted: true}} 398 ) 399 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 400 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 401 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, digestIDs).Return(digestR, errAny) 402 403 got, err := finder.Exists(ctx, Quorum, shard, id) 404 assert.Nil(t, err) 405 assert.Equal(t, false, got) 406 }) 407 } 408 409 func TestFinderExistsWithConsistencyLevelOne(t *testing.T) { 410 var ( 411 id = strfmt.UUID("123") 412 cls = "C1" 413 shard = "SH1" 414 nodes = []string{"A", "B"} 415 ctx = context.Background() 416 ) 417 418 t.Run("Success", func(t *testing.T) { 419 var ( 420 f = newFakeFactory("C1", shard, nodes) 421 finder = f.newFinder("A") 422 digestIDs = []strfmt.UUID{id} 423 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 3}} 424 ) 425 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, errAny) 426 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, digestIDs).Return(digestR, nil) 427 428 got, err := finder.Exists(ctx, One, shard, id) 429 assert.Nil(t, err) 430 assert.Equal(t, true, got) 431 }) 432 433 t.Run("NotFound", func(t *testing.T) { 434 var ( 435 f = newFakeFactory("C1", shard, nodes) 436 finder = f.newFinder("A") 437 digestIDs = []strfmt.UUID{id} 438 digestR = []RepairResponse{{ID: id.String(), UpdateTime: 0, Deleted: true}} 439 ) 440 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shard, digestIDs).Return(digestR, nil) 441 442 got, err := finder.Exists(ctx, One, shard, id) 443 assert.Nil(t, err) 444 assert.Equal(t, false, got) 445 }) 446 } 447 448 func TestFinderCheckConsistencyALL(t *testing.T) { 449 var ( 450 ids = []strfmt.UUID{"0", "1", "2", "3", "4", "5"} 451 cls = "C1" 452 shards = []string{"S1", "S2", "S3"} 453 nodes = []string{"A", "B", "C"} 454 ctx = context.Background() 455 ) 456 457 t.Run("ExceptOne", func(t *testing.T) { 458 var ( 459 shard = shards[0] 460 f = newFakeFactory("C1", shard, nodes) 461 finder = f.newFinder("A") 462 xs, digestR = genInputs("A", shard, 1, ids) 463 ) 464 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, ids).Return(digestR, nil) 465 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, ids).Return(digestR, errAny) 466 467 err := finder.CheckConsistency(ctx, All, xs) 468 want := setObjectsConsistency(xs, false) 469 assert.ErrorIs(t, err, errRead) 470 assert.ElementsMatch(t, want, xs) 471 f.assertLogErrorContains(t, errRead.Error()) 472 }) 473 474 t.Run("OneShard", func(t *testing.T) { 475 var ( 476 shard = shards[0] 477 f = newFakeFactory("C1", shard, nodes) 478 finder = f.newFinder("A") 479 xs, digestR = genInputs("A", shard, 2, ids) 480 ) 481 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, ids).Return(digestR, nil) 482 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, ids).Return(digestR, nil) 483 484 want := setObjectsConsistency(xs, true) 485 err := finder.CheckConsistency(ctx, All, xs) 486 assert.Nil(t, err) 487 assert.ElementsMatch(t, want, xs) 488 }) 489 490 t.Run("TwoShards", func(t *testing.T) { 491 var ( 492 f = newFakeFactory("C1", shards[0], nodes) 493 finder = f.newFinder("A") 494 idSet1 = ids[:3] 495 idSet2 = ids[3:6] 496 xs1, digestR1 = genInputs("A", shards[0], 1, idSet1) 497 xs2, digestR2 = genInputs("B", shards[1], 2, idSet2) 498 ) 499 xs := make([]*storobj.Object, 0, len(xs1)+len(xs2)) 500 for i := 0; i < 3; i++ { 501 xs = append(xs, xs1[i]) 502 xs = append(xs, xs2[i]) 503 } 504 // first shard 505 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shards[0], idSet1).Return(digestR1, nil) 506 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[0], idSet1).Return(digestR1, nil) 507 508 // second shard 509 f.AddShard(shards[1], nodes) 510 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shards[1], idSet2).Return(digestR2, nil) 511 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[1], idSet2).Return(digestR2, nil) 512 513 want := setObjectsConsistency(xs, true) 514 err := finder.CheckConsistency(ctx, All, xs) 515 assert.Nil(t, err) 516 assert.ElementsMatch(t, want, xs) 517 }) 518 519 t.Run("ThreeShard", func(t *testing.T) { 520 var ( 521 f = newFakeFactory("C1", shards[0], nodes) 522 finder = f.newFinder("A") 523 ids1 = ids[:2] 524 ids2 = ids[2:4] 525 ids3 = ids[4:] 526 xs1, digestR1 = genInputs("A", shards[0], 1, ids1) 527 xs2, digestR2 = genInputs("B", shards[1], 2, ids2) 528 xs3, digestR3 = genInputs("C", shards[2], 3, ids3) 529 ) 530 xs := make([]*storobj.Object, 0, len(xs1)+len(xs2)) 531 for i := 0; i < 2; i++ { 532 xs = append(xs, xs1[i]) 533 xs = append(xs, xs2[i]) 534 xs = append(xs, xs3[i]) 535 } 536 // first shard 537 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shards[0], ids1).Return(digestR1, nil) 538 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[0], ids1).Return(digestR1, nil) 539 540 // second shard 541 f.AddShard(shards[1], nodes) 542 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shards[1], ids2).Return(digestR2, nil) 543 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[1], ids2).Return(digestR2, nil) 544 545 // third shard 546 f.AddShard(shards[2], nodes) 547 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shards[2], ids3).Return(digestR3, nil) 548 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shards[2], ids3).Return(digestR3, nil) 549 550 want := setObjectsConsistency(xs, true) 551 err := finder.CheckConsistency(ctx, All, xs) 552 assert.Nil(t, err) 553 assert.ElementsMatch(t, want, xs) 554 }) 555 556 t.Run("TwoShardSingleNode", func(t *testing.T) { 557 var ( 558 f = newFakeFactory("C1", shards[0], nodes) 559 finder = f.newFinder("A") 560 ids1 = ids[:2] 561 ids2 = ids[2:4] 562 ids3 = ids[4:] 563 xs1, digestR1 = genInputs("A", shards[0], 1, ids1) 564 xs2, digestR2 = genInputs("B", shards[1], 1, ids2) 565 xs3, digestR3 = genInputs("A", shards[2], 2, ids3) 566 ) 567 xs := make([]*storobj.Object, 0, len(xs1)+len(xs2)) 568 for i := 0; i < 2; i++ { 569 xs = append(xs, xs1[i]) 570 xs = append(xs, xs2[i]) 571 xs = append(xs, xs3[i]) 572 } 573 // first shard 574 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shards[0], ids1).Return(digestR1, nil) 575 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[0], ids1).Return(digestR1, nil) 576 577 // second shard 578 f.AddShard(shards[1], nodes) 579 f.RClient.On("DigestObjects", anyVal, nodes[0], cls, shards[1], ids2).Return(digestR2, nil) 580 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[1], ids2).Return(digestR2, nil) 581 582 // third shard 583 f.AddShard(shards[2], nodes) 584 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shards[2], ids3).Return(digestR3, nil) 585 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shards[2], ids3).Return(digestR3, nil) 586 587 want := setObjectsConsistency(xs, true) 588 err := finder.CheckConsistency(ctx, All, xs) 589 assert.Nil(t, err) 590 assert.ElementsMatch(t, want, xs) 591 }) 592 } 593 594 func TestFinderCheckConsistencyQuorum(t *testing.T) { 595 var ( 596 ids = []strfmt.UUID{"10", "20", "30"} 597 cls = "C1" 598 shard = "SH1" 599 nodes = []string{"A", "B", "C"} 600 ctx = context.Background() 601 ) 602 603 t.Run("MalformedInputs", func(t *testing.T) { 604 var ( 605 ids = []strfmt.UUID{"10", "20", "30"} 606 shard = "SH1" 607 nodes = []string{"A", "B", "C"} 608 ctx = context.Background() 609 f = newFakeFactory("C1", shard, nodes) 610 finder = f.newFinder("A") 611 xs1 = []*storobj.Object{ 612 objectEx(ids[0], 4, shard, "A"), 613 nil, 614 objectEx(ids[2], 6, shard, "A"), 615 } 616 // BelongToShard and BelongToNode are empty 617 xs2 = []*storobj.Object{ 618 objectEx(ids[0], 4, shard, "A"), 619 {Object: models.Object{ID: ids[1]}}, 620 objectEx(ids[2], 6, shard, "A"), 621 } 622 ) 623 624 assert.Nil(t, finder.CheckConsistency(ctx, Quorum, nil)) 625 626 err := finder.CheckConsistency(ctx, Quorum, xs1) 627 assert.NotNil(t, err) 628 629 err = finder.CheckConsistency(ctx, Quorum, xs2) 630 assert.NotNil(t, err) 631 }) 632 633 t.Run("None", func(t *testing.T) { 634 var ( 635 f = newFakeFactory("C1", shard, nodes) 636 finder = f.newFinder("A") 637 xs = []*storobj.Object{ 638 objectEx(ids[0], 1, shard, "A"), 639 objectEx(ids[1], 2, shard, "A"), 640 objectEx(ids[2], 3, shard, "A"), 641 } 642 digestR = []RepairResponse{ 643 {ID: ids[0].String(), UpdateTime: 1}, 644 {ID: ids[1].String(), UpdateTime: 2}, 645 {ID: ids[2].String(), UpdateTime: 3}, 646 } 647 ) 648 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, ids).Return(digestR, errAny) 649 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, ids).Return(digestR, errAny) 650 651 err := finder.CheckConsistency(ctx, All, xs) 652 want := setObjectsConsistency(xs, false) 653 assert.ErrorIs(t, err, errRead) 654 assert.ElementsMatch(t, want, xs) 655 f.assertLogErrorContains(t, errRead.Error()) 656 }) 657 658 t.Run("Success", func(t *testing.T) { 659 var ( 660 f = newFakeFactory("C1", shard, nodes) 661 finder = f.newFinder("A") 662 xs = []*storobj.Object{ 663 objectEx(ids[0], 1, shard, "A"), 664 objectEx(ids[1], 2, shard, "A"), 665 objectEx(ids[2], 3, shard, "A"), 666 } 667 digestR = []RepairResponse{ 668 {ID: ids[0].String(), UpdateTime: 1}, 669 {ID: ids[1].String(), UpdateTime: 2}, 670 {ID: ids[2].String(), UpdateTime: 3}, 671 } 672 want = setObjectsConsistency(xs, true) 673 ) 674 f.RClient.On("DigestObjects", anyVal, nodes[1], cls, shard, ids).Return(digestR, nil) 675 f.RClient.On("DigestObjects", anyVal, nodes[2], cls, shard, ids).Return(digestR, errAny) 676 677 err := finder.CheckConsistency(ctx, Quorum, xs) 678 assert.Nil(t, err) 679 assert.ElementsMatch(t, want, xs) 680 }) 681 } 682 683 func TestFinderCheckConsistencyOne(t *testing.T) { 684 var ( 685 ids = []strfmt.UUID{"10", "20", "30"} 686 shard = "SH1" 687 nodes = []string{"A", "B", "C"} 688 ctx = context.Background() 689 f = newFakeFactory("C1", shard, nodes) 690 finder = f.newFinder("A") 691 xs = []*storobj.Object{ 692 objectEx(ids[0], 4, shard, "A"), 693 objectEx(ids[1], 5, shard, "A"), 694 objectEx(ids[2], 6, shard, "A"), 695 } 696 want = setObjectsConsistency(xs, true) 697 ) 698 699 err := finder.CheckConsistency(ctx, One, xs) 700 assert.Nil(t, err) 701 assert.Equal(t, want, xs) 702 }