github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/go/nbs/dynamo_manifest_test.go (about)

     1  // Copyright 2016 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package nbs
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/attic-labs/noms/go/constants"
    11  	"github.com/attic-labs/noms/go/hash"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  const (
    16  	table = "testTable"
    17  	db    = "testDB"
    18  )
    19  
    20  func makeDynamoManifestFake(t *testing.T) (mm manifest, ddb *fakeDDB) {
    21  	ddb = makeFakeDDB(t)
    22  	mm = newDynamoManifest(table, db, ddb)
    23  	return
    24  }
    25  
    26  func TestDynamoManifestParseIfExists(t *testing.T) {
    27  	assert := assert.New(t)
    28  	mm, ddb := makeDynamoManifestFake(t)
    29  	stats := &Stats{}
    30  
    31  	exists, _ := mm.ParseIfExists(stats, nil)
    32  	assert.False(exists)
    33  
    34  	// Simulate another process writing a manifest (with an old Noms version).
    35  	newLock := computeAddr([]byte("locker"))
    36  	newRoot := hash.Of([]byte("new root"))
    37  	tableName := hash.Of([]byte("table1"))
    38  	ddb.putRecord(db, newLock[:], newRoot[:], "0", tableName.String()+":"+"0")
    39  
    40  	// ParseIfExists should now reflect the manifest written above.
    41  	exists, contents := mm.ParseIfExists(stats, nil)
    42  	assert.True(exists)
    43  	assert.Equal("0", contents.vers)
    44  	assert.Equal(newLock, contents.lock)
    45  	assert.Equal(newRoot, contents.root)
    46  	if assert.Len(contents.specs, 1) {
    47  		assert.Equal(tableName.String(), contents.specs[0].name.String())
    48  		assert.Equal(uint32(0), contents.specs[0].chunkCount)
    49  	}
    50  }
    51  
    52  func makeContents(lock, root string, specs []tableSpec) manifestContents {
    53  	return manifestContents{constants.NomsVersion, computeAddr([]byte(lock)), hash.Of([]byte(root)), specs}
    54  }
    55  
    56  func TestDynamoManifestUpdateWontClobberOldVersion(t *testing.T) {
    57  	assert := assert.New(t)
    58  	mm, ddb := makeDynamoManifestFake(t)
    59  	stats := &Stats{}
    60  
    61  	// Simulate another process having already put old Noms data in dir/.
    62  	lock := computeAddr([]byte("locker"))
    63  	badRoot := hash.Of([]byte("bad root"))
    64  	ddb.putRecord(db, lock[:], badRoot[:], "0", "")
    65  
    66  	assert.Panics(func() { mm.Update(lock, manifestContents{vers: constants.NomsVersion}, stats, nil) })
    67  }
    68  
    69  func TestDynamoManifestUpdate(t *testing.T) {
    70  	assert := assert.New(t)
    71  	mm, ddb := makeDynamoManifestFake(t)
    72  	stats := &Stats{}
    73  
    74  	// First, test winning the race against another process.
    75  	contents := makeContents("locker", "nuroot", []tableSpec{{computeAddr([]byte("a")), 3}})
    76  	upstream := mm.Update(addr{}, contents, stats, func() {
    77  		// This should fail to get the lock, and therefore _not_ clobber the manifest. So the Update should succeed.
    78  		lock := computeAddr([]byte("nolock"))
    79  		newRoot2 := hash.Of([]byte("noroot"))
    80  		ddb.putRecord(db, lock[:], newRoot2[:], constants.NomsVersion, "")
    81  	})
    82  	assert.Equal(contents.lock, upstream.lock)
    83  	assert.Equal(contents.root, upstream.root)
    84  	assert.Equal(contents.specs, upstream.specs)
    85  
    86  	// Now, test the case where the optimistic lock fails, and someone else updated the root since last we checked.
    87  	rejected := makeContents("locker 2", "new root 2", nil)
    88  	upstream = mm.Update(addr{}, rejected, stats, nil)
    89  	assert.Equal(contents.lock, upstream.lock)
    90  	assert.Equal(contents.root, upstream.root)
    91  	assert.Equal(contents.specs, upstream.specs)
    92  	upstream = mm.Update(upstream.lock, rejected, stats, nil)
    93  	assert.Equal(rejected.lock, upstream.lock)
    94  	assert.Equal(rejected.root, upstream.root)
    95  	assert.Empty(upstream.specs)
    96  
    97  	// Now, test the case where the optimistic lock fails because someone else updated only the tables since last we checked
    98  	jerkLock := computeAddr([]byte("jerk"))
    99  	tableName := computeAddr([]byte("table1"))
   100  	ddb.putRecord(db, jerkLock[:], upstream.root[:], constants.NomsVersion, tableName.String()+":1")
   101  
   102  	newContents3 := makeContents("locker 3", "new root 3", nil)
   103  	upstream = mm.Update(upstream.lock, newContents3, stats, nil)
   104  	assert.Equal(jerkLock, upstream.lock)
   105  	assert.Equal(rejected.root, upstream.root)
   106  	assert.Equal([]tableSpec{{tableName, 1}}, upstream.specs)
   107  }
   108  
   109  func TestDynamoManifestCaching(t *testing.T) {
   110  	assert := assert.New(t)
   111  	mm, ddb := makeDynamoManifestFake(t)
   112  	stats := &Stats{}
   113  
   114  	// ParseIfExists should hit persistent storage no matter what
   115  	reads := ddb.numGets
   116  	exists, _ := mm.ParseIfExists(stats, nil)
   117  	assert.False(exists)
   118  	assert.Equal(reads+1, ddb.numGets)
   119  
   120  	lock, root := computeAddr([]byte("lock")), hash.Of([]byte("root"))
   121  	ddb.putRecord(db, lock[:], root[:], constants.NomsVersion, "")
   122  
   123  	reads = ddb.numGets
   124  	exists, _ = mm.ParseIfExists(stats, nil)
   125  	assert.True(exists)
   126  	assert.Equal(reads+1, ddb.numGets)
   127  
   128  	// When failing the optimistic lock, we should hit persistent storage.
   129  	reads = ddb.numGets
   130  	contents := makeContents("lock2", "nuroot", []tableSpec{{computeAddr([]byte("a")), 3}})
   131  	upstream := mm.Update(addr{}, contents, stats, nil)
   132  	assert.NotEqual(contents.lock, upstream.lock)
   133  	assert.Equal(reads+1, ddb.numGets)
   134  
   135  	// Successful update should NOT hit persistent storage.
   136  	reads = ddb.numGets
   137  	upstream = mm.Update(upstream.lock, contents, stats, nil)
   138  	assert.Equal(contents.lock, upstream.lock)
   139  	assert.Equal(reads, ddb.numGets)
   140  }
   141  
   142  func TestDynamoManifestUpdateEmpty(t *testing.T) {
   143  	assert := assert.New(t)
   144  	mm, _ := makeDynamoManifestFake(t)
   145  	stats := &Stats{}
   146  
   147  	contents := manifestContents{vers: constants.NomsVersion, lock: computeAddr([]byte{0x01})}
   148  	upstream := mm.Update(addr{}, contents, stats, nil)
   149  	assert.Equal(contents.lock, upstream.lock)
   150  	assert.True(upstream.root.IsEmpty())
   151  	assert.Empty(upstream.specs)
   152  }