swarm/storage: fix process leak in dpa

This commit is contained in:
zelig 2016-07-14 13:33:56 +02:00
parent 801e57f14f
commit 5097da7704
5 changed files with 47 additions and 37 deletions

View file

@ -48,7 +48,7 @@ func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader {
} }
func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) { func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key storage.Key, err error) {
return self.dpa.Store(data, size, wg) return self.dpa.Store(data, size, wg, nil)
} }
type ErrResolve error type ErrResolve error
@ -107,13 +107,13 @@ func (self *Api) parseAndResolve(uri string, nameresolver bool) (contentHash sto
func (self *Api) Put(content, contentType string) (string, error) { func (self *Api) Put(content, contentType string) (string, error) {
r := strings.NewReader(content) r := strings.NewReader(content)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
key, err := self.dpa.Store(r, int64(len(content)), wg) key, err := self.dpa.Store(r, int64(len(content)), wg, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType)
r = strings.NewReader(manifest) r = strings.NewReader(manifest)
key, err = self.dpa.Store(r, int64(len(manifest)), wg) key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -100,7 +100,7 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) {
stat, _ := f.Stat() stat, _ := f.Stat()
var hash storage.Key var hash storage.Key
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
hash, err = self.api.dpa.Store(f, stat.Size(), wg) hash, err = self.api.dpa.Store(f, stat.Size(), wg, nil)
if hash != nil { if hash != nil {
list[i].Hash = hash.String() list[i].Hash = hash.String()
} }

View file

@ -199,7 +199,7 @@ func (self *manifestTrie) recalcAndStore() error {
sr := bytes.NewReader(manifest) sr := bytes.NewReader(manifest)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg) key, err2 := self.dpa.Store(sr, int64(len(manifest)), wg, nil)
wg.Wait() wg.Wait()
self.hash = key self.hash = key
return err2 return err2

View file

@ -27,6 +27,8 @@ const (
retrieveChanCapacity = 100 retrieveChanCapacity = 100
singletonSwarmDbCapacity = 50000 singletonSwarmDbCapacity = 50000
singletonSwarmCacheCapacity = 500 singletonSwarmCacheCapacity = 500
maxStoreProcesses = 100
maxRetrieveProcesses = 100
) )
var ( var (
@ -79,8 +81,8 @@ func (self *DPA) Retrieve(key Key) LazySectionReader {
// Public API. Main entry point for document storage directly. Used by the // Public API. Main entry point for document storage directly. Used by the
// FS-aware API and httpaccess // FS-aware API and httpaccess
func (self *DPA) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key Key, err error) { func (self *DPA) Store(data io.Reader, size int64, swg *sync.WaitGroup, wwg *sync.WaitGroup) (key Key, err error) {
return self.Chunker.Split(data, size, self.storeC, nil, wg) return self.Chunker.Split(data, size, self.storeC, swg, wwg)
} }
func (self *DPA) Start() { func (self *DPA) Start() {
@ -90,6 +92,8 @@ func (self *DPA) Start() {
return return
} }
self.running = true self.running = true
self.retrieveC = make(chan *Chunk, retrieveChanCapacity)
self.storeC = make(chan *Chunk, storeChanCapacity)
self.quitC = make(chan bool) self.quitC = make(chan bool)
self.storeLoop() self.storeLoop()
self.retrieveLoop() self.retrieveLoop()
@ -108,13 +112,14 @@ func (self *DPA) Stop() {
// retrieveLoop dispatches the parallel chunk retrieval requests received on the // retrieveLoop dispatches the parallel chunk retrieval requests received on the
// retrieve channel to its ChunkStore (NetStore or LocalStore) // retrieve channel to its ChunkStore (NetStore or LocalStore)
func (self *DPA) retrieveLoop() { func (self *DPA) retrieveLoop() {
self.retrieveC = make(chan *Chunk, retrieveChanCapacity) for i:=0; i< maxRetrieveProcesses; i++ {
go self.retrieveWorker()
}
glog.V(logger.Detail).Infof("[BZZ] dpa: retrieve loop spawning %v workers", maxRetrieveProcesses)
}
go func() { func (self *DPA) retrieveWorker() {
RETRIEVE: for chunk := range self.retrieveC {
for ch := range self.retrieveC {
go func(chunk *Chunk) {
glog.V(logger.Detail).Infof("[BZZ] dpa: retrieve loop : chunk %v", chunk.Key.Log()) glog.V(logger.Detail).Infof("[BZZ] dpa: retrieve loop : chunk %v", chunk.Key.Log())
storedChunk, err := self.Get(chunk.Key) storedChunk, err := self.Get(chunk.Key)
if err == notFound { if err == notFound {
@ -126,37 +131,39 @@ func (self *DPA) retrieveLoop() {
chunk.Size = storedChunk.Size chunk.Size = storedChunk.Size
} }
close(chunk.C) close(chunk.C)
}(ch)
select { select {
case <-self.quitC: case <-self.quitC:
break RETRIEVE return
default: default:
}
} }
} }
}()
// storeLoop dispatches the parallel chunk store request processors
// received on the store channel to its ChunkStore (NetStore or LocalStore)
func (self *DPA) storeLoop() {
for i:=0; i< maxStoreProcesses; i++ {
go self.storeWorker()
}
glog.V(logger.Detail).Infof("[BZZ] dpa: store spawning %v workers", maxStoreProcesses)
} }
// storeLoop dispatches the parallel chunk store requests received on the func (self *DPA) storeWorker() {
// store channel to its ChunkStore (NetStore or LocalStore)
func (self *DPA) storeLoop() { for chunk := range self.storeC {
self.storeC = make(chan *Chunk)
go func() {
STORE:
for ch := range self.storeC {
go func(chunk *Chunk) {
self.Put(chunk) self.Put(chunk)
if chunk.wg != nil { if chunk.wg != nil {
glog.V(logger.Detail).Infof("[BZZ] dpa: store loop %v", chunk.Key.Log()) glog.V(logger.Detail).Infof("[BZZ] dpa: store processor %v", chunk.Key.Log())
chunk.wg.Done() chunk.wg.Done()
} }
}(ch)
select { select {
case <-self.quitC: case <-self.quitC:
break STORE return
default: default:
} }
} }
}()
} }
// DpaChunkStore implements the ChunkStore interface, // DpaChunkStore implements the ChunkStore interface,

View file

@ -14,10 +14,10 @@ const testDataSize = 0x1000000
func TestDPArandom(t *testing.T) { func TestDPArandom(t *testing.T) {
os.RemoveAll("/tmp/bzz") os.RemoveAll("/tmp/bzz")
dbStore, err := NewDbStore("/tmp/bzz", MakeHashFunc(defaultHash), defaultDbCapacity, defaultRadius) dbStore, err := NewDbStore("/tmp/bzz", MakeHashFunc(defaultHash), defaultDbCapacity, defaultRadius)
dbStore.setCapacity(50000)
if err != nil { if err != nil {
t.Errorf("DB error: %v", err) t.Errorf("DB error: %v", err)
} }
dbStore.setCapacity(50000)
memStore := NewMemStore(dbStore, defaultCacheCapacity) memStore := NewMemStore(dbStore, defaultCacheCapacity)
localStore := &LocalStore{ localStore := &LocalStore{
memStore, memStore,
@ -29,9 +29,12 @@ func TestDPArandom(t *testing.T) {
ChunkStore: localStore, ChunkStore: localStore,
} }
dpa.Start() dpa.Start()
defer dpa.Stop()
defer os.RemoveAll("/tmp/bzz")
reader, slice := testDataReaderAndSlice(testDataSize) reader, slice := testDataReaderAndSlice(testDataSize)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
key, err := dpa.Store(reader, testDataSize, wg) key, err := dpa.Store(reader, testDataSize, wg, nil)
if err != nil { if err != nil {
t.Errorf("Store error: %v", err) t.Errorf("Store error: %v", err)
} }
@ -87,7 +90,7 @@ func TestDPA_capacity(t *testing.T) {
dpa.Start() dpa.Start()
reader, slice := testDataReaderAndSlice(testDataSize) reader, slice := testDataReaderAndSlice(testDataSize)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
key, err := dpa.Store(reader, testDataSize, wg) key, err := dpa.Store(reader, testDataSize, wg, nil)
if err != nil { if err != nil {
t.Errorf("Store error: %v", err) t.Errorf("Store error: %v", err)
} }