gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/repair_test.go (about) 1 package renter 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "os" 7 "testing" 8 "time" 9 10 "gitlab.com/NebulousLabs/errors" 11 "gitlab.com/NebulousLabs/fastrand" 12 "go.sia.tech/siad/crypto" 13 "go.sia.tech/siad/persist" 14 15 "gitlab.com/SkynetLabs/skyd/build" 16 "gitlab.com/SkynetLabs/skyd/siatest/dependencies" 17 "gitlab.com/SkynetLabs/skyd/skymodules" 18 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siadir" 19 ) 20 21 // updateFileMetadatas updates the metadata of all siafiles within a dir. 22 func (rt *renterTester) updateFileMetadatas(dirSiaPath skymodules.SiaPath) error { 23 // Get cached offline and goodforrenew maps. 24 offlineMap, goodForRenewMap, contracts, used := rt.renter.callRenterContractsAndUtilities() 25 return rt.renter.managedUpdateFileMetadatasParams(dirSiaPath, offlineMap, goodForRenewMap, contracts, used) 26 } 27 28 // openAndUpdateDir is a helper method for updating a siadir metadata 29 func (rt *renterTester) openAndUpdateDir(siapath skymodules.SiaPath, metadata siadir.Metadata) error { 30 siadir, err := rt.renter.staticFileSystem.OpenSiaDir(siapath) 31 if err != nil { 32 return err 33 } 34 err = siadir.UpdateMetadata(metadata) 35 return errors.Compose(err, siadir.Close()) 36 } 37 38 // TestDirectoryModTime verifies that the last update time of a directory is 39 // accurately reported 40 func TestDirectoryModTime(t *testing.T) { 41 if testing.Short() { 42 t.SkipNow() 43 } 44 t.Parallel() 45 46 // Create a test directory with sub folders 47 // 48 // root/ file 49 // root/SubDir1/ 50 // root/SubDir1/SubDir2/ file 51 52 // Create test renter 53 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 54 if err != nil { 55 t.Fatal(err) 56 } 57 defer func() { 58 if err := rt.Close(); err != nil { 59 t.Fatal(err) 60 } 61 }() 62 63 // Create directory tree 64 subDir1, err := skymodules.NewSiaPath("SubDir1") 65 if err != nil { 66 t.Fatal(err) 67 } 68 subDir2, err := skymodules.NewSiaPath("SubDir2") 69 if err != nil { 70 t.Fatal(err) 71 } 72 if err := rt.renter.CreateDir(subDir1, skymodules.DefaultDirPerm); err != nil { 73 t.Fatal(err) 74 } 75 subDir1_2, err := subDir1.Join(subDir2.String()) 76 if err != nil { 77 t.Fatal(err) 78 } 79 if err := rt.renter.CreateDir(subDir1_2, skymodules.DefaultDirPerm); err != nil { 80 t.Fatal(err) 81 } 82 83 // Call Bubble to update filesystem ModTimes so there are no zero times 84 if err := rt.renter.UpdateMetadata(subDir1_2, false); err != nil { 85 t.Fatal(err) 86 } 87 // Sleep for 1 second to allow bubbles to update filesystem. Retry doesn't 88 // work here as we are waiting for the ModTime to be fully updated but we 89 // don't know what that value will be. We need this value to be updated and 90 // static before we create the SiaFiles to be able to ensure the ModTimes of 91 // the SiaFiles are the most recent 92 time.Sleep(time.Second) 93 94 // Add files 95 sp1 := skymodules.RandomSiaPath() 96 rsc, _ := skymodules.NewRSCode(1, 1) 97 up := skymodules.FileUploadParams{ 98 Source: "", 99 SiaPath: sp1, 100 ErasureCode: rsc, 101 } 102 fileSize := uint64(100) 103 f1, err := rt.newTestSiaFile(up.SiaPath, up.Source, up.ErasureCode, fileSize) 104 if err != nil { 105 t.Fatal(err) 106 } 107 defer func() { 108 if err := f1.Close(); err != nil { 109 t.Fatal(err) 110 } 111 }() 112 sp2, err := subDir1_2.Join(hex.EncodeToString(fastrand.Bytes(8))) 113 if err != nil { 114 t.Fatal(err) 115 } 116 up.SiaPath = sp2 117 f2, err := rt.newTestSiaFile(up.SiaPath, up.Source, up.ErasureCode, fileSize) 118 if err != nil { 119 t.Fatal(err) 120 } 121 defer func() { 122 if err := f2.Close(); err != nil { 123 t.Fatal(err) 124 } 125 }() 126 127 // Call bubble on lowest lever and confirm top level reports accurate last 128 // update time 129 if err := rt.renter.UpdateMetadata(subDir1_2, false); err != nil { 130 t.Fatal(err) 131 } 132 err = build.Retry(100, 100*time.Millisecond, func() error { 133 dirInfo, err := rt.renter.staticFileSystem.DirInfo(skymodules.RootSiaPath()) 134 if err != nil { 135 return err 136 } 137 if dirInfo.MostRecentModTime != f1.ModTime() { 138 return fmt.Errorf("MostRecentModTime is incorrect, got %v expected %v", dirInfo.MostRecentModTime, f1.ModTime()) 139 } 140 if dirInfo.AggregateMostRecentModTime != f2.ModTime() { 141 return fmt.Errorf("AggregateMostRecentModTime is incorrect, got %v expected %v", dirInfo.AggregateMostRecentModTime, f2.ModTime()) 142 } 143 return nil 144 }) 145 if err != nil { 146 t.Fatal(err) 147 } 148 } 149 150 // TestRandomStuckDirectory probes managedStuckDirectory to make sure it 151 // randomly picks a correct directory 152 func TestRandomStuckDirectory(t *testing.T) { 153 if testing.Short() { 154 t.SkipNow() 155 } 156 t.Parallel() 157 158 // Create test renter 159 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer func() { 164 if err := rt.Close(); err != nil { 165 t.Fatal(err) 166 } 167 }() 168 169 // Create a test directory with sub folders 170 // 171 // root/home/siafiles/ 172 // root/home/siafiles/SubDir1/ 173 // root/home/siafiles/SubDir1/SubDir2/ 174 // root/home/siafiles/SubDir2/ 175 subDir1, err := skymodules.NewSiaPath("SubDir1") 176 if err != nil { 177 t.Fatal(err) 178 } 179 subDir2, err := skymodules.NewSiaPath("SubDir2") 180 if err != nil { 181 t.Fatal(err) 182 } 183 if err := rt.renter.CreateDir(subDir1, skymodules.DefaultDirPerm); err != nil { 184 t.Fatal(err) 185 } 186 if err := rt.renter.CreateDir(subDir2, skymodules.DefaultDirPerm); err != nil { 187 t.Fatal(err) 188 } 189 subDir1_2, err := subDir1.Join(subDir2.String()) 190 if err != nil { 191 t.Fatal(err) 192 } 193 if err := rt.renter.CreateDir(subDir1_2, skymodules.DefaultDirPerm); err != nil { 194 t.Fatal(err) 195 } 196 197 // Add a file to siafiles and SubDir1/SubDir2 and mark the first chunk as 198 // stuck in each file 199 // 200 // This will test the edge case of continuing to find stuck files when a 201 // directory has no files only directories 202 rsc, _ := skymodules.NewRSCode(1, 1) 203 up := skymodules.FileUploadParams{ 204 Source: "", 205 SiaPath: skymodules.RandomSiaPath(), 206 ErasureCode: rsc, 207 } 208 f, err := rt.newTestSiaFile(up.SiaPath, up.Source, up.ErasureCode, 100) 209 if err != nil { 210 t.Fatal(err) 211 } 212 err = rt.renter.SetFileStuck(up.SiaPath, true) 213 if err != nil { 214 t.Fatal(err) 215 } 216 if err = f.SetStuck(uint64(0), true); err != nil { 217 t.Fatal(err) 218 } 219 if err := f.Close(); err != nil { 220 t.Fatal(err) 221 } 222 up.SiaPath, err = subDir1_2.Join(hex.EncodeToString(fastrand.Bytes(8))) 223 if err != nil { 224 t.Fatal(err) 225 } 226 f, err = rt.newTestSiaFile(up.SiaPath, up.Source, up.ErasureCode, 100) 227 if err != nil { 228 t.Fatal(err) 229 } 230 err = f.GrowNumChunks(2) 231 if err != nil { 232 t.Fatal(err) 233 } 234 err = rt.renter.SetFileStuck(up.SiaPath, true) 235 if err != nil { 236 t.Fatal(err) 237 } 238 if err = f.SetStuck(uint64(0), true); err != nil { 239 t.Fatal(err) 240 } 241 if err := f.Close(); err != nil { 242 t.Fatal(err) 243 } 244 245 // Bubble directory information so NumStuckChunks is updated, there should 246 // be at least 3 stuck chunks because of the 3 we manually marked as stuck, 247 // but the repair loop could have marked the rest as stuck so we just want 248 // to ensure that the root directory reflects at least the 3 we marked as 249 // stuck 250 if err := rt.renter.UpdateMetadata(skymodules.RootSiaPath(), true); err != nil { 251 t.Fatal(err) 252 } 253 err = build.Retry(100, 100*time.Millisecond, func() error { 254 // Get Root Directory Metadata 255 metadata, err := rt.renter.managedDirectoryMetadata(skymodules.RootSiaPath()) 256 if err != nil { 257 return err 258 } 259 // Check Aggregate number of stuck chunks 260 if metadata.AggregateNumStuckChunks < uint64(3) { 261 return fmt.Errorf("Incorrect number of stuck chunks, got %v expected at least 3", metadata.AggregateNumStuckChunks) 262 } 263 return nil 264 }) 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 // Find a stuck directory randomly, it should never find root/SubDir1 or 270 // root/SubDir2 and should find root/SubDir1/SubDir2 more than root 271 var count1_2, countRoot, countSiaFiles int 272 for i := 0; i < 100; i++ { 273 dir, err := rt.renter.managedStuckDirectory() 274 if err != nil { 275 t.Fatal(err) 276 } 277 if dir.Equals(subDir1_2) { 278 count1_2++ 279 continue 280 } 281 if dir.Equals(skymodules.RootSiaPath()) { 282 countRoot++ 283 continue 284 } 285 if dir.Equals(skymodules.UserFolder) { 286 countSiaFiles++ 287 continue 288 } 289 t.Fatal("Unstuck dir found", dir.String()) 290 } 291 292 // Randomness is weighted so we should always find file 1 more often 293 if countRoot > count1_2 { 294 t.Log("Should have found root/SubDir1/SubDir2 more than root") 295 t.Fatalf("Found root/SubDir1/SubDir2 %v times and root %v times", count1_2, countRoot) 296 } 297 // If we never find root/SubDir1/SubDir2 then that is a failure 298 if count1_2 == 0 { 299 t.Fatal("Found root/SubDir1/SubDir2 0 times") 300 } 301 // If we never find root that is not ideal, Log this error. If it happens 302 // a lot then the weighted randomness should be improved 303 if countRoot == 0 { 304 t.Logf("Found root 0 times. Consider improving the weighted randomness") 305 } 306 t.Log("Found root/SubDir1/SubDir2", count1_2, "times and root", countRoot, "times") 307 } 308 309 // TestRandomStuckFile tests that the renter can randomly find stuck files 310 // weighted by the number of stuck chunks 311 func TestRandomStuckFile(t *testing.T) { 312 if testing.Short() { 313 t.SkipNow() 314 } 315 t.Parallel() 316 317 // Create Renter 318 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 319 if err != nil { 320 t.Fatal(err) 321 } 322 defer func() { 323 if err := rt.Close(); err != nil { 324 t.Fatal(err) 325 } 326 }() 327 328 // Create 3 files at root 329 // 330 // File 1 will have all chunks stuck 331 file1, err := rt.renter.newRenterTestFile() 332 if err != nil { 333 t.Fatal(err) 334 } 335 err = file1.GrowNumChunks(3) 336 if err != nil { 337 t.Fatal(err) 338 } 339 siaPath1 := rt.renter.staticFileSystem.FileSiaPath(file1) 340 err = rt.renter.SetFileStuck(siaPath1, true) 341 if err != nil { 342 t.Fatal(err) 343 } 344 345 // File 2 will have only 1 chunk stuck 346 file2, err := rt.renter.newRenterTestFile() 347 if err != nil { 348 t.Fatal(err) 349 } 350 siaPath2 := rt.renter.staticFileSystem.FileSiaPath(file2) 351 err = file2.SetStuck(0, true) 352 if err != nil { 353 t.Fatal(err) 354 } 355 356 // File 3 will be unstuck 357 file3, err := rt.renter.newRenterTestFile() 358 if err != nil { 359 t.Fatal(err) 360 } 361 siaPath3 := rt.renter.staticFileSystem.FileSiaPath(file3) 362 363 // Since we disabled the health loop for this test, call it manually to 364 // update the directory metadata 365 if err := rt.renter.UpdateMetadata(skymodules.UserFolder, false); err != nil { 366 t.Fatal(err) 367 } 368 i := 0 369 err = build.Retry(100, 100*time.Millisecond, func() error { 370 i++ 371 if i%10 == 0 { 372 if err := rt.renter.UpdateMetadata(skymodules.RootSiaPath(), true); err != nil { 373 t.Fatal(err) 374 } 375 } 376 // Get Root Directory Metadata 377 metadata, err := rt.renter.managedDirectoryMetadata(skymodules.RootSiaPath()) 378 if err != nil { 379 return err 380 } 381 // Check Aggregate number of stuck chunks 382 if metadata.AggregateNumStuckChunks == 0 { 383 return errors.New("no stuck chunks found") 384 } 385 return nil 386 }) 387 if err != nil { 388 t.Fatal(err) 389 } 390 checkFindRandomFile(t, rt.renter, skymodules.RootSiaPath(), siaPath1, siaPath2, siaPath3) 391 392 // Create a directory 393 dir, err := skymodules.NewSiaPath("Dir") 394 if err != nil { 395 t.Fatal(err) 396 } 397 if err := rt.renter.CreateDir(dir, skymodules.DefaultDirPerm); err != nil { 398 t.Fatal(err) 399 } 400 401 // Move siafiles to dir 402 newSiaPath1, err := dir.Join(siaPath1.String()) 403 if err != nil { 404 t.Fatal(err) 405 } 406 err = rt.renter.RenameFile(siaPath1, newSiaPath1) 407 if err != nil { 408 t.Fatal(err) 409 } 410 newSiaPath2, err := dir.Join(siaPath2.String()) 411 if err != nil { 412 t.Fatal(err) 413 } 414 err = rt.renter.RenameFile(siaPath2, newSiaPath2) 415 if err != nil { 416 t.Fatal(err) 417 } 418 newSiaPath3, err := dir.Join(siaPath3.String()) 419 if err != nil { 420 t.Fatal(err) 421 } 422 err = rt.renter.RenameFile(siaPath3, newSiaPath3) 423 if err != nil { 424 t.Fatal(err) 425 } 426 // Since we disabled the health loop for this test, call it manually to 427 // update the directory metadata 428 if err := rt.renter.UpdateMetadata(dir, false); err != nil { 429 t.Fatal(err) 430 } 431 i = 0 432 err = build.Retry(100, 100*time.Millisecond, func() error { 433 i++ 434 if i%10 == 0 { 435 if err := rt.renter.UpdateMetadata(dir, false); err != nil { 436 t.Fatal(err) 437 } 438 } 439 // Get Directory Metadata 440 metadata, err := rt.renter.managedDirectoryMetadata(dir) 441 if err != nil { 442 return err 443 } 444 // Check Aggregate number of stuck chunks 445 if metadata.AggregateNumStuckChunks == 0 { 446 return errors.New("no stuck chunks found") 447 } 448 return nil 449 }) 450 if err != nil { 451 t.Fatal(err) 452 } 453 checkFindRandomFile(t, rt.renter, dir, newSiaPath1, newSiaPath2, newSiaPath3) 454 } 455 456 // checkFindRandomFile is a helper function that checks the output from 457 // managedStuckFile in a loop 458 func checkFindRandomFile(t *testing.T, r *Renter, dir, siaPath1, siaPath2, siaPath3 skymodules.SiaPath) { 459 // Find a stuck file randomly, it should never find file 3 and should find 460 // file 1 more than file 2. 461 var count1, count2 int 462 for i := 0; i < 100; i++ { 463 siaPath, err := r.managedStuckFile(dir) 464 if err != nil { 465 t.Fatal(err) 466 } 467 if siaPath.Equals(siaPath1) { 468 count1++ 469 } 470 if siaPath.Equals(siaPath2) { 471 count2++ 472 } 473 if siaPath.Equals(siaPath3) { 474 t.Fatal("Unstuck file 3 found") 475 } 476 } 477 478 // Randomness is weighted so we should always find file 1 more often 479 if count2 > count1 { 480 t.Log("Should have found file 1 more than file 2") 481 t.Fatalf("Found file 1 %v times and file 2 %v times", count1, count2) 482 } 483 // If we never find file 1 then that is a failure 484 if count1 == 0 { 485 t.Fatal("Found file 1 0 times") 486 } 487 // If we never find file 2 that is not ideal, Log this error. If it happens 488 // a lot then the weighted randomness should be improved 489 if count2 == 0 { 490 t.Logf("Found file 2 0 times. Consider improving the weighted randomness") 491 } 492 t.Log("Found file1", count1, "times and file2", count2, "times") 493 } 494 495 // TestCalculateFileMetadata checks that the values returned from 496 // managedCalculateFileMetadata make sense 497 func TestCalculateFileMetadata(t *testing.T) { 498 if testing.Short() { 499 t.SkipNow() 500 } 501 t.Parallel() 502 503 // Create renter 504 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 505 if err != nil { 506 t.Fatal(err) 507 } 508 defer func() { 509 if err := rt.Close(); err != nil { 510 t.Fatal(err) 511 } 512 }() 513 514 // Create a file at root with a skylink 515 rsc, _ := skymodules.NewRSCode(1, 1) 516 siaPath, err := skymodules.NewSiaPath("rootFile") 517 if err != nil { 518 t.Fatal(err) 519 } 520 up := skymodules.FileUploadParams{ 521 Source: "", 522 SiaPath: siaPath, 523 ErasureCode: rsc, 524 } 525 fileSize := uint64(100) 526 err = rt.renter.staticFileSystem.NewSiaFile(up.SiaPath, up.Source, up.ErasureCode, crypto.GenerateSiaKey(crypto.RandomCipherType()), fileSize, persist.DefaultDiskPermissionsTest) 527 if err != nil { 528 t.Fatal(err) 529 } 530 sf, err := rt.renter.staticFileSystem.OpenSiaFile(up.SiaPath) 531 if err != nil { 532 t.Fatal(err) 533 } 534 defer func() { 535 if err := sf.Close(); err != nil { 536 t.Fatal(err) 537 } 538 }() 539 var skylink skymodules.Skylink 540 err = sf.AddSkylink(skylink) 541 if err != nil { 542 t.Fatal(err) 543 } 544 545 // Grab initial metadata values 546 rt.renter.managedUpdateRenterContractsAndUtilities() 547 offline, goodForRenew, _, _ := rt.renter.callRenterContractsAndUtilities() 548 health, stuckHealth, _, _, numStuckChunks, repairBytes, stuckBytes := sf.Health(offline, goodForRenew) 549 redundancy, _, err := sf.Redundancy(offline, goodForRenew) 550 if err != nil { 551 t.Fatal(err) 552 } 553 lastHealthCheckTime := sf.LastHealthCheckTime() 554 modTime := sf.ModTime() 555 556 // Update the file metadata. 557 err = rt.updateFileMetadatas(skymodules.RootSiaPath()) 558 if err != nil { 559 t.Fatal(err) 560 } 561 562 // Check calculated metadata 563 bubbledMetadatas, err := rt.renter.managedCachedFileMetadatas([]skymodules.SiaPath{up.SiaPath}) 564 if err != nil { 565 t.Fatal(err) 566 } 567 fileMetadata := bubbledMetadatas[0].bm 568 569 // Check siafile calculated metadata 570 if fileMetadata.Health != health { 571 t.Fatalf("health incorrect, expected %v got %v", health, fileMetadata.Health) 572 } 573 if fileMetadata.StuckHealth != stuckHealth { 574 t.Fatalf("stuckHealth incorrect, expected %v got %v", stuckHealth, fileMetadata.StuckHealth) 575 } 576 if fileMetadata.Redundancy != redundancy { 577 t.Fatalf("redundancy incorrect, expected %v got %v", redundancy, fileMetadata.Redundancy) 578 } 579 if fileMetadata.RepairBytes != repairBytes { 580 t.Fatalf("RepairBytes incorrect, expected %v got %v", repairBytes, fileMetadata.RepairBytes) 581 } 582 if fileMetadata.StuckBytes != stuckBytes { 583 t.Fatalf("StuckBytes incorrect, expected %v got %v", stuckBytes, fileMetadata.StuckBytes) 584 } 585 if fileMetadata.Size != fileSize { 586 t.Fatalf("size incorrect, expected %v got %v", fileSize, fileMetadata.Size) 587 } 588 if fileMetadata.NumStuckChunks != numStuckChunks { 589 t.Fatalf("numstuckchunks incorrect, expected %v got %v", numStuckChunks, fileMetadata.NumStuckChunks) 590 } 591 if fileMetadata.LastHealthCheckTime.Equal(lastHealthCheckTime) || fileMetadata.LastHealthCheckTime.IsZero() { 592 t.Log("Initial lasthealthchecktime", lastHealthCheckTime) 593 t.Log("Calculated lasthealthchecktime", fileMetadata.LastHealthCheckTime) 594 t.Fatal("Expected lasthealthchecktime to have updated and be non zero") 595 } 596 if !fileMetadata.ModTime.Equal(modTime) { 597 t.Fatalf("Unexpected modtime, expected %v got %v", modTime, fileMetadata.ModTime) 598 } 599 if fileMetadata.NumSkylinks != 1 { 600 t.Fatalf("NumSkylinks incorrect, expected %v got %v", 1, fileMetadata.NumSkylinks) 601 } 602 if fileMetadata.Lost { 603 t.Fatal("expected file not to be lost") 604 } 605 } 606 607 // TestCreateMissingSiaDir confirms that the repair code creates a siadir file 608 // if one is not found 609 func TestCreateMissingSiaDir(t *testing.T) { 610 if testing.Short() { 611 t.SkipNow() 612 } 613 t.Parallel() 614 615 // Create test renter 616 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 617 if err != nil { 618 t.Fatal(err) 619 } 620 defer func() { 621 if err := rt.Close(); err != nil { 622 t.Fatal(err) 623 } 624 }() 625 626 // Confirm the siadir file is on disk 627 siaDirPath := skymodules.RootSiaPath().SiaDirMetadataSysPath(rt.renter.staticFileSystem.Root()) 628 _, err = os.Stat(siaDirPath) 629 if err != nil { 630 t.Fatal(err) 631 } 632 633 // Remove .siadir file on disk 634 err = os.Remove(siaDirPath) 635 if err != nil { 636 t.Fatal(err) 637 } 638 639 // Confirm siadir is gone 640 _, err = os.Stat(siaDirPath) 641 if !os.IsNotExist(err) { 642 t.Fatal("Err should have been IsNotExist", err) 643 } 644 645 // Create siadir file with managedDirectoryMetadata 646 _, err = rt.renter.managedDirectoryMetadata(skymodules.RootSiaPath()) 647 if err != nil { 648 t.Fatal(err) 649 } 650 651 // Confirm it is on disk 652 _, err = os.Stat(siaDirPath) 653 if err != nil { 654 t.Fatal(err) 655 } 656 } 657 658 // TestAddStuckChunksToHeap probes the managedAddStuckChunksToHeap method 659 func TestAddStuckChunksToHeap(t *testing.T) { 660 if testing.Short() { 661 t.SkipNow() 662 } 663 t.Parallel() 664 665 // create renter with dependencies, first to disable the background health, 666 // repair, and stuck loops from running, then update it to bypass the worker 667 // pool length check in managedBuildUnfinishedChunks 668 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 669 if err != nil { 670 t.Fatal(err) 671 } 672 defer func() { 673 if err := rt.Close(); err != nil { 674 t.Fatal(err) 675 } 676 }() 677 678 // create file with no stuck chunks 679 rsc, _ := skymodules.NewRSCode(1, 1) 680 up := skymodules.FileUploadParams{ 681 Source: "", 682 SiaPath: skymodules.RandomSiaPath(), 683 ErasureCode: rsc, 684 } 685 f, err := rt.newTestSiaFile(up.SiaPath, up.Source, up.ErasureCode, 100) 686 if err != nil { 687 t.Fatal(err) 688 } 689 690 // Create maps for method inputs 691 hosts := make(map[string]struct{}) 692 offline := make(map[string]bool) 693 goodForRenew := make(map[string]bool) 694 695 // Manually add workers to worker pool 696 rt.renter.staticWorkerPool.mu.Lock() 697 for i := 0; i < int(f.NumChunks()); i++ { 698 rt.renter.staticWorkerPool.workers[fmt.Sprint(i)] = &worker{ 699 wakeChan: make(chan struct{}, 1), 700 } 701 } 702 rt.renter.staticWorkerPool.mu.Unlock() 703 704 // call managedAddStuckChunksToHeap, no chunks should be added 705 err = rt.renter.managedAddStuckChunksToHeap(up.SiaPath, hosts, offline, goodForRenew) 706 if !errors.Contains(err, errNoStuckChunks) { 707 t.Fatal(err) 708 } 709 if rt.renter.staticUploadHeap.managedLen() != 0 { 710 t.Fatal("Expected uploadHeap to be of length 0 got", rt.renter.staticUploadHeap.managedLen()) 711 } 712 713 // make chunk stuck 714 if err = f.SetStuck(uint64(0), true); err != nil { 715 t.Fatal(err) 716 } 717 718 // call managedAddStuckChunksToHeap, chunk should be added to heap 719 err = rt.renter.managedAddStuckChunksToHeap(up.SiaPath, hosts, offline, goodForRenew) 720 if err != nil { 721 t.Fatal(err) 722 } 723 if rt.renter.staticUploadHeap.managedLen() != 1 { 724 t.Fatal("Expected uploadHeap to be of length 1 got", rt.renter.staticUploadHeap.managedLen()) 725 } 726 727 // Pop chunk, chunk should be marked as fileRecentlySuccessful true 728 chunk := rt.renter.staticUploadHeap.managedPop() 729 if !chunk.fileRecentlySuccessful { 730 t.Fatal("chunk not marked as fileRecentlySuccessful true") 731 } 732 } 733 734 // TestRandomStuckFileRegression tests an edge case where no siapath was being 735 // returned from managedStuckFile. 736 func TestRandomStuckFileRegression(t *testing.T) { 737 if testing.Short() { 738 t.SkipNow() 739 } 740 t.Parallel() 741 742 // Create Renter 743 rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{}) 744 if err != nil { 745 t.Fatal(err) 746 } 747 defer func() { 748 if err := rt.Close(); err != nil { 749 t.Fatal(err) 750 } 751 }() 752 753 // Create 1 file at root with all chunks stuck 754 file, err := rt.renter.newRenterTestFile() 755 if err != nil { 756 t.Fatal(err) 757 } 758 siaPath := rt.renter.staticFileSystem.FileSiaPath(file) 759 err = rt.renter.SetFileStuck(siaPath, true) 760 if err != nil { 761 t.Fatal(err) 762 } 763 764 // Set the root directories metadata to have a large number of aggregate 765 // stuck chunks. Since there is only 1 stuck chunk this was causing the 766 // likelihood of the stuck file being chosen to be very low. 767 rootDir, err := rt.renter.staticFileSystem.OpenSiaDir(skymodules.RootSiaPath()) 768 if err != nil { 769 t.Fatal(err) 770 } 771 md, err := rootDir.Metadata() 772 if err != nil { 773 t.Fatal(err) 774 } 775 md.AggregateNumStuckChunks = 50000 776 md.NumStuckChunks = 1 777 md.NumFiles = 1 778 err = rootDir.UpdateMetadata(md) 779 if err != nil { 780 t.Fatal(err) 781 } 782 783 stuckSiaPath, err := rt.renter.managedStuckFile(skymodules.RootSiaPath()) 784 if err != nil { 785 t.Fatal(err) 786 } 787 if !stuckSiaPath.Equals(siaPath) { 788 t.Fatalf("Stuck siapath should have been the one file in the directory, expected %v got %v", siaPath, stuckSiaPath) 789 } 790 }