github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/storagetest/chunkstore.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package storagetest 6 7 import ( 8 "context" 9 "errors" 10 "testing" 11 12 postagetesting "github.com/ethersphere/bee/v2/pkg/postage/testing" 13 storage "github.com/ethersphere/bee/v2/pkg/storage" 14 chunktest "github.com/ethersphere/bee/v2/pkg/storage/testing" 15 "github.com/ethersphere/bee/v2/pkg/swarm" 16 ) 17 18 // TestChunkStore runs a correctness test suite on a given ChunkStore. 19 func TestChunkStore(t *testing.T, st storage.ChunkStore) { 20 t.Helper() 21 22 testChunks := chunktest.GenerateTestRandomChunks(50) 23 24 t.Run("put chunks", func(t *testing.T) { 25 for _, ch := range testChunks { 26 err := st.Put(context.TODO(), ch) 27 if err != nil { 28 t.Fatalf("failed putting new chunk: %v", err) 29 } 30 } 31 }) 32 33 t.Run("put existing chunks", func(t *testing.T) { 34 for _, ch := range testChunks { 35 err := st.Put(context.TODO(), ch) 36 if err != nil { 37 t.Fatalf("failed putting new chunk: %v", err) 38 } 39 } 40 }) 41 42 t.Run("get chunks", func(t *testing.T) { 43 for _, ch := range testChunks { 44 readCh, err := st.Get(context.TODO(), ch.Address()) 45 if err != nil { 46 t.Fatalf("failed getting chunk: %v", err) 47 } 48 if !readCh.Equal(ch) { 49 t.Fatal("read chunk doesn't match") 50 } 51 } 52 }) 53 54 t.Run("get non-existing chunk", func(t *testing.T) { 55 stamp := postagetesting.MustNewStamp() 56 ch := chunktest.GenerateTestRandomChunk().WithStamp(stamp) 57 58 _, err := st.Get(context.TODO(), ch.Address()) 59 if !errors.Is(err, storage.ErrNotFound) { 60 t.Fatalf("expected error %v", storage.ErrNotFound) 61 } 62 }) 63 64 t.Run("has chunks", func(t *testing.T) { 65 for _, ch := range testChunks { 66 exists, err := st.Has(context.TODO(), ch.Address()) 67 if err != nil { 68 t.Fatalf("failed getting chunk: %v", err) 69 } 70 if !exists { 71 t.Fatalf("chunk not found: %s", ch.Address()) 72 } 73 } 74 }) 75 76 t.Run("iterate chunks", func(t *testing.T) { 77 count := 0 78 err := st.Iterate(context.TODO(), func(_ swarm.Chunk) (bool, error) { 79 count++ 80 return false, nil 81 }) 82 if err != nil { 83 t.Fatalf("unexpected error while iteration: %v", err) 84 } 85 if count != 50 { 86 t.Fatalf("unexpected no of chunks, exp: %d, found: %d", 50, count) 87 } 88 }) 89 90 t.Run("delete chunks", func(t *testing.T) { 91 for idx, ch := range testChunks { 92 // Delete all even numbered indexes along with 0 93 if idx%2 == 0 { 94 err := st.Delete(context.TODO(), ch.Address()) 95 if err != nil { 96 t.Fatalf("failed deleting chunk: %v", err) 97 } 98 _, err = st.Get(context.TODO(), ch.Address()) 99 if err != nil { 100 t.Fatalf("expected no error, found: %v", err) 101 } 102 // delete twice as it was put twice 103 err = st.Delete(context.TODO(), ch.Address()) 104 if err != nil { 105 t.Fatalf("failed deleting chunk: %v", err) 106 } 107 } 108 } 109 }) 110 111 t.Run("check deleted chunks", func(t *testing.T) { 112 for idx, ch := range testChunks { 113 if idx%2 == 0 { 114 // Check even numbered indexes are deleted 115 _, err := st.Get(context.TODO(), ch.Address()) 116 if !errors.Is(err, storage.ErrNotFound) { 117 t.Fatalf("expected storage not found error found: %v", err) 118 } 119 found, err := st.Has(context.TODO(), ch.Address()) 120 if err != nil { 121 t.Fatalf("unexpected error in Has: %v", err) 122 } 123 if found { 124 t.Fatal("expected chunk to not be found") 125 } 126 } else { 127 // Check rest of the entries are intact 128 readCh, err := st.Get(context.TODO(), ch.Address()) 129 if err != nil { 130 t.Fatalf("failed getting chunk: %v", err) 131 } 132 if !readCh.Equal(ch) { 133 t.Fatal("read chunk doesn't match") 134 } 135 exists, err := st.Has(context.TODO(), ch.Address()) 136 if err != nil { 137 t.Fatalf("failed getting chunk: %v", err) 138 } 139 if !exists { 140 t.Fatalf("chunk not found: %s", ch.Address()) 141 } 142 } 143 } 144 }) 145 146 t.Run("iterate chunks after delete", func(t *testing.T) { 147 count := 0 148 err := st.Iterate(context.TODO(), func(_ swarm.Chunk) (bool, error) { 149 count++ 150 return false, nil 151 }) 152 if err != nil { 153 t.Fatalf("unexpected error while iteration: %v", err) 154 } 155 if count != 25 { 156 t.Fatalf("unexpected no of chunks, exp: %d, found: %d", 25, count) 157 } 158 }) 159 } 160 161 func RunChunkStoreBenchmarkTests(b *testing.B, s storage.ChunkStore) { 162 b.Helper() 163 164 b.Run("WriteSequential", func(b *testing.B) { 165 BenchmarkChunkStoreWriteSequential(b, s) 166 }) 167 b.Run("WriteRandom", func(b *testing.B) { 168 BenchmarkChunkStoreWriteRandom(b, s) 169 }) 170 b.Run("ReadSequential", func(b *testing.B) { 171 BenchmarkChunkStoreReadSequential(b, s) 172 }) 173 b.Run("ReadRandom", func(b *testing.B) { 174 BenchmarkChunkStoreReadRandom(b, s) 175 }) 176 b.Run("ReadRandomMissing", func(b *testing.B) { 177 BenchmarkChunkStoreReadRandomMissing(b, s) 178 }) 179 b.Run("ReadReverse", func(b *testing.B) { 180 BenchmarkChunkStoreReadReverse(b, s) 181 }) 182 b.Run("ReadRedHot", func(b *testing.B) { 183 BenchmarkChunkStoreReadHot(b, s) 184 }) 185 b.Run("IterateSequential", func(b *testing.B) { 186 BenchmarkChunkStoreIterateSequential(b, s) 187 }) 188 b.Run("IterateReverse", func(b *testing.B) { 189 BenchmarkChunkStoreIterateReverse(b, s) 190 }) 191 b.Run("DeleteRandom", func(b *testing.B) { 192 BenchmarkChunkStoreDeleteRandom(b, s) 193 }) 194 b.Run("DeleteSequential", func(b *testing.B) { 195 BenchmarkChunkStoreDeleteSequential(b, s) 196 }) 197 } 198 199 func BenchmarkChunkStoreWriteSequential(b *testing.B, s storage.Putter) { 200 b.Helper() 201 202 doWriteChunk(b, s, newSequentialEntryGenerator(b.N)) 203 } 204 205 func BenchmarkChunkStoreWriteRandom(b *testing.B, s storage.Putter) { 206 b.Helper() 207 208 doWriteChunk(b, s, newFullRandomEntryGenerator(0, b.N)) 209 } 210 211 func BenchmarkChunkStoreReadSequential(b *testing.B, s storage.ChunkStore) { 212 b.Helper() 213 214 g := newSequentialKeyGenerator(b.N) 215 doWriteChunk(b, s, newFullRandomEntryGenerator(0, b.N)) 216 resetBenchmark(b) 217 doReadChunk(b, s, g, false) 218 } 219 220 func BenchmarkChunkStoreReadRandom(b *testing.B, s storage.ChunkStore) { 221 b.Helper() 222 223 g := newRandomKeyGenerator(b.N) 224 doWriteChunk(b, s, newFullRandomEntryGenerator(0, b.N)) 225 resetBenchmark(b) 226 doReadChunk(b, s, g, false) 227 } 228 229 func BenchmarkChunkStoreReadRandomMissing(b *testing.B, s storage.ChunkStore) { 230 b.Helper() 231 232 g := newRandomMissingKeyGenerator(b.N) 233 resetBenchmark(b) 234 doReadChunk(b, s, g, true) 235 } 236 237 func BenchmarkChunkStoreReadReverse(b *testing.B, db storage.ChunkStore) { 238 b.Helper() 239 240 g := newReversedKeyGenerator(newSequentialKeyGenerator(b.N)) 241 doWriteChunk(b, db, newFullRandomEntryGenerator(0, b.N)) 242 resetBenchmark(b) 243 doReadChunk(b, db, g, false) 244 } 245 246 func BenchmarkChunkStoreReadHot(b *testing.B, s storage.ChunkStore) { 247 b.Helper() 248 249 k := maxInt((b.N+99)/100, 1) 250 g := newRoundKeyGenerator(newRandomKeyGenerator(k)) 251 doWriteChunk(b, s, newFullRandomEntryGenerator(0, b.N)) 252 resetBenchmark(b) 253 doReadChunk(b, s, g, false) 254 } 255 256 func BenchmarkChunkStoreIterateSequential(b *testing.B, s storage.ChunkStore) { 257 b.Helper() 258 259 var counter int 260 _ = s.Iterate(context.Background(), func(c swarm.Chunk) (stop bool, err error) { 261 counter++ 262 if counter > b.N { 263 return true, nil 264 } 265 return false, nil 266 }) 267 } 268 269 func BenchmarkChunkStoreIterateReverse(b *testing.B, s storage.ChunkStore) { 270 b.Helper() 271 272 b.Skip("not implemented") 273 } 274 275 func BenchmarkChunkStoreDeleteRandom(b *testing.B, s storage.ChunkStore) { 276 b.Helper() 277 278 doDeleteChunk(b, s, newFullRandomEntryGenerator(0, b.N)) 279 } 280 281 func BenchmarkChunkStoreDeleteSequential(b *testing.B, s storage.ChunkStore) { 282 b.Helper() 283 284 doDeleteChunk(b, s, newSequentialEntryGenerator(b.N)) 285 }