github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/nbs/block_store_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package nbs
    23  
    24  import (
    25  	"bytes"
    26  	"context"
    27  	"crypto/rand"
    28  	"errors"
    29  	"io/ioutil"
    30  	"os"
    31  	"path/filepath"
    32  	"sort"
    33  	"testing"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  	"github.com/stretchr/testify/suite"
    38  
    39  	"github.com/dolthub/dolt/go/libraries/utils/osutil"
    40  	"github.com/dolthub/dolt/go/store/chunks"
    41  	"github.com/dolthub/dolt/go/store/constants"
    42  	"github.com/dolthub/dolt/go/store/d"
    43  	"github.com/dolthub/dolt/go/store/hash"
    44  )
    45  
    46  const testMemTableSize = 1 << 8
    47  
    48  func TestBlockStoreSuite(t *testing.T) {
    49  	suite.Run(t, &BlockStoreSuite{})
    50  }
    51  
    52  type BlockStoreSuite struct {
    53  	suite.Suite
    54  	dir        string
    55  	store      *NomsBlockStore
    56  	putCountFn func() int
    57  }
    58  
    59  func (suite *BlockStoreSuite) SetupTest() {
    60  	var err error
    61  	suite.dir, err = ioutil.TempDir("", "")
    62  	suite.NoError(err)
    63  	suite.store, err = NewLocalStore(context.Background(), constants.FormatDefaultString, suite.dir, testMemTableSize)
    64  	suite.NoError(err)
    65  	suite.putCountFn = func() int {
    66  		return int(suite.store.putCount)
    67  	}
    68  }
    69  
    70  func (suite *BlockStoreSuite) TearDownTest() {
    71  	err := suite.store.Close()
    72  	suite.NoError(err)
    73  	err = os.RemoveAll(suite.dir)
    74  	if !osutil.IsWindowsSharingViolation(err) {
    75  		suite.NoError(err)
    76  	}
    77  }
    78  
    79  func (suite *BlockStoreSuite) TestChunkStoreMissingDir() {
    80  	newDir := filepath.Join(suite.dir, "does-not-exist")
    81  	_, err := NewLocalStore(context.Background(), constants.FormatDefaultString, newDir, testMemTableSize)
    82  	suite.Error(err)
    83  }
    84  
    85  func (suite *BlockStoreSuite) TestChunkStoreNotDir() {
    86  	existingFile := filepath.Join(suite.dir, "path-exists-but-is-a-file")
    87  	_, err := os.Create(existingFile)
    88  	suite.NoError(err)
    89  
    90  	_, err = NewLocalStore(context.Background(), constants.FormatDefaultString, existingFile, testMemTableSize)
    91  	suite.Error(err)
    92  }
    93  
    94  func (suite *BlockStoreSuite) TestChunkStorePut() {
    95  	input := []byte("abc")
    96  	c := chunks.NewChunk(input)
    97  	err := suite.store.Put(context.Background(), c)
    98  	suite.NoError(err)
    99  	h := c.Hash()
   100  
   101  	// See http://www.di-mgt.com.au/sha_testvectors.html
   102  	suite.Equal("rmnjb8cjc5tblj21ed4qs821649eduie", h.String())
   103  
   104  	rt, err := suite.store.Root(context.Background())
   105  	suite.NoError(err)
   106  	success, err := suite.store.Commit(context.Background(), h, rt) // Commit writes
   107  	suite.NoError(err)
   108  	suite.True(success)
   109  
   110  	// And reading it via the API should work...
   111  	assertInputInStore(input, h, suite.store, suite.Assert())
   112  	if suite.putCountFn != nil {
   113  		suite.Equal(1, suite.putCountFn())
   114  	}
   115  
   116  	// Re-writing the same data should cause a second put
   117  	c = chunks.NewChunk(input)
   118  	err = suite.store.Put(context.Background(), c)
   119  	suite.NoError(err)
   120  	suite.Equal(h, c.Hash())
   121  	assertInputInStore(input, h, suite.store, suite.Assert())
   122  	rt, err = suite.store.Root(context.Background())
   123  	suite.NoError(err)
   124  	_, err = suite.store.Commit(context.Background(), h, rt) // Commit writes
   125  	suite.NoError(err)
   126  
   127  	if suite.putCountFn != nil {
   128  		suite.Equal(2, suite.putCountFn())
   129  	}
   130  }
   131  
   132  func (suite *BlockStoreSuite) TestChunkStorePutMany() {
   133  	input1, input2 := []byte("abc"), []byte("def")
   134  	c1, c2 := chunks.NewChunk(input1), chunks.NewChunk(input2)
   135  	err := suite.store.Put(context.Background(), c1)
   136  	suite.NoError(err)
   137  	err = suite.store.Put(context.Background(), c2)
   138  	suite.NoError(err)
   139  
   140  	rt, err := suite.store.Root(context.Background())
   141  	suite.NoError(err)
   142  	success, err := suite.store.Commit(context.Background(), c1.Hash(), rt) // Commit writes
   143  	suite.NoError(err)
   144  	suite.True(success)
   145  
   146  	// And reading it via the API should work...
   147  	assertInputInStore(input1, c1.Hash(), suite.store, suite.Assert())
   148  	assertInputInStore(input2, c2.Hash(), suite.store, suite.Assert())
   149  	if suite.putCountFn != nil {
   150  		suite.Equal(2, suite.putCountFn())
   151  	}
   152  }
   153  
   154  func (suite *BlockStoreSuite) TestChunkStoreStatsSummary() {
   155  	input1, input2 := []byte("abc"), []byte("def")
   156  	c1, c2 := chunks.NewChunk(input1), chunks.NewChunk(input2)
   157  	err := suite.store.Put(context.Background(), c1)
   158  	suite.NoError(err)
   159  	err = suite.store.Put(context.Background(), c2)
   160  	suite.NoError(err)
   161  
   162  	rt, err := suite.store.Root(context.Background())
   163  	suite.NoError(err)
   164  	success, err := suite.store.Commit(context.Background(), c1.Hash(), rt) // Commit writes
   165  	suite.True(success)
   166  	suite.NoError(err)
   167  
   168  	summary := suite.store.StatsSummary()
   169  	suite.Contains(summary, c1.Hash().String())
   170  	suite.NotEqual("Unsupported", summary)
   171  }
   172  
   173  func (suite *BlockStoreSuite) TestChunkStorePutMoreThanMemTable() {
   174  	input1, input2 := make([]byte, testMemTableSize/2+1), make([]byte, testMemTableSize/2+1)
   175  	_, err := rand.Read(input1)
   176  	suite.NoError(err)
   177  	_, err = rand.Read(input2)
   178  	suite.NoError(err)
   179  	c1, c2 := chunks.NewChunk(input1), chunks.NewChunk(input2)
   180  	err = suite.store.Put(context.Background(), c1)
   181  	suite.NoError(err)
   182  	err = suite.store.Put(context.Background(), c2)
   183  	suite.NoError(err)
   184  
   185  	rt, err := suite.store.Root(context.Background())
   186  	suite.NoError(err)
   187  	success, err := suite.store.Commit(context.Background(), c1.Hash(), rt) // Commit writes
   188  	suite.NoError(err)
   189  	suite.True(success)
   190  
   191  	// And reading it via the API should work...
   192  	assertInputInStore(input1, c1.Hash(), suite.store, suite.Assert())
   193  	assertInputInStore(input2, c2.Hash(), suite.store, suite.Assert())
   194  	if suite.putCountFn != nil {
   195  		suite.Equal(2, suite.putCountFn())
   196  	}
   197  	specs, err := suite.store.tables.ToSpecs()
   198  	suite.NoError(err)
   199  	suite.Len(specs, 2)
   200  }
   201  
   202  func (suite *BlockStoreSuite) TestChunkStoreGetMany() {
   203  	inputs := [][]byte{make([]byte, testMemTableSize/2+1), make([]byte, testMemTableSize/2+1), []byte("abc")}
   204  	_, err := rand.Read(inputs[0])
   205  	suite.NoError(err)
   206  	_, err = rand.Read(inputs[1])
   207  	suite.NoError(err)
   208  	chnx := make([]chunks.Chunk, len(inputs))
   209  	for i, data := range inputs {
   210  		chnx[i] = chunks.NewChunk(data)
   211  		err = suite.store.Put(context.Background(), chnx[i])
   212  		suite.NoError(err)
   213  	}
   214  
   215  	rt, err := suite.store.Root(context.Background())
   216  	suite.NoError(err)
   217  	_, err = suite.store.Commit(context.Background(), chnx[0].Hash(), rt) // Commit writes
   218  	suite.NoError(err)
   219  
   220  	hashes := make(hash.HashSlice, len(chnx))
   221  	for i, c := range chnx {
   222  		hashes[i] = c.Hash()
   223  	}
   224  
   225  	chunkChan := make(chan *chunks.Chunk, len(hashes))
   226  	err = suite.store.GetMany(context.Background(), hashes.HashSet(), func(c *chunks.Chunk) { chunkChan <- c })
   227  	suite.NoError(err)
   228  	close(chunkChan)
   229  
   230  	found := make(hash.HashSlice, 0)
   231  	for c := range chunkChan {
   232  		found = append(found, c.Hash())
   233  	}
   234  
   235  	sort.Sort(found)
   236  	sort.Sort(hashes)
   237  	suite.True(found.Equals(hashes))
   238  }
   239  
   240  func (suite *BlockStoreSuite) TestChunkStoreHasMany() {
   241  	chnx := []chunks.Chunk{
   242  		chunks.NewChunk([]byte("abc")),
   243  		chunks.NewChunk([]byte("def")),
   244  	}
   245  	for _, c := range chnx {
   246  		err := suite.store.Put(context.Background(), c)
   247  		suite.NoError(err)
   248  	}
   249  
   250  	rt, err := suite.store.Root(context.Background())
   251  	suite.NoError(err)
   252  	success, err := suite.store.Commit(context.Background(), chnx[0].Hash(), rt) // Commit writes
   253  	suite.NoError(err)
   254  	suite.True(success)
   255  	notPresent := chunks.NewChunk([]byte("ghi")).Hash()
   256  
   257  	hashes := hash.NewHashSet(chnx[0].Hash(), chnx[1].Hash(), notPresent)
   258  	absent, err := suite.store.HasMany(context.Background(), hashes)
   259  	suite.NoError(err)
   260  
   261  	suite.Len(absent, 1)
   262  	for _, c := range chnx {
   263  		suite.False(absent.Has(c.Hash()), "%s present in %v", c.Hash(), absent)
   264  	}
   265  	suite.True(absent.Has(notPresent))
   266  }
   267  
   268  func (suite *BlockStoreSuite) TestChunkStoreFlushOptimisticLockFail() {
   269  	input1, input2 := []byte("abc"), []byte("def")
   270  	c1, c2 := chunks.NewChunk(input1), chunks.NewChunk(input2)
   271  	root, err := suite.store.Root(context.Background())
   272  	suite.NoError(err)
   273  
   274  	interloper, err := NewLocalStore(context.Background(), constants.FormatDefaultString, suite.dir, testMemTableSize)
   275  	suite.NoError(err)
   276  	err = interloper.Put(context.Background(), c1)
   277  	suite.NoError(err)
   278  	h, err := interloper.Root(context.Background())
   279  	suite.NoError(err)
   280  	success, err := interloper.Commit(context.Background(), h, h)
   281  	suite.NoError(err)
   282  	suite.True(success)
   283  
   284  	err = suite.store.Put(context.Background(), c2)
   285  	suite.NoError(err)
   286  	h, err = suite.store.Root(context.Background())
   287  	suite.NoError(err)
   288  	success, err = suite.store.Commit(context.Background(), h, h)
   289  	suite.NoError(err)
   290  	suite.True(success)
   291  
   292  	// Reading c2 via the API should work...
   293  	assertInputInStore(input2, c2.Hash(), suite.store, suite.Assert())
   294  	// And so should reading c1 via the API
   295  	assertInputInStore(input1, c1.Hash(), suite.store, suite.Assert())
   296  
   297  	h, err = interloper.Root(context.Background())
   298  	suite.NoError(err)
   299  	success, err = interloper.Commit(context.Background(), c1.Hash(), h) // Commit root
   300  	suite.NoError(err)
   301  	suite.True(success)
   302  
   303  	// Updating from stale root should fail...
   304  	success, err = suite.store.Commit(context.Background(), c2.Hash(), root)
   305  	suite.NoError(err)
   306  	suite.False(success)
   307  
   308  	// ...but new root should succeed
   309  	h, err = suite.store.Root(context.Background())
   310  	suite.NoError(err)
   311  	success, err = suite.store.Commit(context.Background(), c2.Hash(), h)
   312  	suite.NoError(err)
   313  	suite.True(success)
   314  }
   315  
   316  func (suite *BlockStoreSuite) TestChunkStoreRebaseOnNoOpFlush() {
   317  	input1 := []byte("abc")
   318  	c1 := chunks.NewChunk(input1)
   319  
   320  	interloper, err := NewLocalStore(context.Background(), constants.FormatDefaultString, suite.dir, testMemTableSize)
   321  	suite.NoError(err)
   322  	err = interloper.Put(context.Background(), c1)
   323  	suite.NoError(err)
   324  	root, err := interloper.Root(context.Background())
   325  	suite.NoError(err)
   326  	success, err := interloper.Commit(context.Background(), c1.Hash(), root)
   327  	suite.NoError(err)
   328  	suite.True(success)
   329  
   330  	has, err := suite.store.Has(context.Background(), c1.Hash())
   331  	suite.NoError(err)
   332  	suite.False(has)
   333  
   334  	root, err = suite.store.Root(context.Background())
   335  	suite.NoError(err)
   336  	suite.Equal(hash.Hash{}, root)
   337  
   338  	// Should Rebase, even though there's no work to do.
   339  	root, err = suite.store.Root(context.Background())
   340  	suite.NoError(err)
   341  	success, err = suite.store.Commit(context.Background(), root, root)
   342  	suite.NoError(err)
   343  	suite.True(success)
   344  
   345  	// Reading c1 via the API should work
   346  	assertInputInStore(input1, c1.Hash(), suite.store, suite.Assert())
   347  	suite.True(suite.store.Has(context.Background(), c1.Hash()))
   348  }
   349  
   350  func (suite *BlockStoreSuite) TestChunkStorePutWithRebase() {
   351  	input1, input2 := []byte("abc"), []byte("def")
   352  	c1, c2 := chunks.NewChunk(input1), chunks.NewChunk(input2)
   353  	root, err := suite.store.Root(context.Background())
   354  	suite.NoError(err)
   355  
   356  	interloper, err := NewLocalStore(context.Background(), constants.FormatDefaultString, suite.dir, testMemTableSize)
   357  	suite.NoError(err)
   358  	err = interloper.Put(context.Background(), c1)
   359  	suite.NoError(err)
   360  	h, err := interloper.Root(context.Background())
   361  	suite.NoError(err)
   362  	success, err := interloper.Commit(context.Background(), h, h)
   363  	suite.NoError(err)
   364  	suite.True(success)
   365  
   366  	err = suite.store.Put(context.Background(), c2)
   367  	suite.NoError(err)
   368  
   369  	// Reading c2 via the API should work pre-rebase
   370  	assertInputInStore(input2, c2.Hash(), suite.store, suite.Assert())
   371  	// Shouldn't have c1 yet.
   372  	suite.False(suite.store.Has(context.Background(), c1.Hash()))
   373  
   374  	err = suite.store.Rebase(context.Background())
   375  	suite.NoError(err)
   376  
   377  	// Reading c2 via the API should work post-rebase
   378  	assertInputInStore(input2, c2.Hash(), suite.store, suite.Assert())
   379  	// And so should reading c1 via the API
   380  	assertInputInStore(input1, c1.Hash(), suite.store, suite.Assert())
   381  
   382  	// Commit interloper root
   383  	h, err = interloper.Root(context.Background())
   384  	suite.NoError(err)
   385  	success, err = interloper.Commit(context.Background(), c1.Hash(), h)
   386  	suite.NoError(err)
   387  	suite.True(success)
   388  
   389  	// suite.store should still have its initial root
   390  	h, err = suite.store.Root(context.Background())
   391  	suite.NoError(err)
   392  	suite.EqualValues(root, h)
   393  	err = suite.store.Rebase(context.Background())
   394  	suite.NoError(err)
   395  
   396  	// Rebase grabbed the new root, so updating should now succeed!
   397  	h, err = suite.store.Root(context.Background())
   398  	suite.NoError(err)
   399  	success, err = suite.store.Commit(context.Background(), c2.Hash(), h)
   400  	suite.NoError(err)
   401  	suite.True(success)
   402  
   403  	// Interloper shouldn't see c2 yet....
   404  	suite.False(interloper.Has(context.Background(), c2.Hash()))
   405  	err = interloper.Rebase(context.Background())
   406  	suite.NoError(err)
   407  	// ...but post-rebase it must
   408  	assertInputInStore(input2, c2.Hash(), interloper, suite.Assert())
   409  }
   410  
   411  func TestBlockStoreConjoinOnCommit(t *testing.T) {
   412  	stats := &Stats{}
   413  	assertContainAll := func(t *testing.T, store chunks.ChunkStore, srcs ...chunkSource) {
   414  		rdrs := make(chunkReaderGroup, len(srcs))
   415  		for i, src := range srcs {
   416  			rdrs[i] = src.Clone()
   417  		}
   418  		chunkChan := make(chan extractRecord, mustUint32(rdrs.count()))
   419  		err := rdrs.extract(context.Background(), chunkChan)
   420  		require.NoError(t, err)
   421  		close(chunkChan)
   422  
   423  		for rec := range chunkChan {
   424  			ok, err := store.Has(context.Background(), hash.Hash(rec.a))
   425  			require.NoError(t, err)
   426  			assert.True(t, ok)
   427  		}
   428  	}
   429  
   430  	makeManifestManager := func(m manifest) manifestManager {
   431  		return manifestManager{m, newManifestCache(0), newManifestLocks()}
   432  	}
   433  
   434  	newChunk := chunks.NewChunk([]byte("gnu"))
   435  
   436  	t.Run("NoConjoin", func(t *testing.T) {
   437  		mm := makeManifestManager(&fakeManifest{})
   438  		p := newFakeTablePersister()
   439  		c := &fakeConjoiner{}
   440  
   441  		smallTableStore, err := newNomsBlockStore(context.Background(), constants.FormatDefaultString, mm, p, c, testMemTableSize)
   442  		require.NoError(t, err)
   443  
   444  		root, err := smallTableStore.Root(context.Background())
   445  		require.NoError(t, err)
   446  		err = smallTableStore.Put(context.Background(), newChunk)
   447  		require.NoError(t, err)
   448  		success, err := smallTableStore.Commit(context.Background(), newChunk.Hash(), root)
   449  		require.NoError(t, err)
   450  		assert.True(t, success)
   451  
   452  		ok, err := smallTableStore.Has(context.Background(), newChunk.Hash())
   453  		require.NoError(t, err)
   454  		assert.True(t, ok)
   455  	})
   456  
   457  	makeCanned := func(conjoinees, keepers []tableSpec, p tablePersister) cannedConjoin {
   458  		srcs := chunkSources{}
   459  		for _, sp := range conjoinees {
   460  			cs, err := p.Open(context.Background(), sp.name, sp.chunkCount, nil)
   461  			require.NoError(t, err)
   462  			srcs = append(srcs, cs)
   463  		}
   464  		conjoined, err := p.ConjoinAll(context.Background(), srcs, stats)
   465  		require.NoError(t, err)
   466  		cannedSpecs := []tableSpec{{mustAddr(conjoined.hash()), mustUint32(conjoined.count())}}
   467  		return cannedConjoin{true, append(cannedSpecs, keepers...)}
   468  	}
   469  
   470  	t.Run("ConjoinSuccess", func(t *testing.T) {
   471  		fm := &fakeManifest{}
   472  		p := newFakeTablePersister()
   473  
   474  		srcs := makeTestSrcs(t, []uint32{1, 1, 3, 7}, p)
   475  		upstream, err := toSpecs(srcs)
   476  		require.NoError(t, err)
   477  		fm.set(constants.NomsVersion, computeAddr([]byte{0xbe}), hash.Of([]byte{0xef}), upstream, nil)
   478  		c := &fakeConjoiner{
   479  			[]cannedConjoin{makeCanned(upstream[:2], upstream[2:], p)},
   480  		}
   481  
   482  		smallTableStore, err := newNomsBlockStore(context.Background(), constants.FormatDefaultString, makeManifestManager(fm), p, c, testMemTableSize)
   483  		require.NoError(t, err)
   484  
   485  		root, err := smallTableStore.Root(context.Background())
   486  		require.NoError(t, err)
   487  		err = smallTableStore.Put(context.Background(), newChunk)
   488  		require.NoError(t, err)
   489  		success, err := smallTableStore.Commit(context.Background(), newChunk.Hash(), root)
   490  		require.NoError(t, err)
   491  		assert.True(t, success)
   492  		ok, err := smallTableStore.Has(context.Background(), newChunk.Hash())
   493  		require.NoError(t, err)
   494  		assert.True(t, ok)
   495  		assertContainAll(t, smallTableStore, srcs...)
   496  		for _, src := range srcs {
   497  			err := src.Close()
   498  			require.NoError(t, err)
   499  		}
   500  	})
   501  
   502  	t.Run("ConjoinRetry", func(t *testing.T) {
   503  		fm := &fakeManifest{}
   504  		p := newFakeTablePersister()
   505  
   506  		srcs := makeTestSrcs(t, []uint32{1, 1, 3, 7, 13}, p)
   507  		upstream, err := toSpecs(srcs)
   508  		require.NoError(t, err)
   509  		fm.set(constants.NomsVersion, computeAddr([]byte{0xbe}), hash.Of([]byte{0xef}), upstream, nil)
   510  		c := &fakeConjoiner{
   511  			[]cannedConjoin{
   512  				makeCanned(upstream[:2], upstream[2:], p),
   513  				makeCanned(upstream[:4], upstream[4:], p),
   514  			},
   515  		}
   516  
   517  		smallTableStore, err := newNomsBlockStore(context.Background(), constants.FormatDefaultString, makeManifestManager(fm), p, c, testMemTableSize)
   518  		require.NoError(t, err)
   519  
   520  		root, err := smallTableStore.Root(context.Background())
   521  		require.NoError(t, err)
   522  		err = smallTableStore.Put(context.Background(), newChunk)
   523  		require.NoError(t, err)
   524  		success, err := smallTableStore.Commit(context.Background(), newChunk.Hash(), root)
   525  		require.NoError(t, err)
   526  		assert.True(t, success)
   527  		ok, err := smallTableStore.Has(context.Background(), newChunk.Hash())
   528  		require.NoError(t, err)
   529  		assert.True(t, ok)
   530  		assertContainAll(t, smallTableStore, srcs...)
   531  		for _, src := range srcs {
   532  			err := src.Close()
   533  			require.NoError(t, err)
   534  		}
   535  	})
   536  }
   537  
   538  type cannedConjoin struct {
   539  	should bool
   540  	specs  []tableSpec // Must name tables that are already persisted
   541  }
   542  
   543  type fakeConjoiner struct {
   544  	canned []cannedConjoin
   545  }
   546  
   547  func (fc *fakeConjoiner) ConjoinRequired(ts tableSet) bool {
   548  	if len(fc.canned) == 0 {
   549  		return false
   550  	}
   551  	return fc.canned[0].should
   552  }
   553  
   554  func (fc *fakeConjoiner) Conjoin(ctx context.Context, upstream manifestContents, mm manifestUpdater, p tablePersister, stats *Stats) (manifestContents, error) {
   555  	d.PanicIfTrue(len(fc.canned) == 0)
   556  	canned := fc.canned[0]
   557  	fc.canned = fc.canned[1:]
   558  
   559  	newContents := manifestContents{
   560  		vers:  constants.NomsVersion,
   561  		root:  upstream.root,
   562  		specs: canned.specs,
   563  		lock:  generateLockHash(upstream.root, canned.specs),
   564  	}
   565  
   566  	var err error
   567  	upstream, err = mm.Update(context.Background(), upstream.lock, newContents, stats, nil)
   568  
   569  	if err != nil {
   570  		return manifestContents{}, err
   571  	}
   572  
   573  	if upstream.lock != newContents.lock {
   574  		return manifestContents{}, errors.New("lock failed")
   575  	}
   576  
   577  	return upstream, err
   578  }
   579  
   580  func assertInputInStore(input []byte, h hash.Hash, s chunks.ChunkStore, assert *assert.Assertions) {
   581  	c, err := s.Get(context.Background(), h)
   582  	assert.NoError(err)
   583  	assert.False(c.IsEmpty(), "Shouldn't get empty chunk for %s", h.String())
   584  	assert.Zero(bytes.Compare(input, c.Data()), "%s != %s", string(input), string(c.Data()))
   585  }
   586  
   587  func (suite *BlockStoreSuite) TestChunkStoreGetNonExisting() {
   588  	h := hash.Parse("11111111111111111111111111111111")
   589  	c, err := suite.store.Get(context.Background(), h)
   590  	suite.NoError(err)
   591  	suite.True(c.IsEmpty())
   592  }