github.com/gochain-io/gochain@v2.2.26+incompatible/swarm/network/syncdb_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 network
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"sync"
    29  
    30  	"github.com/gochain-io/gochain/crypto"
    31  	"github.com/gochain-io/gochain/log"
    32  	"github.com/gochain-io/gochain/swarm/storage"
    33  )
    34  
    35  func init() {
    36  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlCrit, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
    37  }
    38  
    39  type testSyncDb struct {
    40  	*syncDb
    41  	c      int
    42  	t      *testing.T
    43  	fromDb chan bool
    44  	sent   []int
    45  	dbdir  string
    46  	at     int
    47  
    48  	deliveredMu sync.RWMutex
    49  	delivered   [][]byte
    50  }
    51  
    52  func newTestSyncDb(priority, bufferSize, batchSize int, dbdir string, t *testing.T) *testSyncDb {
    53  	if len(dbdir) == 0 {
    54  		tmp, err := ioutil.TempDir(os.TempDir(), "syncdb-test")
    55  		if err != nil {
    56  			t.Fatalf("unable to create temporary direcory %v: %v", tmp, err)
    57  		}
    58  		dbdir = tmp
    59  	}
    60  	db, err := storage.NewLDBDatabase(filepath.Join(dbdir, "requestdb"))
    61  	if err != nil {
    62  		t.Fatalf("unable to create db: %v", err)
    63  	}
    64  	self := &testSyncDb{
    65  		fromDb: make(chan bool),
    66  		dbdir:  dbdir,
    67  		t:      t,
    68  	}
    69  	h := crypto.Keccak256Hash([]byte{0})
    70  	key := storage.Key(h[:])
    71  	self.syncDb = newSyncDb(db, key, uint(priority), uint(bufferSize), uint(batchSize), self.deliver)
    72  	// kick off db iterator right away, if no items on db this will allow
    73  	// reading from the buffer
    74  	return self
    75  
    76  }
    77  
    78  func (t *testSyncDb) close() {
    79  	t.db.Close()
    80  	os.RemoveAll(t.dbdir)
    81  }
    82  
    83  func (t *testSyncDb) push(n int) {
    84  	for i := 0; i < n; i++ {
    85  		t.buffer <- storage.Key(crypto.Keccak256([]byte{byte(t.c)}))
    86  		t.sent = append(t.sent, t.c)
    87  		t.c++
    88  	}
    89  	log.Debug(fmt.Sprintf("pushed %v requests", n))
    90  }
    91  
    92  func (t *testSyncDb) draindb() {
    93  	it := t.db.NewIterator()
    94  	defer it.Release()
    95  	for {
    96  		it.Seek(t.start)
    97  		if !it.Valid() {
    98  			return
    99  		}
   100  		k := it.Key()
   101  		if len(k) == 0 || k[0] == 1 {
   102  			return
   103  		}
   104  		it.Release()
   105  		it = t.db.NewIterator()
   106  	}
   107  }
   108  
   109  func (t *testSyncDb) deliver(req interface{}, quit chan bool) bool {
   110  	_, db := req.(*syncDbEntry)
   111  	key, _, _, _, err := parseRequest(req)
   112  	if err != nil {
   113  		t.t.Fatalf("unexpected error of key %v: %v", key, err)
   114  	}
   115  	t.deliveredMu.Lock()
   116  	t.delivered = append(t.delivered, key)
   117  	t.deliveredMu.Unlock()
   118  	select {
   119  	case t.fromDb <- db:
   120  		return true
   121  	case <-quit:
   122  		return false
   123  	}
   124  }
   125  
   126  func (t *testSyncDb) expect(n int, db bool) {
   127  	var ok bool
   128  	// for n items
   129  	for i := 0; i < n; i++ {
   130  		ok = <-t.fromDb
   131  		t.deliveredMu.RLock()
   132  		l := len(t.delivered)
   133  		t.deliveredMu.RUnlock()
   134  		if t.at+1 > l {
   135  			t.t.Fatalf("expected %v, got %v", t.at+1, l)
   136  		}
   137  		t.deliveredMu.RLock()
   138  		data := t.delivered[t.at]
   139  		t.deliveredMu.RUnlock()
   140  		if len(t.sent) > t.at && !bytes.Equal(crypto.Keccak256([]byte{byte(t.sent[t.at])}), data) {
   141  			t.t.Fatalf("expected delivery %v/%v/%v to be hash of  %v, from db: %v = %v", i, n, t.at, t.sent[t.at], ok, db)
   142  			log.Debug(fmt.Sprintf("%v/%v/%v to be hash of  %v, from db: %v = %v", i, n, t.at, t.sent[t.at], ok, db))
   143  		}
   144  		if !ok && db {
   145  			t.t.Fatalf("expected delivery %v/%v/%v from db", i, n, t.at)
   146  		}
   147  		if ok && !db {
   148  			t.t.Fatalf("expected delivery %v/%v/%v from cache", i, n, t.at)
   149  		}
   150  		t.at++
   151  	}
   152  }
   153  
   154  func TestSyncDb(t *testing.T) {
   155  	t.Skip("fails randomly on all platforms")
   156  
   157  	priority := High
   158  	bufferSize := 5
   159  	batchSize := 2 * bufferSize
   160  	s := newTestSyncDb(priority, bufferSize, batchSize, "", t)
   161  	defer s.close()
   162  	defer s.stop()
   163  	s.dbRead(false, 0, s.deliver)
   164  	s.draindb()
   165  
   166  	s.push(4)
   167  	s.expect(1, false)
   168  	// 3 in buffer
   169  	time.Sleep(100 * time.Millisecond)
   170  	s.push(3)
   171  	// push over limit
   172  	s.expect(1, false)
   173  	// one popped from the buffer, then contention detected
   174  	s.expect(4, true)
   175  	s.push(4)
   176  	s.expect(5, true)
   177  	// depleted db, switch back to buffer
   178  	s.draindb()
   179  	s.push(5)
   180  	s.expect(4, false)
   181  	s.push(3)
   182  	s.expect(4, false)
   183  	// buffer depleted
   184  	time.Sleep(100 * time.Millisecond)
   185  	s.push(6)
   186  	s.expect(1, false)
   187  	// push into buffer full, switch to db
   188  	s.expect(5, true)
   189  	s.draindb()
   190  	s.push(1)
   191  	s.expect(1, false)
   192  }
   193  
   194  func TestSaveSyncDb(t *testing.T) {
   195  	amount := 30
   196  	priority := High
   197  	bufferSize := amount
   198  	batchSize := 10
   199  	s := newTestSyncDb(priority, bufferSize, batchSize, "", t)
   200  	go s.dbRead(false, 0, s.deliver)
   201  	s.push(amount)
   202  	s.stop()
   203  	s.db.Close()
   204  
   205  	s = newTestSyncDb(priority, bufferSize, batchSize, s.dbdir, t)
   206  	go s.dbRead(false, 0, s.deliver)
   207  	s.expect(amount, true)
   208  	s.deliveredMu.RLock()
   209  	for i, key := range s.delivered {
   210  		expKey := crypto.Keccak256([]byte{byte(i)})
   211  		if !bytes.Equal(key, expKey) {
   212  			s.deliveredMu.RUnlock()
   213  			t.Fatalf("delivery %v expected to be key %x, got %x", i, expKey, key)
   214  		}
   215  	}
   216  	s.deliveredMu.RUnlock()
   217  	s.push(amount)
   218  	s.expect(amount, false)
   219  	s.deliveredMu.RLock()
   220  	for i := amount; i < 2*amount; i++ {
   221  		key := s.delivered[i]
   222  		expKey := crypto.Keccak256([]byte{byte(i - amount)})
   223  		if !bytes.Equal(key, expKey) {
   224  			s.deliveredMu.RUnlock()
   225  			t.Fatalf("delivery %v expected to be key %x, got %x", i, expKey, key)
   226  		}
   227  	}
   228  	s.deliveredMu.RUnlock()
   229  	s.stop()
   230  	s.db.Close()
   231  
   232  	s = newTestSyncDb(priority, bufferSize, batchSize, s.dbdir, t)
   233  	defer s.close()
   234  	defer s.stop()
   235  
   236  	go s.dbRead(false, 0, s.deliver)
   237  	s.push(1)
   238  	s.expect(1, false)
   239  
   240  }