Added wait group that waits until local storages finish

This commit is contained in:
zelig 2015-02-12 12:32:07 +01:00
parent 68b6ee18f8
commit dfd3cc90b5
7 changed files with 53 additions and 25 deletions

View file

@ -58,10 +58,11 @@ type Chunker interface {
/* /*
When splitting, data is given as a SectionReader, and the key is a hashSize long byte slice (Key), the root hash of the entire content will fill this once processing finishes. When splitting, data is given as a SectionReader, and the key is a hashSize long byte slice (Key), the root hash of the entire content will fill this once processing finishes.
New chunks to store are coming to caller via the chunk storage channel, which the caller provides. New chunks to store are coming to caller via the chunk storage channel, which the caller provides.
wg is a Waitgroup (can be nil) that can be used to block until the local storage finishes
The caller gets returned an error channel, if an error is encountered during splitting, it is fed to errC error channel. The caller gets returned an error channel, if an error is encountered during splitting, it is fed to errC error channel.
A closed error signals process completion at which point the key can be considered final if there were no errors. A closed error signals process completion at which point the key can be considered final if there were no errors.
*/ */
Split(key Key, data SectionReader, chunkC chan *Chunk) chan error Split(key Key, data SectionReader, chunkC chan *Chunk, wg *sync.WaitGroup) chan error
/* /*
Join reconstructs original content based on a root key. Join reconstructs original content based on a root key.
When joining, the caller gets returned a Lazy SectionReader When joining, the caller gets returned a Lazy SectionReader
@ -133,7 +134,12 @@ func (self *TreeChunker) Hash(size int64, input []byte) []byte {
return hasher.Sum(nil) return hasher.Sum(nil)
} }
func (self *TreeChunker) Split(key Key, data SectionReader, chunkC chan *Chunk) (errC chan error) { func (self *TreeChunker) Split(key Key, data SectionReader, chunkC chan *Chunk, swg *sync.WaitGroup) (errC chan error) {
if swg != nil {
swg.Add(1)
defer swg.Done()
}
if self.chunkSize <= 0 { if self.chunkSize <= 0 {
panic("chunker must be initialised") panic("chunker must be initialised")
@ -164,7 +170,7 @@ func (self *TreeChunker) Split(key Key, data SectionReader, chunkC chan *Chunk)
// dpaLogger.Debugf("split request received for data (%v bytes, depth: %v)", size, depth) // dpaLogger.Debugf("split request received for data (%v bytes, depth: %v)", size, depth)
//launch actual recursive function passing the workgroup //launch actual recursive function passing the workgroup
self.split(depth, treeSize/self.Branches, key, data, chunkC, rerrC, wg) self.split(depth, treeSize/self.Branches, key, data, chunkC, rerrC, wg, swg)
}() }()
// closes internal error channel if all subprocesses in the workgroup finished // closes internal error channel if all subprocesses in the workgroup finished
@ -190,7 +196,7 @@ func (self *TreeChunker) Split(key Key, data SectionReader, chunkC chan *Chunk)
return return
} }
func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionReader, chunkC chan *Chunk, errc chan error, parentWg *sync.WaitGroup) { func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionReader, chunkC chan *Chunk, errc chan error, parentWg *sync.WaitGroup, swg *sync.WaitGroup) {
defer parentWg.Done() defer parentWg.Done()
@ -238,7 +244,7 @@ func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionR
subTreeKey := chunk[i*self.hashSize : (i+1)*self.hashSize] subTreeKey := chunk[i*self.hashSize : (i+1)*self.hashSize]
childrenWg.Add(1) childrenWg.Add(1)
go self.split(depth-1, treeSize/self.Branches, subTreeKey, subTreeData, chunkC, errc, childrenWg) go self.split(depth-1, treeSize/self.Branches, subTreeKey, subTreeData, chunkC, errc, childrenWg, swg)
i++ i++
pos += treeSize pos += treeSize
@ -255,6 +261,11 @@ func (self *TreeChunker) split(depth int, treeSize int64, key Key, data SectionR
Key: hash, Key: hash,
Data: chunkData, Data: chunkData,
Size: size, Size: size,
wg: swg,
}
if swg != nil {
swg.Add(1)
} }
} }
// send off new chunk to storage // send off new chunk to storage

View file

@ -2,7 +2,7 @@ package bzz
import ( import (
"bytes" "bytes"
"fmt" // "fmt"
"io" "io"
"testing" "testing"
"time" "time"
@ -37,7 +37,7 @@ func (self *chunkerTester) Split(chunker *TreeChunker, l int) (key Key, input []
input = slice input = slice
key = make([]byte, 32) key = make([]byte, 32)
chunkC := make(chan *Chunk, 1000) chunkC := make(chan *Chunk, 1000)
errC := chunker.Split(key, data, chunkC) errC := chunker.Split(key, data, chunkC, nil)
quitC := make(chan bool) quitC := make(chan bool)
timeout := time.After(600 * time.Second) timeout := time.After(600 * time.Second)
@ -121,19 +121,17 @@ func (self *chunkerTester) Join(chunker *TreeChunker, key Key, c int) SectionRea
func testRandomData(chunker *TreeChunker, tester *chunkerTester, n int, chunks int, t *testing.T) { func testRandomData(chunker *TreeChunker, tester *chunkerTester, n int, chunks int, t *testing.T) {
key, input := tester.Split(chunker, n) key, input := tester.Split(chunker, n)
fmt.Printf("split returned\n")
fmt.Printf("input\n%x\n", input)
tester.checkChunks(t, chunks) tester.checkChunks(t, chunks)
t.Logf("chunks: %v", tester.chunks) time.Sleep(100 * time.Millisecond)
fmt.Printf("chunks: %v", tester.chunks)
reader := tester.Join(chunker, key, 0) reader := tester.Join(chunker, key, 0)
output := make([]byte, n) output := make([]byte, n)
_, err := reader.Read(output) _, err := reader.Read(output)
if err != io.EOF { if err != io.EOF {
t.Errorf("read error %v\n", err) t.Errorf("read error %v\n", err)
} }
t.Logf(" IN: %x\nOUT: %x\n", input, output) // t.Logf(" IN: %x\nOUT: %x\n", input, output)
if !bytes.Equal(output, input) { if !bytes.Equal(output, input) {
t.Errorf("input and output mismatch\n IN: %x\nOUT: %x\n", input, output) t.Errorf("input and output mismatch\n IN: %x\nOUT: %x\n", input, output)
} }
@ -151,7 +149,7 @@ func TestRandomData(t *testing.T) {
testRandomData(chunker, tester, 70, 3, t) testRandomData(chunker, tester, 70, 3, t)
testRandomData(chunker, tester, 179, 5, t) testRandomData(chunker, tester, 179, 5, t)
testRandomData(chunker, tester, 253, 7, t) testRandomData(chunker, tester, 253, 7, t)
t.Logf("chunks %v", tester.chunks) // t.Logf("chunks %v", tester.chunks)
} }
func chunkerAndTester() (chunker *TreeChunker, tester *chunkerTester) { func chunkerAndTester() (chunker *TreeChunker, tester *chunkerTester) {

View file

@ -3,6 +3,7 @@ package bzz
import ( import (
"crypto/rand" "crypto/rand"
"io" "io"
"sync"
"testing" "testing"
) )
@ -26,7 +27,9 @@ func randomChunks(l int64, branches int64, chunkC chan *Chunk) (key Key, errC ch
if err != nil { if err != nil {
panic("no rand") panic("no rand")
} }
errC = chunker.Split(key, NewChunkReaderFromBytes(b), chunkC) wg := &sync.WaitGroup{}
errC = chunker.Split(key, NewChunkReaderFromBytes(b), chunkC, wg)
wg.Wait()
return return
} }

View file

@ -46,7 +46,7 @@ type DPA struct {
lock sync.Mutex lock sync.Mutex
running bool running bool
wg sync.WaitGroup wg *sync.WaitGroup
quitC chan bool quitC chan bool
} }
@ -61,6 +61,7 @@ type Chunk struct {
Key Key // always Key Key // always
C chan bool // to signal data delivery by the dpa C chan bool // to signal data delivery by the dpa
req *requestStatus // req *requestStatus //
wg *sync.WaitGroup
} }
type ChunkStore interface { type ChunkStore interface {
@ -74,9 +75,9 @@ func (self *DPA) Retrieve(key Key) SectionReader {
// we can add subscriptions etc. or timeout here // we can add subscriptions etc. or timeout here
} }
func (self *DPA) Store(data SectionReader) (key Key, err error) { func (self *DPA) Store(data SectionReader, wg *sync.WaitGroup) (key Key, err error) {
key = make([]byte, self.Chunker.KeySize()) key = make([]byte, self.Chunker.KeySize())
errC := self.Chunker.Split(key, data, self.storeC) errC := self.Chunker.Split(key, data, self.storeC, wg)
SPLIT: SPLIT:
for { for {
@ -154,6 +155,9 @@ func (self *DPA) storeLoop() {
for ch := range self.storeC { for ch := range self.storeC {
// go func(chunk *Chunk) { // go func(chunk *Chunk) {
self.ChunkStore.Put(ch) self.ChunkStore.Put(ch)
if ch.wg != nil {
ch.wg.Done()
}
// self.ChunkStore.Put(chunk) // self.ChunkStore.Put(chunk)
// }(ch) // }(ch)
select { select {

View file

@ -6,8 +6,8 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"sync"
"testing" "testing"
// "time"
) )
const testDataSize = 0x1000000 const testDataSize = 0x1000000
@ -33,10 +33,12 @@ func TestDPArandom(t *testing.T) {
} }
dpa.Start() dpa.Start()
reader, slice := testDataReader(testDataSize) reader, slice := testDataReader(testDataSize)
key, err := dpa.Store(reader) wg := &sync.WaitGroup{}
key, err := dpa.Store(reader, wg)
if err != nil { if err != nil {
t.Errorf("Store error: %v", err) t.Errorf("Store error: %v", err)
} }
wg.Wait()
resultReader := dpa.Retrieve(key) resultReader := dpa.Retrieve(key)
resultSlice := make([]byte, len(slice)) resultSlice := make([]byte, len(slice))
n, err := resultReader.ReadAt(resultSlice, 0) n, err := resultReader.ReadAt(resultSlice, 0)
@ -46,9 +48,9 @@ func TestDPArandom(t *testing.T) {
if n != len(slice) { if n != len(slice) {
t.Errorf("Slice size error got %d, expected %d.", n, len(slice)) t.Errorf("Slice size error got %d, expected %d.", n, len(slice))
} }
// if !bytes.Equal(slice, resultSlice) { if !bytes.Equal(slice, resultSlice) {
// t.Errorf("Comparison error.") t.Errorf("Comparison error.")
// } }
ioutil.WriteFile("/tmp/slice.bzz.16M", slice, 0666) ioutil.WriteFile("/tmp/slice.bzz.16M", slice, 0666)
ioutil.WriteFile("/tmp/result.bzz.16M", resultSlice, 0666) ioutil.WriteFile("/tmp/result.bzz.16M", resultSlice, 0666)
localStore.memStore = newMemStore(dbStore) localStore.memStore = newMemStore(dbStore)
@ -89,10 +91,12 @@ func TestDPA_capacity(t *testing.T) {
} }
dpa.Start() dpa.Start()
reader, slice := testDataReader(testDataSize) reader, slice := testDataReader(testDataSize)
key, err := dpa.Store(reader) wg := &sync.WaitGroup{}
key, err := dpa.Store(reader, wg)
if err != nil { if err != nil {
t.Errorf("Store error: %v", err) t.Errorf("Store error: %v", err)
} }
wg.Wait()
resultReader := dpa.Retrieve(key) resultReader := dpa.Retrieve(key)
resultSlice := make([]byte, len(slice)) resultSlice := make([]byte, len(slice))
n, err := resultReader.ReadAt(resultSlice, 0) n, err := resultReader.ReadAt(resultSlice, 0)

View file

@ -98,7 +98,7 @@ func handler(w http.ResponseWriter, r *http.Request, dpa *DPA) {
key, err := dpa.Store(io.NewSectionReader(&sequentialReader{ key, err := dpa.Store(io.NewSectionReader(&sequentialReader{
reader: r.Body, reader: r.Body,
ahead: make(map[int64]chan bool), ahead: make(map[int64]chan bool),
}, 0, r.ContentLength)) }, 0, r.ContentLength), nil)
if err == nil { if err == nil {
fmt.Fprintf(w, "%064x", key) fmt.Fprintf(w, "%064x", key)
dpaLogger.Debugf("Swarm: Object %064x stored", key) dpaLogger.Debugf("Swarm: Object %064x stored", key)

View file

@ -10,7 +10,15 @@ type localStore struct {
// its integrity is checked ? // its integrity is checked ?
func (self *localStore) Put(chunk *Chunk) { func (self *localStore) Put(chunk *Chunk) {
self.memStore.Put(chunk) self.memStore.Put(chunk)
go self.dbStore.Put(chunk) if chunk.wg != nil {
chunk.wg.Add(1)
}
go func() {
self.dbStore.Put(chunk)
if chunk.wg != nil {
chunk.wg.Done()
}
}()
} }
// Get(chunk *Chunk) looks up a chunk in the local stores // Get(chunk *Chunk) looks up a chunk in the local stores