Fix dbstore iterator bug and add even more logging

This commit is contained in:
Janos Guljas 2018-01-24 17:56:36 +01:00
parent 6d7cc72c91
commit c55b99418b
11 changed files with 71 additions and 63 deletions

View file

@ -34,6 +34,7 @@ import (
"reflect" "reflect"
"sync" "sync"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
) )
@ -210,6 +211,7 @@ func (p *Peer) Run(handler func(msg interface{}) error) error {
// if they are useful for other protocols // if they are useful for other protocols
// overwrite Disconnect for testing, so that protocol readloop quits // overwrite Disconnect for testing, so that protocol readloop quits
func (p *Peer) Drop(err error) { func (p *Peer) Drop(err error) {
log.Error("p2p protocol DROP", "err", err)
p.Disconnect(p2p.DiscSubprotocolError) p.Disconnect(p2p.DiscSubprotocolError)
} }

View file

@ -19,7 +19,6 @@ package stream
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -100,9 +99,14 @@ func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64
} }
// GetData retrives chunk data from db store // GetData retrives chunk data from db store
func (s *SwarmChunkServer) GetData(key []byte) []byte { func (s *SwarmChunkServer) GetData(key []byte) ([]byte, error) {
chunk, _ := s.db.Get(storage.Key(key)) chunk, err := s.db.Get(storage.Key(key))
return chunk.SData if err == storage.ErrFetching {
<-chunk.ReqC
} else if err != nil {
return nil, err
}
return chunk.SData, nil
} }
// RetrieveRequestMsg is the protocol msg for chunk retrieve requests // RetrieveRequestMsg is the protocol msg for chunk retrieve requests
@ -141,7 +145,7 @@ func (d *Delivery) handleRetrieveRequestMsg(sp *Peer, req *RetrieveRequestMsg) e
if req.SkipCheck { if req.SkipCheck {
err := sp.Deliver(chunk, s.priority) err := sp.Deliver(chunk, s.priority)
if err != nil { if err != nil {
sp.Drop(err) sp.Drop(fmt.Errorf("handleRetrieveRequestMsg: %v", err))
} }
} }
streamer.deliveryC <- chunk.Key[:] streamer.deliveryC <- chunk.Key[:]
@ -173,9 +177,9 @@ R:
for req := range d.receiveC { for req := range d.receiveC {
// this should be has locally // this should be has locally
chunk, err := d.db.Get(req.Key) chunk, err := d.db.Get(req.Key)
fmt.Fprintln(os.Stderr, "pick from receiveC", "chunk", chunk.Key.Hex(), "reqC", chunk.ReqC, "err", err) log.Error("pick from receiveC", "chunk", chunk.Key.Hex(), "reqC", chunk.ReqC, "err", err)
if err == nil { if err == nil {
fmt.Fprintln(os.Stderr, "found existing?", "hash", chunk.Key.Hex()) log.Error("found existing?", "hash", chunk.Key.Hex())
continue R continue R
} }
if err != storage.ErrFetching { if err != storage.ErrFetching {
@ -183,19 +187,19 @@ R:
} }
select { select {
case <-chunk.ReqC: case <-chunk.ReqC:
fmt.Fprintln(os.Stderr, "someone else delivered?", "hash", chunk.Key.Hex()) log.Error("someone else delivered?", "hash", chunk.Key.Hex())
continue R continue R
default: default:
} }
go func() { go func() {
chunk.SData = req.SData chunk.SData = req.SData
fmt.Fprintln(os.Stderr, "received delivery", "hash", chunk.Key.Hex()) log.Error("received delivery", "hash", chunk.Key.Hex())
d.db.Put(chunk) d.db.Put(chunk)
fmt.Fprintln(os.Stderr, "put to db", "hash", chunk.Key.Hex()) log.Error("put to db", "hash", chunk.Key.Hex())
chunk.WaitToStore() chunk.WaitToStore()
close(chunk.ReqC) close(chunk.ReqC)
//log.Warn("received delivery stored", "hash", chunk.Key) //log.Warn("received delivery stored", "hash", chunk.Key)
fmt.Fprintln(os.Stderr, "requesters notified", "hash", chunk.Key.Hex()) log.Error("requesters notified", "hash", chunk.Key.Hex())
d.counterDone++ d.counterDone++
}() }()
} }

View file

@ -380,7 +380,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
// which responds to chunk retrieve requests all but the last node in the chain does not // which responds to chunk retrieve requests all but the last node in the chain does not
var j int var j int
err := sim.CallClient(func(client *rpc.Client) error { err := sim.CallClient(func(client *rpc.Client) error {
err := streamTesting.WatchDisconnections(sim.IDs[j], client, errc, quitC) err := streamTesting.WatchDisconnections(sim.IDs[j], client, peerCount(sim.IDs[j]), errc, quitC)
if err != nil { if err != nil {
return err return err
} }
@ -547,7 +547,7 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skip
// which responds to chunk retrieve requests // which responds to chunk retrieve requests
var j int var j int
simErrC <- sim.CallClient(func(client *rpc.Client) error { simErrC <- sim.CallClient(func(client *rpc.Client) error {
err := streamTesting.WatchDisconnections(sim.IDs[j], client, simErrC, quitC) err := streamTesting.WatchDisconnections(sim.IDs[j], client, peerCount(sim.IDs[j]), simErrC, quitC)
if err != nil { if err != nil {
return err return err
} }

View file

@ -83,7 +83,7 @@ func (p *Peer) handleSubscribeMsg(req *SubscribeMsg) error {
log.Debug("received subscription", "peer", p.ID(), "stream", req.Stream, "Key", req.Key, "from", req.From, "to", req.To) log.Debug("received subscription", "peer", p.ID(), "stream", req.Stream, "Key", req.Key, "from", req.From, "to", req.To)
go func() { go func() {
if err := p.SendOfferedHashes(os, req.From, req.To); err != nil { if err := p.SendOfferedHashes(os, req.From, req.To); err != nil {
p.Drop(err) p.Drop(fmt.Errorf("handleSubscribeMsg SendOfferedHashes: %v", err))
} }
}() }()
return nil return nil
@ -176,7 +176,7 @@ func (p *Peer) handleOfferedHashesMsg(req *OfferedHashesMsg) error {
return return
case err := <-s.next: case err := <-s.next:
if err != nil { if err != nil {
p.Drop(err) p.Drop(fmt.Errorf("handleOfferedHashesMsg next: %v", err))
return return
} }
case <-s.quit: case <-s.quit:
@ -185,7 +185,7 @@ func (p *Peer) handleOfferedHashesMsg(req *OfferedHashesMsg) error {
log.Trace("sending want batch", "peer", p.ID(), "stream", msg.Stream, "Key", msg.Key, "from", msg.From, "to", msg.To) log.Trace("sending want batch", "peer", p.ID(), "stream", msg.Stream, "Key", msg.Key, "from", msg.From, "to", msg.To)
err := p.SendPriority(msg, s.priority) err := p.SendPriority(msg, s.priority)
if err != nil { if err != nil {
p.Drop(err) p.Drop(fmt.Errorf("handleOfferedHashesMsg set priority: %v", err))
} }
}() }()
return nil return nil
@ -218,7 +218,7 @@ func (p *Peer) handleWantedHashesMsg(req *WantedHashesMsg) error {
// launch in go routine since GetBatch blocks until new hashes arrive // launch in go routine since GetBatch blocks until new hashes arrive
go func() { go func() {
if err := p.SendOfferedHashes(s, req.From, req.To); err != nil { if err := p.SendOfferedHashes(s, req.From, req.To); err != nil {
p.Drop(err) p.Drop(fmt.Errorf("handleWantedHashesMsg SendOfferedHashes: %v", err))
} }
}() }()
// go p.SendOfferedHashes(s, req.From, req.To) // go p.SendOfferedHashes(s, req.From, req.To)
@ -230,9 +230,9 @@ func (p *Peer) handleWantedHashesMsg(req *WantedHashesMsg) error {
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
if want.Get(i) { if want.Get(i) {
hash := hashes[i*HashSize : (i+1)*HashSize] hash := hashes[i*HashSize : (i+1)*HashSize]
data := s.GetData(hash) data, err := s.GetData(hash)
if data == nil { if err != nil {
return errors.New("not found") return fmt.Errorf("handleWantedHashesMsg get data %x: %v", hash, err)
} }
chunk := storage.NewChunk(hash, nil) chunk := storage.NewChunk(hash, nil)
chunk.SData = data chunk.SData = data

View file

@ -256,7 +256,7 @@ type server struct {
// Server interface for outgoing peer Streamer // Server interface for outgoing peer Streamer
type Server interface { type Server interface {
SetNextBatch(uint64, uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) SetNextBatch(uint64, uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error)
GetData([]byte) []byte GetData([]byte) ([]byte, error)
} }
type client struct { type client struct {

View file

@ -81,8 +81,8 @@ func (self *testServer) SetNextBatch(from uint64, to uint64) ([]byte, uint64, ui
return make([]byte, HashSize), from + 1, to + 1, nil, nil return make([]byte, HashSize), from + 1, to + 1, nil, nil
} }
func (self *testServer) GetData([]byte) []byte { func (self *testServer) GetData([]byte) ([]byte, error) {
return nil return nil, nil
} }
func TestStreamerDownstreamSubscribeMsgExchange(t *testing.T) { func TestStreamerDownstreamSubscribeMsgExchange(t *testing.T) {

View file

@ -73,14 +73,14 @@ func RegisterSwarmSyncerServer(streamer *Registry, db *storage.DBAPI) {
} }
// GetSection retrieves the actual chunk from localstore // GetSection retrieves the actual chunk from localstore
func (s *SwarmSyncerServer) GetData(key []byte) []byte { func (s *SwarmSyncerServer) GetData(key []byte) ([]byte, error) {
chunk, err := s.db.Get(storage.Key(key)) chunk, err := s.db.Get(storage.Key(key))
if err == storage.ErrFetching { if err == storage.ErrFetching {
<-chunk.ReqC <-chunk.ReqC
} else if err != nil { } else if err != nil {
return nil return nil, err
} }
return chunk.SData return chunk.SData, nil
} }
// GetBatch retrieves the next batch of hashes from the dbstore // GetBatch retrieves the next batch of hashes from the dbstore
@ -111,7 +111,7 @@ func (s *SwarmSyncerServer) SetNextBatch(from, to uint64) ([]byte, uint64, uint6
} }
log.Debug("Swarm syncer offer batch", "po", s.po, "len", i, "from", from, "to", to, "current store count", s.db.CurrentBucketStorageIndex(s.po)) log.Debug("Swarm syncer offer batch", "po", s.po, "len", i, "from", from, "to", to, "current store count", s.db.CurrentBucketStorageIndex(s.po))
return batch, from, to + 1, nil, nil return batch, from, to, nil, nil
} }
// SwarmSyncerClient // SwarmSyncerClient

View file

@ -22,7 +22,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"os"
"testing" "testing"
"time" "time"
@ -142,7 +141,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
// each node Subscribes to each other's swarmChunkServerStreamName // each node Subscribes to each other's swarmChunkServerStreamName
j := 0 j := 0
return sim.CallClient(func(client *rpc.Client) error { return sim.CallClient(func(client *rpc.Client) error {
err := streamTesting.WatchDisconnections(sim.IDs[j], client, errc, quitC) err := streamTesting.WatchDisconnections(sim.IDs[j], client, peerCount(sim.IDs[j]), errc, quitC)
if err != nil { if err != nil {
return err return err
} }
@ -182,7 +181,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
} else if err == nil { } else if err == nil {
nodeHashFound++ nodeHashFound++
} else { } else {
fmt.Fprintln(os.Stderr, time.Now(), "not found", "index", i, "origin", j, "key", key.Hex(), "err", err) log.Error("not found", "index", i, "origin", j, "key", key.Hex(), "err", err)
} }
} }
} }

View file

@ -154,9 +154,9 @@ func NewSimulation(conf *RunConfig) (*Simulation, func(), error) {
// set nodes number of Stores available // set nodes number of Stores available
stores, storeTeardown, err := SetStores(addrs...) stores, storeTeardown, err := SetStores(addrs...)
teardown = func() { teardown = func() {
storeTeardown()
adapterTeardown()
net.Shutdown() net.Shutdown()
adapterTeardown()
storeTeardown()
} }
if err != nil { if err != nil {
return nil, teardown, err return nil, teardown, err
@ -208,7 +208,7 @@ func (s *Simulation) Run(ctx context.Context, conf *RunConfig) (*simulations.Ste
return result, nil return result, nil
} }
func WatchDisconnections(id discover.NodeID, client *rpc.Client, errc chan error, quitC chan struct{}) error { func WatchDisconnections(id discover.NodeID, client *rpc.Client, expectedConnCount int, errc chan error, quitC chan struct{}) error {
events := make(chan *p2p.PeerEvent) events := make(chan *p2p.PeerEvent)
sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents") sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
if err != nil { if err != nil {
@ -218,10 +218,14 @@ func WatchDisconnections(id discover.NodeID, client *rpc.Client, errc chan error
defer sub.Unsubscribe() defer sub.Unsubscribe()
select { select {
case <-quitC: case <-quitC:
return if expectedConnCount <= 0 {
return
}
case e := <-events: case e := <-events:
expectedConnCount--
errc <- fmt.Errorf("peerEvent for node %v: %v", id, e) errc <- fmt.Errorf("peerEvent for node %v: %v", id, e)
case err := <-sub.Err(): case err := <-sub.Err():
expectedConnCount = 0
if err != nil { if err != nil {
errc <- fmt.Errorf("error getting peer events for node %v: %v", id, err) errc <- fmt.Errorf("error getting peer events for node %v: %v", id, err)
} }

View file

@ -30,7 +30,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os"
"sync" "sync"
"time" "time"
@ -540,16 +539,16 @@ func (s *DbStore) CurrentStorageIndex() uint64 {
} }
func (s *DbStore) Put(chunk *Chunk) { func (s *DbStore) Put(chunk *Chunk) {
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put", "hash", chunk.Key.Hex()) log.Error("DbStore.Put", "hash", chunk.Key.Hex())
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
go func() { go func() {
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put WAITER STARTED", "hash", chunk.Key.Hex()) log.Error("DbStore.Put WAITER STARTED", "hash", chunk.Key.Hex())
select { select {
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put WAITING", "hash", chunk.Key.Hex()) log.Error("DbStore.Put WAITING", "hash", chunk.Key.Hex())
case <-done: case <-done:
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put EXITED", "hash", chunk.Key.Hex()) log.Error("DbStore.Put EXITED", "hash", chunk.Key.Hex())
} }
}() }()
@ -557,35 +556,35 @@ func (s *DbStore) Put(chunk *Chunk) {
var index dpaDBIndex var index dpaDBIndex
po := s.po(chunk.Key) po := s.po(chunk.Key)
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.db.Get is being called...", "hash", chunk.Key.Hex()) log.Error("DbStore.db.Get is being called...", "hash", chunk.Key.Hex())
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.LOCK acquiring", "hash", chunk.Key.Hex()) log.Error("DbStore.LOCK acquiring", "hash", chunk.Key.Hex())
s.lock.Lock() s.lock.Lock()
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.LOCK acquired", "hash", chunk.Key.Hex()) log.Error("DbStore.LOCK acquired", "hash", chunk.Key.Hex())
defer s.lock.Unlock() defer s.lock.Unlock()
idata, err := s.db.Get(ikey) idata, err := s.db.Get(ikey)
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.db.Get done", "hash", chunk.Key.Hex(), "err", err) log.Error("DbStore.db.Get done", "hash", chunk.Key.Hex(), "err", err)
if err != nil { if err != nil {
s.doPut(chunk, ikey, &index, po) s.doPut(chunk, ikey, &index, po)
batchC := s.batchC
go func() { go func() {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put PANIC", "hash", chunk.Key.Hex(), "err", err) log.Error("DbStore.Put PANIC", "hash", chunk.Key.Hex(), "err", err)
} }
}() }()
<-batchC
close(chunk.dbStored)
}() }()
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put doPut", "hash", chunk.Key.Hex(), "dataIdx", s.dataIdx) log.Error("DbStore.Put doPut", "hash", chunk.Key.Hex(), "dataIdx", s.dataIdx)
} else { } else {
log.Trace(fmt.Sprintf("DbStore: chunk already exists, only update access")) log.Trace(fmt.Sprintf("DbStore: chunk already exists, only update access"))
decodeIndex(idata, &index) decodeIndex(idata, &index)
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.Put already found", "hash", chunk.Key.Hex())
}
batchC := s.batchC
go func() {
<-batchC
close(chunk.dbStored) close(chunk.dbStored)
}() log.Error("DbStore.Put already found", "hash", chunk.Key.Hex())
}
index.Access = s.accessCnt index.Access = s.accessCnt
s.accessCnt++ s.accessCnt++
idata = encodeIndex(&index) idata = encodeIndex(&index)
@ -594,7 +593,7 @@ func (s *DbStore) Put(chunk *Chunk) {
case s.batchesC <- struct{}{}: case s.batchesC <- struct{}{}:
default: default:
} }
fmt.Fprintln(os.Stderr, time.Now(), "DbStore.db.Put done", "hash", chunk.Key.Hex(), "err", err) log.Error("DbStore.db.Put done", "hash", chunk.Key.Hex(), "err", err)
} }
// force putting into db, does not check access index // force putting into db, does not check access index
@ -716,9 +715,9 @@ func (s *DbStore) get(key Key) (chunk *Chunk, err error) {
hash := hasher.Sum(nil) hash := hasher.Sum(nil)
if !bytes.Equal(hash, key) { if !bytes.Equal(hash, key) {
log.Trace(fmt.Sprintf("Apparent key/hash mismatch. Hash %x, key %v", hash, key[:])) log.Error(fmt.Sprintf("Apparent key/hash mismatch. Hash %x, key %v", hash, key[:]))
s.delete(indx.Idx, getIndexKey(key), s.po(key)) s.delete(indx.Idx, getIndexKey(key), s.po(key))
log.Warn("Invalid Chunk in Database. Please repair with command: 'swarm cleandb'") log.Error("Invalid Chunk in Database. Please repair with command: 'swarm cleandb'")
} }
} }
@ -784,15 +783,18 @@ func (s *DbStore) Close() {
// initialises a sync iterator from a syncToken (passed in with the handshake) // initialises a sync iterator from a syncToken (passed in with the handshake)
func (s *DbStore) SyncIterator(since uint64, until uint64, po uint8, f func(Key, uint64) bool) error { func (s *DbStore) SyncIterator(since uint64, until uint64, po uint8, f func(Key, uint64) bool) error {
s.lock.Lock() // probably, the lock is not needed
defer s.lock.Unlock() // s.lock.Lock()
// defer s.lock.Unlock()
untilkey := getDataKey(until, po) untilkey := getDataKey(until, po)
it := s.db.NewIterator() it := s.db.NewIterator()
seek := getDataKey(since, po) seek := getDataKey(since, po)
it.Seek(seek) it.Seek(seek)
defer it.Release() defer it.Release()
for it.Valid() {
for it.Next() {
dbkey := it.Key() dbkey := it.Key()
if dbkey[0] != keyData || dbkey[1] != byte(po) || bytes.Compare(untilkey, dbkey) < 0 { if dbkey[0] != keyData || dbkey[1] != byte(po) || bytes.Compare(untilkey, dbkey) < 0 {
break break
@ -803,9 +805,8 @@ func (s *DbStore) SyncIterator(since uint64, until uint64, po uint8, f func(Key,
if !f(Key(key), binary.BigEndian.Uint64(dbkey[2:])) { if !f(Key(key), binary.BigEndian.Uint64(dbkey[2:])) {
break break
} }
it.Next()
} }
return nil return it.Error()
} }
func databaseExists(path string) bool { func databaseExists(path string) bool {

View file

@ -19,9 +19,7 @@ package storage
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/storage/mock" "github.com/ethereum/go-ethereum/swarm/storage/mock"
@ -98,9 +96,9 @@ func NewTestLocalStoreForAddr(path string, basekey []byte) (*LocalStore, error)
func (self *LocalStore) Put(chunk *Chunk) { func (self *LocalStore) Put(chunk *Chunk) {
chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
self.memStore.Put(chunk) self.memStore.Put(chunk)
fmt.Fprintln(os.Stderr, time.Now(), "put to memstore", "hash", chunk.Key.Hex()) log.Error("put to memstore", "hash", chunk.Key.Hex())
self.DbStore.Put(chunk) self.DbStore.Put(chunk)
fmt.Fprintln(os.Stderr, time.Now(), "put to dbstore", "hash", chunk.Key.Hex()) log.Error("put to dbstore", "hash", chunk.Key.Hex())
} }
// Get(chunk *Chunk) looks up a chunk in the local stores // Get(chunk *Chunk) looks up a chunk in the local stores