storage: wrap closing chunk stored chan in mutex

This commit fixes issue #341
This commit is contained in:
Bartek Borkowski 2018-03-28 13:35:22 +02:00 committed by Bartek Borkowski
parent 2acb592647
commit bf35da28ac
10 changed files with 40 additions and 25 deletions

View file

@ -298,7 +298,7 @@ func (self *TreeChunker) hashChunk(hasher SwarmHash, job *hashJob, chunkC chan *
storeWg.Add(1) storeWg.Add(1)
go func() { go func() {
defer storeWg.Done() defer storeWg.Done()
<-newChunk.dbStored <-newChunk.dbStoredC
}() }()
} }
} }

View file

@ -65,7 +65,7 @@ func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, c
case chunk := <-chunkC: case chunk := <-chunkC:
// self.chunks = append(self.chunks, chunk) // self.chunks = append(self.chunks, chunk)
self.chunks[chunk.Key.Hex()] = chunk self.chunks[chunk.Key.Hex()] = chunk
close(chunk.dbStored) chunk.markAsStored()
} }
} }
@ -105,12 +105,12 @@ func (self *chunkerTester) Append(chunker Splitter, rootKey Key, data io.Reader,
if !success { if !success {
// Requesting data // Requesting data
self.chunks[chunk.Key.Hex()] = chunk self.chunks[chunk.Key.Hex()] = chunk
close(chunk.dbStored) chunk.markAsStored()
} else { } else {
// getting data // getting data
chunk.SData = stored.SData chunk.SData = stored.SData
chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
close(chunk.dbStored) chunk.markAsStored()
if chunk.C != nil { if chunk.C != nil {
close(chunk.C) close(chunk.C)
} }

View file

@ -101,7 +101,7 @@ func mput(store ChunkStore, processors int, n int, f func(i int) *Chunk) (hs []K
store.Put(chunk) store.Put(chunk)
<-chunk.dbStored <-chunk.dbStoredC
}() }()
} }
}() }()
@ -110,7 +110,7 @@ func mput(store ChunkStore, processors int, n int, f func(i int) *Chunk) (hs []K
if _, ok := store.(*MemStore); ok { if _, ok := store.(*MemStore); ok {
fa = func(i int) *Chunk { fa = func(i int) *Chunk {
chunk := f(i) chunk := f(i)
close(chunk.dbStored) chunk.markAsStored()
return chunk return chunk
} }
} }

View file

@ -163,6 +163,7 @@ func NewMockDbStore(path string, hash SwarmHasher, capacity uint64, po func(Key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// replace put and get with mock store functionality // replace put and get with mock store functionality
if mockStore != nil { if mockStore != nil {
s.encodeDataFunc = newMockEncodeDataFunc(mockStore) s.encodeDataFunc = newMockEncodeDataFunc(mockStore)
@ -421,7 +422,7 @@ func (s *LDBStore) Import(in io.Reader) (int64, error) {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
<-chunk.dbStored <-chunk.dbStoredC
}() }()
count++ count++
} }
@ -561,12 +562,12 @@ func (s *LDBStore) Put(chunk *Chunk) {
batchC := s.batchC batchC := s.batchC
go func() { go func() {
<-batchC <-batchC
close(chunk.dbStored) chunk.markAsStored()
}() }()
} else { } else {
log.Trace("ldbstore.put: chunk already exists, only update access", "key", chunk.Key) log.Trace("ldbstore.put: chunk already exists, only update access", "key", chunk.Key)
decodeIndex(idata, &index) decodeIndex(idata, &index)
close(chunk.dbStored) chunk.markAsStored()
} }
index.Access = s.accessCnt index.Access = s.accessCnt
s.accessCnt++ s.accessCnt++
@ -707,7 +708,7 @@ func (s *LDBStore) get(key Key) (chunk *Chunk, err error) {
} }
chunk = NewChunk(key, nil) chunk = NewChunk(key, nil)
close(chunk.dbStored) chunk.markAsStored()
decodeData(data, chunk) decodeData(data, chunk)
} else { } else {
err = ErrChunkNotFound err = ErrChunkNotFound

View file

@ -182,7 +182,7 @@ func testIterator(t *testing.T, mock bool) {
j := i j := i
go func() { go func() {
defer wg.Done() defer wg.Done()
<-chunks[j].dbStored <-chunks[j].dbStoredC
}() }()
} }

