github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/prefetcher_test.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "context" 9 "math" 10 "runtime" 11 "testing" 12 "time" 13 14 "github.com/keybase/backoff" 15 "github.com/keybase/client/go/kbfs/data" 16 "github.com/keybase/client/go/kbfs/env" 17 "github.com/keybase/client/go/kbfs/kbfsblock" 18 "github.com/keybase/client/go/kbfs/libkey" 19 libkeytest "github.com/keybase/client/go/kbfs/libkey/test" 20 "github.com/keybase/client/go/kbfs/tlf" 21 "github.com/keybase/client/go/protocol/keybase1" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func makeRandomBlockInfo(t *testing.T) data.BlockInfo { 26 return data.BlockInfo{ 27 BlockPointer: makeRandomBlockPointer(t), 28 EncodedSize: testFakeBlockSize, 29 } 30 } 31 32 func makeRandomDirEntry( 33 t *testing.T, typ data.EntryType, size uint64, path string) data.DirEntry { 34 return data.DirEntry{ 35 BlockInfo: makeRandomBlockInfo(t), 36 EntryInfo: data.EntryInfo{ 37 Type: typ, 38 Size: size, 39 SymPath: path, 40 Mtime: 101, 41 Ctime: 102, 42 TeamWriter: "", 43 PrevRevisions: nil, 44 }, 45 } 46 } 47 func makeFakeIndirectFilePtr( 48 t *testing.T, off data.Int64Offset) data.IndirectFilePtr { 49 return data.IndirectFilePtr{ 50 BlockInfo: makeRandomBlockInfo(t), 51 Off: off, 52 Holes: false, 53 } 54 } 55 56 func makeFakeIndirectDirPtr( 57 t *testing.T, off data.StringOffset) data.IndirectDirPtr { 58 return data.IndirectDirPtr{ 59 BlockInfo: makeRandomBlockInfo(t), 60 Off: off, 61 } 62 } 63 64 func makeFakeDirBlock(t *testing.T, name string) *data.DirBlock { 65 return &data.DirBlock{ 66 CommonBlock: data.NewCommonBlockForTesting(false, testFakeBlockSize), 67 Children: map[string]data.DirEntry{ 68 name: makeRandomDirEntry(t, data.Dir, 100, name), 69 }, 70 } 71 } 72 73 func makeFakeDirBlockWithChildren(children map[string]data.DirEntry) *data.DirBlock { 74 return &data.DirBlock{ 75 CommonBlock: data.NewCommonBlockForTesting(false, testFakeBlockSize), 76 Children: children, 77 } 78 } 79 80 func makeFakeDirBlockWithIPtrs(iptrs []data.IndirectDirPtr) *data.DirBlock { 81 return &data.DirBlock{ 82 CommonBlock: data.NewCommonBlockForTesting(true, testFakeBlockSize), 83 Children: map[string]data.DirEntry{}, 84 IPtrs: iptrs, 85 } 86 } 87 88 func initPrefetcherTestWithDiskCache(t *testing.T, dbc DiskBlockCache) ( 89 *blockRetrievalQueue, *fakeBlockGetter, *testBlockRetrievalConfig) { 90 t.Helper() 91 // We don't want the block getter to respect cancelation, because we need 92 // <-q.Prefetcher().Shutdown() to represent whether the retrieval requests 93 // _actually_ completed. 94 bg := newFakeBlockGetter(false) 95 config := newTestBlockRetrievalConfig(t, bg, dbc) 96 q := newBlockRetrievalQueue(1, 1, 0, config, env.EmptyAppStateUpdater{}) 97 require.NotNil(t, q) 98 99 return q, bg, config 100 } 101 102 func initPrefetcherTest(t *testing.T) (*blockRetrievalQueue, 103 *fakeBlockGetter, *testBlockRetrievalConfig) { 104 return initPrefetcherTestWithDiskCache(t, nil) 105 } 106 107 func shutdownPrefetcherTest(t *testing.T, q *blockRetrievalQueue, syncCh chan struct{}) { 108 ch := q.Shutdown() 109 if syncCh != nil { 110 select { 111 case _, isOpen := <-syncCh: 112 if isOpen { 113 close(syncCh) 114 } 115 default: 116 close(syncCh) 117 } 118 } 119 <-ch 120 } 121 122 func testPrefetcherCheckGet( 123 t *testing.T, bcache data.BlockCache, ptr data.BlockPointer, expectedBlock data.Block, 124 expectedPrefetchStatus PrefetchStatus, tlfID tlf.ID, 125 dcache DiskBlockCache) { 126 block, err := bcache.Get(ptr) 127 require.NoError(t, err) 128 if dcache == nil { 129 return 130 } 131 if dbcw, ok := dcache.(*diskBlockCacheWrapped); ok { 132 err := dbcw.waitForDeletes(context.Background()) 133 require.NoError(t, err) 134 } 135 if expectedFB, ok := expectedBlock.(*data.FileBlock); ok && 136 !expectedFB.IsIndirect() { 137 fb, ok := block.(*data.FileBlock) 138 require.True(t, ok) 139 // For direct file blocks, the unexported hash pointer might 140 // differ between two instances of the same block, so just 141 // check the contents explicitly. 142 require.Equal(t, expectedFB.Contents, fb.Contents) 143 } else { 144 require.Equal(t, expectedBlock, block) 145 } 146 prefetchStatus, err := dcache.GetPrefetchStatus( 147 context.Background(), tlfID, ptr.ID, DiskBlockAnyCache) 148 require.NoError(t, err) 149 require.Equal(t, expectedPrefetchStatus.String(), prefetchStatus.String(), ptr.String()) 150 } 151 152 func getStack() string { 153 stacktrace := make([]byte, 16384) 154 length := runtime.Stack(stacktrace, true) 155 return string(stacktrace[:length]) 156 } 157 158 func waitForPrefetchOrBust( 159 ctx context.Context, t *testing.T, pre Prefetcher, ptr data.BlockPointer) { 160 t.Helper() 161 ch, err := pre.WaitChannelForBlockPrefetch(ctx, ptr) 162 require.NoError(t, err) 163 164 select { 165 case <-ch: 166 case <-time.After(time.Second): 167 t.Fatal("Failed to wait for prefetch. Stack:\n" + getStack()) 168 } 169 } 170 171 func notifyContinueCh(ch chan<- error) { 172 go func() { 173 ch <- nil 174 }() 175 } 176 177 func notifySyncCh(t *testing.T, ch chan<- struct{}) { 178 t.Helper() 179 select { 180 case ch <- struct{}{}: 181 t.Log("Notified sync channel.") 182 case <-time.After(time.Second): 183 t.Fatal("Error notifying sync channel. Stack:\n" + getStack()) 184 } 185 } 186 187 func TestPrefetcherIndirectFileBlock(t *testing.T) { 188 t.Log("Test indirect file block prefetching.") 189 q, bg, config := initPrefetcherTest(t) 190 defer shutdownPrefetcherTest(t, q, nil) 191 192 t.Log("Initialize an indirect file block pointing to 2 file data blocks.") 193 ptrs := []data.IndirectFilePtr{ 194 makeFakeIndirectFilePtr(t, 0), 195 makeFakeIndirectFilePtr(t, 150), 196 } 197 rootPtr := makeRandomBlockPointer(t) 198 rootBlock := &data.FileBlock{IPtrs: ptrs} 199 rootBlock.IsInd = true 200 indBlock1 := makeFakeFileBlock(t, true) 201 indBlock2 := makeFakeFileBlock(t, true) 202 203 _, continueChRootBlock := bg.setBlockToReturn(rootPtr, rootBlock) 204 _, continueChIndBlock1 := 205 bg.setBlockToReturn(ptrs[0].BlockPointer, indBlock1) 206 _, continueChIndBlock2 := 207 bg.setBlockToReturn(ptrs[1].BlockPointer, indBlock2) 208 209 var block data.Block = &data.FileBlock{} 210 ctx, cancel := context.WithTimeout( 211 context.Background(), individualTestTimeout) 212 defer cancel() 213 kmd := makeKMD() 214 ch := q.Request( 215 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, block, 216 data.TransientEntry, BlockRequestWithPrefetch) 217 continueChRootBlock <- nil 218 err := <-ch 219 require.NoError(t, err) 220 require.Equal(t, rootBlock, block) 221 222 t.Log("Release the prefetched indirect blocks.") 223 continueChIndBlock1 <- nil 224 continueChIndBlock2 <- nil 225 226 t.Log("Wait for the prefetch to finish.") 227 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 228 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[0].BlockPointer) 229 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[1].BlockPointer) 230 231 t.Log("Ensure that the prefetched blocks are in the cache.") 232 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootBlock, 233 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 234 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[0].BlockPointer, 235 indBlock1, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 236 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[1].BlockPointer, 237 indBlock2, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 238 } 239 240 func TestPrefetcherIndirectDirBlock(t *testing.T) { 241 t.Log("Test indirect dir block prefetching.") 242 q, bg, config := initPrefetcherTest(t) 243 defer shutdownPrefetcherTest(t, q, nil) 244 245 t.Log("Initialize an indirect dir block pointing to 2 dir data blocks.") 246 ptrs := []data.IndirectDirPtr{ 247 makeFakeIndirectDirPtr(t, "a"), 248 makeFakeIndirectDirPtr(t, "b"), 249 } 250 rootPtr := makeRandomBlockPointer(t) 251 rootBlock := makeFakeDirBlockWithIPtrs(ptrs) 252 indBlock1 := makeFakeDirBlock(t, "a") 253 indBlock2 := makeFakeDirBlock(t, "b") 254 255 _, continueChRootBlock := bg.setBlockToReturn(rootPtr, rootBlock) 256 _, continueChIndBlock1 := 257 bg.setBlockToReturn(ptrs[0].BlockPointer, indBlock1) 258 _, continueChIndBlock2 := 259 bg.setBlockToReturn(ptrs[1].BlockPointer, indBlock2) 260 261 block := data.NewDirBlock() 262 ctx, cancel := context.WithTimeout( 263 context.Background(), individualTestTimeout) 264 defer cancel() 265 kmd := makeKMD() 266 ch := q.Request( 267 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, block, 268 data.TransientEntry, BlockRequestWithPrefetch) 269 continueChRootBlock <- nil 270 err := <-ch 271 require.NoError(t, err) 272 require.Equal(t, rootBlock, block) 273 274 t.Log("Release the prefetched indirect blocks.") 275 continueChIndBlock1 <- nil 276 continueChIndBlock2 <- nil 277 278 t.Log("Wait for the prefetch to finish.") 279 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 280 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[0].BlockPointer) 281 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[1].BlockPointer) 282 283 t.Log("Ensure that the prefetched blocks are in the cache.") 284 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootBlock, 285 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 286 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[0].BlockPointer, 287 indBlock1, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 288 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[1].BlockPointer, 289 indBlock2, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 290 } 291 292 func testPrefetcherIndirectDirBlockTail( 293 t *testing.T, q *blockRetrievalQueue, bg *fakeBlockGetter, 294 config *testBlockRetrievalConfig, withSync bool) { 295 t.Log("Initialize an indirect dir block pointing to 2 dir data blocks.") 296 ptrs := []data.IndirectDirPtr{ 297 makeFakeIndirectDirPtr(t, "a"), 298 makeFakeIndirectDirPtr(t, "b"), 299 } 300 rootPtr := makeRandomBlockPointer(t) 301 rootBlock := &data.DirBlock{IPtrs: ptrs, Children: make(map[string]data.DirEntry)} 302 rootBlock.IsInd = true 303 indBlock1 := makeFakeDirBlock(t, "a") 304 indBlock2 := makeFakeDirBlock(t, "b") 305 306 _, continueChRootBlock := bg.setBlockToReturn(rootPtr, rootBlock) 307 _, continueChIndBlock1 := 308 bg.setBlockToReturn(ptrs[0].BlockPointer, indBlock1) 309 _, continueChIndBlock2 := 310 bg.setBlockToReturn(ptrs[1].BlockPointer, indBlock2) 311 312 block := data.NewDirBlock() 313 action := BlockRequestPrefetchTail 314 if withSync { 315 action = BlockRequestPrefetchTailWithSync 316 } 317 ctx, cancel := context.WithTimeout( 318 context.Background(), individualTestTimeout) 319 defer cancel() 320 kmd := makeKMD() 321 ch := q.Request( 322 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, block, 323 data.TransientEntry, action) 324 continueChRootBlock <- nil 325 err := <-ch 326 require.NoError(t, err) 327 require.Equal(t, rootBlock, block) 328 329 if withSync { 330 t.Log("Release the prefetched indirect blocks.") 331 continueChIndBlock1 <- nil 332 continueChIndBlock2 <- nil 333 } 334 335 t.Log("Wait for the prefetch to finish.") 336 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 337 338 t.Log("Ensure that the prefetched blocks are in the cache.") 339 rootStatus := NoPrefetch 340 if withSync { 341 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[0].BlockPointer) 342 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrs[1].BlockPointer) 343 rootStatus = TriggeredPrefetch 344 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[0].BlockPointer, 345 indBlock1, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 346 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[1].BlockPointer, 347 indBlock2, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 348 } 349 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootBlock, 350 rootStatus, kmd.TlfID(), config.DiskBlockCache()) 351 352 } 353 354 func TestPrefetcherIndirectDirBlockTail(t *testing.T) { 355 t.Log("Test indirect dir block tail prefetching.") 356 q, bg, config := initPrefetcherTest(t) 357 defer shutdownPrefetcherTest(t, q, nil) 358 359 testPrefetcherIndirectDirBlockTail(t, q, bg, config, false) 360 } 361 362 func TestPrefetcherIndirectDirBlockTailWithSync(t *testing.T) { 363 t.Log("Test indirect dir block tail prefetching with sync.") 364 q, bg, config := initPrefetcherTest(t) 365 defer shutdownPrefetcherTest(t, q, nil) 366 367 testPrefetcherIndirectDirBlockTail(t, q, bg, config, true) 368 } 369 370 func TestPrefetcherDirectDirBlock(t *testing.T) { 371 t.Log("Test direct dir block prefetching.") 372 q, bg, config := initPrefetcherTest(t) 373 defer shutdownPrefetcherTest(t, q, nil) 374 375 t.Log("Initialize a direct dir block with entries pointing to 3 files.") 376 fileA := makeFakeFileBlock(t, true) 377 fileC := makeFakeFileBlock(t, true) 378 rootPtr := makeRandomBlockPointer(t) 379 rootDir := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 380 "a": makeRandomDirEntry(t, data.File, 100, "a"), 381 "b": makeRandomDirEntry(t, data.Dir, 60, "b"), 382 "c": makeRandomDirEntry(t, data.Exec, 20, "c"), 383 }) 384 dirB := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 385 "d": makeRandomDirEntry(t, data.File, 100, "d"), 386 }) 387 dirBfileD := makeFakeFileBlock(t, true) 388 389 _, continueChRootDir := bg.setBlockToReturn(rootPtr, rootDir) 390 _, continueChFileA := 391 bg.setBlockToReturn(rootDir.Children["a"].BlockPointer, fileA) 392 _, continueChDirB := 393 bg.setBlockToReturn(rootDir.Children["b"].BlockPointer, dirB) 394 _, continueChFileC := 395 bg.setBlockToReturn(rootDir.Children["c"].BlockPointer, fileC) 396 _, _ = bg.setBlockToReturn(dirB.Children["d"].BlockPointer, dirBfileD) 397 398 var block data.Block = &data.DirBlock{} 399 ctx, cancel := context.WithTimeout( 400 context.Background(), individualTestTimeout) 401 defer cancel() 402 kmd := makeKMD() 403 ch := q.Request( 404 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, block, 405 data.TransientEntry, BlockRequestWithPrefetch) 406 continueChRootDir <- nil 407 err := <-ch 408 require.NoError(t, err) 409 require.Equal(t, rootDir, block) 410 411 t.Log("Release the blocks in ascending order of their size. The largest " + 412 "block will error.") 413 continueChFileC <- nil 414 continueChDirB <- nil 415 continueChFileA <- context.Canceled 416 t.Log("Wait for the prefetch to finish.") 417 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 418 waitForPrefetchOrBust( 419 ctx, t, q.Prefetcher(), rootDir.Children["c"].BlockPointer) 420 421 t.Log("Ensure that the prefetched blocks are in the cache.") 422 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootDir, 423 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 424 testPrefetcherCheckGet(t, config.BlockCache(), 425 rootDir.Children["c"].BlockPointer, fileC, FinishedPrefetch, 426 kmd.TlfID(), config.DiskBlockCache()) 427 testPrefetcherCheckGet(t, config.BlockCache(), 428 rootDir.Children["b"].BlockPointer, dirB, NoPrefetch, 429 kmd.TlfID(), config.DiskBlockCache()) 430 431 t.Log("Ensure that the largest block isn't in the cache.") 432 _, err = config.BlockCache().Get(rootDir.Children["a"].BlockPointer) 433 require.EqualError(t, err, 434 data.NoSuchBlockError{ 435 ID: rootDir.Children["a"].BlockPointer.ID, 436 }.Error()) 437 t.Log("Ensure that the second-level directory didn't cause a prefetch.") 438 _, err = config.BlockCache().Get(dirB.Children["d"].BlockPointer) 439 require.EqualError(t, err, 440 data.NoSuchBlockError{ID: dirB.Children["d"].BlockPointer.ID}.Error()) 441 } 442 443 func TestPrefetcherAlreadyCached(t *testing.T) { 444 t.Log("Test direct dir block prefetching when the dir block is cached.") 445 q, bg, config := initPrefetcherTest(t) 446 cache := config.BlockCache() 447 defer shutdownPrefetcherTest(t, q, nil) 448 449 t.Log("Initialize a direct dir block with an entry pointing to 1 " + 450 "folder, which in turn points to 1 file.") 451 fileB := makeFakeFileBlock(t, true) 452 rootPtr := makeRandomBlockPointer(t) 453 rootDir := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 454 "a": makeRandomDirEntry(t, data.Dir, 60, "a"), 455 }) 456 dirA := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 457 "b": makeRandomDirEntry(t, data.File, 100, "b"), 458 }) 459 460 _, continueChRootDir := bg.setBlockToReturn(rootPtr, rootDir) 461 _, continueChDirA := 462 bg.setBlockToReturn(rootDir.Children["a"].BlockPointer, dirA) 463 _, continueChFileB := 464 bg.setBlockToReturn(dirA.Children["b"].BlockPointer, fileB) 465 466 t.Log("Request the root block.") 467 kmd := makeKMD() 468 var block data.Block = &data.DirBlock{} 469 ctx, cancel := context.WithTimeout( 470 context.Background(), individualTestTimeout) 471 defer cancel() 472 ch := q.Request( 473 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, 474 block, data.TransientEntry, BlockRequestWithPrefetch) 475 continueChRootDir <- nil 476 err := <-ch 477 require.NoError(t, err) 478 require.Equal(t, rootDir, block) 479 480 t.Log("Release the prefetch for dirA.") 481 continueChDirA <- nil 482 t.Log("Wait for the prefetch to finish.") 483 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 484 485 t.Log("Ensure that the prefetched block is in the cache.") 486 block, err = cache.Get(rootDir.Children["a"].BlockPointer) 487 require.NoError(t, err) 488 require.Equal(t, dirA, block) 489 t.Log("Ensure that the second-level directory didn't cause a prefetch.") 490 _, err = cache.Get(dirA.Children["b"].BlockPointer) 491 require.EqualError(t, err, 492 data.NoSuchBlockError{ID: dirA.Children["b"].BlockPointer.ID}.Error()) 493 494 t.Log("Request the already-cached second-level directory block. We don't " + 495 "need to unblock this one.") 496 block = &data.DirBlock{} 497 ch = q.Request( 498 ctx, defaultOnDemandRequestPriority, kmd, 499 rootDir.Children["a"].BlockPointer, block, data.TransientEntry, 500 BlockRequestWithPrefetch) 501 err = <-ch 502 require.NoError(t, err) 503 require.Equal(t, dirA, block) 504 505 t.Log("Release the prefetch for fileB.") 506 continueChFileB <- nil 507 t.Log("Wait for the prefetch to finish.") 508 waitForPrefetchOrBust( 509 ctx, t, q.Prefetcher(), rootDir.Children["a"].BlockPointer) 510 waitForPrefetchOrBust( 511 ctx, t, q.Prefetcher(), rootDir.Children["b"].BlockPointer) 512 513 testPrefetcherCheckGet(t, cache, dirA.Children["b"].BlockPointer, fileB, 514 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 515 // Check that the dir block is marked as having been prefetched. 516 testPrefetcherCheckGet(t, cache, rootDir.Children["a"].BlockPointer, dirA, 517 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 518 519 t.Log("Remove the prefetched file block from the cache.") 520 err = cache.DeleteTransient(dirA.Children["b"].BlockPointer.ID, kmd.TlfID()) 521 require.NoError(t, err) 522 _, err = cache.Get(dirA.Children["b"].BlockPointer) 523 require.EqualError(t, err, 524 data.NoSuchBlockError{ID: dirA.Children["b"].BlockPointer.ID}.Error()) 525 526 t.Log("Request the second-level directory block again. No prefetches " + 527 "should be triggered.") 528 block = &data.DirBlock{} 529 ch = q.Request( 530 context.Background(), defaultOnDemandRequestPriority, kmd, 531 rootDir.Children["a"].BlockPointer, block, data.TransientEntry, 532 BlockRequestWithPrefetch) 533 err = <-ch 534 require.NoError(t, err) 535 require.Equal(t, dirA, block) 536 537 t.Log("Wait for the prefetch to finish.") 538 waitForPrefetchOrBust( 539 ctx, t, q.Prefetcher(), rootDir.Children["a"].BlockPointer) 540 } 541 542 func TestPrefetcherNoRepeatedPrefetch(t *testing.T) { 543 t.Log("Test that prefetches are only triggered once for a given block.") 544 q, bg, config := initPrefetcherTest(t) 545 cache := config.BlockCache().(*data.BlockCacheStandard) 546 defer shutdownPrefetcherTest(t, q, nil) 547 548 t.Log("Initialize a direct dir block with an entry pointing to 1 file.") 549 fileA := makeFakeFileBlock(t, true) 550 rootPtr := makeRandomBlockPointer(t) 551 rootDir := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 552 "a": makeRandomDirEntry(t, data.File, 60, "a"), 553 }) 554 ptrA := rootDir.Children["a"].BlockPointer 555 556 _, continueChRootDir := bg.setBlockToReturn(rootPtr, rootDir) 557 _, continueChFileA := bg.setBlockToReturn(ptrA, fileA) 558 559 t.Log("Request the root block.") 560 var block data.Block = &data.DirBlock{} 561 kmd := makeKMD() 562 ctx, cancel := context.WithTimeout( 563 context.Background(), individualTestTimeout) 564 defer cancel() 565 ch := q.Request( 566 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, 567 block, data.TransientEntry, BlockRequestWithPrefetch) 568 continueChRootDir <- nil 569 err := <-ch 570 require.NoError(t, err) 571 require.Equal(t, rootDir, block) 572 573 t.Log("Release the prefetched block.") 574 continueChFileA <- nil 575 576 t.Log("Wait for the prefetch to finish, then verify that the prefetched " + 577 "block is in the cache.") 578 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), ptrA) 579 testPrefetcherCheckGet( 580 t, config.BlockCache(), ptrA, fileA, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 581 582 t.Log("Remove the prefetched block from the cache.") 583 err = cache.DeleteTransient(ptrA.ID, kmd.TlfID()) 584 require.NoError(t, err) 585 _, err = cache.Get(ptrA) 586 require.EqualError(t, err, data.NoSuchBlockError{ID: ptrA.ID}.Error()) 587 588 t.Log("Request the root block again. It should be cached, so it should " + 589 "return without needing to release the block.") 590 block = &data.DirBlock{} 591 ch = q.Request( 592 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, 593 block, data.TransientEntry, BlockRequestWithPrefetch) 594 err = <-ch 595 require.NoError(t, err) 596 require.Equal(t, rootDir, block) 597 598 t.Log("Wait for the prefetch to finish, then verify that the child " + 599 "block is still not in the cache.") 600 _, err = cache.Get(ptrA) 601 require.EqualError(t, err, data.NoSuchBlockError{ID: ptrA.ID}.Error()) 602 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 603 } 604 605 func TestPrefetcherEmptyDirectDirBlock(t *testing.T) { 606 t.Log("Test empty direct dir block prefetching.") 607 q, bg, config := initPrefetcherTest(t) 608 prefetchSyncCh := make(chan struct{}) 609 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 610 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 611 defer func() { <-p1Ch }() 612 notifySyncCh(t, prefetchSyncCh) 613 614 t.Log("Initialize an empty direct dir block.") 615 rootPtr := makeRandomBlockPointer(t) 616 rootDir := makeFakeDirBlockWithChildren(map[string]data.DirEntry{}) 617 618 _, continueChRootDir := bg.setBlockToReturn(rootPtr, rootDir) 619 620 var block data.Block = &data.DirBlock{} 621 ctx, cancel := context.WithTimeout( 622 context.Background(), individualTestTimeout) 623 defer cancel() 624 kmd := makeKMD() 625 ch := q.Request( 626 ctx, defaultOnDemandRequestPriority, kmd, 627 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 628 continueChRootDir <- nil 629 err := <-ch 630 require.NoError(t, err) 631 require.Equal(t, rootDir, block) 632 633 t.Log("Wait for prefetching to complete.") 634 notifySyncCh(t, prefetchSyncCh) 635 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 636 637 t.Log("Ensure that the directory block is in the cache.") 638 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootDir, 639 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 640 } 641 642 func testPrefetcherForSyncedTLF( 643 t *testing.T, q *blockRetrievalQueue, bg *fakeBlockGetter, 644 config *testBlockRetrievalConfig, prefetchSyncCh chan struct{}, 645 kmd libkey.KeyMetadata, explicitSync bool) { 646 t.Log("Initialize a direct dir block with entries pointing to 2 files " + 647 "and 1 directory. The directory has an entry pointing to another " + 648 "file, which has 2 indirect blocks.") 649 fileA := makeFakeFileBlock(t, true) 650 fileC := makeFakeFileBlock(t, true) 651 rootPtr := makeRandomBlockPointer(t) 652 rootDir := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 653 "a": makeRandomDirEntry(t, data.File, 100, "a"), 654 "b": makeRandomDirEntry(t, data.Dir, 60, "b"), 655 "c": makeRandomDirEntry(t, data.Exec, 20, "c"), 656 }) 657 dirB := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 658 "d": makeRandomDirEntry(t, data.File, 100, "d"), 659 }) 660 dirBfileDptrs := []data.IndirectFilePtr{ 661 makeFakeIndirectFilePtr(t, 0), 662 makeFakeIndirectFilePtr(t, 150), 663 } 664 dirBfileD := makeFakeFileBlockWithIPtrs(dirBfileDptrs) 665 dirBfileDblock1 := makeFakeFileBlock(t, true) 666 dirBfileDblock2 := makeFakeFileBlock(t, true) 667 668 _, continueChRootDir := bg.setBlockToReturn(rootPtr, rootDir) 669 _, continueChFileA := 670 bg.setBlockToReturn(rootDir.Children["a"].BlockPointer, fileA) 671 _, continueChDirB := 672 bg.setBlockToReturn(rootDir.Children["b"].BlockPointer, dirB) 673 _, continueChFileC := 674 bg.setBlockToReturn(rootDir.Children["c"].BlockPointer, fileC) 675 _, continueChDirBfileD := 676 bg.setBlockToReturn(dirB.Children["d"].BlockPointer, dirBfileD) 677 678 _, continueChDirBfileDblock1 := 679 bg.setBlockToReturn(dirBfileDptrs[0].BlockPointer, dirBfileDblock1) 680 _, continueChDirBfileDblock2 := 681 bg.setBlockToReturn(dirBfileDptrs[1].BlockPointer, dirBfileDblock2) 682 683 var block data.Block = &data.DirBlock{} 684 action := BlockRequestWithPrefetch 685 if explicitSync { 686 action = BlockRequestWithDeepSync 687 } 688 ch := q.Request( 689 context.Background(), defaultOnDemandRequestPriority, kmd, rootPtr, 690 block, data.TransientEntry, action) 691 continueChRootDir <- nil 692 err := <-ch 693 require.NoError(t, err) 694 require.Equal(t, rootDir, block) 695 696 t.Log("Release all the blocks.") 697 ctx, cancel := context.WithTimeout( 698 context.Background(), individualTestTimeout) 699 defer cancel() 700 waitChCh := make(chan (<-chan struct{}), 1) 701 statusCh := make(chan PrefetchProgress) 702 go func() { 703 waitCh, err := q.Prefetcher().WaitChannelForBlockPrefetch(ctx, rootPtr) 704 if err != nil { 705 waitChCh <- nil 706 } else { 707 waitChCh <- waitCh 708 } 709 status, _ := q.Prefetcher().Status(ctx, rootPtr) 710 statusCh <- status 711 overallStatus := q.Prefetcher().OverallSyncStatus() 712 statusCh <- overallStatus 713 continueChFileC <- nil 714 continueChDirB <- nil 715 // After this, the prefetch worker can either pick up the third child of 716 // dir1 (continueCh2), or the first child of dir2 (continueCh5). 717 // TODO: The prefetcher should have a "global" prefetch priority 718 // reservation system that goes down with each next set of prefetches. 719 notifyContinueCh(continueChFileA) 720 notifyContinueCh(continueChDirBfileD) 721 notifyContinueCh(continueChDirBfileDblock1) 722 notifyContinueCh(continueChDirBfileDblock2) 723 }() 724 725 t.Log("Wait for prefetching to complete.") 726 // Release after prefetching rootDir 727 notifySyncCh(t, prefetchSyncCh) 728 var waitCh <-chan struct{} 729 select { 730 case waitCh = <-waitChCh: 731 require.NotNil(t, waitCh) 732 case <-ctx.Done(): 733 t.Fatal(ctx.Err()) 734 } 735 // Release after getting waitCh. 736 notifySyncCh(t, prefetchSyncCh) 737 select { 738 case status := <-statusCh: 739 // The root block has 3 children (the root block itself 740 // doesn't count in the bytes total). 741 require.Equal(t, uint64(3*testFakeBlockSize), status.SubtreeBytesTotal) 742 require.Equal(t, uint64(0), status.SubtreeBytesFetched) 743 require.Equal(t, config.Clock().Now(), status.Start) 744 case <-ctx.Done(): 745 t.Fatal(ctx.Err()) 746 } 747 select { 748 case overallStatus := <-statusCh: 749 // The root block _does_ count in the overall total, and has 750 // already been fetched. 751 require.Equal( 752 t, uint64(4*testFakeBlockSize), overallStatus.SubtreeBytesTotal) 753 require.Equal( 754 t, uint64(1*testFakeBlockSize), overallStatus.SubtreeBytesFetched) 755 require.Equal(t, config.Clock().Now(), overallStatus.Start) 756 case <-ctx.Done(): 757 t.Fatal(ctx.Err()) 758 } 759 // Release after prefetching fileC 760 notifySyncCh(t, prefetchSyncCh) 761 // Release after prefetching dirB 762 notifySyncCh(t, prefetchSyncCh) 763 // Release after prefetching fileA 764 notifySyncCh(t, prefetchSyncCh) 765 // Release after prefetching dirBfileD 766 notifySyncCh(t, prefetchSyncCh) 767 // Release after prefetching dirBfileDblock1 768 notifySyncCh(t, prefetchSyncCh) 769 // Release after prefetching dirBfileDblock2 770 notifySyncCh(t, prefetchSyncCh) 771 // Then we wait for the pending prefetches to complete. 772 t.Log("Waiting for prefetcher to signal completion") 773 select { 774 case <-waitCh: 775 case <-ctx.Done(): 776 t.Fatal(ctx.Err()) 777 } 778 779 t.Log("Ensure that the prefetched blocks are all in the cache.") 780 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootDir, 781 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 782 testPrefetcherCheckGet(t, config.BlockCache(), 783 rootDir.Children["c"].BlockPointer, fileC, FinishedPrefetch, 784 kmd.TlfID(), config.DiskBlockCache()) 785 testPrefetcherCheckGet(t, config.BlockCache(), 786 rootDir.Children["b"].BlockPointer, dirB, FinishedPrefetch, 787 kmd.TlfID(), config.DiskBlockCache()) 788 testPrefetcherCheckGet(t, config.BlockCache(), 789 rootDir.Children["a"].BlockPointer, fileA, FinishedPrefetch, 790 kmd.TlfID(), config.DiskBlockCache()) 791 testPrefetcherCheckGet(t, config.BlockCache(), 792 dirB.Children["d"].BlockPointer, dirBfileD, FinishedPrefetch, 793 kmd.TlfID(), config.DiskBlockCache()) 794 testPrefetcherCheckGet(t, config.BlockCache(), 795 dirBfileDptrs[0].BlockPointer, dirBfileDblock1, FinishedPrefetch, 796 kmd.TlfID(), config.DiskBlockCache()) 797 testPrefetcherCheckGet(t, config.BlockCache(), 798 dirBfileDptrs[1].BlockPointer, dirBfileDblock2, FinishedPrefetch, 799 kmd.TlfID(), config.DiskBlockCache()) 800 801 block = &data.DirBlock{} 802 ch = q.Request( 803 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, 804 block, data.TransientEntry, BlockRequestWithPrefetch) 805 // We don't need to release the block this time because it should be cached 806 // already. 807 err = <-ch 808 require.NoError(t, err) 809 require.Equal(t, rootDir, block) 810 811 notifySyncCh(t, prefetchSyncCh) 812 t.Log("Wait for prefetching to complete.") 813 814 // FIXME: Unknown synchronization flake coming from the prefetches above. 815 // To make CI work again, we close the `prefetchSyncCh` which unblocks all 816 // prefetches. 817 close(prefetchSyncCh) 818 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 819 820 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootDir, 821 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 822 } 823 824 func TestPrefetcherForSyncedTLF(t *testing.T) { 825 t.Log("Test synced TLF prefetching.") 826 q, bg, config := initPrefetcherTest(t) 827 prefetchSyncCh := make(chan struct{}) 828 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 829 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 830 defer func() { <-p1Ch }() 831 notifySyncCh(t, prefetchSyncCh) 832 833 kmd := makeKMD() 834 _, err := config.SetTlfSyncState( 835 context.Background(), kmd.TlfID(), FolderSyncConfig{ 836 Mode: keybase1.FolderSyncMode_ENABLED, 837 }) 838 require.NoError(t, err) 839 testPrefetcherForSyncedTLF(t, q, bg, config, prefetchSyncCh, kmd, false) 840 } 841 842 func TestPrefetcherForRequestedSync(t *testing.T) { 843 t.Log("Test explicitly-requested synced prefetching.") 844 q, bg, config := initPrefetcherTest(t) 845 prefetchSyncCh := make(chan struct{}) 846 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 847 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 848 defer func() { <-p1Ch }() 849 notifySyncCh(t, prefetchSyncCh) 850 851 kmd := makeKMD() 852 testPrefetcherForSyncedTLF(t, q, bg, config, prefetchSyncCh, kmd, true) 853 } 854 855 func TestPrefetcherMultiLevelIndirectFile(t *testing.T) { 856 t.Log("Test multi-level indirect file block prefetching.") 857 q, bg, config := initPrefetcherTest(t) 858 prefetchSyncCh := make(chan struct{}) 859 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 860 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 861 defer func() { <-p1Ch }() 862 notifySyncCh(t, prefetchSyncCh) 863 ctx, cancel := context.WithTimeout( 864 context.Background(), individualTestTimeout) 865 defer cancel() 866 867 t.Log("Initialize an indirect file block pointing to 2 file data blocks.") 868 ptrs := []data.IndirectFilePtr{ 869 makeFakeIndirectFilePtr(t, 0), 870 makeFakeIndirectFilePtr(t, 150), 871 } 872 rootPtr := makeRandomBlockPointer(t) 873 rootBlock := &data.FileBlock{IPtrs: ptrs} 874 rootBlock.IsInd = true 875 indBlock1 := &data.FileBlock{IPtrs: []data.IndirectFilePtr{ 876 makeFakeIndirectFilePtr(t, 10), 877 makeFakeIndirectFilePtr(t, 20), 878 }} 879 indBlock1.IsInd = true 880 indBlock2 := &data.FileBlock{IPtrs: []data.IndirectFilePtr{ 881 makeFakeIndirectFilePtr(t, 30), 882 makeFakeIndirectFilePtr(t, 40), 883 }} 884 indBlock2.IsInd = true 885 indBlock11 := makeFakeFileBlock(t, true) 886 indBlock12 := makeFakeFileBlock(t, true) 887 indBlock21 := makeFakeFileBlock(t, true) 888 indBlock22 := makeFakeFileBlock(t, true) 889 890 _, continueChRootBlock := bg.setBlockToReturn(rootPtr, rootBlock) 891 _, continueChIndBlock1 := 892 bg.setBlockToReturn(ptrs[0].BlockPointer, indBlock1) 893 _, continueChIndBlock2 := 894 bg.setBlockToReturn(ptrs[1].BlockPointer, indBlock2) 895 _, continueChIndBlock11 := 896 bg.setBlockToReturn(indBlock1.IPtrs[0].BlockPointer, indBlock11) 897 _, continueChIndBlock12 := 898 bg.setBlockToReturn(indBlock1.IPtrs[1].BlockPointer, indBlock12) 899 _, continueChIndBlock21 := 900 bg.setBlockToReturn(indBlock2.IPtrs[0].BlockPointer, indBlock21) 901 _, continueChIndBlock22 := 902 bg.setBlockToReturn(indBlock2.IPtrs[1].BlockPointer, indBlock22) 903 904 var block data.Block = &data.FileBlock{} 905 kmd := makeKMD() 906 ch := q.Request( 907 ctx, defaultOnDemandRequestPriority, kmd, rootPtr, block, 908 data.TransientEntry, BlockRequestWithPrefetch) 909 continueChRootBlock <- nil 910 notifySyncCh(t, prefetchSyncCh) 911 err := <-ch 912 require.NoError(t, err) 913 require.Equal(t, rootBlock, block) 914 915 t.Log("Release the prefetched indirect blocks.") 916 // Release 2 blocks 917 continueChIndBlock1 <- nil 918 notifySyncCh(t, prefetchSyncCh) 919 continueChIndBlock2 <- nil 920 notifySyncCh(t, prefetchSyncCh) 921 922 t.Log("Wait for the prefetch to finish.") 923 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 924 notifySyncCh(t, prefetchSyncCh) 925 926 t.Log("Ensure that the prefetched blocks are in the cache.") 927 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, rootBlock, 928 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 929 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[0].BlockPointer, 930 indBlock1, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 931 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[1].BlockPointer, 932 indBlock2, NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 933 934 t.Log("Fetch indirect block1 on-demand.") 935 block = &data.FileBlock{} 936 ch = q.Request( 937 ctx, defaultOnDemandRequestPriority, kmd, 938 rootBlock.IPtrs[0].BlockPointer, block, data.TransientEntry, 939 BlockRequestWithPrefetch) 940 notifySyncCh(t, prefetchSyncCh) 941 err = <-ch 942 require.NoError(t, err) 943 944 t.Log("Release the prefetch for indirect block1.") 945 // Release 2 blocks 946 continueChIndBlock11 <- nil 947 notifySyncCh(t, prefetchSyncCh) 948 continueChIndBlock12 <- nil 949 notifySyncCh(t, prefetchSyncCh) 950 951 t.Log("Fetch indirect block2 on-demand.") 952 block = &data.FileBlock{} 953 ch = q.Request( 954 ctx, defaultOnDemandRequestPriority, kmd, 955 rootBlock.IPtrs[1].BlockPointer, block, data.TransientEntry, 956 BlockRequestWithPrefetch) 957 notifySyncCh(t, prefetchSyncCh) 958 err = <-ch 959 require.NoError(t, err) 960 961 t.Log("Release the prefetch for indirect block2.") 962 // Release 2 blocks 963 continueChIndBlock21 <- nil 964 notifySyncCh(t, prefetchSyncCh) 965 continueChIndBlock22 <- nil 966 notifySyncCh(t, prefetchSyncCh) 967 968 t.Log("Fetch indirect block11 on-demand.") 969 block = &data.FileBlock{} 970 ch = q.Request( 971 ctx, defaultOnDemandRequestPriority, kmd, 972 indBlock1.IPtrs[0].BlockPointer, block, data.TransientEntry, 973 BlockRequestWithPrefetch) 974 notifySyncCh(t, prefetchSyncCh) 975 err = <-ch 976 require.NoError(t, err) 977 978 t.Log("Fetch indirect block12 on-demand.") 979 block = &data.FileBlock{} 980 ch = q.Request( 981 ctx, defaultOnDemandRequestPriority, kmd, 982 indBlock1.IPtrs[1].BlockPointer, block, data.TransientEntry, 983 BlockRequestWithPrefetch) 984 notifySyncCh(t, prefetchSyncCh) 985 err = <-ch 986 require.NoError(t, err) 987 988 t.Log("Wait for the prefetch to finish.") 989 waitForPrefetchOrBust( 990 ctx, t, q.Prefetcher(), indBlock1.IPtrs[1].BlockPointer) 991 992 t.Log("Ensure that the prefetched blocks are in the cache, " + 993 "and the prefetch statuses are correct.") 994 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[1].BlockPointer, 995 indBlock2, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 996 testPrefetcherCheckGet( 997 t, config.BlockCache(), indBlock2.IPtrs[0].BlockPointer, indBlock21, 998 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 999 testPrefetcherCheckGet( 1000 t, config.BlockCache(), indBlock2.IPtrs[1].BlockPointer, indBlock22, 1001 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1002 testPrefetcherCheckGet(t, config.BlockCache(), ptrs[0].BlockPointer, 1003 indBlock1, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1004 testPrefetcherCheckGet(t, config.BlockCache(), 1005 indBlock1.IPtrs[0].BlockPointer, indBlock11, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1006 testPrefetcherCheckGet(t, config.BlockCache(), 1007 indBlock1.IPtrs[1].BlockPointer, indBlock12, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1008 } 1009 1010 func TestPrefetcherBackwardPrefetch(t *testing.T) { 1011 t.Log("Test synced TLF prefetching in a more complex fetch order.") 1012 q, bg, config := initPrefetcherTest(t) 1013 kmd := makeKMD() 1014 prefetchSyncCh := make(chan struct{}) 1015 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1016 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1017 defer func() { <-p1Ch }() 1018 notifySyncCh(t, prefetchSyncCh) 1019 1020 t.Log("Initialize a folder tree with structure: " + 1021 "root -> {b, a -> {ab, aa -> {aab, aaa}}}") 1022 rootPtr := makeRandomBlockPointer(t) 1023 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1024 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 1025 "b": makeRandomDirEntry(t, data.File, 20, "b"), 1026 }) 1027 a := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1028 "aa": makeRandomDirEntry(t, data.Dir, 30, "aa"), 1029 "ab": makeRandomDirEntry(t, data.File, 40, "ab"), 1030 }) 1031 b := makeFakeFileBlock(t, true) 1032 aa := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1033 "aaa": makeRandomDirEntry(t, data.File, 50, "aaa"), 1034 "aab": makeRandomDirEntry(t, data.File, 60, "aab"), 1035 }) 1036 ab := makeFakeFileBlock(t, true) 1037 aaa := makeFakeFileBlock(t, true) 1038 aab := makeFakeFileBlock(t, true) 1039 1040 _, contChRoot := bg.setBlockToReturn(rootPtr, root) 1041 _, contChA := bg.setBlockToReturn(root.Children["a"].BlockPointer, a) 1042 _, contChB := bg.setBlockToReturn(root.Children["b"].BlockPointer, b) 1043 _, contChAA := bg.setBlockToReturn(a.Children["aa"].BlockPointer, aa) 1044 _, contChAB := bg.setBlockToReturn(a.Children["ab"].BlockPointer, ab) 1045 _, contChAAA := bg.setBlockToReturn(aa.Children["aaa"].BlockPointer, aaa) 1046 _, contChAAB := bg.setBlockToReturn(aa.Children["aab"].BlockPointer, aab) 1047 1048 t.Log("Fetch dir aa.") 1049 var block data.Block = &data.DirBlock{} 1050 ch := q.Request( 1051 context.Background(), defaultOnDemandRequestPriority, kmd, 1052 a.Children["aa"].BlockPointer, block, data.TransientEntry, 1053 BlockRequestWithPrefetch) 1054 contChAA <- nil 1055 notifySyncCh(t, prefetchSyncCh) 1056 err := <-ch 1057 require.NoError(t, err) 1058 require.Equal(t, aa, block) 1059 1060 t.Log("Release prefetched children of dir aa.") 1061 contChAAA <- nil 1062 notifySyncCh(t, prefetchSyncCh) 1063 contChAAB <- nil 1064 notifySyncCh(t, prefetchSyncCh) 1065 1066 t.Log("Fetch file aaa.") 1067 block = &data.FileBlock{} 1068 ch = q.Request( 1069 context.Background(), defaultOnDemandRequestPriority, kmd, 1070 aa.Children["aaa"].BlockPointer, block, data.TransientEntry, 1071 BlockRequestWithPrefetch) 1072 notifySyncCh(t, prefetchSyncCh) 1073 err = <-ch 1074 require.NoError(t, err) 1075 1076 t.Log("Fetch file aab.") 1077 block = &data.FileBlock{} 1078 ch = q.Request( 1079 context.Background(), defaultOnDemandRequestPriority, kmd, 1080 aa.Children["aab"].BlockPointer, block, data.TransientEntry, 1081 BlockRequestWithPrefetch) 1082 notifySyncCh(t, prefetchSyncCh) 1083 err = <-ch 1084 require.NoError(t, err) 1085 1086 t.Log("Fetch file ab.") 1087 block = &data.FileBlock{} 1088 ch = q.Request( 1089 context.Background(), defaultOnDemandRequestPriority, kmd, 1090 a.Children["ab"].BlockPointer, block, data.TransientEntry, 1091 BlockRequestWithPrefetch) 1092 contChAB <- nil 1093 notifySyncCh(t, prefetchSyncCh) 1094 err = <-ch 1095 require.NoError(t, err) 1096 1097 t.Log("Fetch dir a.") 1098 block = &data.DirBlock{} 1099 ch = q.Request( 1100 context.Background(), defaultOnDemandRequestPriority, kmd, 1101 root.Children["a"].BlockPointer, block, data.TransientEntry, 1102 BlockRequestWithPrefetch) 1103 contChA <- nil 1104 notifySyncCh(t, prefetchSyncCh) 1105 err = <-ch 1106 require.NoError(t, err) 1107 1108 t.Log("Release prefetched children of dir a.") 1109 notifySyncCh(t, prefetchSyncCh) 1110 notifySyncCh(t, prefetchSyncCh) 1111 1112 t.Log("Fetch file b.") 1113 block = &data.FileBlock{} 1114 ctx, cancel := context.WithTimeout( 1115 context.Background(), individualTestTimeout) 1116 defer cancel() 1117 ch = q.Request( 1118 ctx, defaultOnDemandRequestPriority, kmd, 1119 root.Children["b"].BlockPointer, block, data.TransientEntry, 1120 BlockRequestWithPrefetch) 1121 contChB <- nil 1122 notifySyncCh(t, prefetchSyncCh) 1123 err = <-ch 1124 require.NoError(t, err) 1125 1126 t.Log("Fetch dir root.") 1127 block = &data.DirBlock{} 1128 ch = q.Request( 1129 ctx, defaultOnDemandRequestPriority, kmd, 1130 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1131 contChRoot <- nil 1132 notifySyncCh(t, prefetchSyncCh) 1133 err = <-ch 1134 require.NoError(t, err) 1135 1136 t.Log("Release prefetched children of root.") 1137 notifySyncCh(t, prefetchSyncCh) 1138 notifySyncCh(t, prefetchSyncCh) 1139 1140 t.Log("Wait for the prefetch to finish.") 1141 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1142 1143 t.Log("Ensure that the prefetched blocks are in the cache, " + 1144 "and the prefetch statuses are correct.") 1145 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1146 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1147 testPrefetcherCheckGet(t, config.BlockCache(), 1148 root.Children["a"].BlockPointer, a, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1149 testPrefetcherCheckGet(t, config.BlockCache(), 1150 root.Children["b"].BlockPointer, b, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1151 testPrefetcherCheckGet(t, config.BlockCache(), 1152 a.Children["aa"].BlockPointer, aa, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1153 testPrefetcherCheckGet(t, config.BlockCache(), 1154 a.Children["ab"].BlockPointer, ab, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1155 testPrefetcherCheckGet(t, config.BlockCache(), 1156 aa.Children["aaa"].BlockPointer, aaa, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1157 testPrefetcherCheckGet(t, config.BlockCache(), 1158 aa.Children["aab"].BlockPointer, aab, FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1159 } 1160 1161 func TestPrefetcherUnsyncedThenSyncedPrefetch(t *testing.T) { 1162 t.Log("Test synced TLF prefetching in a more complex fetch order.") 1163 q, bg, config := initPrefetcherTest(t) 1164 kmd := makeKMD() 1165 prefetchSyncCh := make(chan struct{}) 1166 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1167 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1168 defer func() { <-p1Ch }() 1169 notifySyncCh(t, prefetchSyncCh) 1170 1171 t.Log("Initialize a folder tree with structure: " + 1172 "root -> {b, a -> {ab, aa -> {aab, aaa}}}") 1173 rootPtr := makeRandomBlockPointer(t) 1174 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1175 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 1176 "b": makeRandomDirEntry(t, data.File, 20, "b"), 1177 }) 1178 aPtr := root.Children["a"].BlockPointer 1179 a := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1180 "aa": makeRandomDirEntry(t, data.Dir, 30, "aa"), 1181 "ab": makeRandomDirEntry(t, data.File, 40, "ab"), 1182 }) 1183 bPtr := root.Children["b"].BlockPointer 1184 b := makeFakeFileBlock(t, true) 1185 aaPtr := a.Children["aa"].BlockPointer 1186 aa := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1187 "aaa": makeRandomDirEntry(t, data.File, 50, "aaa"), 1188 "aab": makeRandomDirEntry(t, data.File, 60, "aab"), 1189 }) 1190 abPtr := a.Children["ab"].BlockPointer 1191 ab := makeFakeFileBlock(t, true) 1192 aaaPtr := aa.Children["aaa"].BlockPointer 1193 aaa := makeFakeFileBlock(t, true) 1194 aabPtr := aa.Children["aab"].BlockPointer 1195 aab := makeFakeFileBlock(t, true) 1196 1197 _, contChRoot := bg.setBlockToReturn(rootPtr, root) 1198 _, contChA := bg.setBlockToReturn(aPtr, a) 1199 _, contChB := bg.setBlockToReturn(bPtr, b) 1200 _, contChAA := bg.setBlockToReturn(aaPtr, aa) 1201 _, contChAB := bg.setBlockToReturn(abPtr, ab) 1202 _, contChAAA := bg.setBlockToReturn(aaaPtr, aaa) 1203 _, contChAAB := bg.setBlockToReturn(aabPtr, aab) 1204 1205 t.Log("Fetch dir root.") 1206 block := &data.DirBlock{} 1207 ctx, cancel := context.WithTimeout( 1208 context.Background(), individualTestTimeout) 1209 defer cancel() 1210 ch := q.Request( 1211 ctx, defaultOnDemandRequestPriority, kmd, 1212 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1213 contChRoot <- nil 1214 notifySyncCh(t, prefetchSyncCh) 1215 err := <-ch 1216 require.NoError(t, err) 1217 1218 t.Log("Release prefetched children of root.") 1219 contChA <- nil 1220 notifySyncCh(t, prefetchSyncCh) 1221 contChB <- nil 1222 notifySyncCh(t, prefetchSyncCh) 1223 1224 t.Log("Now set the folder to sync.") 1225 _, err = config.SetTlfSyncState(ctx, kmd.TlfID(), FolderSyncConfig{ 1226 Mode: keybase1.FolderSyncMode_ENABLED, 1227 }) 1228 require.NoError(t, err) 1229 p2Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1230 defer func() { <-p2Ch }() 1231 notifySyncCh(t, prefetchSyncCh) 1232 1233 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1234 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1235 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, NoPrefetch, 1236 kmd.TlfID(), config.DiskBlockCache()) 1237 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, FinishedPrefetch, 1238 kmd.TlfID(), config.DiskBlockCache()) 1239 1240 t.Log("Fetch dir root again.") 1241 block = &data.DirBlock{} 1242 ch = q.Request( 1243 ctx, defaultOnDemandRequestPriority, kmd, 1244 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1245 err = <-ch 1246 require.NoError(t, err) 1247 1248 t.Log("Release all the blocks.") 1249 go func() { 1250 // After this, the prefetch worker order is less clear due to 1251 // priorities. 1252 // TODO: The prefetcher should have a "global" prefetch priority 1253 // reservation system that goes down with each next set of prefetches. 1254 notifyContinueCh(contChAA) 1255 notifyContinueCh(contChAB) 1256 notifyContinueCh(contChAAA) 1257 notifyContinueCh(contChAAB) 1258 }() 1259 1260 t.Log("Wait for prefetching to complete.") 1261 // Release after prefetching root 1262 notifySyncCh(t, prefetchSyncCh) 1263 // Release after prefetching a 1264 notifySyncCh(t, prefetchSyncCh) 1265 // Release after prefetching b 1266 notifySyncCh(t, prefetchSyncCh) 1267 // Release after prefetching aa 1268 notifySyncCh(t, prefetchSyncCh) 1269 // Release after prefetching ab 1270 notifySyncCh(t, prefetchSyncCh) 1271 // Release after prefetching aaa 1272 notifySyncCh(t, prefetchSyncCh) 1273 // Release after prefetching aab 1274 notifySyncCh(t, prefetchSyncCh) 1275 // Then we wait for the pending prefetches to complete. 1276 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1277 1278 t.Log("Ensure that the prefetched blocks are in the cache, " + 1279 "and the prefetch statuses are correct.") 1280 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1281 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1282 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, 1283 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1284 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, 1285 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1286 testPrefetcherCheckGet(t, config.BlockCache(), aaPtr, aa, 1287 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1288 testPrefetcherCheckGet(t, config.BlockCache(), abPtr, ab, 1289 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1290 testPrefetcherCheckGet(t, config.BlockCache(), aaaPtr, aaa, 1291 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1292 testPrefetcherCheckGet(t, config.BlockCache(), aabPtr, aab, 1293 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1294 } 1295 1296 func setLimiterLimits( 1297 limiter *backpressureDiskLimiter, syncLimit, workingLimit int64) { 1298 limiter.lock.Lock() 1299 defer limiter.lock.Unlock() 1300 limiter.syncCacheByteTracker.limit = syncLimit 1301 limiter.syncCacheByteTracker.updateSemaphoreMax() 1302 limiter.diskCacheByteTracker.limit = workingLimit 1303 limiter.diskCacheByteTracker.updateSemaphoreMax() 1304 } 1305 1306 func testGetDiskCacheBytes(syncCache, workingCache *DiskBlockCacheLocal) ( 1307 syncBytes, workingBytes int64) { 1308 syncBytes = int64(syncCache.getCurrBytes()) 1309 workingBytes = int64(workingCache.getCurrBytes()) 1310 return syncBytes, workingBytes 1311 } 1312 1313 func TestSyncBlockCacheWithPrefetcher(t *testing.T) { 1314 t.Log("Test synced TLF prefetching with the disk cache.") 1315 cache, dbcConfig := initDiskBlockCacheTest(t) 1316 q, bg, config := initPrefetcherTestWithDiskCache(t, cache) 1317 ctx, cancel := context.WithTimeout( 1318 context.Background(), individualTestTimeout) 1319 defer cancel() 1320 defer cache.Shutdown(ctx) 1321 kmd := makeKMD() 1322 prefetchSyncCh := make(chan struct{}) 1323 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1324 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1325 defer func() { <-p1Ch }() 1326 notifySyncCh(t, prefetchSyncCh) 1327 1328 syncCache := cache.syncCache 1329 workingCache := cache.workingSetCache 1330 1331 t.Log("Initialize a folder tree with structure: " + 1332 "root -> {b, a -> {ab, aa -> {aab, aaa}}}") 1333 rootPtr := makeRandomBlockPointer(t) 1334 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1335 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 1336 "b": makeRandomDirEntry(t, data.File, 20, "b"), 1337 }) 1338 aPtr := root.Children["a"].BlockPointer 1339 a := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1340 "aa": makeRandomDirEntry(t, data.Dir, 30, "aa"), 1341 "ab": makeRandomDirEntry(t, data.File, 40, "ab"), 1342 }) 1343 bPtr := root.Children["b"].BlockPointer 1344 b := makeFakeFileBlock(t, true) 1345 1346 encRoot, serverHalfRoot := 1347 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 1348 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, a, dbcConfig) 1349 encB, serverHalfB := setupRealBlockForDiskCache(t, bPtr, b, dbcConfig) 1350 1351 _, _ = bg.setBlockToReturn(rootPtr, root) 1352 _, _ = bg.setBlockToReturn(aPtr, a) 1353 _, _ = bg.setBlockToReturn(bPtr, b) 1354 err := cache.Put( 1355 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 1356 DiskBlockAnyCache) 1357 require.NoError(t, err) 1358 err = cache.Put( 1359 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 1360 require.NoError(t, err) 1361 err = cache.Put( 1362 ctx, kmd.TlfID(), bPtr.ID, encB, serverHalfB, DiskBlockAnyCache) 1363 require.NoError(t, err) 1364 1365 t.Log("Fetch dir root.") 1366 block := &data.DirBlock{} 1367 ch := q.Request( 1368 context.Background(), defaultOnDemandRequestPriority, kmd, 1369 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1370 notifySyncCh(t, prefetchSyncCh) 1371 err = <-ch 1372 require.NoError(t, err) 1373 1374 t.Log("Release prefetched children of root.") 1375 notifySyncCh(t, prefetchSyncCh) 1376 notifySyncCh(t, prefetchSyncCh) 1377 1378 t.Log("Now set the folder to sync.") 1379 _, err = config.SetTlfSyncState(ctx, kmd.TlfID(), FolderSyncConfig{ 1380 Mode: keybase1.FolderSyncMode_ENABLED, 1381 }) 1382 require.NoError(t, err) 1383 p2Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1384 defer func() { <-p2Ch }() 1385 notifySyncCh(t, prefetchSyncCh) 1386 1387 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1388 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1389 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, NoPrefetch, 1390 kmd.TlfID(), config.DiskBlockCache()) 1391 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, FinishedPrefetch, 1392 kmd.TlfID(), config.DiskBlockCache()) 1393 1394 t.Log("Set the cache maximum bytes to the current total.") 1395 syncBytes, workingBytes := testGetDiskCacheBytes(syncCache, workingCache) 1396 limiter := dbcConfig.DiskLimiter().(*backpressureDiskLimiter) 1397 setLimiterLimits(limiter, syncBytes, workingBytes) 1398 1399 t.Log("Fetch dir root again.") 1400 block = &data.DirBlock{} 1401 ch = q.Request( 1402 ctx, defaultOnDemandRequestPriority, kmd, 1403 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1404 err = <-ch 1405 require.NoError(t, err) 1406 1407 // Notify the sync chan once for the canceled prefetch. 1408 notifySyncCh(t, prefetchSyncCh) 1409 1410 t.Log("Prefetching shouldn't happen because the disk caches are full.") 1411 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1412 } 1413 1414 func TestPrefetcherBasicUnsyncedPrefetch(t *testing.T) { 1415 t.Log("Test basic unsynced prefetching with only 2 blocks.") 1416 q, bg, config := initPrefetcherTest(t) 1417 kmd := makeKMD() 1418 prefetchSyncCh := make(chan struct{}) 1419 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1420 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1421 defer func() { <-p1Ch }() 1422 notifySyncCh(t, prefetchSyncCh) 1423 1424 t.Log("Initialize a folder tree with structure: " + 1425 "root -> {a}") 1426 rootPtr := makeRandomBlockPointer(t) 1427 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1428 "a": makeRandomDirEntry(t, data.File, 10, "a"), 1429 }) 1430 aPtr := root.Children["a"].BlockPointer 1431 a := makeFakeFileBlock(t, true) 1432 1433 _, contChRoot := bg.setBlockToReturn(rootPtr, root) 1434 _, contChA := bg.setBlockToReturn(aPtr, a) 1435 1436 t.Log("Fetch dir root.") 1437 var block data.Block = &data.DirBlock{} 1438 ch := q.Request( 1439 context.Background(), defaultOnDemandRequestPriority, kmd, 1440 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1441 contChRoot <- nil 1442 notifySyncCh(t, prefetchSyncCh) 1443 err := <-ch 1444 require.NoError(t, err) 1445 1446 t.Log("Fetch child block \"a\" on demand.") 1447 block = &data.FileBlock{} 1448 ctx, cancel := context.WithTimeout( 1449 context.Background(), individualTestTimeout) 1450 defer cancel() 1451 ch = q.Request( 1452 ctx, defaultOnDemandRequestPriority, kmd, 1453 aPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1454 t.Log("Release child block \"a\".") 1455 contChA <- nil 1456 notifySyncCh(t, prefetchSyncCh) 1457 err = <-ch 1458 require.NoError(t, err) 1459 1460 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1461 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1462 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1463 kmd.TlfID(), config.DiskBlockCache()) 1464 1465 // Then we wait for the pending prefetches to complete. 1466 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1467 } 1468 1469 func TestPrefetcherBasicUnsyncedBackwardPrefetch(t *testing.T) { 1470 t.Log("Test basic unsynced prefetching with only 2 blocks fetched " + 1471 "in reverse.") 1472 q, bg, config := initPrefetcherTest(t) 1473 kmd := makeKMD() 1474 prefetchSyncCh := make(chan struct{}) 1475 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1476 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1477 defer func() { <-p1Ch }() 1478 notifySyncCh(t, prefetchSyncCh) 1479 1480 t.Log("Initialize a folder tree with structure: " + 1481 "root -> {a}") 1482 rootPtr := makeRandomBlockPointer(t) 1483 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1484 "a": makeRandomDirEntry(t, data.File, 10, "a"), 1485 }) 1486 aPtr := root.Children["a"].BlockPointer 1487 a := makeFakeFileBlock(t, true) 1488 1489 _, contChRoot := bg.setBlockToReturn(rootPtr, root) 1490 _, contChA := bg.setBlockToReturn(aPtr, a) 1491 1492 t.Log("Fetch child block \"a\" on demand.") 1493 var block data.Block = &data.FileBlock{} 1494 ctx, cancel := context.WithTimeout( 1495 context.Background(), individualTestTimeout) 1496 defer cancel() 1497 ch := q.Request( 1498 ctx, defaultOnDemandRequestPriority, kmd, 1499 aPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1500 t.Log("Release child block \"a\".") 1501 contChA <- nil 1502 notifySyncCh(t, prefetchSyncCh) 1503 err := <-ch 1504 require.NoError(t, err) 1505 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1506 kmd.TlfID(), config.DiskBlockCache()) 1507 1508 t.Log("Fetch dir root.") 1509 block = &data.DirBlock{} 1510 ch = q.Request( 1511 ctx, defaultOnDemandRequestPriority, kmd, 1512 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1513 contChRoot <- nil 1514 notifySyncCh(t, prefetchSyncCh) 1515 err = <-ch 1516 require.NoError(t, err) 1517 notifySyncCh(t, prefetchSyncCh) 1518 1519 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1520 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1521 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1522 kmd.TlfID(), config.DiskBlockCache()) 1523 1524 // Then we wait for the pending prefetches to complete. 1525 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1526 } 1527 1528 func TestPrefetcherUnsyncedPrefetchEvicted(t *testing.T) { 1529 t.Log("Test basic unsynced prefetching with a block that has been evicted.") 1530 dbc, dbcConfig := initDiskBlockCacheTest(t) 1531 q, bg, config := initPrefetcherTestWithDiskCache(t, dbc) 1532 // We don't want any of these blocks cached in memory. 1533 bcache := config.testCache 1534 ctx, cancel := context.WithTimeout( 1535 context.Background(), individualTestTimeout) 1536 defer cancel() 1537 kmd := makeKMD() 1538 prefetchSyncCh := make(chan struct{}) 1539 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1540 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1541 defer func() { <-p1Ch }() 1542 notifySyncCh(t, prefetchSyncCh) 1543 1544 t.Log("Initialize a folder tree with structure: " + 1545 "root -> {a}") 1546 rootPtr := makeRandomBlockPointer(t) 1547 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1548 "a": makeRandomDirEntry(t, data.File, 10, "a"), 1549 }) 1550 aPtr := root.Children["a"].BlockPointer 1551 a := makeFakeFileBlock(t, true) 1552 1553 encRoot, serverHalfRoot := 1554 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 1555 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, a, dbcConfig) 1556 1557 _, _ = bg.setBlockToReturn(rootPtr, root) 1558 _, _ = bg.setBlockToReturn(aPtr, a) 1559 err := dbc.Put( 1560 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 1561 DiskBlockAnyCache) 1562 require.NoError(t, err) 1563 err = dbc.Put( 1564 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 1565 require.NoError(t, err) 1566 1567 t.Log("Fetch dir root.") 1568 var block data.Block = &data.DirBlock{} 1569 ch := q.Request( 1570 context.Background(), defaultOnDemandRequestPriority, kmd, 1571 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1572 notifySyncCh(t, prefetchSyncCh) 1573 err = <-ch 1574 require.NoError(t, err) 1575 // Notify sync channel for the child block `a`. 1576 notifySyncCh(t, prefetchSyncCh) 1577 1578 t.Log("Set the metadata of the block in the disk cache to NoPrefetch, " + 1579 "simulating an eviction and a BlockServer.Get.") 1580 err = dbc.UpdateMetadata( 1581 ctx, kmd.TlfID(), rootPtr.ID, NoPrefetch, DiskBlockAnyCache) 1582 require.NoError(t, err) 1583 1584 t.Log("Evict the root block from the block cache.") 1585 err = bcache.DeleteTransient(rootPtr.ID, kmd.TlfID()) 1586 require.NoError(t, err) 1587 1588 t.Log("Fetch dir root again.") 1589 block = &data.DirBlock{} 1590 ch = q.Request( 1591 ctx, defaultOnDemandRequestPriority, kmd, 1592 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1593 notifySyncCh(t, prefetchSyncCh) 1594 err = <-ch 1595 require.NoError(t, err) 1596 1597 t.Log("Fetch child block \"a\" on demand.") 1598 block = &data.FileBlock{} 1599 ch = q.Request( 1600 ctx, defaultOnDemandRequestPriority, kmd, 1601 aPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1602 // Notify sync channel for the child block `a`. 1603 notifySyncCh(t, prefetchSyncCh) 1604 err = <-ch 1605 require.NoError(t, err) 1606 1607 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1608 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1609 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1610 kmd.TlfID(), config.DiskBlockCache()) 1611 1612 // Then we wait for the pending prefetches to complete. 1613 close(prefetchSyncCh) 1614 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1615 } 1616 1617 func TestPrefetcherUnsyncedPrefetchChildCanceled(t *testing.T) { 1618 t.Log("Partial regression test for KBFS-2588: when a prefetched block " + 1619 "has children waiting on a prefetch, a child cancelation should not " + 1620 "result in a panic for a future parent prefetch.") 1621 // Note: this test actually passes, because as long as the block count of 1622 // the parent is non-zero, the first child that completes it will also 1623 // remove it from the tree. See 1624 // TestPrefetcherUnsyncedPrefetchRootEvictedCanceled for the actual 1625 // regression test that reproduces the panic. 1626 dbc, dbcConfig := initDiskBlockCacheTest(t) 1627 q, bg, config := initPrefetcherTestWithDiskCache(t, dbc) 1628 bcache := config.testCache 1629 ctx, cancel := context.WithTimeout( 1630 context.Background(), individualTestTimeout) 1631 defer cancel() 1632 kmd := makeKMD() 1633 prefetchSyncCh := make(chan struct{}) 1634 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1635 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1636 defer func() { <-p1Ch }() 1637 notifySyncCh(t, prefetchSyncCh) 1638 1639 t.Log("Initialize a folder tree with structure: " + 1640 "root -> {a, b}") 1641 rootPtr := makeRandomBlockPointer(t) 1642 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1643 "a": makeRandomDirEntry(t, data.File, 10, "a"), 1644 "b": makeRandomDirEntry(t, data.File, 10, "b"), 1645 }) 1646 aPtr := root.Children["a"].BlockPointer 1647 bPtr := root.Children["b"].BlockPointer 1648 a := makeFakeFileBlock(t, true) 1649 b := makeFakeFileBlock(t, true) 1650 1651 encRoot, serverHalfRoot := 1652 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 1653 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, a, dbcConfig) 1654 encB, serverHalfB := setupRealBlockForDiskCache(t, bPtr, b, dbcConfig) 1655 1656 _, _ = bg.setBlockToReturn(rootPtr, root) 1657 _, _ = bg.setBlockToReturn(aPtr, a) 1658 _, _ = bg.setBlockToReturn(bPtr, b) 1659 err := dbc.Put( 1660 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 1661 DiskBlockAnyCache) 1662 require.NoError(t, err) 1663 err = dbc.Put( 1664 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 1665 require.NoError(t, err) 1666 err = dbc.Put( 1667 ctx, kmd.TlfID(), bPtr.ID, encB, serverHalfB, DiskBlockAnyCache) 1668 require.NoError(t, err) 1669 1670 t.Log("Fetch dir root.") 1671 var block data.Block = &data.DirBlock{} 1672 ch := q.Request( 1673 context.Background(), defaultOnDemandRequestPriority, kmd, 1674 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1675 notifySyncCh(t, prefetchSyncCh) 1676 err = <-ch 1677 require.NoError(t, err) 1678 // Notify sync channel for the child blocks `a` and `b`. 1679 notifySyncCh(t, prefetchSyncCh) 1680 notifySyncCh(t, prefetchSyncCh) 1681 1682 t.Log("Cancel the prefetch for block 'a'.") 1683 q.Prefetcher().CancelPrefetch(aPtr) 1684 // Notify sync channel for the cancelation. 1685 notifySyncCh(t, prefetchSyncCh) 1686 1687 t.Log("Set the metadata of the root block in the disk cache to " + 1688 "NoPrefetch, simulating an eviction and a BlockServer.Get.") 1689 err = dbc.UpdateMetadata( 1690 ctx, kmd.TlfID(), rootPtr.ID, NoPrefetch, DiskBlockAnyCache) 1691 require.NoError(t, err) 1692 1693 t.Log("Evict the root block from the block cache.") 1694 err = bcache.DeleteTransient(rootPtr.ID, kmd.TlfID()) 1695 require.NoError(t, err) 1696 1697 t.Log("Fetch dir root again.") 1698 block = &data.DirBlock{} 1699 ch = q.Request( 1700 context.Background(), defaultOnDemandRequestPriority, kmd, 1701 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1702 err = <-ch 1703 require.NoError(t, err) 1704 // Notify sync channel for only child block `a`, since `b` wasn't newly 1705 // triggered. 1706 notifySyncCh(t, prefetchSyncCh) 1707 1708 t.Log("Fetch child block \"a\" on demand.") 1709 block = &data.FileBlock{} 1710 ch = q.Request( 1711 ctx, defaultOnDemandRequestPriority, kmd, 1712 aPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1713 // Notify sync channel for the child block `a`. 1714 notifySyncCh(t, prefetchSyncCh) 1715 err = <-ch 1716 require.NoError(t, err) 1717 1718 t.Log("Fetch child block \"b\" on demand.") 1719 block = &data.FileBlock{} 1720 ch = q.Request( 1721 ctx, defaultOnDemandRequestPriority, kmd, 1722 bPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1723 // Notify sync channel for the child block `b`. 1724 notifySyncCh(t, prefetchSyncCh) 1725 err = <-ch 1726 require.NoError(t, err) 1727 1728 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1729 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1730 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1731 kmd.TlfID(), config.DiskBlockCache()) 1732 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, FinishedPrefetch, 1733 kmd.TlfID(), config.DiskBlockCache()) 1734 1735 // Then we wait for the pending prefetches to complete. 1736 close(prefetchSyncCh) 1737 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1738 } 1739 1740 func TestPrefetcherUnsyncedPrefetchParentCanceled(t *testing.T) { 1741 t.Log("Regression test for KBFS-2588: when a prefetched block has " + 1742 "children waiting on a prefetch, and it is canceled, subsequent " + 1743 "attempts to prefetch that parent block panic.") 1744 dbc, dbcConfig := initDiskBlockCacheTest(t) 1745 q, bg, config := initPrefetcherTestWithDiskCache(t, dbc) 1746 bcache := config.testCache 1747 ctx, cancel := context.WithTimeout( 1748 context.Background(), individualTestTimeout) 1749 defer cancel() 1750 kmd := makeKMD() 1751 prefetchSyncCh := make(chan struct{}) 1752 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1753 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1754 defer func() { <-p1Ch }() 1755 notifySyncCh(t, prefetchSyncCh) 1756 1757 t.Log("Initialize a folder tree with structure: " + 1758 "root -> {a, b}") 1759 rootPtr := makeRandomBlockPointer(t) 1760 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1761 "a": makeRandomDirEntry(t, data.File, 10, "a"), 1762 "b": makeRandomDirEntry(t, data.File, 10, "b"), 1763 }) 1764 aPtr := root.Children["a"].BlockPointer 1765 bPtr := root.Children["b"].BlockPointer 1766 a := makeFakeFileBlock(t, true) 1767 b := makeFakeFileBlock(t, true) 1768 1769 encRoot, serverHalfRoot := 1770 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 1771 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, a, dbcConfig) 1772 encB, serverHalfB := setupRealBlockForDiskCache(t, bPtr, b, dbcConfig) 1773 1774 _, _ = bg.setBlockToReturn(rootPtr, root) 1775 _, _ = bg.setBlockToReturn(aPtr, a) 1776 _, _ = bg.setBlockToReturn(bPtr, b) 1777 err := dbc.Put( 1778 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 1779 DiskBlockAnyCache) 1780 require.NoError(t, err) 1781 err = dbc.Put( 1782 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 1783 require.NoError(t, err) 1784 err = dbc.Put( 1785 ctx, kmd.TlfID(), bPtr.ID, encB, serverHalfB, DiskBlockAnyCache) 1786 require.NoError(t, err) 1787 1788 t.Log("Fetch dir root.") 1789 var block data.Block = &data.DirBlock{} 1790 ch := q.Request( 1791 ctx, defaultOnDemandRequestPriority, kmd, 1792 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1793 notifySyncCh(t, prefetchSyncCh) 1794 err = <-ch 1795 require.NoError(t, err) 1796 // Notify sync channel for the child blocks `a` and `b`. 1797 notifySyncCh(t, prefetchSyncCh) 1798 notifySyncCh(t, prefetchSyncCh) 1799 1800 t.Log("Cancel the prefetch for the root block.") 1801 q.Prefetcher().CancelPrefetch(rootPtr) 1802 // Notify sync channel for the cancelation. 1803 notifySyncCh(t, prefetchSyncCh) 1804 1805 t.Log("Set the metadata of the root block in the disk cache to " + 1806 "NoPrefetch, simulating an eviction and a BlockServer.Get.") 1807 err = dbc.UpdateMetadata( 1808 ctx, kmd.TlfID(), rootPtr.ID, NoPrefetch, DiskBlockAnyCache) 1809 require.NoError(t, err) 1810 1811 t.Log("Evict the root block from the block cache.") 1812 err = bcache.DeleteTransient(rootPtr.ID, kmd.TlfID()) 1813 require.NoError(t, err) 1814 1815 t.Log("Fetch dir root again.") 1816 block = &data.DirBlock{} 1817 ch = q.Request( 1818 ctx, defaultOnDemandRequestPriority, kmd, 1819 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1820 notifySyncCh(t, prefetchSyncCh) 1821 err = <-ch 1822 require.NoError(t, err) 1823 // Neither blocks `a` nor `b` got triggered, since they already existed. 1824 // So no need to notify the sync channel. 1825 1826 t.Log("Fetch child block \"a\" on demand.") 1827 block = &data.FileBlock{} 1828 ch = q.Request( 1829 ctx, defaultOnDemandRequestPriority, kmd, 1830 aPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1831 // Notify sync channel for the child block `a`. 1832 notifySyncCh(t, prefetchSyncCh) 1833 err = <-ch 1834 require.NoError(t, err) 1835 1836 t.Log("Fetch child block \"b\" on demand.") 1837 block = &data.FileBlock{} 1838 ch = q.Request( 1839 ctx, defaultOnDemandRequestPriority, kmd, 1840 bPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1841 // Notify sync channel for the child block `b`. 1842 notifySyncCh(t, prefetchSyncCh) 1843 err = <-ch 1844 require.NoError(t, err) 1845 1846 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1847 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1848 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, FinishedPrefetch, 1849 kmd.TlfID(), config.DiskBlockCache()) 1850 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, FinishedPrefetch, 1851 kmd.TlfID(), config.DiskBlockCache()) 1852 1853 // Then we wait for the pending prefetches to complete. 1854 close(prefetchSyncCh) 1855 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 1856 } 1857 1858 func waitDoneCh(ctx context.Context, t *testing.T, doneCh <-chan struct{}) { 1859 t.Helper() 1860 select { 1861 case <-doneCh: 1862 case <-ctx.Done(): 1863 t.Fatal(ctx.Err()) 1864 } 1865 } 1866 1867 func TestPrefetcherReschedules(t *testing.T) { 1868 t.Log("Test synced TLF prefetch rescheduling.") 1869 cache, dbcConfig := initDiskBlockCacheTest(t) 1870 q, bg, config := initPrefetcherTestWithDiskCache(t, cache) 1871 ctx, cancel := context.WithTimeout( 1872 context.Background(), individualTestTimeout) 1873 defer cancel() 1874 kmd := makeKMD() 1875 1876 // Declare and defer the close of this channel before the defer of 1877 // the prefetcher shutdown, to avoid data races where the 1878 // prefetcher could send to the channel after it's closed. 1879 prefetchDoneCh := make(chan struct{}) 1880 defer close(prefetchDoneCh) 1881 1882 prefetchSyncCh := make(chan struct{}) 1883 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 1884 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 1885 defer func() { <-p1Ch }() 1886 notifySyncCh(t, prefetchSyncCh) 1887 1888 syncCache := cache.syncCache 1889 workingCache := cache.workingSetCache 1890 1891 t.Log("Initialize a folder tree with structure: " + 1892 "root -> {b, a -> {ab, aa}}") 1893 rootPtr := makeRandomBlockPointer(t) 1894 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1895 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 1896 "b": makeRandomDirEntry(t, data.File, 20, "b"), 1897 }) 1898 aPtr := root.Children["a"].BlockPointer 1899 a := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 1900 "aa": makeRandomDirEntry(t, data.File, 30, "aa"), 1901 "ab": makeRandomDirEntry(t, data.File, 40, "ab"), 1902 }) 1903 aaPtr := a.Children["aa"].BlockPointer 1904 aa := makeFakeFileBlock(t, true) 1905 abPtr := a.Children["ab"].BlockPointer 1906 ab := makeFakeFileBlock(t, true) 1907 bPtr := root.Children["b"].BlockPointer 1908 b := makeFakeFileBlock(t, true) 1909 1910 encRoot, serverHalfRoot := 1911 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 1912 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, a, dbcConfig) 1913 encB, serverHalfB := setupRealBlockForDiskCache(t, bPtr, b, dbcConfig) 1914 encAA, serverHalfAA := setupRealBlockForDiskCache(t, aaPtr, aa, dbcConfig) 1915 encAB, serverHalfAB := setupRealBlockForDiskCache(t, abPtr, ab, dbcConfig) 1916 1917 _, _ = bg.setBlockToReturn(rootPtr, root) 1918 _, _ = bg.setBlockToReturn(aPtr, a) 1919 _, _ = bg.setBlockToReturn(aaPtr, aa) 1920 _, _ = bg.setBlockToReturn(abPtr, ab) 1921 _, _ = bg.setBlockToReturn(bPtr, b) 1922 err := cache.Put( 1923 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 1924 DiskBlockAnyCache) 1925 require.NoError(t, err) 1926 err = cache.Put( 1927 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 1928 require.NoError(t, err) 1929 err = cache.Put( 1930 ctx, kmd.TlfID(), aaPtr.ID, encAA, serverHalfAA, DiskBlockAnyCache) 1931 require.NoError(t, err) 1932 err = cache.Put( 1933 ctx, kmd.TlfID(), abPtr.ID, encAB, serverHalfAB, DiskBlockAnyCache) 1934 require.NoError(t, err) 1935 err = cache.Put( 1936 ctx, kmd.TlfID(), bPtr.ID, encB, serverHalfB, DiskBlockAnyCache) 1937 require.NoError(t, err) 1938 1939 _, err = config.SetTlfSyncState(ctx, kmd.TlfID(), FolderSyncConfig{ 1940 Mode: keybase1.FolderSyncMode_ENABLED, 1941 }) 1942 require.NoError(t, err) 1943 // We must use a special channel here to learn when each 1944 // prefetcher operation has finished. That's because we shouldn't 1945 // adjust the disk limiter limits during the processing of a 1946 // prefetcher operation. If we do, we introduce racy behavior 1947 // where sometimes the prefetcher will be able to write stuff to 1948 // the cache, and sometimes not. 1949 p2Ch := q.TogglePrefetcher(true, prefetchSyncCh, prefetchDoneCh) 1950 defer func() { <-p2Ch }() 1951 q.Prefetcher().(*blockPrefetcher).makeNewBackOff = func() backoff.BackOff { 1952 return &backoff.ZeroBackOff{} 1953 } 1954 1955 t.Log("Set the cache maximum bytes to the current total.") 1956 syncBytes, workingBytes := testGetDiskCacheBytes(syncCache, workingCache) 1957 limiter := dbcConfig.DiskLimiter().(*backpressureDiskLimiter) 1958 setLimiterLimits(limiter, syncBytes, workingBytes) 1959 1960 t.Log("Fetch dir root.") 1961 block := &data.DirBlock{} 1962 ch := q.Request( 1963 ctx, defaultOnDemandRequestPriority, kmd, 1964 rootPtr, block, data.TransientEntry, BlockRequestWithPrefetch) 1965 err = <-ch 1966 require.NoError(t, err) 1967 1968 t.Log("Release reschedule request of root.") 1969 notifySyncCh(t, prefetchSyncCh) 1970 waitDoneCh(ctx, t, prefetchDoneCh) 1971 1972 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 1973 NoPrefetch, kmd.TlfID(), config.DiskBlockCache()) 1974 1975 t.Log("Make room in the cache.") 1976 setLimiterLimits(limiter, math.MaxInt64, math.MaxInt64) 1977 1978 t.Log("Handle root's prefetch request") 1979 notifySyncCh(t, prefetchSyncCh) 1980 waitDoneCh(ctx, t, prefetchDoneCh) 1981 1982 t.Log("Handle two child prefetch requests (a and b)") 1983 notifySyncCh(t, prefetchSyncCh) 1984 waitDoneCh(ctx, t, prefetchDoneCh) 1985 notifySyncCh(t, prefetchSyncCh) 1986 waitDoneCh(ctx, t, prefetchDoneCh) 1987 1988 blockA := &data.DirBlock{} 1989 chA := q.Request( 1990 ctx, defaultOnDemandRequestPriority, kmd, 1991 aPtr, blockA, data.TransientEntry, BlockRequestWithPrefetch) 1992 err = <-chA 1993 require.NoError(t, err) 1994 blockB := &data.FileBlock{} 1995 chB := q.Request( 1996 ctx, defaultOnDemandRequestPriority, kmd, 1997 bPtr, blockB, data.TransientEntry, BlockRequestWithPrefetch) 1998 err = <-chB 1999 require.NoError(t, err) 2000 2001 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 2002 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2003 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, 2004 TriggeredPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2005 testPrefetcherCheckGet(t, config.BlockCache(), bPtr, b, 2006 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2007 2008 t.Log("Set the cache maximum bytes to the current total again.") 2009 syncBytes, workingBytes = testGetDiskCacheBytes(syncCache, workingCache) 2010 setLimiterLimits(limiter, syncBytes, workingBytes) 2011 2012 t.Log("Process requests of two more children (aa and ab)") 2013 notifySyncCh(t, prefetchSyncCh) 2014 waitDoneCh(ctx, t, prefetchDoneCh) 2015 notifySyncCh(t, prefetchSyncCh) 2016 waitDoneCh(ctx, t, prefetchDoneCh) 2017 2018 t.Log("Make room in the cache again.") 2019 setLimiterLimits(limiter, math.MaxInt64, math.MaxInt64) 2020 2021 t.Log("Finish all the prefetching.") 2022 close(prefetchSyncCh) 2023 // We can't close the done channel right away since the prefetcher 2024 // still needs to send on it for every remaining operation it 2025 // processes, so just spawn a goroutine to drain it, and close the 2026 // channel when the test is over. (The defered close is at the top 2027 // of this function.) 2028 go func() { 2029 for range prefetchDoneCh { 2030 } 2031 }() 2032 2033 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 2034 2035 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 2036 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2037 testPrefetcherCheckGet(t, config.BlockCache(), aPtr, a, 2038 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2039 testPrefetcherCheckGet(t, config.BlockCache(), aaPtr, aa, 2040 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2041 testPrefetcherCheckGet(t, config.BlockCache(), abPtr, ab, 2042 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2043 <-q.Prefetcher().Shutdown() 2044 } 2045 2046 func TestPrefetcherWithDedupBlocks(t *testing.T) { 2047 t.Log("Test how the prefetcher works with block IDs that " + 2048 "have multiple refs.") 2049 2050 cache, dbcConfig := initDiskBlockCacheTest(t) 2051 q, bg, config := initPrefetcherTestWithDiskCache(t, cache) 2052 ctx, cancel := context.WithTimeout( 2053 context.Background(), individualTestTimeout) 2054 defer cancel() 2055 kmd := makeKMD() 2056 prefetchSyncCh := make(chan struct{}) 2057 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 2058 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 2059 defer func() { <-p1Ch }() 2060 notifySyncCh(t, prefetchSyncCh) 2061 2062 t.Log("Initialize a folder tree with structure: " + 2063 "root -> {a, b}, where a and b are refs to the same block ID.") 2064 rootPtr := makeRandomBlockPointer(t) 2065 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2066 "a": makeRandomDirEntry(t, data.File, 10, "a"), 2067 }) 2068 aPtr := root.Children["a"].BlockPointer 2069 childB := root.Children["a"] 2070 bNonce, err := kbfsblock.MakeRefNonce() 2071 require.NoError(t, err) 2072 childB.RefNonce = bNonce 2073 root.Children["b"] = childB 2074 bPtr := childB.BlockPointer 2075 2076 aBlock := makeFakeFileBlock(t, true) 2077 2078 encRoot, serverHalfRoot := 2079 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 2080 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, aBlock, dbcConfig) 2081 2082 err = cache.Put( 2083 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 2084 DiskBlockAnyCache) 2085 require.NoError(t, err) 2086 err = cache.Put( 2087 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 2088 require.NoError(t, err) 2089 2090 _, _ = bg.setBlockToReturn(rootPtr, root) 2091 _, _ = bg.setBlockToReturn(aPtr, aBlock) 2092 _, _ = bg.setBlockToReturn(bPtr, aBlock) 2093 2094 t.Log("Fetch dir root.") 2095 block := &data.DirBlock{} 2096 ch := q.Request( 2097 ctx, defaultOnDemandRequestPriority, kmd, 2098 rootPtr, block, data.TransientEntry, BlockRequestWithDeepSync) 2099 notifySyncCh(t, prefetchSyncCh) 2100 err = <-ch 2101 require.NoError(t, err) 2102 2103 t.Log("Release prefetches of the one unique child pointer.") 2104 notifySyncCh(t, prefetchSyncCh) 2105 2106 t.Log("Wait for the prefetch to finish.") 2107 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr) 2108 2109 t.Log("Ensure that the prefetched blocks are in the cache, " + 2110 "and the prefetch statuses are correct.") 2111 testPrefetcherCheckGet(t, config.BlockCache(), rootPtr, root, 2112 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2113 } 2114 2115 func TestPrefetcherWithCanceledDedupBlocks(t *testing.T) { 2116 t.Log("Test how the prefetcher works with block IDs that " + 2117 "have multiple refs.") 2118 2119 cache, dbcConfig := initDiskBlockCacheTest(t) 2120 q, bg, config := initPrefetcherTestWithDiskCache(t, cache) 2121 ctx, cancel := context.WithTimeout( 2122 context.Background(), individualTestTimeout) 2123 defer cancel() 2124 kmd := makeKMD() 2125 prefetchSyncCh := make(chan struct{}) 2126 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 2127 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 2128 defer func() { <-p1Ch }() 2129 notifySyncCh(t, prefetchSyncCh) 2130 2131 t.Log("Initialize a folder tree with structure: root -> a -> b") 2132 rootPtr := makeRandomBlockPointer(t) 2133 root := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2134 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 2135 }) 2136 aPtr := root.Children["a"].BlockPointer 2137 aBlock := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2138 "b": makeRandomDirEntry(t, data.File, 10, "b"), 2139 }) 2140 bPtr := aBlock.Children["b"].BlockPointer 2141 bBlock := makeFakeFileBlock(t, true) 2142 2143 encRoot, serverHalfRoot := 2144 setupRealBlockForDiskCache(t, rootPtr, root, dbcConfig) 2145 encA, serverHalfA := setupRealBlockForDiskCache(t, aPtr, aBlock, dbcConfig) 2146 encB, serverHalfB := setupRealBlockForDiskCache(t, bPtr, bBlock, dbcConfig) 2147 2148 err := cache.Put( 2149 ctx, kmd.TlfID(), rootPtr.ID, encRoot, serverHalfRoot, 2150 DiskBlockAnyCache) 2151 require.NoError(t, err) 2152 err = cache.Put( 2153 ctx, kmd.TlfID(), aPtr.ID, encA, serverHalfA, DiskBlockAnyCache) 2154 require.NoError(t, err) 2155 err = cache.Put( 2156 ctx, kmd.TlfID(), bPtr.ID, encB, serverHalfB, DiskBlockAnyCache) 2157 require.NoError(t, err) 2158 2159 _, _ = bg.setBlockToReturn(rootPtr, root) 2160 _, _ = bg.setBlockToReturn(aPtr, aBlock) 2161 _, _ = bg.setBlockToReturn(bPtr, bBlock) 2162 2163 t.Log("Fetch dir root.") 2164 block := &data.DirBlock{} 2165 ch := q.Request( 2166 ctx, defaultOnDemandRequestPriority, kmd, 2167 rootPtr, block, data.TransientEntry, BlockRequestWithDeepSync) 2168 notifySyncCh(t, prefetchSyncCh) 2169 err = <-ch 2170 require.NoError(t, err) 2171 2172 t.Log("Release prefetch of a.") 2173 notifySyncCh(t, prefetchSyncCh) 2174 2175 t.Log("Before prefetching the child, update the root block to have a " + 2176 "new subdir pointing to the same ID but different nonce.") 2177 2178 root2Ptr := makeRandomBlockPointer(t) 2179 root2 := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2180 "a2": makeRandomDirEntry(t, data.Dir, 10, "a2"), 2181 }) 2182 a2Ptr := root2.Children["a2"].BlockPointer 2183 childB2 := aBlock.Children["b"] 2184 b2Nonce, err := kbfsblock.MakeRefNonce() 2185 require.NoError(t, err) 2186 childB2.RefNonce = b2Nonce 2187 a2Block := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2188 "b2": childB2, 2189 }) 2190 b2Ptr := a2Block.Children["b2"].BlockPointer 2191 _, _ = bg.setBlockToReturn(a2Ptr, a2Block) 2192 _, _ = bg.setBlockToReturn(b2Ptr, bBlock) 2193 2194 encRoot2, serverHalfRoot2 := 2195 setupRealBlockForDiskCache(t, root2Ptr, root2, dbcConfig) 2196 encA2, serverHalfA2 := setupRealBlockForDiskCache( 2197 t, a2Ptr, a2Block, dbcConfig) 2198 2199 err = cache.Put( 2200 ctx, kmd.TlfID(), root2Ptr.ID, encRoot2, serverHalfRoot2, 2201 DiskBlockAnyCache) 2202 require.NoError(t, err) 2203 err = cache.Put( 2204 ctx, kmd.TlfID(), a2Ptr.ID, encA2, serverHalfA2, DiskBlockAnyCache) 2205 require.NoError(t, err) 2206 2207 t.Log("Start prefetch of a2, which adds a parent entry for bPtr.ID.") 2208 block2 := &data.DirBlock{} 2209 ch = q.Request( 2210 ctx, defaultOnDemandRequestPriority, kmd, 2211 a2Ptr, block2, data.TransientEntry, BlockRequestWithDeepSync) 2212 notifySyncCh(t, prefetchSyncCh) 2213 err = <-ch 2214 require.NoError(t, err) 2215 2216 t.Log("Cancel the original b prefetch") 2217 q.Prefetcher().CancelPrefetch(bPtr) 2218 2219 t.Log("Release all the cancels and prefetches.") 2220 close(prefetchSyncCh) 2221 2222 t.Log("Wait for the prefetch to finish.") 2223 waitCh, err := q.Prefetcher().WaitChannelForBlockPrefetch(ctx, a2Ptr) 2224 require.NoError(t, err) 2225 2226 select { 2227 case <-waitCh: 2228 case <-ctx.Done(): 2229 t.Fatal(ctx.Err()) 2230 } 2231 2232 t.Log("Ensure that the prefetched blocks are in the cache, " + 2233 "and the prefetch statuses are correct.") 2234 testPrefetcherCheckGet(t, config.BlockCache(), a2Ptr, a2Block, 2235 FinishedPrefetch, kmd.TlfID(), config.DiskBlockCache()) 2236 2237 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), a2Ptr) 2238 } 2239 2240 func TestPrefetcherCancelTlfPrefetches(t *testing.T) { 2241 t.Log("Test that prefetches from a given TLF can all be canceled.") 2242 2243 cache, dbcConfig := initDiskBlockCacheTest(t) 2244 q, bg, _ := initPrefetcherTestWithDiskCache(t, cache) 2245 ctx, cancel := context.WithTimeout( 2246 context.Background(), individualTestTimeout) 2247 defer cancel() 2248 prefetchSyncCh := make(chan struct{}) 2249 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 2250 p1Ch := q.TogglePrefetcher(true, prefetchSyncCh, nil) 2251 defer func() { <-p1Ch }() 2252 notifySyncCh(t, prefetchSyncCh) 2253 2254 kmd1 := libkeytest.NewEmptyKeyMetadata(tlf.FakeID(1, tlf.Private), 1) 2255 kmd2 := libkeytest.NewEmptyKeyMetadata(tlf.FakeID(2, tlf.Private), 1) 2256 2257 bg.respectCancel = true 2258 2259 t.Log("Make two blocks each for two different TLFs") 2260 rootPtr1 := makeRandomBlockPointer(t) 2261 root1 := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2262 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 2263 }) 2264 encRoot1, serverHalfRoot1 := 2265 setupRealBlockForDiskCache(t, rootPtr1, root1, dbcConfig) 2266 err := cache.Put( 2267 ctx, kmd1.TlfID(), rootPtr1.ID, encRoot1, serverHalfRoot1, 2268 DiskBlockAnyCache) 2269 require.NoError(t, err) 2270 _, _ = bg.setBlockToReturn(rootPtr1, root1) 2271 2272 rootPtr2 := makeRandomBlockPointer(t) 2273 root2 := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2274 "a": makeRandomDirEntry(t, data.Dir, 10, "a"), 2275 }) 2276 encRoot2, serverHalfRoot2 := 2277 setupRealBlockForDiskCache(t, rootPtr2, root2, dbcConfig) 2278 err = cache.Put( 2279 ctx, kmd2.TlfID(), rootPtr2.ID, encRoot2, serverHalfRoot2, 2280 DiskBlockAnyCache) 2281 require.NoError(t, err) 2282 _, _ = bg.setBlockToReturn(rootPtr2, root2) 2283 2284 aPtr1 := root1.Children["a"].BlockPointer 2285 aBlock1 := makeFakeDirBlockWithChildren(map[string]data.DirEntry{ 2286 "b": makeRandomDirEntry(t, data.Dir, 10, "b"), 2287 }) 2288 // Don't put this block in the disk cache; let the prefetcher try 2289 // to fetch it from the block getter, and never release it. 2290 require.NoError(t, err) 2291 getA1, _ := bg.setBlockToReturn(aPtr1, aBlock1) 2292 2293 aPtr2 := root2.Children["a"].BlockPointer 2294 aBlock2 := makeFakeDirBlockWithChildren(map[string]data.DirEntry{}) 2295 encA2, serverHalfA2 := 2296 setupRealBlockForDiskCache(t, aPtr2, aBlock2, dbcConfig) 2297 err = cache.Put( 2298 ctx, kmd2.TlfID(), aPtr2.ID, encA2, serverHalfA2, 2299 DiskBlockAnyCache) 2300 require.NoError(t, err) 2301 _, _ = bg.setBlockToReturn(aPtr2, aBlock2) 2302 2303 t.Log("Request both roots") 2304 block1 := &data.DirBlock{} 2305 ch1 := q.Request( 2306 ctx, defaultOnDemandRequestPriority, kmd1, 2307 rootPtr1, block1, data.TransientEntry, BlockRequestPrefetchUntilFull) 2308 block2 := &data.DirBlock{} 2309 ch2 := q.Request( 2310 ctx, defaultOnDemandRequestPriority, kmd2, 2311 rootPtr2, block2, data.TransientEntry, BlockRequestPrefetchUntilFull) 2312 err = <-ch1 2313 require.NoError(t, err) 2314 err = <-ch2 2315 require.NoError(t, err) 2316 2317 t.Log("Release both root blocks for prefetching") 2318 notifySyncCh(t, prefetchSyncCh) 2319 notifySyncCh(t, prefetchSyncCh) 2320 2321 select { 2322 case <-getA1: 2323 case <-ctx.Done(): 2324 t.Fatal(ctx.Err()) 2325 } 2326 2327 t.Log("Cancel the first TLF's prefetches") 2328 ch := make(chan struct{}) 2329 go func() { 2330 notifySyncCh(t, prefetchSyncCh) 2331 close(ch) 2332 }() 2333 err = q.Prefetcher().CancelTlfPrefetches(ctx, kmd1.TlfID()) 2334 <-ch 2335 require.NoError(t, err) 2336 2337 // Handle another CancelPrefetch call from the a1's request 2338 // context being canceled. 2339 notifySyncCh(t, prefetchSyncCh) 2340 2341 t.Log("Now we should be able to wait for the prefetcher to " + 2342 "complete with only a single notify, for root2's child.") 2343 notifySyncCh(t, prefetchSyncCh) 2344 waitForPrefetchOrBust(ctx, t, q.Prefetcher(), rootPtr2) 2345 } 2346 2347 type testAppStateUpdater struct { 2348 c <-chan keybase1.MobileNetworkState 2349 calls chan<- keybase1.MobileNetworkState 2350 nCalls int 2351 } 2352 2353 func (tasu *testAppStateUpdater) NextAppStateUpdate( 2354 _ *keybase1.MobileAppState) <-chan keybase1.MobileAppState { 2355 // Receiving on a nil channel blocks forever. 2356 return nil 2357 } 2358 2359 func (tasu *testAppStateUpdater) NextNetworkStateUpdate( 2360 lastState *keybase1.MobileNetworkState) <-chan keybase1.MobileNetworkState { 2361 if tasu.nCalls > 0 { 2362 tasu.calls <- *lastState 2363 tasu.nCalls-- 2364 } 2365 return tasu.c 2366 } 2367 2368 func TestPrefetcherCellularPause(t *testing.T) { 2369 t.Log("Test that a cell mobile network pauses prefetching.") 2370 bg := newFakeBlockGetter(false) 2371 config := newTestBlockRetrievalConfig(t, bg, nil) 2372 stateCh := make(chan keybase1.MobileNetworkState) 2373 callCh := make(chan keybase1.MobileNetworkState) 2374 q := newBlockRetrievalQueue( 2375 1, 1, 0, config, &testAppStateUpdater{stateCh, callCh, 4}) 2376 require.NotNil(t, q) 2377 <-callCh // Initial prefetcher, before sync ch is set. 2378 2379 prefetchSyncCh := make(chan struct{}) 2380 defer shutdownPrefetcherTest(t, q, prefetchSyncCh) 2381 <-q.TogglePrefetcher(true, prefetchSyncCh, nil) 2382 2383 notifySyncCh(t, prefetchSyncCh) 2384 last := <-callCh 2385 require.Equal(t, keybase1.MobileNetworkState_NONE, last) 2386 2387 t.Log("Switch to cell and make sure it pauses") 2388 stateCh <- keybase1.MobileNetworkState_CELLULAR 2389 // Should be called again without a call to syncCh. 2390 last = <-callCh 2391 require.Equal(t, keybase1.MobileNetworkState_CELLULAR, last) 2392 2393 t.Log("Make sure we get a wait channel right away") 2394 ptr := makeRandomBlockPointer(t) 2395 ctx, cancel := context.WithTimeout( 2396 context.Background(), individualTestTimeout) 2397 defer cancel() 2398 _, err := q.Prefetcher().WaitChannelForBlockPrefetch(ctx, ptr) 2399 require.NoError(t, err) 2400 2401 t.Log("Unpause it to make it notify again") 2402 stateCh <- keybase1.MobileNetworkState_NONE 2403 notifySyncCh(t, prefetchSyncCh) 2404 last = <-callCh 2405 require.Equal(t, keybase1.MobileNetworkState_NONE, last) 2406 }