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 }