View file

@ -91,10 +91,12 @@ 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]))
c := &Chunk{ c := &Chunk{
Key: Key(append([]byte{}, chunk.Key...)), Key: Key(append([]byte{}, chunk.Key...)),
SData: append([]byte{}, chunk.SData...), SData: append([]byte{}, chunk.SData...),
Size: chunk.Size, Size: chunk.Size,
dbStored: chunk.dbStored, dbStored: chunk.dbStored,
dbStoredC: chunk.dbStoredC,
dbStoredMu: chunk.dbStoredMu,
} }
dbStorePutCounter.Inc(1) dbStorePutCounter.Inc(1)

View file

@ -302,7 +302,7 @@ func (s *MemStore) removeOldest() {
if node.entry.ReqC == nil { if node.entry.ReqC == nil {
log.Trace(fmt.Sprintf("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log())) log.Trace(fmt.Sprintf("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log()))
<-node.entry.dbStored <-node.entry.dbStoredC
log.Trace(fmt.Sprintf("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log())) log.Trace(fmt.Sprintf("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log()))
memstoreRemoveCounter.Inc(1) memstoreRemoveCounter.Inc(1)

View file

@ -285,7 +285,7 @@ func (self *PyramidChunker) processChunk(id int64, hasher SwarmHash, job *chunkJ
storageWG.Add(1) storageWG.Add(1)
go func() { go func() {
defer storageWG.Done() defer storageWG.Done()
<-newChunk.dbStored <-newChunk.dbStoredC
}() }()
} }
} }

View file

@ -635,7 +635,7 @@ func (self *ResourceHandler) update(ctx context.Context, name string, data []byt
self.Put(chunk) self.Put(chunk)
timeout := time.NewTimer(self.storeTimeout) timeout := time.NewTimer(self.storeTimeout)
select { select {
case <-chunk.dbStored: case <-chunk.dbStoredC:
case <-timeout.C: case <-timeout.C:
return nil, NewResourceError(ErrIO, "chunk store timeout") return nil, NewResourceError(ErrIO, "chunk store timeout")
} }

View file

@ -174,11 +174,13 @@ type Chunk struct {
SData []byte // nil if request, to be supplied by dpa SData []byte // nil if request, to be supplied by dpa
Size int64 // size of the data covered by the subtree encoded in this chunk Size int64 // size of the data covered by the subtree encoded in this chunk
//Source Peer // peer //Source Peer // peer
C chan bool // to signal data delivery by the dpa C chan bool // to signal data delivery by the dpa
ReqC chan bool // to signal the request done ReqC chan bool // to signal the request done
dbStored chan bool // never remove a chunk from memStore before it is written to dbStore dbStoredC chan bool // never remove a chunk from memStore before it is written to dbStore
errored bool // flag which is set when the chunk request has errored or timeouted dbStored bool // never remove a chunk from memStore before it is written to dbStore
erroredMu sync.Mutex dbStoredMu sync.Mutex
errored bool // flag which is set when the chunk request has errored or timeouted
erroredMu sync.Mutex
} }
func (c *Chunk) SetErrored(val bool) { func (c *Chunk) SetErrored(val bool) {
@ -196,11 +198,21 @@ func (c *Chunk) GetErrored() bool {
} }
func NewChunk(key Key, reqC chan bool) *Chunk { func NewChunk(key Key, reqC chan bool) *Chunk {
return &Chunk{Key: key, ReqC: reqC, dbStored: make(chan bool)} return &Chunk{Key: key, ReqC: reqC, dbStoredC: make(chan bool)}
}
func (c *Chunk) markAsStored() {
c.dbStoredMu.Lock()
defer c.dbStoredMu.Unlock()
if !c.dbStored {
close(c.dbStoredC)
c.dbStored = true
}
} }
func (c *Chunk) WaitToStore() { func (c *Chunk) WaitToStore() {
<-c.dbStored <-c.dbStoredC
} }
func FakeChunk(size int64, count int, chunks []*Chunk) int { func FakeChunk(size int64, count int, chunks []*Chunk) int {