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  }