mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
swarm/storage/localstore: add semaphore for updateGC goroutine
This commit is contained in:
parent
e6bdda7078
commit
b6f5b7a23f
3 changed files with 81 additions and 0 deletions
|
|
@ -41,6 +41,10 @@ var (
|
||||||
ErraddressLockTimeout = errors.New("update lock timeout")
|
ErraddressLockTimeout = errors.New("update lock timeout")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Limit the number of goroutines created by Getters
|
||||||
|
// that call updateGC function. Value 0 sets no limit.
|
||||||
|
var maxParallelUpdateGC = 1000
|
||||||
|
|
||||||
// DB is the local store implementation and holds
|
// DB is the local store implementation and holds
|
||||||
// database related objects.
|
// database related objects.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
|
|
@ -68,6 +72,11 @@ type DB struct {
|
||||||
// number of elements in garbage collection index
|
// number of elements in garbage collection index
|
||||||
gcSize int64
|
gcSize int64
|
||||||
|
|
||||||
|
// a buffered channel acting as a semaphore
|
||||||
|
// to limit the maximal number of goroutines
|
||||||
|
// created by Getters to call updateGC function
|
||||||
|
updateGCSem chan struct{}
|
||||||
|
|
||||||
baseKey []byte
|
baseKey []byte
|
||||||
|
|
||||||
addressLocks sync.Map
|
addressLocks sync.Map
|
||||||
|
|
@ -103,6 +112,9 @@ func New(path string, baseKey []byte, o *Options) (db *DB, err error) {
|
||||||
baseKey: baseKey,
|
baseKey: baseKey,
|
||||||
useRetrievalCompositeIndex: o.UseRetrievalCompositeIndex,
|
useRetrievalCompositeIndex: o.UseRetrievalCompositeIndex,
|
||||||
}
|
}
|
||||||
|
if maxParallelUpdateGC > 0 {
|
||||||
|
db.updateGCSem = make(chan struct{}, maxParallelUpdateGC)
|
||||||
|
}
|
||||||
|
|
||||||
db.shed, err = shed.NewDB(path)
|
db.shed, err = shed.NewDB(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -90,6 +91,64 @@ func TestDB_useRetrievalCompositeIndex(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDB_updateGCSem tests maxParallelUpdateGC limit.
|
||||||
|
// This test temporary sets the limit to a low number,
|
||||||
|
// makes updateGC function execution time longer by
|
||||||
|
// setting a custom testHookUpdateGC function with a sleep
|
||||||
|
// and a count current and maximal number of goroutines.
|
||||||
|
func TestDB_updateGCSem(t *testing.T) {
|
||||||
|
defer func(m int) { maxParallelUpdateGC = m }(maxParallelUpdateGC)
|
||||||
|
maxParallelUpdateGC = 3
|
||||||
|
|
||||||
|
db, cleanupFunc := newTestDB(t, nil)
|
||||||
|
defer cleanupFunc()
|
||||||
|
|
||||||
|
chunk := generateRandomChunk()
|
||||||
|
|
||||||
|
err := db.NewPutter(ModePutUpload).Put(chunk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGCSleep := time.Second
|
||||||
|
var count int
|
||||||
|
var max int
|
||||||
|
var mu sync.Mutex
|
||||||
|
defer setTestHookUpdateGC(func() {
|
||||||
|
mu.Lock()
|
||||||
|
// add to the count of current goroutines
|
||||||
|
count++
|
||||||
|
if count > max {
|
||||||
|
// set maximal detected numbers of goroutines
|
||||||
|
max = count
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// wait for some time to ensure multiple parallel goroutines
|
||||||
|
time.Sleep(updateGCSleep)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
count--
|
||||||
|
mu.Unlock()
|
||||||
|
})()
|
||||||
|
|
||||||
|
getter := db.NewGetter(ModeGetRequest)
|
||||||
|
|
||||||
|
// get more chunks then maxParallelUpdateGC
|
||||||
|
// in time shorter then updateGCSleep
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
_, err = getter.Get(chunk.Address())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if max != maxParallelUpdateGC {
|
||||||
|
t.Errorf("got max %v, want %v", max, maxParallelUpdateGC)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// BenchmarkNew measures the time that New function
|
// BenchmarkNew measures the time that New function
|
||||||
// needs to initialize and count the number of key/value
|
// needs to initialize and count the number of key/value
|
||||||
// pairs in GC index.
|
// pairs in GC index.
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,17 @@ func (db *DB) get(mode ModeGet, addr storage.Address) (out shed.IndexItem, err e
|
||||||
switch mode {
|
switch mode {
|
||||||
// update the access timestamp and gc index
|
// update the access timestamp and gc index
|
||||||
case ModeGetRequest:
|
case ModeGetRequest:
|
||||||
|
if db.updateGCSem != nil {
|
||||||
|
// wait before creating new goroutines
|
||||||
|
// if updateGCSem buffer id full
|
||||||
|
db.updateGCSem <- struct{}{}
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
if db.updateGCSem != nil {
|
||||||
|
// free a spot in updateGCSem buffer
|
||||||
|
// for a new goroutine
|
||||||
|
defer func() { <-db.updateGCSem }()
|
||||||
|
}
|
||||||
err := db.updateGC(out)
|
err := db.updateGC(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("localstore update gc", "err", err)
|
log.Error("localstore update gc", "err", err)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue