Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
prefix = "_sc_"
)

var mutexList = make(map[string]*sync.Mutex)
var mutexList = make(map[string]*sync.RWMutex)

// Collection describes a collection of key value pairs
type Collection struct {
Expand All @@ -28,7 +28,7 @@ type Collection struct {
// New return a instance of collection
func New(name string) (*Collection, error) {
if len(name) <= 0 {
return &Collection{}, errors.New("Collection name can not be empty!")
return &Collection{}, errors.New("Collection name can not be empty")
}
//make file path correct
dir := prefix + filepath.Clean(name)
Expand All @@ -42,10 +42,11 @@ func New(name string) (*Collection, error) {
//Put store a new key with value in the collection
func (c *Collection) Put(key string, value interface{}) error {
if len(key) <= 0 {
return errors.New("Key can not be empty!")
return errors.New("Key can not be empty")
}
path := filepath.Join(c.dir, key+extension)
m := c.getMutex(path)
// need get read lock and write lock
m := c.getPathMutex(path)
m.Lock()
defer m.Unlock()
file, err := os.Create(path)
Expand All @@ -60,14 +61,14 @@ func (c *Collection) Put(key string, value interface{}) error {
//Get retrieve a value from collection by key
func (c *Collection) Get(key string, value interface{}) error {
if len(key) <= 0 {
return errors.New("Key can not be empty!")
return errors.New("Key can not be empty")
}
path := filepath.Join(c.dir, key+extension)
m := c.getMutex(path)
m.Lock()
defer m.Unlock()
m := c.getPathMutex(path)
m.RLock()
defer m.RUnlock()
if !c.Has(key) {
return fmt.Errorf("Key %s does not exist!", key)
return fmt.Errorf("Key %s does not exist", key)
}
file, err := os.Open(path)
defer file.Close()
Expand All @@ -81,16 +82,16 @@ func (c *Collection) Get(key string, value interface{}) error {
//Remove delete a key from collection
func (c *Collection) Remove(key string) error {
if len(key) <= 0 {
return errors.New("Key can not be empty!")
return errors.New("Key can not be empty")
}
path := filepath.Join(c.dir, key+extension)
m := c.getMutex(path)
m := c.getPathMutex(path)
m.Lock()
defer m.Unlock()
if c.Has(key) {
return os.Remove(path)
}
return fmt.Errorf("Key %s does not exist!", key)
return fmt.Errorf("Key %s does not exist", key)
}

//Flush delete a collection with its value
Expand Down Expand Up @@ -135,14 +136,16 @@ func (c *Collection) TotalItem() int {
return len(list)
}

//populate a package level mutex list
// populate a package level mutex list
// with key of full path of an item
func (c *Collection) getMutex(path string) *sync.Mutex {
c.mutex.Lock()
defer c.mutex.Unlock()
func (c *Collection) getPathMutex(path string) *sync.RWMutex {
m, ok := mutexList[path]
if !ok {
m = &sync.Mutex{}
c.mutex.Lock()
defer c.mutex.Unlock()
if m == nil {
m = &sync.RWMutex{}
}
mutexList[path] = m
}
return m
Expand Down
27 changes: 24 additions & 3 deletions snapshot_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package snapshot

import (
"sync"
"testing"
"time"
)
Expand Down Expand Up @@ -76,15 +77,35 @@ func TestCollection_Flush(t *testing.T) {
func BenchmarkCollection_Put(b *testing.B) {
john := user{"John Doe", "[email protected]", 9898787, time.Now()}
for n := 0; n < b.N; n++ {
userCollection.Put("john", &john)
go userCollection.Put("john", &john)
}
}

func BenchmarkCollection_Get(b *testing.B) {
john := user{}
for n := 0; n < b.N; n++ {
userCollection.Get("john", &john)
}
go userCollection.Get("john", &john)
}
}

func BenchmarkCollection_Concurency(b *testing.B) {
group := &sync.WaitGroup{}
group.Add(2)
go func() {
for i := 0; i < b.N; i++ {
john := user{"John Doe", "[email protected]", 9898787, time.Now()}
userCollection.Put("john", john)
}
group.Done()
}()
go func() {
for i := 0; i < b.N; i++ {
john := user{}
userCollection.Get("john", john)
}
group.Done()
}()
group.Wait()
}

func BenchmarkCollection_Has(b *testing.B) {
Expand Down