github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/tlf_journal_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  	"math"
     9  	"os"
    10  	"reflect"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/golang/mock/gomock"
    16  	"github.com/keybase/client/go/kbfs/data"
    17  	"github.com/keybase/client/go/kbfs/idutil"
    18  	"github.com/keybase/client/go/kbfs/ioutil"
    19  	"github.com/keybase/client/go/kbfs/kbfsblock"
    20  	"github.com/keybase/client/go/kbfs/kbfscodec"
    21  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    22  	"github.com/keybase/client/go/kbfs/kbfshash"
    23  	"github.com/keybase/client/go/kbfs/kbfsmd"
    24  	"github.com/keybase/client/go/kbfs/test/clocktest"
    25  	"github.com/keybase/client/go/kbfs/tlf"
    26  	"github.com/keybase/client/go/libkb"
    27  	"github.com/keybase/client/go/protocol/keybase1"
    28  	"github.com/pkg/errors"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  	"golang.org/x/net/context"
    32  )
    33  
    34  // testBWDelegate is a delegate we pass to tlfJournal to get info
    35  // about its state transitions.
    36  type testBWDelegate struct {
    37  	t *testing.T
    38  	// Store a context so that the tlfJournal's background context
    39  	// will also obey the test timeout.
    40  	testCtx    context.Context
    41  	stateCh    chan bwState
    42  	shutdownCh chan struct{}
    43  	testDoneCh chan struct{}
    44  }
    45  
    46  func (d testBWDelegate) GetBackgroundContext() context.Context {
    47  	return d.testCtx
    48  }
    49  
    50  func (d testBWDelegate) OnNewState(ctx context.Context, bws bwState) {
    51  	select {
    52  	case d.stateCh <- bws:
    53  	case <-d.testDoneCh:
    54  		// The test is over, so anything waiting on a state change is
    55  		// likely some errant race that's probably not worth fixing.
    56  	case <-ctx.Done():
    57  		assert.Fail(d.t, ctx.Err().Error())
    58  	}
    59  }
    60  
    61  func (d testBWDelegate) OnShutdown(ctx context.Context) {
    62  	select {
    63  	case d.shutdownCh <- struct{}{}:
    64  	case <-ctx.Done():
    65  		assert.Fail(d.t, ctx.Err().Error())
    66  	}
    67  }
    68  
    69  func (d testBWDelegate) requireNextState(
    70  	ctx context.Context, expectedState ...bwState) bwState {
    71  	select {
    72  	case bws := <-d.stateCh:
    73  		require.Contains(d.t, expectedState, bws)
    74  		return bws
    75  	case <-ctx.Done():
    76  		assert.Fail(d.t, ctx.Err().Error())
    77  		return bwIdle
    78  	}
    79  }
    80  
    81  // testTLFJournalConfig is the config we pass to the tlfJournal, and
    82  // also contains some helper functions for testing.
    83  type testTLFJournalConfig struct {
    84  	codecGetter
    85  	logMaker
    86  	*testSyncedTlfGetterSetter
    87  	t                            *testing.T
    88  	tlfID                        tlf.ID
    89  	splitter                     data.BlockSplitter
    90  	crypto                       *CryptoLocal
    91  	bcache                       data.BlockCache
    92  	bops                         BlockOps
    93  	mdcache                      MDCache
    94  	ver                          kbfsmd.MetadataVer
    95  	reporter                     Reporter
    96  	uid                          keybase1.UID
    97  	verifyingKey                 kbfscrypto.VerifyingKey
    98  	ekg                          singleEncryptionKeyGetter
    99  	nug                          idutil.NormalizedUsernameGetter
   100  	mdserver                     MDServer
   101  	dlTimeout                    time.Duration
   102  	subscriptionManagerPublisher SubscriptionManagerPublisher
   103  }
   104  
   105  func (c testTLFJournalConfig) SubscriptionManagerPublisher() SubscriptionManagerPublisher {
   106  	return c.subscriptionManagerPublisher
   107  }
   108  
   109  func (c testTLFJournalConfig) BlockSplitter() data.BlockSplitter {
   110  	return c.splitter
   111  }
   112  
   113  func (c testTLFJournalConfig) Clock() Clock {
   114  	return data.WallClock{}
   115  }
   116  
   117  func (c testTLFJournalConfig) Crypto() Crypto {
   118  	return c.crypto
   119  }
   120  
   121  func (c testTLFJournalConfig) BlockCache() data.BlockCache {
   122  	return c.bcache
   123  }
   124  
   125  func (c testTLFJournalConfig) BlockOps() BlockOps {
   126  	return c.bops
   127  }
   128  
   129  func (c testTLFJournalConfig) MDCache() MDCache {
   130  	return c.mdcache
   131  }
   132  
   133  func (c testTLFJournalConfig) MetadataVersion() kbfsmd.MetadataVer {
   134  	return c.ver
   135  }
   136  
   137  func (c testTLFJournalConfig) Reporter() Reporter {
   138  	return c.reporter
   139  }
   140  
   141  func (c testTLFJournalConfig) cryptoPure() cryptoPure {
   142  	return c.crypto
   143  }
   144  
   145  func (c testTLFJournalConfig) encryptionKeyGetter() encryptionKeyGetter {
   146  	return c.ekg
   147  }
   148  
   149  func (c testTLFJournalConfig) mdDecryptionKeyGetter() mdDecryptionKeyGetter {
   150  	return c.ekg
   151  }
   152  
   153  func (c testTLFJournalConfig) usernameGetter() idutil.NormalizedUsernameGetter {
   154  	return c.nug
   155  }
   156  
   157  func (c testTLFJournalConfig) resolver() idutil.Resolver {
   158  	return nil
   159  }
   160  
   161  func (c testTLFJournalConfig) MDServer() MDServer {
   162  	return c.mdserver
   163  }
   164  
   165  func (c testTLFJournalConfig) teamMembershipChecker() kbfsmd.TeamMembershipChecker {
   166  	// TODO: support team TLF tests.
   167  	return nil
   168  }
   169  
   170  func (c testTLFJournalConfig) diskLimitTimeout() time.Duration {
   171  	return c.dlTimeout
   172  }
   173  
   174  func (c testTLFJournalConfig) BGFlushDirOpBatchSize() int {
   175  	return 1
   176  }
   177  
   178  func (c testTLFJournalConfig) makeBlock(data []byte) (
   179  	kbfsblock.ID, kbfsblock.Context, kbfscrypto.BlockCryptKeyServerHalf) {
   180  	id, err := kbfsblock.MakePermanentID(data, kbfscrypto.EncryptionSecretboxWithKeyNonce)
   181  	require.NoError(c.t, err)
   182  	bCtx := kbfsblock.MakeFirstContext(
   183  		c.uid.AsUserOrTeam(), keybase1.BlockType_DATA)
   184  	serverHalf, err := kbfscrypto.MakeRandomBlockCryptKeyServerHalf()
   185  	require.NoError(c.t, err)
   186  	return id, bCtx, serverHalf
   187  }
   188  
   189  func (c testTLFJournalConfig) makeMD(
   190  	revision kbfsmd.Revision, prevRoot kbfsmd.ID) *RootMetadata {
   191  	return makeMDForTest(c.t, c.ver, c.tlfID, revision, c.uid, c.crypto, prevRoot)
   192  }
   193  
   194  func (c testTLFJournalConfig) checkMD(rmds *RootMetadataSigned,
   195  	extra kbfsmd.ExtraMetadata, expectedRevision kbfsmd.Revision,
   196  	expectedPrevRoot kbfsmd.ID, expectedMergeStatus kbfsmd.MergeStatus,
   197  	expectedBranchID kbfsmd.BranchID) {
   198  	verifyingKey := c.crypto.SigningKeySigner.Key.GetVerifyingKey()
   199  	checkBRMD(c.t, c.uid, verifyingKey, c.Codec(),
   200  		rmds.MD, extra, expectedRevision, expectedPrevRoot,
   201  		expectedMergeStatus, expectedBranchID)
   202  	err := rmds.IsValidAndSigned(
   203  		context.Background(), c.Codec(), nil, extra,
   204  		keybase1.OfflineAvailability_NONE)
   205  	require.NoError(c.t, err)
   206  	err = rmds.IsLastModifiedBy(c.uid, verifyingKey)
   207  	require.NoError(c.t, err)
   208  }
   209  
   210  func (c testTLFJournalConfig) checkRange(rmdses []rmdsWithExtra,
   211  	firstRevision kbfsmd.Revision, firstPrevRoot kbfsmd.ID,
   212  	mStatus kbfsmd.MergeStatus, bid kbfsmd.BranchID) {
   213  	c.checkMD(rmdses[0].rmds, rmdses[0].extra, firstRevision,
   214  		firstPrevRoot, mStatus, bid)
   215  
   216  	for i := 1; i < len(rmdses); i++ {
   217  		prevID, err := kbfsmd.MakeID(c.Codec(), rmdses[i-1].rmds.MD)
   218  		require.NoError(c.t, err)
   219  		c.checkMD(rmdses[i].rmds, rmdses[i].extra,
   220  			firstRevision+kbfsmd.Revision(i), prevID, mStatus, bid)
   221  		err = rmdses[i-1].rmds.MD.CheckValidSuccessor(
   222  			prevID, rmdses[i].rmds.MD)
   223  		require.NoError(c.t, err)
   224  	}
   225  }
   226  
   227  func setupTLFJournalTest(
   228  	t *testing.T, ver kbfsmd.MetadataVer, bwStatus TLFJournalBackgroundWorkStatus) (
   229  	tempdir string, config *testTLFJournalConfig, ctx context.Context,
   230  	cancel context.CancelFunc, tlfJournal *tlfJournal,
   231  	delegate testBWDelegate) {
   232  	// Set up config and dependencies.
   233  	bsplitter, err := data.NewBlockSplitterSimpleExact(
   234  		64*1024, int(64*1024/data.BPSize), 8*1024)
   235  	require.NoError(t, err)
   236  	codec := kbfscodec.NewMsgpack()
   237  	signingKey := kbfscrypto.MakeFakeSigningKeyOrBust("client sign")
   238  	cryptPrivateKey := kbfscrypto.MakeFakeCryptPrivateKeyOrBust("client crypt private")
   239  	crypto := NewCryptoLocal(
   240  		codec, signingKey, cryptPrivateKey, makeBlockCryptV1())
   241  	uid := keybase1.MakeTestUID(1)
   242  	verifyingKey := signingKey.GetVerifyingKey()
   243  	ekg := singleEncryptionKeyGetter{kbfscrypto.MakeTLFCryptKey([32]byte{0x1})}
   244  
   245  	cig := singleCurrentSessionGetter{
   246  		idutil.SessionInfo{
   247  			Name:         "fake_user",
   248  			UID:          uid,
   249  			VerifyingKey: verifyingKey,
   250  		},
   251  	}
   252  	mdserver, err := NewMDServerMemory(newTestMDServerLocalConfig(t, cig))
   253  	require.NoError(t, err)
   254  
   255  	mockPublisher := NewMockSubscriptionManagerPublisher(gomock.NewController(t))
   256  	config = &testTLFJournalConfig{
   257  		newTestCodecGetter(), newTestLogMakerWithVDebug(t, libkb.VLog1String),
   258  		newTestSyncedTlfGetterSetter(), t,
   259  		tlf.FakeID(1, tlf.Private), bsplitter, crypto,
   260  		nil, nil, NewMDCacheStandard(10), ver,
   261  		NewReporterSimple(clocktest.NewTestClockNow(), 10), uid, verifyingKey, ekg, nil,
   262  		mdserver, defaultDiskLimitMaxDelay + time.Second,
   263  		mockPublisher,
   264  	}
   265  	mockPublisher.EXPECT().PublishChange(
   266  		keybase1.SubscriptionTopic_FAVORITES).AnyTimes()
   267  	mockPublisher.EXPECT().PublishChange(
   268  		keybase1.SubscriptionTopic_JOURNAL_STATUS).AnyTimes()
   269  	mockPublisher.EXPECT().PublishChange(
   270  		keybase1.SubscriptionTopic_FILES_TAB_BADGE).AnyTimes()
   271  
   272  	ctx, cancel = context.WithTimeout(
   273  		context.Background(), individualTestTimeout)
   274  
   275  	// Clean up the context if the rest of the setup fails.
   276  	setupSucceeded := false
   277  	defer func() {
   278  		if !setupSucceeded {
   279  			cancel()
   280  		}
   281  	}()
   282  
   283  	delegate = testBWDelegate{
   284  		t:          t,
   285  		testCtx:    ctx,
   286  		stateCh:    make(chan bwState),
   287  		shutdownCh: make(chan struct{}),
   288  		testDoneCh: make(chan struct{}),
   289  	}
   290  
   291  	tempdir, err = ioutil.TempDir(os.TempDir(), "tlf_journal")
   292  	require.NoError(t, err)
   293  
   294  	// Clean up the tempdir if anything in the rest of the setup
   295  	// fails.
   296  	defer func() {
   297  		if !setupSucceeded {
   298  			err := ioutil.RemoveAll(tempdir)
   299  			assert.NoError(t, err)
   300  		}
   301  	}()
   302  
   303  	delegateBlockServer := NewBlockServerMemory(config.MakeLogger(""))
   304  
   305  	diskLimitSemaphore := newSemaphoreDiskLimiter(
   306  		math.MaxInt64, math.MaxInt64, math.MaxInt64)
   307  	tlfJournal, err = makeTLFJournal(ctx, uid, verifyingKey,
   308  		tempdir, config.tlfID, uid.AsUserOrTeam(), config, delegateBlockServer,
   309  		bwStatus, delegate, nil, nil, diskLimitSemaphore, tlf.NullID)
   310  	require.NoError(t, err)
   311  
   312  	switch bwStatus {
   313  	case TLFJournalBackgroundWorkEnabled:
   314  		// Same as the single op case.
   315  		fallthrough
   316  	case TLFJournalSingleOpBackgroundWorkEnabled:
   317  		// Read the state changes triggered by the initial
   318  		// work signal.
   319  		delegate.requireNextState(ctx, bwIdle)
   320  		delegate.requireNextState(ctx, bwBusy)
   321  		delegate.requireNextState(ctx, bwIdle)
   322  
   323  	case TLFJournalBackgroundWorkPaused:
   324  		delegate.requireNextState(ctx, bwPaused)
   325  
   326  	default:
   327  		require.FailNow(t, "Unknown bwStatus %s", bwStatus)
   328  	}
   329  
   330  	setupSucceeded = true
   331  	return tempdir, config, ctx, cancel, tlfJournal, delegate
   332  }
   333  
   334  func teardownTLFJournalTest(
   335  	ctx context.Context, tempdir string, config *testTLFJournalConfig,
   336  	cancel context.CancelFunc, tlfJournal *tlfJournal,
   337  	delegate testBWDelegate) {
   338  	// If there are any errant state changes left in the journal, this
   339  	// will cause them to be aborted.
   340  	close(delegate.testDoneCh)
   341  
   342  	// Shutdown first so we don't get the Done() signal (from the
   343  	// cancel() call) spuriously.
   344  	tlfJournal.shutdown(ctx)
   345  	select {
   346  	case <-delegate.shutdownCh:
   347  	case <-ctx.Done():
   348  		assert.Fail(config.t, ctx.Err().Error())
   349  	}
   350  
   351  	cancel()
   352  
   353  	select {
   354  	case bws := <-delegate.stateCh:
   355  		assert.Fail(config.t, "Unexpected state %s", bws)
   356  	default:
   357  	}
   358  
   359  	config.mdserver.Shutdown()
   360  	tlfJournal.delegateBlockServer.Shutdown(ctx)
   361  
   362  	err := ioutil.RemoveAll(tempdir)
   363  	assert.NoError(config.t, err)
   364  }
   365  
   366  func putOneMD(ctx context.Context, config *testTLFJournalConfig,
   367  	tlfJournal *tlfJournal) {
   368  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   369  	_, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
   370  	require.NoError(config.t, err)
   371  }
   372  
   373  // The tests below primarily test the background work thread's
   374  // behavior.
   375  
   376  func testTLFJournalBasic(t *testing.T, ver kbfsmd.MetadataVer) {
   377  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   378  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   379  	defer teardownTLFJournalTest(
   380  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   381  
   382  	putOneMD(ctx, config, tlfJournal)
   383  
   384  	// Wait for it to be processed.
   385  
   386  	delegate.requireNextState(ctx, bwBusy)
   387  	delegate.requireNextState(ctx, bwIdle)
   388  }
   389  
   390  func testTLFJournalPauseResume(t *testing.T, ver kbfsmd.MetadataVer) {
   391  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   392  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   393  	defer teardownTLFJournalTest(
   394  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   395  
   396  	tlfJournal.pauseBackgroundWork()
   397  	delegate.requireNextState(ctx, bwPaused)
   398  
   399  	putOneMD(ctx, config, tlfJournal)
   400  
   401  	// Unpause and wait for it to be processed.
   402  
   403  	tlfJournal.resumeBackgroundWork()
   404  	delegate.requireNextState(ctx, bwIdle)
   405  	delegate.requireNextState(ctx, bwBusy)
   406  	delegate.requireNextState(ctx, bwIdle)
   407  }
   408  
   409  func testTLFJournalPauseShutdown(t *testing.T, ver kbfsmd.MetadataVer) {
   410  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   411  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   412  	defer teardownTLFJournalTest(
   413  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   414  
   415  	tlfJournal.pauseBackgroundWork()
   416  	delegate.requireNextState(ctx, bwPaused)
   417  
   418  	putOneMD(ctx, config, tlfJournal)
   419  
   420  	// Should still be able to shut down while paused.
   421  }
   422  
   423  type hangingBlockServer struct {
   424  	BlockServer
   425  	// Closed on put.
   426  	onPutCh chan struct{}
   427  }
   428  
   429  func (bs hangingBlockServer) Put(
   430  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
   431  	context kbfsblock.Context,
   432  	buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   433  	_ DiskBlockCacheType) error {
   434  	close(bs.onPutCh)
   435  	// Hang until the context is cancelled.
   436  	<-ctx.Done()
   437  	return ctx.Err()
   438  }
   439  
   440  func (bs hangingBlockServer) waitForPut(ctx context.Context, t *testing.T) {
   441  	select {
   442  	case <-bs.onPutCh:
   443  	case <-ctx.Done():
   444  		require.FailNow(t, ctx.Err().Error())
   445  	}
   446  }
   447  
   448  func putBlock(ctx context.Context,
   449  	t *testing.T, config *testTLFJournalConfig,
   450  	tlfJournal *tlfJournal, data []byte) {
   451  	id, bCtx, serverHalf := config.makeBlock(data)
   452  	err := tlfJournal.putBlockData(ctx, id, bCtx, data, serverHalf)
   453  	require.NoError(t, err)
   454  }
   455  
   456  func testTLFJournalBlockOpBasic(t *testing.T, ver kbfsmd.MetadataVer) {
   457  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   458  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   459  	defer teardownTLFJournalTest(
   460  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   461  
   462  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   463  	numFlushed, rev, converted, err :=
   464  		tlfJournal.flushBlockEntries(ctx, firstValidJournalOrdinal+1,
   465  			kbfsmd.ID{})
   466  	require.NoError(t, err)
   467  	require.Equal(t, 1, numFlushed)
   468  	require.Equal(t, rev, kbfsmd.RevisionUninitialized)
   469  	require.False(t, converted)
   470  }
   471  
   472  func testTLFJournalBlockOpBusyPause(t *testing.T, ver kbfsmd.MetadataVer) {
   473  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   474  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   475  	defer teardownTLFJournalTest(
   476  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   477  
   478  	bs := hangingBlockServer{tlfJournal.delegateBlockServer,
   479  		make(chan struct{})}
   480  	tlfJournal.delegateBlockServer = bs
   481  
   482  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   483  
   484  	bs.waitForPut(ctx, t)
   485  	delegate.requireNextState(ctx, bwBusy)
   486  
   487  	// Should still be able to pause while busy.
   488  
   489  	tlfJournal.pauseBackgroundWork()
   490  	delegate.requireNextState(ctx, bwPaused)
   491  }
   492  
   493  func testTLFJournalBlockOpBusyShutdown(t *testing.T, ver kbfsmd.MetadataVer) {
   494  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   495  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   496  	defer teardownTLFJournalTest(
   497  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   498  
   499  	bs := hangingBlockServer{tlfJournal.delegateBlockServer,
   500  		make(chan struct{})}
   501  	tlfJournal.delegateBlockServer = bs
   502  
   503  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   504  
   505  	bs.waitForPut(ctx, t)
   506  	delegate.requireNextState(ctx, bwBusy)
   507  
   508  	// Should still be able to shut down while busy.
   509  }
   510  
   511  func testTLFJournalSecondBlockOpWhileBusy(t *testing.T, ver kbfsmd.MetadataVer) {
   512  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   513  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   514  	defer teardownTLFJournalTest(
   515  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   516  
   517  	bs := hangingBlockServer{tlfJournal.delegateBlockServer,
   518  		make(chan struct{})}
   519  	tlfJournal.delegateBlockServer = bs
   520  
   521  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   522  
   523  	bs.waitForPut(ctx, t)
   524  	delegate.requireNextState(ctx, bwBusy)
   525  
   526  	// Should still be able to put a second block while busy.
   527  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4, 5})
   528  }
   529  
   530  func testTLFJournalBlockOpDiskByteLimit(t *testing.T, ver kbfsmd.MetadataVer) {
   531  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   532  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   533  	defer teardownTLFJournalTest(
   534  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   535  
   536  	tlfJournal.diskLimiter.onJournalEnable(
   537  		ctx, math.MaxInt64-6, 0, 0, tlfJournal.uid.AsUserOrTeam())
   538  
   539  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   540  
   541  	errCh := make(chan error, 1)
   542  	go func() {
   543  		data2 := []byte{5, 6, 7}
   544  		id, bCtx, serverHalf := config.makeBlock(data2)
   545  		errCh <- tlfJournal.putBlockData(
   546  			ctx, id, bCtx, data2, serverHalf)
   547  	}()
   548  
   549  	numFlushed, rev, converted, err :=
   550  		tlfJournal.flushBlockEntries(ctx, firstValidJournalOrdinal+1,
   551  			kbfsmd.ID{})
   552  	require.NoError(t, err)
   553  	require.Equal(t, 1, numFlushed)
   554  	require.Equal(t, rev, kbfsmd.RevisionUninitialized)
   555  	require.False(t, converted)
   556  
   557  	// Fake an MD flush.
   558  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   559  	err = tlfJournal.doOnMDFlushAndRemoveFlushedMDEntry(
   560  		ctx, kbfsmd.ID{}, &RootMetadataSigned{RootMetadataSigned: kbfsmd.RootMetadataSigned{MD: md.bareMd}})
   561  	require.Error(t, err)
   562  	require.Equal(t, "mdJournal unexpectedly empty", err.Error())
   563  
   564  	select {
   565  	case err := <-errCh:
   566  		require.NoError(t, err)
   567  	case <-ctx.Done():
   568  		t.Fatal(ctx.Err())
   569  	}
   570  }
   571  
   572  func testTLFJournalBlockOpDiskFileLimit(t *testing.T, ver kbfsmd.MetadataVer) {
   573  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   574  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   575  	defer teardownTLFJournalTest(
   576  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   577  
   578  	tlfJournal.diskLimiter.onJournalEnable(
   579  		ctx, 0, 0, math.MaxInt64-2*filesPerBlockMax+1,
   580  		tlfJournal.uid.AsUserOrTeam())
   581  
   582  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   583  
   584  	errCh := make(chan error, 1)
   585  	go func() {
   586  		data2 := []byte{5, 6, 7}
   587  		id, bCtx, serverHalf := config.makeBlock(data2)
   588  		errCh <- tlfJournal.putBlockData(
   589  			ctx, id, bCtx, data2, serverHalf)
   590  	}()
   591  
   592  	numFlushed, rev, converted, err :=
   593  		tlfJournal.flushBlockEntries(ctx, firstValidJournalOrdinal+1,
   594  			kbfsmd.ID{})
   595  	require.NoError(t, err)
   596  	require.Equal(t, 1, numFlushed)
   597  	require.Equal(t, rev, kbfsmd.RevisionUninitialized)
   598  	require.False(t, converted)
   599  
   600  	// Fake an MD flush.
   601  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   602  	err = tlfJournal.doOnMDFlushAndRemoveFlushedMDEntry(
   603  		ctx, kbfsmd.ID{}, &RootMetadataSigned{RootMetadataSigned: kbfsmd.RootMetadataSigned{MD: md.bareMd}})
   604  	require.Error(t, err)
   605  	require.Equal(t, "mdJournal unexpectedly empty", err.Error())
   606  
   607  	select {
   608  	case err := <-errCh:
   609  		require.NoError(t, err)
   610  	case <-ctx.Done():
   611  		t.Fatal(ctx.Err())
   612  	}
   613  }
   614  
   615  func testTLFJournalBlockOpDiskQuotaLimit(t *testing.T, ver kbfsmd.MetadataVer) {
   616  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   617  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   618  	defer teardownTLFJournalTest(
   619  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   620  
   621  	tlfJournal.diskLimiter.onJournalEnable(
   622  		ctx, 0, math.MaxInt64-6, 0, tlfJournal.uid.AsUserOrTeam())
   623  
   624  	data1 := []byte{1, 2, 3, 4}
   625  	putBlock(ctx, t, config, tlfJournal, data1)
   626  
   627  	usedQuotaBytes, quotaBytes :=
   628  		tlfJournal.diskLimiter.getQuotaInfo(tlfJournal.uid.AsUserOrTeam())
   629  	require.Equal(t,
   630  		int64(math.MaxInt64-6)+int64(len(data1)), usedQuotaBytes)
   631  	require.Equal(t, int64(math.MaxInt64), quotaBytes)
   632  
   633  	data2 := []byte{5, 6, 7}
   634  	errCh := make(chan error, 1)
   635  	go func() {
   636  		id, bCtx, serverHalf := config.makeBlock(data2)
   637  		errCh <- tlfJournal.putBlockData(
   638  			ctx, id, bCtx, data2, serverHalf)
   639  	}()
   640  
   641  	numFlushed, rev, converted, err :=
   642  		tlfJournal.flushBlockEntries(ctx, firstValidJournalOrdinal+1,
   643  			kbfsmd.ID{})
   644  	require.NoError(t, err)
   645  	require.Equal(t, 1, numFlushed)
   646  	require.Equal(t, rev, kbfsmd.RevisionUninitialized)
   647  	require.False(t, converted)
   648  
   649  	select {
   650  	case err := <-errCh:
   651  		require.NoError(t, err)
   652  	case <-ctx.Done():
   653  		t.Fatal(ctx.Err())
   654  	}
   655  
   656  	usedQuotaBytes, quotaBytes =
   657  		tlfJournal.diskLimiter.getQuotaInfo(tlfJournal.uid.AsUserOrTeam())
   658  	require.Equal(t,
   659  		int64(math.MaxInt64-6)+int64(len(data2)), usedQuotaBytes)
   660  	require.Equal(t, int64(math.MaxInt64), quotaBytes)
   661  }
   662  
   663  func testTLFJournalBlockOpDiskQuotaLimitResolve(t *testing.T, ver kbfsmd.MetadataVer) {
   664  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   665  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   666  	defer teardownTLFJournalTest(
   667  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   668  
   669  	tlfJournal.diskLimiter.onJournalEnable(
   670  		ctx, 0, math.MaxInt64-6, 0, tlfJournal.uid.AsUserOrTeam())
   671  
   672  	data1 := []byte{1, 2, 3, 4}
   673  	id1, bCtx1, serverHalf1 := config.makeBlock(data1)
   674  	err := tlfJournal.putBlockData(ctx, id1, bCtx1, data1, serverHalf1)
   675  	require.NoError(t, err)
   676  
   677  	usedQuotaBytes, quotaBytes :=
   678  		tlfJournal.diskLimiter.getQuotaInfo(tlfJournal.uid.AsUserOrTeam())
   679  	require.Equal(t,
   680  		int64(math.MaxInt64-6)+int64(len(data1)), usedQuotaBytes)
   681  	require.Equal(t, int64(math.MaxInt64), quotaBytes)
   682  
   683  	data2 := []byte{5, 6, 7}
   684  	errCh := make(chan error, 1)
   685  	go func() {
   686  		id2, bCtx2, serverHalf2 := config.makeBlock(data2)
   687  		errCh <- tlfJournal.putBlockData(
   688  			ctx, id2, bCtx2, data2, serverHalf2)
   689  	}()
   690  
   691  	md1 := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   692  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
   693  	require.NoError(t, err)
   694  	mdID1 := irmd.mdID
   695  
   696  	err = tlfJournal.convertMDsToBranch(ctx)
   697  	require.NoError(t, err)
   698  
   699  	bid, err := tlfJournal.getBranchID()
   700  	require.NoError(t, err)
   701  
   702  	// Ignore the block instead of flushing it.
   703  	md2 := config.makeMD(kbfsmd.RevisionInitial+1, mdID1)
   704  	_, retry, err := tlfJournal.doResolveBranch(
   705  		ctx, bid, []kbfsblock.ID{id1}, md2,
   706  		unflushedPathMDInfo{}, unflushedPathsPerRevMap{}, tlfJournal.key, nil)
   707  	require.NoError(t, err)
   708  	require.False(t, retry)
   709  
   710  	select {
   711  	case err := <-errCh:
   712  		require.NoError(t, err)
   713  	case <-ctx.Done():
   714  		t.Fatal(ctx.Err())
   715  	}
   716  
   717  	usedQuotaBytes, quotaBytes =
   718  		tlfJournal.diskLimiter.getQuotaInfo(tlfJournal.uid.AsUserOrTeam())
   719  	require.Equal(t,
   720  		int64(math.MaxInt64-6)+int64(len(data2)), usedQuotaBytes)
   721  	require.Equal(t, int64(math.MaxInt64), quotaBytes)
   722  }
   723  
   724  func testTLFJournalBlockOpDiskLimitDuplicate(t *testing.T, ver kbfsmd.MetadataVer) {
   725  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   726  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   727  	defer teardownTLFJournalTest(
   728  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   729  
   730  	tlfJournal.diskLimiter.onJournalEnable(
   731  		ctx, math.MaxInt64-8, 0, math.MaxInt64-2*filesPerBlockMax,
   732  		tlfJournal.uid.AsUserOrTeam())
   733  
   734  	data := []byte{1, 2, 3, 4}
   735  	id, bCtx, serverHalf := config.makeBlock(data)
   736  	err := tlfJournal.putBlockData(ctx, id, bCtx, data, serverHalf)
   737  	require.NoError(t, err)
   738  
   739  	// This should acquire some bytes and files, but then release
   740  	// them.
   741  	err = tlfJournal.putBlockData(ctx, id, bCtx, data, serverHalf)
   742  	require.NoError(t, err)
   743  
   744  	// If the above incorrectly does not release bytes or files,
   745  	// this will hang.
   746  	err = tlfJournal.putBlockData(ctx, id, bCtx, data, serverHalf)
   747  	require.NoError(t, err)
   748  }
   749  
   750  func testTLFJournalBlockOpDiskLimitCancel(t *testing.T, ver kbfsmd.MetadataVer) {
   751  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   752  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   753  	defer teardownTLFJournalTest(
   754  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   755  
   756  	tlfJournal.diskLimiter.onJournalEnable(
   757  		ctx, math.MaxInt64, 0, 0, tlfJournal.uid.AsUserOrTeam())
   758  
   759  	ctx2, cancel2 := context.WithCancel(ctx)
   760  	cancel2()
   761  
   762  	data := []byte{1, 2, 3, 4}
   763  	id, bCtx, serverHalf := config.makeBlock(data)
   764  	err := tlfJournal.putBlockData(ctx2, id, bCtx, data, serverHalf)
   765  	require.Equal(t, context.Canceled, errors.Cause(err))
   766  }
   767  
   768  func testTLFJournalBlockOpDiskLimitTimeout(t *testing.T, ver kbfsmd.MetadataVer) {
   769  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   770  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   771  	defer teardownTLFJournalTest(
   772  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   773  
   774  	tlfJournal.diskLimiter.onJournalEnable(
   775  		ctx, math.MaxInt64, 0, math.MaxInt64-1, tlfJournal.uid.AsUserOrTeam())
   776  	config.dlTimeout = 3 * time.Microsecond
   777  
   778  	data := []byte{1, 2, 3, 4}
   779  	id, bCtx, serverHalf := config.makeBlock(data)
   780  	putCtx := context.Background() // rely on default disk limit timeout
   781  	err := tlfJournal.putBlockData(putCtx, id, bCtx, data, serverHalf)
   782  	timeoutErr, ok := errors.Cause(err).(*ErrDiskLimitTimeout)
   783  	require.True(t, ok)
   784  	require.Error(t, timeoutErr.err)
   785  	timeoutErr.err = nil
   786  	require.Equal(t, ErrDiskLimitTimeout{
   787  		3 * time.Microsecond, int64(len(data)),
   788  		filesPerBlockMax, 0, 1, 0, 1, math.MaxInt64, math.MaxInt64, nil, false,
   789  	}, *timeoutErr)
   790  }
   791  
   792  func testTLFJournalBlockOpDiskLimitPutFailure(t *testing.T, ver kbfsmd.MetadataVer) {
   793  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   794  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   795  	defer teardownTLFJournalTest(
   796  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   797  
   798  	tlfJournal.diskLimiter.onJournalEnable(
   799  		ctx, math.MaxInt64-6, 0, math.MaxInt64-filesPerBlockMax,
   800  		tlfJournal.uid.AsUserOrTeam())
   801  
   802  	data := []byte{1, 2, 3, 4}
   803  	id, bCtx, serverHalf := config.makeBlock(data)
   804  	err := tlfJournal.putBlockData(ctx, id, bCtx, []byte{1}, serverHalf)
   805  	require.IsType(t, kbfshash.HashMismatchError{}, errors.Cause(err))
   806  
   807  	// If the above incorrectly does not release bytes or files from
   808  	// diskLimiter on error, this will hang.
   809  	err = tlfJournal.putBlockData(ctx, id, bCtx, data, serverHalf)
   810  	require.NoError(t, err)
   811  }
   812  
   813  type hangingMDServer struct {
   814  	MDServer
   815  	// Closed on put.
   816  	onPutCh chan struct{}
   817  }
   818  
   819  func (md hangingMDServer) Put(ctx context.Context, rmds *RootMetadataSigned,
   820  	_ kbfsmd.ExtraMetadata, _ *keybase1.LockContext, _ keybase1.MDPriority) error {
   821  	close(md.onPutCh)
   822  	// Hang until the context is cancelled.
   823  	<-ctx.Done()
   824  	return ctx.Err()
   825  }
   826  
   827  func (md hangingMDServer) waitForPut(ctx context.Context, t *testing.T) {
   828  	select {
   829  	case <-md.onPutCh:
   830  	case <-ctx.Done():
   831  		require.FailNow(t, ctx.Err().Error())
   832  	}
   833  }
   834  
   835  func testTLFJournalMDServerBusyPause(t *testing.T, ver kbfsmd.MetadataVer) {
   836  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   837  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   838  	defer teardownTLFJournalTest(
   839  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   840  
   841  	mdserver := hangingMDServer{config.MDServer(), make(chan struct{})}
   842  	config.mdserver = mdserver
   843  
   844  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   845  	_, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
   846  	require.NoError(t, err)
   847  
   848  	mdserver.waitForPut(ctx, t)
   849  	delegate.requireNextState(ctx, bwBusy)
   850  
   851  	// Should still be able to pause while busy.
   852  
   853  	tlfJournal.pauseBackgroundWork()
   854  	delegate.requireNextState(ctx, bwPaused)
   855  }
   856  
   857  func testTLFJournalMDServerBusyShutdown(t *testing.T, ver kbfsmd.MetadataVer) {
   858  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   859  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   860  	defer teardownTLFJournalTest(
   861  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   862  
   863  	mdserver := hangingMDServer{config.MDServer(), make(chan struct{})}
   864  	config.mdserver = mdserver
   865  
   866  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   867  	_, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
   868  	require.NoError(t, err)
   869  
   870  	mdserver.waitForPut(ctx, t)
   871  	delegate.requireNextState(ctx, bwBusy)
   872  
   873  	// Should still be able to shutdown while busy.
   874  }
   875  
   876  func testTLFJournalBlockOpWhileBusy(t *testing.T, ver kbfsmd.MetadataVer) {
   877  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   878  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkEnabled)
   879  	defer teardownTLFJournalTest(
   880  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   881  
   882  	mdserver := hangingMDServer{config.MDServer(), make(chan struct{})}
   883  	config.mdserver = mdserver
   884  
   885  	md := config.makeMD(kbfsmd.RevisionInitial, kbfsmd.ID{})
   886  	_, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
   887  	require.NoError(t, err)
   888  
   889  	mdserver.waitForPut(ctx, t)
   890  	delegate.requireNextState(ctx, bwBusy)
   891  
   892  	// Should still be able to put a block while busy.
   893  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2, 3, 4})
   894  }
   895  
   896  type rmdsWithExtra struct {
   897  	rmds  *RootMetadataSigned
   898  	extra kbfsmd.ExtraMetadata
   899  }
   900  
   901  type shimMDServer struct {
   902  	MDServer
   903  	rmdses          []rmdsWithExtra
   904  	nextGetRange    []*RootMetadataSigned
   905  	nextErr         error
   906  	getForTLFCalled bool
   907  }
   908  
   909  func (s *shimMDServer) GetRange(
   910  	ctx context.Context, id tlf.ID, bid kbfsmd.BranchID, mStatus kbfsmd.MergeStatus,
   911  	start, stop kbfsmd.Revision, _ *keybase1.LockID) ([]*RootMetadataSigned, error) {
   912  	rmdses := s.nextGetRange
   913  	s.nextGetRange = nil
   914  	return rmdses, nil
   915  }
   916  
   917  func (s *shimMDServer) Put(ctx context.Context, rmds *RootMetadataSigned,
   918  	extra kbfsmd.ExtraMetadata, _ *keybase1.LockContext, _ keybase1.MDPriority) error {
   919  	if s.nextErr != nil {
   920  		err := s.nextErr
   921  		s.nextErr = nil
   922  		return err
   923  	}
   924  	s.rmdses = append(s.rmdses, rmdsWithExtra{rmds, extra})
   925  
   926  	// Pretend all cancels happen after the actual put.
   927  	select {
   928  	case <-ctx.Done():
   929  		return ctx.Err()
   930  	default:
   931  	}
   932  	return nil
   933  }
   934  
   935  func (s *shimMDServer) GetForTLF(
   936  	ctx context.Context, id tlf.ID, bid kbfsmd.BranchID, mStatus kbfsmd.MergeStatus, _ *keybase1.LockID) (
   937  	*RootMetadataSigned, error) {
   938  	s.getForTLFCalled = true
   939  	if len(s.rmdses) == 0 {
   940  		return nil, nil
   941  	}
   942  	return s.rmdses[len(s.rmdses)-1].rmds, nil
   943  }
   944  
   945  func (s *shimMDServer) IsConnected() bool {
   946  	return true
   947  }
   948  
   949  func (s *shimMDServer) Shutdown() {
   950  }
   951  
   952  func requireJournalEntryCounts(t *testing.T, j *tlfJournal,
   953  	expectedBlockEntryCount, expectedMDEntryCount uint64) {
   954  	blockEntryCount, mdEntryCount, err := j.getJournalEntryCounts()
   955  	require.NoError(t, err)
   956  	require.Equal(t, expectedBlockEntryCount, blockEntryCount)
   957  	require.Equal(t, expectedMDEntryCount, mdEntryCount)
   958  }
   959  
   960  // The tests below test tlfJournal's MD flushing behavior.
   961  
   962  func testTLFJournalFlushMDBasic(t *testing.T, ver kbfsmd.MetadataVer) {
   963  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
   964  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
   965  	defer teardownTLFJournalTest(
   966  		ctx, tempdir, config, cancel, tlfJournal, delegate)
   967  
   968  	firstRevision := kbfsmd.Revision(10)
   969  	firstPrevRoot := kbfsmd.FakeID(1)
   970  	mdCount := 10
   971  
   972  	prevRoot := firstPrevRoot
   973  	for i := 0; i < mdCount; i++ {
   974  		revision := firstRevision + kbfsmd.Revision(i)
   975  		md := config.makeMD(revision, prevRoot)
   976  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
   977  		require.NoError(t, err)
   978  		prevRoot = irmd.mdID
   979  	}
   980  
   981  	// Flush all entries.
   982  	var mdserver shimMDServer
   983  	config.mdserver = &mdserver
   984  
   985  	_, mdEnd, _, err := tlfJournal.getJournalEnds(ctx)
   986  	require.NoError(t, err)
   987  
   988  	for i := 0; i < mdCount; i++ {
   989  		flushed, err := tlfJournal.flushOneMDOp(ctx, mdEnd, defaultFlushContext())
   990  		require.NoError(t, err)
   991  		require.True(t, flushed)
   992  	}
   993  	flushed, err := tlfJournal.flushOneMDOp(ctx, mdEnd, defaultFlushContext())
   994  	require.NoError(t, err)
   995  	require.False(t, flushed)
   996  	requireJournalEntryCounts(t, tlfJournal, uint64(mdCount), 0)
   997  	testMDJournalGCd(t, tlfJournal.mdJournal)
   998  
   999  	// Check RMDSes on the server.
  1000  
  1001  	rmdses := mdserver.rmdses
  1002  	require.Equal(t, mdCount, len(rmdses))
  1003  	config.checkRange(
  1004  		rmdses, firstRevision, firstPrevRoot, kbfsmd.Merged, kbfsmd.NullBranchID)
  1005  }
  1006  
  1007  func testTLFJournalFlushMDConflict(t *testing.T, ver kbfsmd.MetadataVer) {
  1008  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1009  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1010  	defer teardownTLFJournalTest(
  1011  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1012  
  1013  	firstRevision := kbfsmd.Revision(10)
  1014  	firstPrevRoot := kbfsmd.FakeID(1)
  1015  	mdCount := 10
  1016  
  1017  	prevRoot := firstPrevRoot
  1018  	for i := 0; i < mdCount/2; i++ {
  1019  		revision := firstRevision + kbfsmd.Revision(i)
  1020  		md := config.makeMD(revision, prevRoot)
  1021  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1022  		require.NoError(t, err)
  1023  		prevRoot = irmd.mdID
  1024  	}
  1025  
  1026  	var mdserver shimMDServer
  1027  	mdserver.nextErr = kbfsmd.ServerErrorConflictRevision{}
  1028  	config.mdserver = &mdserver
  1029  
  1030  	_, mdEnd, _, err := tlfJournal.getJournalEnds(ctx)
  1031  	require.NoError(t, err)
  1032  
  1033  	// Simulate a flush with a conflict error halfway through.
  1034  	{
  1035  		flushed, err := tlfJournal.flushOneMDOp(ctx, mdEnd, defaultFlushContext())
  1036  		require.NoError(t, err)
  1037  		require.False(t, flushed)
  1038  
  1039  		revision := firstRevision + kbfsmd.Revision(mdCount/2)
  1040  		md := config.makeMD(revision, prevRoot)
  1041  		_, err = tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1042  		require.IsType(t, MDJournalConflictError{}, err)
  1043  
  1044  		md.SetUnmerged()
  1045  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1046  		require.NoError(t, err)
  1047  		prevRoot = irmd.mdID
  1048  	}
  1049  
  1050  	for i := mdCount/2 + 1; i < mdCount; i++ {
  1051  		revision := firstRevision + kbfsmd.Revision(i)
  1052  		md := config.makeMD(revision, prevRoot)
  1053  		md.SetUnmerged()
  1054  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1055  		require.NoError(t, err)
  1056  		prevRoot = irmd.mdID
  1057  	}
  1058  
  1059  	// The journal won't flush anything while on a branch.
  1060  	requireJournalEntryCounts(t, tlfJournal, uint64(mdCount), uint64(mdCount))
  1061  }
  1062  
  1063  // orderedBlockServer and orderedMDServer appends onto their shared
  1064  // puts slice when their Put() methods are called.
  1065  
  1066  type orderedBlockServer struct {
  1067  	BlockServer
  1068  	lock      *sync.Mutex
  1069  	puts      *[]interface{}
  1070  	onceOnPut func()
  1071  }
  1072  
  1073  func (s *orderedBlockServer) Put(
  1074  	ctx context.Context, tlfID tlf.ID, id kbfsblock.ID,
  1075  	context kbfsblock.Context,
  1076  	buf []byte, serverHalf kbfscrypto.BlockCryptKeyServerHalf,
  1077  	_ DiskBlockCacheType) error {
  1078  	s.lock.Lock()
  1079  	defer s.lock.Unlock()
  1080  	*s.puts = append(*s.puts, id)
  1081  	if s.onceOnPut != nil {
  1082  		s.onceOnPut()
  1083  		s.onceOnPut = nil
  1084  	}
  1085  	return nil
  1086  }
  1087  
  1088  func (s *orderedBlockServer) Shutdown(context.Context) {}
  1089  
  1090  type orderedMDServer struct {
  1091  	MDServer
  1092  	lock      *sync.Mutex
  1093  	puts      *[]interface{}
  1094  	onceOnPut func() error
  1095  }
  1096  
  1097  func (s *orderedMDServer) Put(
  1098  	ctx context.Context, rmds *RootMetadataSigned, _ kbfsmd.ExtraMetadata,
  1099  	_ *keybase1.LockContext, _ keybase1.MDPriority) error {
  1100  	s.lock.Lock()
  1101  	defer s.lock.Unlock()
  1102  	*s.puts = append(*s.puts, rmds.MD.RevisionNumber())
  1103  	if s.onceOnPut != nil {
  1104  		err := s.onceOnPut()
  1105  		s.onceOnPut = nil
  1106  		if err != nil {
  1107  			return err
  1108  		}
  1109  	}
  1110  	return nil
  1111  }
  1112  
  1113  func (s *orderedMDServer) Shutdown() {}
  1114  
  1115  func testTLFJournalGCd(t *testing.T, tlfJournal *tlfJournal) {
  1116  	// The root dir shouldn't exist.
  1117  	_, err := ioutil.Stat(tlfJournal.dir)
  1118  	require.True(t, ioutil.IsNotExist(err))
  1119  
  1120  	func() {
  1121  		tlfJournal.journalLock.Lock()
  1122  		defer tlfJournal.journalLock.Unlock()
  1123  		unflushedPaths := tlfJournal.unflushedPaths.getUnflushedPaths()
  1124  		require.Nil(t, unflushedPaths)
  1125  		require.Equal(t, uint64(0), tlfJournal.unsquashedBytes)
  1126  		require.Equal(t, 0, len(tlfJournal.flushingBlocks))
  1127  	}()
  1128  
  1129  	requireJournalEntryCounts(t, tlfJournal, 0, 0)
  1130  
  1131  	// Check child journals.
  1132  	testBlockJournalGCd(t, tlfJournal.blockJournal)
  1133  	testMDJournalGCd(t, tlfJournal.mdJournal)
  1134  }
  1135  
  1136  // testTLFJournalFlushOrdering tests that we respect the relative
  1137  // orderings of blocks and MD ops when flushing, i.e. if a block op
  1138  // was added to the block journal before an MD op was added to the MD
  1139  // journal, then that block op will be flushed before that MD op.
  1140  func testTLFJournalFlushOrdering(t *testing.T, ver kbfsmd.MetadataVer) {
  1141  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1142  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1143  	defer teardownTLFJournalTest(
  1144  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1145  
  1146  	bid1, bCtx1, serverHalf1 := config.makeBlock([]byte{1})
  1147  	bid2, bCtx2, serverHalf2 := config.makeBlock([]byte{2})
  1148  	bid3, bCtx3, serverHalf3 := config.makeBlock([]byte{3})
  1149  
  1150  	md1 := config.makeMD(kbfsmd.Revision(10), kbfsmd.FakeID(1))
  1151  
  1152  	var lock sync.Mutex
  1153  	var puts []interface{}
  1154  
  1155  	bserver := orderedBlockServer{
  1156  		lock: &lock,
  1157  		puts: &puts,
  1158  	}
  1159  
  1160  	tlfJournal.delegateBlockServer.Shutdown(ctx)
  1161  	tlfJournal.delegateBlockServer = &bserver
  1162  
  1163  	mdserver := orderedMDServer{
  1164  		lock: &lock,
  1165  		puts: &puts,
  1166  	}
  1167  
  1168  	config.mdserver = &mdserver
  1169  
  1170  	// bid1 is-put-before kbfsmd.Revision(10).
  1171  	err := tlfJournal.putBlockData(
  1172  		ctx, bid1, bCtx1, []byte{1}, serverHalf1)
  1173  	require.NoError(t, err)
  1174  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
  1175  	require.NoError(t, err)
  1176  	prevRoot := irmd.mdID
  1177  
  1178  	bserver.onceOnPut = func() {
  1179  		// bid2 is-put-before kbfsmd.Revision(11).
  1180  		err := tlfJournal.putBlockData(
  1181  			ctx, bid2, bCtx2, []byte{2}, serverHalf2)
  1182  		require.NoError(t, err)
  1183  		md2 := config.makeMD(kbfsmd.Revision(11), prevRoot)
  1184  		irmd, err := tlfJournal.putMD(ctx, md2, tlfJournal.key, nil)
  1185  		require.NoError(t, err)
  1186  		prevRoot = irmd.mdID
  1187  	}
  1188  
  1189  	mdserver.onceOnPut = func() error {
  1190  		// bid3 is-put-before kbfsmd.Revision(12).
  1191  		err := tlfJournal.putBlockData(
  1192  			ctx, bid3, bCtx3, []byte{3}, serverHalf3)
  1193  		require.NoError(t, err)
  1194  		md3 := config.makeMD(kbfsmd.Revision(12), prevRoot)
  1195  		irmd, err := tlfJournal.putMD(ctx, md3, tlfJournal.key, nil)
  1196  		require.NoError(t, err)
  1197  		prevRoot = irmd.mdID
  1198  		return nil
  1199  	}
  1200  
  1201  	err = tlfJournal.flush(ctx)
  1202  	require.NoError(t, err)
  1203  	testTLFJournalGCd(t, tlfJournal)
  1204  
  1205  	// These two orderings depend on the exact flushing process,
  1206  	// but there are other possible orderings which respect the
  1207  	// above is-put-before constraints and also respect the
  1208  	// kbfsmd.Revision ordering.
  1209  	expectedPuts1 := []interface{}{
  1210  		bid1, kbfsmd.Revision(10), bid2, bid3,
  1211  		kbfsmd.Revision(11), kbfsmd.Revision(12),
  1212  	}
  1213  	// This is possible since block puts are done in parallel.
  1214  	expectedPuts2 := []interface{}{
  1215  		bid1, kbfsmd.Revision(10), bid3, bid2,
  1216  		kbfsmd.Revision(11), kbfsmd.Revision(12),
  1217  	}
  1218  	require.True(t, reflect.DeepEqual(puts, expectedPuts1) ||
  1219  		reflect.DeepEqual(puts, expectedPuts2),
  1220  		"Expected %v or %v, got %v", expectedPuts1,
  1221  		expectedPuts2, puts)
  1222  }
  1223  
  1224  // testTLFJournalFlushOrderingAfterSquashAndCR tests that after a
  1225  // branch is squashed multiple times, and then hits a conflict, the
  1226  // blocks are flushed completely before the conflict-resolving MD.
  1227  func testTLFJournalFlushOrderingAfterSquashAndCR(
  1228  	t *testing.T, ver kbfsmd.MetadataVer) {
  1229  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1230  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1231  	defer teardownTLFJournalTest(
  1232  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1233  	tlfJournal.forcedSquashByBytes = 20
  1234  
  1235  	firstRev := kbfsmd.Revision(10)
  1236  	firstPrevRoot := kbfsmd.FakeID(1)
  1237  	md1 := config.makeMD(firstRev, firstPrevRoot)
  1238  
  1239  	var lock sync.Mutex
  1240  	var puts []interface{}
  1241  
  1242  	bserver := orderedBlockServer{
  1243  		lock: &lock,
  1244  		puts: &puts,
  1245  	}
  1246  
  1247  	tlfJournal.delegateBlockServer.Shutdown(ctx)
  1248  	tlfJournal.delegateBlockServer = &bserver
  1249  
  1250  	var mdserverShim shimMDServer
  1251  	mdserver := orderedMDServer{
  1252  		MDServer: &mdserverShim,
  1253  		lock:     &lock,
  1254  		puts:     &puts,
  1255  	}
  1256  
  1257  	config.mdserver = &mdserver
  1258  
  1259  	// Put almost a full batch worth of block before revs 10 and 11.
  1260  	blockEnd := uint64(maxJournalBlockFlushBatchSize - 1)
  1261  	for i := uint64(0); i < blockEnd; i++ {
  1262  		data := []byte{byte(i)}
  1263  		bid, bCtx, serverHalf := config.makeBlock(data)
  1264  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1265  		require.NoError(t, err)
  1266  	}
  1267  
  1268  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
  1269  	require.NoError(t, err)
  1270  	prevRoot := irmd.mdID
  1271  	md2 := config.makeMD(firstRev+1, prevRoot)
  1272  	require.NoError(t, err)
  1273  	irmd, err = tlfJournal.putMD(ctx, md2, tlfJournal.key, nil)
  1274  	require.NoError(t, err)
  1275  
  1276  	// Squash revs 10 and 11.  No blocks should actually be flushed
  1277  	// yet.
  1278  	err = tlfJournal.flush(ctx)
  1279  	require.NoError(t, err)
  1280  	require.Equal(
  1281  		t, kbfsmd.PendingLocalSquashBranchID, tlfJournal.mdJournal.getBranchID())
  1282  	requireJournalEntryCounts(t, tlfJournal, blockEnd+2, 2)
  1283  
  1284  	squashMD := config.makeMD(firstRev, firstPrevRoot)
  1285  	irmd, err = tlfJournal.resolveBranch(
  1286  		ctx, kbfsmd.PendingLocalSquashBranchID, []kbfsblock.ID{}, squashMD,
  1287  		tlfJournal.key, nil)
  1288  	require.NoError(t, err)
  1289  	prevRoot = irmd.mdID
  1290  	requireJournalEntryCounts(t, tlfJournal, blockEnd+3, 1)
  1291  
  1292  	// Another revision 11, with a squashable number of blocks to
  1293  	// complete the initial batch.
  1294  	for i := blockEnd; i < blockEnd+20; i++ {
  1295  		data := []byte{byte(i)}
  1296  		bid, bCtx, serverHalf := config.makeBlock(data)
  1297  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1298  		require.NoError(t, err)
  1299  	}
  1300  	blockEnd += 20
  1301  	md2 = config.makeMD(firstRev+1, prevRoot)
  1302  	require.NoError(t, err)
  1303  	irmd, err = tlfJournal.putMD(ctx, md2, tlfJournal.key, nil)
  1304  	require.NoError(t, err)
  1305  
  1306  	// Let it squash (avoiding a branch this time since there's only one MD).
  1307  	err = tlfJournal.flush(ctx)
  1308  	require.NoError(t, err)
  1309  	require.Equal(t, kbfsmd.NullBranchID, tlfJournal.mdJournal.getBranchID())
  1310  	requireJournalEntryCounts(t, tlfJournal, blockEnd+4, 2)
  1311  
  1312  	// Simulate an MD conflict and try to flush again.  This will
  1313  	// flush a full batch of blocks before hitting the conflict, as
  1314  	// well as the marker for rev 10.
  1315  	mdserver.onceOnPut = func() error {
  1316  		return kbfsmd.ServerErrorConflictRevision{}
  1317  	}
  1318  	mergedBare := config.makeMD(md2.Revision(), firstPrevRoot).bareMd
  1319  	mergedBare.SetSerializedPrivateMetadata([]byte{1})
  1320  	rmds, err := SignBareRootMetadata(
  1321  		ctx, config.Codec(), config.Crypto(), config.Crypto(),
  1322  		mergedBare, time.Now())
  1323  	require.NoError(t, err)
  1324  	mdserverShim.nextGetRange = []*RootMetadataSigned{rmds}
  1325  	err = tlfJournal.flush(ctx)
  1326  	require.NoError(t, err)
  1327  	branchID := tlfJournal.mdJournal.getBranchID()
  1328  	require.NotEqual(t, kbfsmd.PendingLocalSquashBranchID, branchID)
  1329  	require.NotEqual(t, kbfsmd.NullBranchID, branchID)
  1330  	// Blocks: All the unflushed blocks, plus two unflushed rev markers.
  1331  	requireJournalEntryCounts(
  1332  		t, tlfJournal, blockEnd-maxJournalBlockFlushBatchSize+2, 2)
  1333  
  1334  	// More blocks that are part of the resolution.
  1335  	blockEnd2 := blockEnd + maxJournalBlockFlushBatchSize + 2
  1336  	for i := blockEnd; i < blockEnd2; i++ {
  1337  		data := []byte{byte(i)}
  1338  		bid, bCtx, serverHalf := config.makeBlock(data)
  1339  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1340  		require.NoError(t, err)
  1341  	}
  1342  
  1343  	// Use revision 11 (as if two revisions had been merged by another
  1344  	// device).
  1345  	resolveMD := config.makeMD(md2.Revision(), firstPrevRoot)
  1346  	_, err = tlfJournal.resolveBranch(
  1347  		ctx, branchID, []kbfsblock.ID{}, resolveMD, tlfJournal.key, nil)
  1348  	require.NoError(t, err)
  1349  	// Blocks: the ones from the last check, plus the new blocks, plus
  1350  	// the resolve rev marker.
  1351  	requireJournalEntryCounts(
  1352  		t, tlfJournal, blockEnd2-maxJournalBlockFlushBatchSize+3, 1)
  1353  
  1354  	// Flush everything remaining.  All blocks should be flushed after
  1355  	// `resolveMD`.
  1356  	err = tlfJournal.flush(ctx)
  1357  	require.NoError(t, err)
  1358  	testTLFJournalGCd(t, tlfJournal)
  1359  
  1360  	require.Equal(t, resolveMD.Revision(), puts[len(puts)-1])
  1361  }
  1362  
  1363  // testTLFJournalFlushInterleaving tests that we interleave block and
  1364  // MD ops while respecting the relative orderings of blocks and MD ops
  1365  // when flushing.
  1366  func testTLFJournalFlushInterleaving(t *testing.T, ver kbfsmd.MetadataVer) {
  1367  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1368  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1369  	defer teardownTLFJournalTest(
  1370  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1371  
  1372  	var lock sync.Mutex
  1373  	var puts []interface{}
  1374  
  1375  	bserver := orderedBlockServer{
  1376  		lock: &lock,
  1377  		puts: &puts,
  1378  	}
  1379  
  1380  	tlfJournal.delegateBlockServer.Shutdown(ctx)
  1381  	tlfJournal.delegateBlockServer = &bserver
  1382  
  1383  	var mdserverShim shimMDServer
  1384  	mdserver := orderedMDServer{
  1385  		MDServer: &mdserverShim,
  1386  		lock:     &lock,
  1387  		puts:     &puts,
  1388  	}
  1389  
  1390  	config.mdserver = &mdserver
  1391  
  1392  	// Revision 1
  1393  	var bids []kbfsblock.ID
  1394  	rev1BlockEnd := maxJournalBlockFlushBatchSize * 2
  1395  	for i := 0; i < rev1BlockEnd; i++ {
  1396  		data := []byte{byte(i)}
  1397  		bid, bCtx, serverHalf := config.makeBlock(data)
  1398  		bids = append(bids, bid)
  1399  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1400  		require.NoError(t, err)
  1401  	}
  1402  	md1 := config.makeMD(kbfsmd.Revision(10), kbfsmd.FakeID(1))
  1403  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
  1404  	require.NoError(t, err)
  1405  	prevRoot := irmd.mdID
  1406  
  1407  	// Revision 2
  1408  	rev2BlockEnd := rev1BlockEnd + maxJournalBlockFlushBatchSize*2
  1409  	for i := rev1BlockEnd; i < rev2BlockEnd; i++ {
  1410  		data := []byte{byte(i)}
  1411  		bid, bCtx, serverHalf := config.makeBlock(data)
  1412  		bids = append(bids, bid)
  1413  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1414  		require.NoError(t, err)
  1415  	}
  1416  	md2 := config.makeMD(kbfsmd.Revision(11), prevRoot)
  1417  	irmd, err = tlfJournal.putMD(ctx, md2, tlfJournal.key, nil)
  1418  	require.NoError(t, err)
  1419  
  1420  	err = tlfJournal.flush(ctx)
  1421  	require.NoError(t, err)
  1422  	testTLFJournalGCd(t, tlfJournal)
  1423  
  1424  	// Make sure the flusher checks in between block flushes for
  1425  	// conflicting MDs on the server.
  1426  	require.True(t, mdserverShim.getForTLFCalled)
  1427  
  1428  	// Make sure that: before revision 1, all the rev1 blocks were
  1429  	// put; rev2 comes last; some blocks are put between the two.
  1430  	bidsSeen := make(map[kbfsblock.ID]bool)
  1431  	md1Slot := 0
  1432  	md2Slot := 0
  1433  	for i, put := range puts {
  1434  		if bid, ok := put.(kbfsblock.ID); ok {
  1435  			t.Logf("Saw bid %s at %d", bid, i)
  1436  			bidsSeen[bid] = true
  1437  			continue
  1438  		}
  1439  
  1440  		mdID, ok := put.(kbfsmd.Revision)
  1441  		require.True(t, ok)
  1442  		if mdID == md1.Revision() {
  1443  			md1Slot = i
  1444  			for j := 0; j < rev1BlockEnd; j++ {
  1445  				t.Logf("Checking bid %s at %d", bids[j], i)
  1446  				require.True(t, bidsSeen[bids[j]])
  1447  			}
  1448  		} else if mdID == md2.Revision() {
  1449  			md2Slot = i
  1450  			require.NotZero(t, md1Slot)
  1451  			require.True(t, md1Slot+1 < i)
  1452  			require.Equal(t, i, len(puts)-1)
  1453  		}
  1454  	}
  1455  	require.NotZero(t, md1Slot)
  1456  	require.NotZero(t, md2Slot)
  1457  }
  1458  
  1459  type testBranchChangeListener struct {
  1460  	c chan<- struct{}
  1461  }
  1462  
  1463  func (tbcl testBranchChangeListener) onTLFBranchChange(_ tlf.ID, _ kbfsmd.BranchID) {
  1464  	tbcl.c <- struct{}{}
  1465  }
  1466  
  1467  func testTLFJournalPauseBlocksAndConvertBranch(ctx context.Context,
  1468  	t *testing.T, tlfJournal *tlfJournal, config *testTLFJournalConfig) (
  1469  	firstRev kbfsmd.Revision, firstRoot kbfsmd.ID,
  1470  	retUnpauseBlockPutCh chan<- struct{}, retErrCh <-chan error,
  1471  	blocksLeftAfterFlush uint64, mdsLeftAfterFlush uint64) {
  1472  	branchCh := make(chan struct{}, 1)
  1473  	tlfJournal.onBranchChange = testBranchChangeListener{branchCh}
  1474  
  1475  	var lock sync.Mutex
  1476  	var puts []interface{}
  1477  
  1478  	unpauseBlockPutCh := make(chan struct{})
  1479  	noticeBlockPutCh := make(chan struct{})
  1480  	bserver := orderedBlockServer{
  1481  		lock: &lock,
  1482  		puts: &puts,
  1483  		onceOnPut: func() {
  1484  			noticeBlockPutCh <- struct{}{}
  1485  			<-unpauseBlockPutCh
  1486  		},
  1487  	}
  1488  
  1489  	tlfJournal.delegateBlockServer.Shutdown(ctx)
  1490  	tlfJournal.delegateBlockServer = &bserver
  1491  
  1492  	// Revision 1
  1493  	rev1BlockEnd := maxJournalBlockFlushBatchSize * 2
  1494  	for i := 0; i < rev1BlockEnd; i++ {
  1495  		data := []byte{byte(i)}
  1496  		bid, bCtx, serverHalf := config.makeBlock(data)
  1497  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1498  		require.NoError(t, err)
  1499  	}
  1500  	firstRev = kbfsmd.Revision(10)
  1501  	firstRoot = kbfsmd.FakeID(1)
  1502  	md1 := config.makeMD(firstRev, firstRoot)
  1503  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
  1504  	require.NoError(t, err)
  1505  	prevRoot := irmd.mdID
  1506  	rev := firstRev
  1507  
  1508  	// Now start the blocks flushing.  One of the block puts will be
  1509  	// stuck.  During that time, put a lot more MD revisions, enough
  1510  	// to trigger branch conversion.  However, no pause should be
  1511  	// called.
  1512  
  1513  	errCh := make(chan error, 1)
  1514  	go func() {
  1515  		errCh <- tlfJournal.flush(ctx)
  1516  	}()
  1517  
  1518  	<-noticeBlockPutCh
  1519  
  1520  	markers := uint64(1)
  1521  	for i := 0; i < ForcedBranchSquashRevThreshold+1; i++ {
  1522  		rev++
  1523  		md := config.makeMD(rev, prevRoot)
  1524  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1525  		if isRevisionConflict(err) {
  1526  			// Branch conversion is done, we can stop now.
  1527  			break
  1528  		}
  1529  		require.NoError(t, err)
  1530  		prevRoot = irmd.mdID
  1531  		markers++
  1532  	}
  1533  
  1534  	// Wait for the local squash branch to appear.
  1535  	select {
  1536  	case <-branchCh:
  1537  	case <-ctx.Done():
  1538  		t.Fatalf("Timeout while waiting for branch change")
  1539  	}
  1540  
  1541  	return firstRev, firstRoot, unpauseBlockPutCh, errCh,
  1542  		maxJournalBlockFlushBatchSize + markers, markers
  1543  }
  1544  
  1545  // testTLFJournalConvertWhileFlushing tests that we can do branch
  1546  // conversion while blocks are still flushing.
  1547  func testTLFJournalConvertWhileFlushing(t *testing.T, ver kbfsmd.MetadataVer) {
  1548  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1549  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1550  	defer teardownTLFJournalTest(
  1551  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1552  
  1553  	_, _, unpauseBlockPutCh, errCh, blocksLeftAfterFlush, mdsLeftAfterFlush :=
  1554  		testTLFJournalPauseBlocksAndConvertBranch(ctx, t, tlfJournal, config)
  1555  
  1556  	// Now finish the block put, and let the flush finish.  We
  1557  	// should be on a local squash branch after this.
  1558  	unpauseBlockPutCh <- struct{}{}
  1559  	err := <-errCh
  1560  	require.NoError(t, err)
  1561  
  1562  	// Should be a full batch worth of blocks left, plus all the
  1563  	// revision markers above.  No squash has actually happened yet,
  1564  	// so all the revisions should be there now, just on a branch.
  1565  	requireJournalEntryCounts(
  1566  		t, tlfJournal, blocksLeftAfterFlush, mdsLeftAfterFlush)
  1567  	require.Equal(
  1568  		t, kbfsmd.PendingLocalSquashBranchID, tlfJournal.mdJournal.getBranchID())
  1569  }
  1570  
  1571  // testTLFJournalSquashWhileFlushing tests that we can do journal
  1572  // coalescing while blocks are still flushing.
  1573  func testTLFJournalSquashWhileFlushing(t *testing.T, ver kbfsmd.MetadataVer) {
  1574  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1575  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1576  	defer teardownTLFJournalTest(
  1577  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1578  
  1579  	firstRev, firstPrevRoot, unpauseBlockPutCh, errCh,
  1580  		blocksLeftAfterFlush, _ :=
  1581  		testTLFJournalPauseBlocksAndConvertBranch(ctx, t, tlfJournal, config)
  1582  
  1583  	// While it's paused, resolve the branch.
  1584  	resolveMD := config.makeMD(firstRev, firstPrevRoot)
  1585  	_, err := tlfJournal.resolveBranch(
  1586  		ctx, tlfJournal.mdJournal.getBranchID(), []kbfsblock.ID{}, resolveMD,
  1587  		tlfJournal.key, nil)
  1588  	require.NoError(t, err)
  1589  	requireJournalEntryCounts(
  1590  		t, tlfJournal, blocksLeftAfterFlush+maxJournalBlockFlushBatchSize+1, 1)
  1591  
  1592  	// Now finish the block put, and let the flush finish.  We
  1593  	// shouldn't be on a branch anymore.
  1594  	unpauseBlockPutCh <- struct{}{}
  1595  	err = <-errCh
  1596  	require.NoError(t, err)
  1597  
  1598  	// Since flush() never saw the branch in conflict, it will finish
  1599  	// flushing everything.
  1600  	testTLFJournalGCd(t, tlfJournal)
  1601  	require.Equal(t, kbfsmd.NullBranchID, tlfJournal.mdJournal.getBranchID())
  1602  }
  1603  
  1604  type testImmediateBackOff struct {
  1605  	numBackOffs int
  1606  	resetCh     chan<- struct{}
  1607  }
  1608  
  1609  func (t *testImmediateBackOff) NextBackOff() time.Duration {
  1610  	t.numBackOffs++
  1611  	return 1 * time.Nanosecond
  1612  }
  1613  
  1614  func (t *testImmediateBackOff) Reset() {
  1615  	close(t.resetCh)
  1616  }
  1617  
  1618  func testTLFJournalFlushRetry(t *testing.T, ver kbfsmd.MetadataVer) {
  1619  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1620  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1621  	defer teardownTLFJournalTest(
  1622  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1623  
  1624  	// Stop the current background loop; replace with one that retries
  1625  	// immediately.
  1626  	tlfJournal.needShutdownCh <- struct{}{}
  1627  	<-tlfJournal.backgroundShutdownCh
  1628  	resetCh := make(chan struct{})
  1629  	b := &testImmediateBackOff{resetCh: resetCh}
  1630  	tlfJournal.backgroundShutdownCh = make(chan struct{})
  1631  	go tlfJournal.doBackgroundWorkLoop(TLFJournalBackgroundWorkPaused, b)
  1632  	select {
  1633  	case <-delegate.shutdownCh:
  1634  	case <-ctx.Done():
  1635  		assert.Fail(config.t, ctx.Err().Error())
  1636  	}
  1637  
  1638  	firstRevision := kbfsmd.Revision(10)
  1639  	firstPrevRoot := kbfsmd.FakeID(1)
  1640  	mdCount := 10
  1641  
  1642  	prevRoot := firstPrevRoot
  1643  	for i := 0; i < mdCount; i++ {
  1644  		revision := firstRevision + kbfsmd.Revision(i)
  1645  		md := config.makeMD(revision, prevRoot)
  1646  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1647  		require.NoError(t, err)
  1648  		prevRoot = irmd.mdID
  1649  	}
  1650  
  1651  	var mdserver shimMDServer
  1652  	mdserver.nextErr = errors.New("Error to force a retry")
  1653  	config.mdserver = &mdserver
  1654  
  1655  	delegate.requireNextState(ctx, bwPaused)
  1656  	tlfJournal.resumeBackgroundWork()
  1657  	delegate.requireNextState(ctx, bwIdle)
  1658  	delegate.requireNextState(ctx, bwBusy)
  1659  	delegate.requireNextState(ctx, bwIdle)
  1660  	delegate.requireNextState(ctx, bwBusy)
  1661  	delegate.requireNextState(ctx, bwIdle)
  1662  	<-resetCh
  1663  
  1664  	require.Equal(t, b.numBackOffs, 1)
  1665  	testTLFJournalGCd(t, tlfJournal)
  1666  }
  1667  
  1668  func testTLFJournalResolveBranch(t *testing.T, ver kbfsmd.MetadataVer) {
  1669  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1670  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1671  	defer teardownTLFJournalTest(
  1672  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1673  
  1674  	var bids []kbfsblock.ID
  1675  	for i := 0; i < 3; i++ {
  1676  		data := []byte{byte(i)}
  1677  		bid, bCtx, serverHalf := config.makeBlock(data)
  1678  		bids = append(bids, bid)
  1679  		err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1680  		require.NoError(t, err)
  1681  	}
  1682  
  1683  	firstRevision := kbfsmd.Revision(10)
  1684  	firstPrevRoot := kbfsmd.FakeID(1)
  1685  	mdCount := 3
  1686  
  1687  	prevRoot := firstPrevRoot
  1688  	for i := 0; i < mdCount; i++ {
  1689  		revision := firstRevision + kbfsmd.Revision(i)
  1690  		md := config.makeMD(revision, prevRoot)
  1691  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1692  		require.NoError(t, err)
  1693  		prevRoot = irmd.mdID
  1694  	}
  1695  
  1696  	var mdserver shimMDServer
  1697  	mdserver.nextErr = kbfsmd.ServerErrorConflictRevision{}
  1698  	config.mdserver = &mdserver
  1699  
  1700  	_, mdEnd, _, err := tlfJournal.getJournalEnds(ctx)
  1701  	require.NoError(t, err)
  1702  
  1703  	// This will convert to a branch.
  1704  	flushed, err := tlfJournal.flushOneMDOp(ctx, mdEnd, defaultFlushContext())
  1705  	require.NoError(t, err)
  1706  	require.False(t, flushed)
  1707  
  1708  	// The background worker was already paused, so we won't get a
  1709  	// paused signal here.  But resume the background work now so that
  1710  	// later when the conflict resolves, it will be able to send a
  1711  	// resume signal.
  1712  	tlfJournal.resumeBackgroundWork()
  1713  
  1714  	// Resolve the branch.
  1715  	resolveMD := config.makeMD(firstRevision, firstPrevRoot)
  1716  	_, err = tlfJournal.resolveBranch(
  1717  		ctx, tlfJournal.mdJournal.getBranchID(), []kbfsblock.ID{bids[1]},
  1718  		resolveMD, tlfJournal.key, nil)
  1719  	require.NoError(t, err)
  1720  
  1721  	blockEnd, newMDEnd, _, err := tlfJournal.getJournalEnds(ctx)
  1722  	require.NoError(t, err)
  1723  	require.Equal(t, firstRevision+1, newMDEnd)
  1724  
  1725  	blocks, b, maxMD, err := tlfJournal.getNextBlockEntriesToFlush(
  1726  		ctx, blockEnd, kbfsmd.ID{})
  1727  	require.NoError(t, err)
  1728  	require.Equal(t, firstRevision, maxMD)
  1729  	// 3 blocks, 3 old MD markers, 1 new MD marker
  1730  	require.Equal(t, 7, blocks.length())
  1731  	require.Equal(t, 2, blocks.puts.numBlocks())
  1732  	require.Equal(t, 0, blocks.adds.numBlocks())
  1733  	// 1 ignored block, 3 ignored MD markers, 1 real MD marker
  1734  	require.Len(t, blocks.other, 5)
  1735  	ptrs := blocks.puts.Ptrs()
  1736  	ids := make([]kbfsblock.ID, len(ptrs))
  1737  	for i, ptr := range ptrs {
  1738  		ids[i] = ptr.ID
  1739  	}
  1740  	require.Contains(t, ids, bids[0])
  1741  	require.Contains(t, ids, bids[2])
  1742  	// 2 bytes of data in 2 unignored blocks.
  1743  	require.Equal(t, int64(2), b)
  1744  
  1745  	// resolveBranch resumes background work.
  1746  	delegate.requireNextState(ctx, bwIdle)
  1747  	delegate.requireNextState(ctx, bwBusy)
  1748  }
  1749  
  1750  func testTLFJournalSquashByBytes(t *testing.T, ver kbfsmd.MetadataVer) {
  1751  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1752  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1753  	defer teardownTLFJournalTest(
  1754  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1755  	tlfJournal.forcedSquashByBytes = 10
  1756  
  1757  	data := make([]byte, tlfJournal.forcedSquashByBytes+1)
  1758  	bid, bCtx, serverHalf := config.makeBlock(data)
  1759  	err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1760  	require.NoError(t, err)
  1761  
  1762  	firstRevision := kbfsmd.Revision(10)
  1763  	firstPrevRoot := kbfsmd.FakeID(1)
  1764  	mdCount := 3
  1765  
  1766  	prevRoot := firstPrevRoot
  1767  	for i := 0; i < mdCount; i++ {
  1768  		revision := firstRevision + kbfsmd.Revision(i)
  1769  		md := config.makeMD(revision, prevRoot)
  1770  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1771  		require.NoError(t, err)
  1772  		prevRoot = irmd.mdID
  1773  	}
  1774  
  1775  	// This should convert it to a branch, based on the number of
  1776  	// outstanding bytes.
  1777  	err = tlfJournal.flush(ctx)
  1778  	require.NoError(t, err)
  1779  	require.Equal(
  1780  		t, kbfsmd.PendingLocalSquashBranchID, tlfJournal.mdJournal.getBranchID())
  1781  }
  1782  
  1783  // Test that the first revision of a TLF doesn't get squashed.
  1784  func testTLFJournalFirstRevNoSquash(t *testing.T, ver kbfsmd.MetadataVer) {
  1785  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1786  		setupTLFJournalTest(t, ver, TLFJournalBackgroundWorkPaused)
  1787  	defer teardownTLFJournalTest(
  1788  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1789  	tlfJournal.forcedSquashByBytes = 10
  1790  
  1791  	data := make([]byte, tlfJournal.forcedSquashByBytes+1)
  1792  	bid, bCtx, serverHalf := config.makeBlock(data)
  1793  	err := tlfJournal.putBlockData(ctx, bid, bCtx, data, serverHalf)
  1794  	require.NoError(t, err)
  1795  
  1796  	firstRevision := kbfsmd.RevisionInitial
  1797  	mdCount := 4
  1798  
  1799  	var firstMdID, prevRoot kbfsmd.ID
  1800  	for i := 0; i < mdCount; i++ {
  1801  		revision := firstRevision + kbfsmd.Revision(i)
  1802  		md := config.makeMD(revision, prevRoot)
  1803  		irmd, err := tlfJournal.putMD(ctx, md, tlfJournal.key, nil)
  1804  		require.NoError(t, err)
  1805  		prevRoot = irmd.mdID
  1806  		if i == 0 {
  1807  			firstMdID = irmd.mdID
  1808  		}
  1809  	}
  1810  
  1811  	// This should convert it to a branch, based on the number of
  1812  	// outstanding bytes.
  1813  	err = tlfJournal.flush(ctx)
  1814  	require.NoError(t, err)
  1815  	require.Equal(
  1816  		t, kbfsmd.PendingLocalSquashBranchID, tlfJournal.mdJournal.getBranchID())
  1817  	requireJournalEntryCounts(t, tlfJournal, 5, 4)
  1818  	unsquashedRange, err := tlfJournal.getMDRange(
  1819  		ctx, kbfsmd.NullBranchID, firstRevision, firstRevision+3)
  1820  	require.NoError(t, err)
  1821  	require.Len(t, unsquashedRange, 1)
  1822  	require.Equal(t, firstRevision, unsquashedRange[0].RevisionNumber())
  1823  	require.Equal(t, firstMdID, unsquashedRange[0].mdID)
  1824  	squashRange, err := tlfJournal.getMDRange(
  1825  		ctx, kbfsmd.PendingLocalSquashBranchID, firstRevision, firstRevision+3)
  1826  	require.NoError(t, err)
  1827  	require.Len(t, squashRange, 3)
  1828  	require.Equal(t, firstRevision+1, squashRange[0].RevisionNumber())
  1829  }
  1830  
  1831  // testTLFJournalSingleOp tests that when the journal is in single op
  1832  // mode, it doesn't flush any MDs until `finishSingleOp()` is called,
  1833  // and then it only flushes one squashed MD.
  1834  func testTLFJournalSingleOp(t *testing.T, ver kbfsmd.MetadataVer) {
  1835  	tempdir, config, ctx, cancel, tlfJournal, delegate :=
  1836  		setupTLFJournalTest(t, ver, TLFJournalSingleOpBackgroundWorkEnabled)
  1837  	defer teardownTLFJournalTest(
  1838  		ctx, tempdir, config, cancel, tlfJournal, delegate)
  1839  
  1840  	var mdserver shimMDServer
  1841  	config.mdserver = &mdserver
  1842  
  1843  	tlfJournal.pauseBackgroundWork()
  1844  	delegate.requireNextState(ctx, bwPaused)
  1845  
  1846  	putBlock(ctx, t, config, tlfJournal, []byte{1, 2})
  1847  	putBlock(ctx, t, config, tlfJournal, []byte{3, 4})
  1848  	putBlock(ctx, t, config, tlfJournal, []byte{5, 6})
  1849  
  1850  	md1 := config.makeMD(kbfsmd.Revision(10), kbfsmd.FakeID(1))
  1851  	irmd, err := tlfJournal.putMD(ctx, md1, tlfJournal.key, nil)
  1852  	require.NoError(t, err)
  1853  	prevRoot := irmd.mdID
  1854  
  1855  	putBlock(ctx, t, config, tlfJournal, []byte{7, 8})
  1856  	putBlock(ctx, t, config, tlfJournal, []byte{9, 10})
  1857  
  1858  	md2 := config.makeMD(kbfsmd.Revision(11), prevRoot)
  1859  	_, err = tlfJournal.putMD(ctx, md2, tlfJournal.key, nil)
  1860  	require.NoError(t, err)
  1861  
  1862  	tlfJournal.resumeBackgroundWork()
  1863  	delegate.requireNextState(ctx, bwIdle)
  1864  	delegate.requireNextState(ctx, bwBusy)
  1865  	delegate.requireNextState(ctx, bwIdle)
  1866  
  1867  	requireJournalEntryCounts(t, tlfJournal, 0, 2)
  1868  
  1869  	// The `finishSingleOp` call below blocks, so we have to do it in
  1870  	// a background goroutine to avoid deadlock.
  1871  	errCh := make(chan error, 1)
  1872  	go func() {
  1873  		errCh <- tlfJournal.finishSingleOp(ctx, nil, keybase1.MDPriorityNormal)
  1874  	}()
  1875  
  1876  	// Background loop awakens after the finish is signaled.  Should
  1877  	// now be on a conflict branch.  The pause signal sent by the
  1878  	// branch-converter races with the background work finishing
  1879  	// (KBFS-2440), and so the second state could be either idle or
  1880  	// paused, depending on what gets processed first.
  1881  	delegate.requireNextState(ctx, bwBusy)
  1882  	nextState := delegate.requireNextState(ctx, bwPaused, bwIdle)
  1883  	if nextState == bwIdle {
  1884  		delegate.requireNextState(ctx, bwPaused)
  1885  	}
  1886  
  1887  	require.Equal(
  1888  		t, kbfsmd.PendingLocalSquashBranchID, tlfJournal.mdJournal.getBranchID())
  1889  	resolveMD := config.makeMD(kbfsmd.Revision(10), kbfsmd.FakeID(1))
  1890  	_, err = tlfJournal.resolveBranch(
  1891  		ctx, tlfJournal.mdJournal.getBranchID(), nil, resolveMD, tlfJournal.key,
  1892  		nil)
  1893  	require.NoError(t, err)
  1894  
  1895  	// Now the flushing should complete.
  1896  	delegate.requireNextState(ctx, bwIdle)
  1897  	delegate.requireNextState(ctx, bwBusy)
  1898  	delegate.requireNextState(ctx, bwIdle)
  1899  
  1900  	select {
  1901  	case err := <-errCh:
  1902  		require.NoError(t, err)
  1903  	case <-ctx.Done():
  1904  		t.Fatal(ctx.Err().Error())
  1905  	}
  1906  	requireJournalEntryCounts(t, tlfJournal, 0, 0)
  1907  
  1908  	require.Len(t, mdserver.rmdses, 1)
  1909  }
  1910  
  1911  func TestTLFJournal(t *testing.T) {
  1912  	tests := []func(*testing.T, kbfsmd.MetadataVer){
  1913  		testTLFJournalBasic,
  1914  		testTLFJournalPauseResume,
  1915  		testTLFJournalPauseShutdown,
  1916  		testTLFJournalBlockOpBasic,
  1917  		testTLFJournalBlockOpBusyPause,
  1918  		testTLFJournalBlockOpBusyShutdown,
  1919  		testTLFJournalSecondBlockOpWhileBusy,
  1920  		testTLFJournalMDServerBusyPause,
  1921  		testTLFJournalMDServerBusyShutdown,
  1922  		testTLFJournalBlockOpWhileBusy,
  1923  		testTLFJournalBlockOpDiskByteLimit,
  1924  		testTLFJournalBlockOpDiskFileLimit,
  1925  		testTLFJournalBlockOpDiskQuotaLimit,
  1926  		testTLFJournalBlockOpDiskQuotaLimitResolve,
  1927  		testTLFJournalBlockOpDiskLimitDuplicate,
  1928  		testTLFJournalBlockOpDiskLimitCancel,
  1929  		testTLFJournalBlockOpDiskLimitTimeout,
  1930  		testTLFJournalBlockOpDiskLimitPutFailure,
  1931  		testTLFJournalFlushMDBasic,
  1932  		testTLFJournalFlushMDConflict,
  1933  		testTLFJournalFlushOrdering,
  1934  		testTLFJournalFlushOrderingAfterSquashAndCR,
  1935  		testTLFJournalFlushInterleaving,
  1936  		testTLFJournalConvertWhileFlushing,
  1937  		testTLFJournalSquashWhileFlushing,
  1938  		testTLFJournalFlushRetry,
  1939  		testTLFJournalResolveBranch,
  1940  		testTLFJournalSquashByBytes,
  1941  		testTLFJournalFirstRevNoSquash,
  1942  		testTLFJournalSingleOp,
  1943  	}
  1944  	runTestsOverMetadataVers(t, "testTLFJournal", tests)
  1945  }