github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }