mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
swarm/storage, swarm/network: Fix race condition
There was a race condition in writing/reading chunk.SData between delivery.processReceivedChunks and dpa.retrieveWorker when the chunk was still fetching
This commit is contained in:
parent
111b53d8bf
commit
295512f5a8
9 changed files with 42 additions and 25 deletions
|
|
@ -18,6 +18,7 @@ package stream
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -167,12 +168,11 @@ 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)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
log.Error("not in db? ", "key", req.Key, "chunk", chunk)
|
|
||||||
continue R
|
continue R
|
||||||
}
|
}
|
||||||
if chunk.ReqC == nil {
|
if err != storage.ErrFetching {
|
||||||
continue R
|
panic(fmt.Sprintf("not in db? key %v chunk %v", req.Key, chunk))
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-chunk.ReqC:
|
case <-chunk.ReqC:
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,28 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
loglevel = flag.Int("loglevel", 2, "verbosity of logs")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Parse()
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
|
}
|
||||||
|
|
||||||
type brokenLimitedReader struct {
|
type brokenLimitedReader struct {
|
||||||
lr io.Reader
|
lr io.Reader
|
||||||
errAt int
|
errAt int
|
||||||
|
|
|
||||||
|
|
@ -699,7 +699,7 @@ func (s *DbStore) get(key Key) (chunk *Chunk, err error) {
|
||||||
decodeData(data, chunk)
|
decodeData(data, chunk)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
err = notFound
|
err = ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -712,8 +712,8 @@ func newMockGetDataFunc(mockStore *mock.NodeStore) func(key Key) (data []byte, e
|
||||||
return func(key Key) (data []byte, err error) {
|
return func(key Key) (data []byte, err error) {
|
||||||
data, err = mockStore.Get(key)
|
data, err = mockStore.Get(key)
|
||||||
if err == mock.ErrNotFound {
|
if err == mock.ErrNotFound {
|
||||||
// preserve notFound error
|
// preserve ErrNotFound error
|
||||||
err = notFound
|
err = ErrNotFound
|
||||||
}
|
}
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,8 @@ func testDbStoreNotFound(t *testing.T, mock bool) {
|
||||||
defer db.close()
|
defer db.close()
|
||||||
|
|
||||||
_, err = db.Get(ZeroKey)
|
_, err = db.Get(ZeroKey)
|
||||||
if err != notFound {
|
if err != ErrNotFound {
|
||||||
t.Errorf("Expected notFound, got %v", err)
|
t.Errorf("Expected ErrNotFound, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
notFound = errors.New("not found")
|
ErrNotFound = errors.New("not found")
|
||||||
|
ErrFetching = errors.New("chunk still fetching")
|
||||||
// timeout interval before retrieval is timed out
|
// timeout interval before retrieval is timed out
|
||||||
searchTimeout = 3 * time.Second
|
searchTimeout = 3 * time.Second
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,15 @@ func (self *LocalStore) Put(chunk *Chunk) {
|
||||||
// ChunkStores are remote and can have long latency
|
// ChunkStores are remote and can have long latency
|
||||||
func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
|
func (self *LocalStore) Get(key Key) (chunk *Chunk, err error) {
|
||||||
chunk, err = self.memStore.Get(key)
|
chunk, err = self.memStore.Get(key)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if chunk.ReqC != nil {
|
||||||
|
select {
|
||||||
|
case <-chunk.ReqC:
|
||||||
|
default:
|
||||||
|
return chunk, ErrFetching
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
chunk, err = self.DbStore.Get(key)
|
chunk, err = self.DbStore.Get(key)
|
||||||
|
|
@ -123,12 +131,11 @@ func (self *LocalStore) GetOrCreateRequest(key Key) (chunk *Chunk, created bool)
|
||||||
var err error
|
var err error
|
||||||
chunk, err = self.Get(key)
|
chunk, err = self.Get(key)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if chunk.ReqC == nil {
|
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", key))
|
||||||
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v found locally", key))
|
return chunk, false
|
||||||
} else {
|
}
|
||||||
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request", key))
|
if err == ErrFetching {
|
||||||
// no need to launch again
|
log.Trace(fmt.Sprintf("LocalStore.GetOrRetrieve: %v hit on an existing request %v", key, chunk.ReqC))
|
||||||
}
|
|
||||||
return chunk, false
|
return chunk, false
|
||||||
}
|
}
|
||||||
// no data and no request status
|
// no data and no request status
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
|
||||||
l := hash.bits(bitpos, node.bits)
|
l := hash.bits(bitpos, node.bits)
|
||||||
st := node.subtree[l]
|
st := node.subtree[l]
|
||||||
if st == nil {
|
if st == nil {
|
||||||
return nil, notFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
bitpos += node.bits
|
bitpos += node.bits
|
||||||
node = st
|
node = st
|
||||||
|
|
@ -232,7 +232,7 @@ func (s *MemStore) Get(hash Key) (chunk *Chunk, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = notFound
|
err = ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@ func TestMemStoreNotFound(t *testing.T) {
|
||||||
defer m.Close()
|
defer m.Close()
|
||||||
|
|
||||||
_, err := m.Get(ZeroKey)
|
_, err := m.Get(ZeroKey)
|
||||||
if err != notFound {
|
if err != ErrNotFound {
|
||||||
t.Errorf("Expected notFound, got %v", err)
|
t.Errorf("Expected ErrNotFound, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ func (self *NetStore) Get(key Key) (chunk *Chunk, err error) {
|
||||||
var created bool
|
var created bool
|
||||||
chunk, created = self.localStore.GetOrCreateRequest(key)
|
chunk, created = self.localStore.GetOrCreateRequest(key)
|
||||||
if chunk.ReqC == nil {
|
if chunk.ReqC == nil {
|
||||||
return
|
return chunk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if created {
|
if created {
|
||||||
|
|
@ -53,10 +52,9 @@ func (self *NetStore) Get(key Key) (chunk *Chunk, err error) {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
return nil, notFound
|
return nil, ErrNotFound
|
||||||
case <-chunk.ReqC:
|
case <-chunk.ReqC:
|
||||||
}
|
}
|
||||||
chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
|
|
||||||
return chunk, nil
|
return chunk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue