github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/ldbstore_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "testing" 26 "time" 27 28 "github.com/FusionFoundation/efsn/common" 29 ch "github.com/FusionFoundation/efsn/swarm/chunk" 30 "github.com/FusionFoundation/efsn/swarm/log" 31 "github.com/FusionFoundation/efsn/swarm/storage/mock/mem" 32 33 ldberrors "github.com/syndtr/goleveldb/leveldb/errors" 34 ) 35 36 type testDbStore struct { 37 *LDBStore 38 dir string 39 } 40 41 func newTestDbStore(mock bool, trusted bool) (*testDbStore, func(), error) { 42 dir, err := ioutil.TempDir("", "bzz-storage-test") 43 if err != nil { 44 return nil, func() {}, err 45 } 46 47 var db *LDBStore 48 storeparams := NewDefaultStoreParams() 49 params := NewLDBStoreParams(storeparams, dir) 50 params.Po = testPoFunc 51 52 if mock { 53 globalStore := mem.NewGlobalStore() 54 addr := common.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") 55 mockStore := globalStore.NewNodeStore(addr) 56 57 db, err = NewMockDbStore(params, mockStore) 58 } else { 59 db, err = NewLDBStore(params) 60 } 61 62 cleanup := func() { 63 if db != nil { 64 db.Close() 65 } 66 err = os.RemoveAll(dir) 67 if err != nil { 68 panic(fmt.Sprintf("db cleanup failed: %v", err)) 69 } 70 } 71 72 return &testDbStore{db, dir}, cleanup, err 73 } 74 75 func testPoFunc(k Address) (ret uint8) { 76 basekey := make([]byte, 32) 77 return uint8(Proximity(basekey, k[:])) 78 } 79 80 func (db *testDbStore) close() { 81 db.Close() 82 err := os.RemoveAll(db.dir) 83 if err != nil { 84 panic(err) 85 } 86 } 87 88 func testDbStoreRandom(n int, chunksize int64, mock bool, t *testing.T) { 89 db, cleanup, err := newTestDbStore(mock, true) 90 defer cleanup() 91 if err != nil { 92 t.Fatalf("init dbStore failed: %v", err) 93 } 94 testStoreRandom(db, n, chunksize, t) 95 } 96 97 func testDbStoreCorrect(n int, chunksize int64, mock bool, t *testing.T) { 98 db, cleanup, err := newTestDbStore(mock, false) 99 defer cleanup() 100 if err != nil { 101 t.Fatalf("init dbStore failed: %v", err) 102 } 103 testStoreCorrect(db, n, chunksize, t) 104 } 105 106 func TestDbStoreRandom_1(t *testing.T) { 107 testDbStoreRandom(1, 0, false, t) 108 } 109 110 func TestDbStoreCorrect_1(t *testing.T) { 111 testDbStoreCorrect(1, 4096, false, t) 112 } 113 114 func TestDbStoreRandom_5k(t *testing.T) { 115 testDbStoreRandom(5000, 0, false, t) 116 } 117 118 func TestDbStoreCorrect_5k(t *testing.T) { 119 testDbStoreCorrect(5000, 4096, false, t) 120 } 121 122 func TestMockDbStoreRandom_1(t *testing.T) { 123 testDbStoreRandom(1, 0, true, t) 124 } 125 126 func TestMockDbStoreCorrect_1(t *testing.T) { 127 testDbStoreCorrect(1, 4096, true, t) 128 } 129 130 func TestMockDbStoreRandom_5k(t *testing.T) { 131 testDbStoreRandom(5000, 0, true, t) 132 } 133 134 func TestMockDbStoreCorrect_5k(t *testing.T) { 135 testDbStoreCorrect(5000, 4096, true, t) 136 } 137 138 func testDbStoreNotFound(t *testing.T, mock bool) { 139 db, cleanup, err := newTestDbStore(mock, false) 140 defer cleanup() 141 if err != nil { 142 t.Fatalf("init dbStore failed: %v", err) 143 } 144 145 _, err = db.Get(context.TODO(), ZeroAddr) 146 if err != ErrChunkNotFound { 147 t.Errorf("Expected ErrChunkNotFound, got %v", err) 148 } 149 } 150 151 func TestDbStoreNotFound(t *testing.T) { 152 testDbStoreNotFound(t, false) 153 } 154 func TestMockDbStoreNotFound(t *testing.T) { 155 testDbStoreNotFound(t, true) 156 } 157 158 func testIterator(t *testing.T, mock bool) { 159 var chunkcount int = 32 160 var i int 161 var poc uint 162 chunkkeys := NewAddressCollection(chunkcount) 163 chunkkeys_results := NewAddressCollection(chunkcount) 164 165 db, cleanup, err := newTestDbStore(mock, false) 166 defer cleanup() 167 if err != nil { 168 t.Fatalf("init dbStore failed: %v", err) 169 } 170 171 chunks := GenerateRandomChunks(ch.DefaultSize, chunkcount) 172 173 for i = 0; i < len(chunks); i++ { 174 chunkkeys[i] = chunks[i].Address() 175 err := db.Put(context.TODO(), chunks[i]) 176 if err != nil { 177 t.Fatalf("dbStore.Put failed: %v", err) 178 } 179 } 180 181 for i = 0; i < len(chunkkeys); i++ { 182 log.Trace(fmt.Sprintf("Chunk array pos %d/%d: '%v'", i, chunkcount, chunkkeys[i])) 183 } 184 i = 0 185 for poc = 0; poc <= 255; poc++ { 186 err := db.SyncIterator(0, uint64(chunkkeys.Len()), uint8(poc), func(k Address, n uint64) bool { 187 log.Trace(fmt.Sprintf("Got key %v number %d poc %d", k, n, uint8(poc))) 188 chunkkeys_results[n] = k 189 i++ 190 return true 191 }) 192 if err != nil { 193 t.Fatalf("Iterator call failed: %v", err) 194 } 195 } 196 197 for i = 0; i < chunkcount; i++ { 198 if !bytes.Equal(chunkkeys[i], chunkkeys_results[i]) { 199 t.Fatalf("Chunk put #%d key '%v' does not match iterator's key '%v'", i, chunkkeys[i], chunkkeys_results[i]) 200 } 201 } 202 203 } 204 205 func TestIterator(t *testing.T) { 206 testIterator(t, false) 207 } 208 func TestMockIterator(t *testing.T) { 209 testIterator(t, true) 210 } 211 212 func benchmarkDbStorePut(n int, processors int, chunksize int64, mock bool, b *testing.B) { 213 db, cleanup, err := newTestDbStore(mock, true) 214 defer cleanup() 215 if err != nil { 216 b.Fatalf("init dbStore failed: %v", err) 217 } 218 benchmarkStorePut(db, n, chunksize, b) 219 } 220 221 func benchmarkDbStoreGet(n int, processors int, chunksize int64, mock bool, b *testing.B) { 222 db, cleanup, err := newTestDbStore(mock, true) 223 defer cleanup() 224 if err != nil { 225 b.Fatalf("init dbStore failed: %v", err) 226 } 227 benchmarkStoreGet(db, n, chunksize, b) 228 } 229 230 func BenchmarkDbStorePut_1_500(b *testing.B) { 231 benchmarkDbStorePut(500, 1, 4096, false, b) 232 } 233 234 func BenchmarkDbStorePut_8_500(b *testing.B) { 235 benchmarkDbStorePut(500, 8, 4096, false, b) 236 } 237 238 func BenchmarkDbStoreGet_1_500(b *testing.B) { 239 benchmarkDbStoreGet(500, 1, 4096, false, b) 240 } 241 242 func BenchmarkDbStoreGet_8_500(b *testing.B) { 243 benchmarkDbStoreGet(500, 8, 4096, false, b) 244 } 245 246 func BenchmarkMockDbStorePut_1_500(b *testing.B) { 247 benchmarkDbStorePut(500, 1, 4096, true, b) 248 } 249 250 func BenchmarkMockDbStorePut_8_500(b *testing.B) { 251 benchmarkDbStorePut(500, 8, 4096, true, b) 252 } 253 254 func BenchmarkMockDbStoreGet_1_500(b *testing.B) { 255 benchmarkDbStoreGet(500, 1, 4096, true, b) 256 } 257 258 func BenchmarkMockDbStoreGet_8_500(b *testing.B) { 259 benchmarkDbStoreGet(500, 8, 4096, true, b) 260 } 261 262 // TestLDBStoreWithoutCollectGarbage tests that we can put a number of random chunks in the LevelDB store, and 263 // retrieve them, provided we don't hit the garbage collection 264 func TestLDBStoreWithoutCollectGarbage(t *testing.T) { 265 capacity := 50 266 n := 10 267 268 ldb, cleanup := newLDBStore(t) 269 ldb.setCapacity(uint64(capacity)) 270 defer cleanup() 271 272 chunks, err := mputRandomChunks(ldb, n, int64(ch.DefaultSize)) 273 if err != nil { 274 t.Fatal(err.Error()) 275 } 276 277 log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) 278 279 for _, ch := range chunks { 280 ret, err := ldb.Get(context.TODO(), ch.Address()) 281 if err != nil { 282 t.Fatal(err) 283 } 284 285 if !bytes.Equal(ret.Data(), ch.Data()) { 286 t.Fatal("expected to get the same data back, but got smth else") 287 } 288 } 289 290 if ldb.entryCnt != uint64(n) { 291 t.Fatalf("expected entryCnt to be equal to %v, but got %v", n, ldb.entryCnt) 292 } 293 294 if ldb.accessCnt != uint64(2*n) { 295 t.Fatalf("expected accessCnt to be equal to %v, but got %v", 2*n, ldb.accessCnt) 296 } 297 } 298 299 // TestLDBStoreCollectGarbage tests that we can put more chunks than LevelDB's capacity, and 300 // retrieve only some of them, because garbage collection must have cleared some of them 301 func TestLDBStoreCollectGarbage(t *testing.T) { 302 capacity := 500 303 n := 2000 304 305 ldb, cleanup := newLDBStore(t) 306 ldb.setCapacity(uint64(capacity)) 307 defer cleanup() 308 309 chunks, err := mputRandomChunks(ldb, n, int64(ch.DefaultSize)) 310 if err != nil { 311 t.Fatal(err.Error()) 312 } 313 log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) 314 315 // wait for garbage collection to kick in on the responsible actor 316 time.Sleep(1 * time.Second) 317 318 var missing int 319 for _, ch := range chunks { 320 ret, err := ldb.Get(context.Background(), ch.Address()) 321 if err == ErrChunkNotFound || err == ldberrors.ErrNotFound { 322 missing++ 323 continue 324 } 325 if err != nil { 326 t.Fatal(err) 327 } 328 329 if !bytes.Equal(ret.Data(), ch.Data()) { 330 t.Fatal("expected to get the same data back, but got smth else") 331 } 332 333 log.Trace("got back chunk", "chunk", ret) 334 } 335 336 if missing < n-capacity { 337 t.Fatalf("gc failure: expected to miss %v chunks, but only %v are actually missing", n-capacity, missing) 338 } 339 340 log.Info("ldbstore", "total", n, "missing", missing, "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) 341 } 342 343 // TestLDBStoreAddRemove tests that we can put and then delete a given chunk 344 func TestLDBStoreAddRemove(t *testing.T) { 345 ldb, cleanup := newLDBStore(t) 346 ldb.setCapacity(200) 347 defer cleanup() 348 349 n := 100 350 chunks, err := mputRandomChunks(ldb, n, int64(ch.DefaultSize)) 351 if err != nil { 352 t.Fatalf(err.Error()) 353 } 354 355 for i := 0; i < n; i++ { 356 // delete all even index chunks 357 if i%2 == 0 { 358 ldb.Delete(chunks[i].Address()) 359 } 360 } 361 362 log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) 363 364 for i := 0; i < n; i++ { 365 ret, err := ldb.Get(nil, chunks[i].Address()) 366 367 if i%2 == 0 { 368 // expect even chunks to be missing 369 if err == nil { 370 // if err != ErrChunkNotFound { 371 t.Fatal("expected chunk to be missing, but got no error") 372 } 373 } else { 374 // expect odd chunks to be retrieved successfully 375 if err != nil { 376 t.Fatalf("expected no error, but got %s", err) 377 } 378 379 if !bytes.Equal(ret.Data(), chunks[i].Data()) { 380 t.Fatal("expected to get the same data back, but got smth else") 381 } 382 } 383 } 384 } 385 386 // TestLDBStoreRemoveThenCollectGarbage tests that we can delete chunks and that we can trigger garbage collection 387 func TestLDBStoreRemoveThenCollectGarbage(t *testing.T) { 388 capacity := 11 389 surplus := 4 390 391 ldb, cleanup := newLDBStore(t) 392 ldb.setCapacity(uint64(capacity)) 393 394 n := capacity 395 396 chunks := []Chunk{} 397 for i := 0; i < n+surplus; i++ { 398 c := GenerateRandomChunk(ch.DefaultSize) 399 chunks = append(chunks, c) 400 log.Trace("generate random chunk", "idx", i, "chunk", c) 401 } 402 403 for i := 0; i < n; i++ { 404 ldb.Put(context.TODO(), chunks[i]) 405 } 406 407 // delete all chunks 408 for i := 0; i < n; i++ { 409 ldb.Delete(chunks[i].Address()) 410 } 411 412 log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) 413 414 if ldb.entryCnt != 0 { 415 t.Fatalf("ldb.entrCnt expected 0 got %v", ldb.entryCnt) 416 } 417 418 expAccessCnt := uint64(n * 2) 419 if ldb.accessCnt != expAccessCnt { 420 t.Fatalf("ldb.accessCnt expected %v got %v", expAccessCnt, ldb.entryCnt) 421 } 422 423 cleanup() 424 425 ldb, cleanup = newLDBStore(t) 426 capacity = 10 427 ldb.setCapacity(uint64(capacity)) 428 defer cleanup() 429 430 n = capacity + surplus 431 432 for i := 0; i < n; i++ { 433 ldb.Put(context.TODO(), chunks[i]) 434 } 435 436 // wait for garbage collection 437 time.Sleep(1 * time.Second) 438 439 // expect first surplus chunks to be missing, because they have the smallest access value 440 for i := 0; i < surplus; i++ { 441 _, err := ldb.Get(context.TODO(), chunks[i].Address()) 442 if err == nil { 443 t.Fatal("expected surplus chunk to be missing, but got no error") 444 } 445 } 446 447 // expect last chunks to be present, as they have the largest access value 448 for i := surplus; i < surplus+capacity; i++ { 449 ret, err := ldb.Get(context.TODO(), chunks[i].Address()) 450 if err != nil { 451 t.Fatalf("chunk %v: expected no error, but got %s", i, err) 452 } 453 if !bytes.Equal(ret.Data(), chunks[i].Data()) { 454 t.Fatal("expected to get the same data back, but got smth else") 455 } 456 } 457 }