swarm/storage/localstore: replace atomics with mutexes for gcSize and tests

This commit is contained in:
Janos Guljas 2019-01-11 11:23:32 +01:00
parent 25a068a048
commit ca1e24f73e
5 changed files with 60 additions and 27 deletions

View file

@ -17,7 +17,6 @@
package localstore package localstore
import ( import (
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -88,7 +87,7 @@ func (db *DB) collectGarbage() (collectedCount int64, done bool, err error) {
} }
defer unlock() defer unlock()
gcSize := atomic.LoadInt64(&db.gcSize) gcSize := db.getGCSize()
if gcSize-collectedCount <= target { if gcSize-collectedCount <= target {
return true, nil return true, nil
} }
@ -131,7 +130,12 @@ func (db *DB) incGCSize(count int64) {
if count == 0 { if count == 0 {
return return
} }
new := atomic.AddInt64(&db.gcSize, count)
db.gcSizeMu.Lock()
new := db.gcSize + count
db.gcSize = new
db.gcSizeMu.Unlock()
select { select {
case db.writeGCSizeTrigger <- struct{}{}: case db.writeGCSizeTrigger <- struct{}{}:
default: default:
@ -141,6 +145,15 @@ func (db *DB) incGCSize(count int64) {
} }
} }
// getGCSize returns gcSize value by locking it
// with gcSizeMu mutex.
func (db *DB) getGCSize() (count int64) {
db.gcSizeMu.RLock()
count = db.gcSize
db.gcSizeMu.RUnlock()
return count
}
// triggerGarbageCollection signals collectGarbageWorker // triggerGarbageCollection signals collectGarbageWorker
// to call collectGarbage. // to call collectGarbage.
func (db *DB) triggerGarbageCollection() { func (db *DB) triggerGarbageCollection() {
@ -159,7 +172,7 @@ func (db *DB) writeGCSizeWorker() {
for { for {
select { select {
case <-db.writeGCSizeTrigger: case <-db.writeGCSizeTrigger:
err := db.writeGCSize(atomic.LoadInt64(&db.gcSize)) err := db.writeGCSize(db.getGCSize())
if err != nil { if err != nil {
log.Error("localstore write gc size", "err", err) log.Error("localstore write gc size", "err", err)
} }

View file

@ -20,7 +20,6 @@ import (
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"os" "os"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -89,25 +88,18 @@ func testDB_collectGarbageWorker(t *testing.T, db *DB) {
gcTarget := db.gcTarget() gcTarget := db.gcTarget()
var totalCollectedCount int64
for { for {
select { select {
case c := <-testHookCollectGarbageChan: case <-testHookCollectGarbageChan:
totalCollectedCount += c
case <-time.After(10 * time.Second): case <-time.After(10 * time.Second):
t.Error("collect garbage timeout") t.Error("collect garbage timeout")
} }
gcSize := atomic.LoadInt64(&db.gcSize) gcSize := db.getGCSize()
if gcSize == gcTarget { if gcSize == gcTarget {
break break
} }
} }
wantTotalCollectedCount := int64(chunkCount) - gcTarget
if totalCollectedCount != wantTotalCollectedCount {
t.Errorf("total collected chunks %v, want %v", totalCollectedCount, wantTotalCollectedCount)
}
t.Run("pull index count", newItemsCountTest(db.pullIndex, int(gcTarget))) t.Run("pull index count", newItemsCountTest(db.pullIndex, int(gcTarget)))
t.Run("gc index count", newItemsCountTest(db.gcIndex, int(gcTarget))) t.Run("gc index count", newItemsCountTest(db.gcIndex, int(gcTarget)))
@ -200,7 +192,7 @@ func TestDB_collectGarbageWorker_withRequests(t *testing.T) {
case <-time.After(10 * time.Second): case <-time.After(10 * time.Second):
t.Error("collect garbage timeout") t.Error("collect garbage timeout")
} }
gcSize := atomic.LoadInt64(&db.gcSize) gcSize := db.getGCSize()
if gcSize == gcTarget { if gcSize == gcTarget {
break break
} }

View file

@ -21,7 +21,6 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -82,7 +81,10 @@ type DB struct {
gcUncountedHashesIndex shed.Index gcUncountedHashesIndex shed.Index
// number of elements in garbage collection index // number of elements in garbage collection index
// it must be always read by getGCSize and
// set with incGCSize which are locking gcSizeMu
gcSize int64 gcSize int64
gcSizeMu sync.RWMutex
// garbage collection is triggered when gcSize exceeds // garbage collection is triggered when gcSize exceeds
// the capacity value // the capacity value
capacity int64 capacity int64
@ -361,7 +363,7 @@ func New(path string, baseKey []byte, o *Options) (db *DB, err error) {
func (db *DB) Close() (err error) { func (db *DB) Close() (err error) {
close(db.close) close(db.close)
db.updateGCWG.Wait() db.updateGCWG.Wait()
if err := db.writeGCSize(atomic.LoadInt64(&db.gcSize)); err != nil { if err := db.writeGCSize(db.getGCSize()); err != nil {
log.Error("localstore: write gc size", "err", err) log.Error("localstore: write gc size", "err", err)
} }
return db.shed.Close() return db.shed.Close()

View file

@ -25,7 +25,6 @@ import (
"sort" "sort"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -408,7 +407,7 @@ func newIndexGCSizeTest(db *DB) func(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
got := atomic.LoadInt64(&db.gcSize) got := db.getGCSize()
if got != want { if got != want {
t.Errorf("got gc size %v, want %v", got, want) t.Errorf("got gc size %v, want %v", got, want)
} }

View file

@ -20,7 +20,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"sync/atomic" "sync"
"testing" "testing"
"time" "time"
@ -143,8 +143,12 @@ func TestDB_SubscribePull_since(t *testing.T) {
var wantedChunksCount int var wantedChunksCount int
lastTimestamp := time.Now().UTC().UnixNano() lastTimestamp := time.Now().UTC().UnixNano()
var lastTimestampMu sync.RWMutex
defer setNow(func() (t int64) { defer setNow(func() (t int64) {
return atomic.AddInt64(&lastTimestamp, 1) lastTimestampMu.Lock()
defer lastTimestampMu.Unlock()
lastTimestamp++
return lastTimestamp
})() })()
uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) { uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) {
@ -167,9 +171,13 @@ func TestDB_SubscribePull_since(t *testing.T) {
wantedChunksCount++ wantedChunksCount++
} }
lastTimestampMu.RLock()
storeTimestamp := lastTimestamp
lastTimestampMu.RUnlock()
last[bin] = ChunkDescriptor{ last[bin] = ChunkDescriptor{
Address: chunk.Address(), Address: chunk.Address(),
StoreTimestamp: atomic.LoadInt64(&lastTimestamp), StoreTimestamp: storeTimestamp,
} }
} }
return last return last
@ -222,8 +230,12 @@ func TestDB_SubscribePull_until(t *testing.T) {
var wantedChunksCount int var wantedChunksCount int
lastTimestamp := time.Now().UTC().UnixNano() lastTimestamp := time.Now().UTC().UnixNano()
var lastTimestampMu sync.RWMutex
defer setNow(func() (t int64) { defer setNow(func() (t int64) {
return atomic.AddInt64(&lastTimestamp, 1) lastTimestampMu.Lock()
defer lastTimestampMu.Unlock()
lastTimestamp++
return lastTimestamp
})() })()
uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) { uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) {
@ -246,9 +258,13 @@ func TestDB_SubscribePull_until(t *testing.T) {
wantedChunksCount++ wantedChunksCount++
} }
lastTimestampMu.RLock()
storeTimestamp := lastTimestamp
lastTimestampMu.RUnlock()
last[bin] = ChunkDescriptor{ last[bin] = ChunkDescriptor{
Address: chunk.Address(), Address: chunk.Address(),
StoreTimestamp: atomic.LoadInt64(&lastTimestamp), StoreTimestamp: storeTimestamp,
} }
} }
return last return last
@ -302,8 +318,12 @@ func TestDB_SubscribePull_sinceAndUntil(t *testing.T) {
var wantedChunksCount int var wantedChunksCount int
lastTimestamp := time.Now().UTC().UnixNano() lastTimestamp := time.Now().UTC().UnixNano()
var lastTimestampMu sync.RWMutex
defer setNow(func() (t int64) { defer setNow(func() (t int64) {
return atomic.AddInt64(&lastTimestamp, 1) lastTimestampMu.Lock()
defer lastTimestampMu.Unlock()
lastTimestamp++
return lastTimestamp
})() })()
uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) { uploadRandomChunks := func(count int, wanted bool) (last map[uint8]ChunkDescriptor) {
@ -326,9 +346,13 @@ func TestDB_SubscribePull_sinceAndUntil(t *testing.T) {
wantedChunksCount++ wantedChunksCount++
} }
lastTimestampMu.RLock()
storeTimestamp := lastTimestamp
lastTimestampMu.RUnlock()
last[bin] = ChunkDescriptor{ last[bin] = ChunkDescriptor{
Address: chunk.Address(), Address: chunk.Address(),
StoreTimestamp: atomic.LoadInt64(&lastTimestamp), StoreTimestamp: storeTimestamp,
} }
} }
return last return last
@ -410,6 +434,9 @@ func readPullSubscriptionBin(ctx context.Context, bin uint8, ch <-chan ChunkDesc
if !ok { if !ok {
return return
} }
if i+1 > len(addrs[bin]) {
errChan <- fmt.Errorf("got more chunk addresses %v, then expected %v, for bin %v", i+1, len(addrs[bin]), bin)
}
want := addrs[bin][i] want := addrs[bin][i]
var err error var err error
if !bytes.Equal(got.Address, want) { if !bytes.Equal(got.Address, want) {