🎨 update memory storage

This commit is contained in:
Fenny
2020-10-31 10:16:11 +01:00
parent 7c012ee154
commit c14befe194
8 changed files with 107 additions and 66 deletions

View File

@@ -5,5 +5,5 @@ An in-memory storage driver.
### Creation
```go
store := memoryStore.New()
store := memory.New()
```

21
memory/config.go Normal file
View File

@@ -0,0 +1,21 @@
package memory
import "time"
// Config defines the config for memory storage.
type Config struct {
GCInterval time.Duration
}
// ConfigDefault is the default config
var ConfigDefault = Config{
GCInterval: 10 * time.Second,
}
// Helper function to set default values
func configDefault(cfg Config) Config {
if int(cfg.GCInterval) == 0 {
cfg.GCInterval = ConfigDefault.GCInterval
}
return cfg
}

View File

@@ -1,5 +0,0 @@
module github.com/gofiber/storage/memory
go 1.15
require github.com/gofiber/utils v0.0.10

View File

@@ -1,2 +0,0 @@
github.com/gofiber/utils v0.0.10 h1:3Mr7X7JdCUo7CWf/i5sajSaDmArEDtti8bM1JUVso2U=
github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo=

View File

@@ -1,88 +1,103 @@
package memoryStore
package memory
import (
"sync"
"time"
)
func New() *MemoryStore {
ms := &MemoryStore{
data: make(map[string]dataPoint),
gcInterval: time.Second * 10,
}
go ms.gc()
return ms
}
type MemoryStore struct {
// Storage interface that is implemented by storage providers
type Storage struct {
mux sync.RWMutex
data map[string]dataPoint
db map[string]entry
gcInterval time.Duration
}
type dataPoint struct {
type entry struct {
data []byte
expiry int64
}
func (ms *MemoryStore) Get(id string) ([]byte, error) {
ms.mux.RLock()
v, ok := ms.data[id]
ms.mux.RUnlock()
// New creates a new memory storage
func New(config ...Config) *Storage {
// Set default config
cfg := ConfigDefault
// Override config if provided
if len(config) > 0 {
cfg = configDefault(config[0])
}
// Create storage
store := &Storage{
db: make(map[string]entry),
gcInterval: cfg.GCInterval,
}
// start garbage collector
go store.gc()
return store
}
// Get value by key
func (s *Storage) Get(key string) ([]byte, error) {
s.mux.RLock()
v, ok := s.db[key]
s.mux.RUnlock()
if !ok {
return []byte{}, nil
return nil, nil
}
if v.expiry < time.Now().Unix() && v.expiry != 0 {
ms.Delete(id)
return []byte{}, nil
s.Delete(key)
return nil, nil
}
return v.data, nil
}
func (ms *MemoryStore) Set(id string, val []byte, expiration time.Duration) error {
var expirationTime int64
if expiration != 0 {
expirationTime = time.Now().Add(expiration).Unix()
// Set key with value
func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
var expire int64
if exp != 0 {
expire = time.Now().Add(exp).Unix()
}
ms.mux.Lock()
ms.data[id] = dataPoint{val, expirationTime}
ms.mux.Unlock()
s.mux.Lock()
s.db[key] = entry{val, expire}
s.mux.Unlock()
return nil
}
func (ms *MemoryStore) Delete(id string) error {
ms.mux.Lock()
delete(ms.data, id)
ms.mux.Unlock()
// Delete key by key
func (s *Storage) Delete(key string) error {
s.mux.Lock()
delete(s.db, key)
s.mux.Unlock()
return nil
}
func (ms *MemoryStore) Clear() error {
ms.mux.Lock()
ms.data = make(map[string]dataPoint)
ms.mux.Unlock()
// Clear all keys
func (s *Storage) Clear() error {
s.mux.Lock()
s.db = make(map[string]entry)
s.mux.Unlock()
return nil
}
func (ms *MemoryStore) gc() {
tick := time.NewTicker(ms.gcInterval)
func (s *Storage) gc() {
tick := time.NewTicker(s.gcInterval)
for {
<-tick.C
ms.mux.Lock()
s.mux.Lock()
now := time.Now().Unix()
for id, v := range ms.data {
for id, v := range s.db {
if v.expiry < now && v.expiry != 0 {
delete(ms.data, id)
delete(s.db, id)
}
}
ms.mux.Unlock()
s.mux.Unlock()
}
}

View File

@@ -1,10 +1,10 @@
package memoryStore
package memory
import (
"testing"
"time"
"github.com/gofiber/utils"
"github.com/gofiber/fiber/v2/utils"
)
func Test_Set(t *testing.T) {
@@ -16,7 +16,7 @@ func Test_Set(t *testing.T) {
store.Set(id, value, 0)
utils.AssertEqual(t, dataPoint{value, 0}, store.data[id])
utils.AssertEqual(t, entry{value, 0}, store.db[id])
}
@@ -31,7 +31,7 @@ func Test_SetExpiry(t *testing.T) {
store.Set(id, value, expiry)
now := time.Now().Unix()
fromStore, found := store.data[id]
fromStore, found := store.db[id]
utils.AssertEqual(t, true, found)
delta := fromStore.expiry - now
@@ -47,8 +47,8 @@ func Test_SetExpiry(t *testing.T) {
func Test_GC(t *testing.T) {
// New() isn't being used here so the gcInterval can be set low
store := &MemoryStore{
data: make(map[string]dataPoint),
store := &Storage{
db: make(map[string]entry),
gcInterval: time.Second * 1,
}
go store.gc()
@@ -58,11 +58,11 @@ func Test_GC(t *testing.T) {
expireAt := time.Now().Add(time.Second * 2).Unix()
store.data[id] = dataPoint{value, expireAt}
store.db[id] = entry{value, expireAt}
time.Sleep(time.Second * 4) // The purpose of the long delay is to ensure the GC has time to run and delete the value
_, found := store.data[id]
_, found := store.db[id]
utils.AssertEqual(t, false, found)
}
@@ -74,7 +74,7 @@ func Test_Get(t *testing.T) {
id := "hello"
value := []byte("Hi there!")
store.data[id] = dataPoint{value, 0}
store.db[id] = entry{value, 0}
returnedValue, err := store.Get(id)
utils.AssertEqual(t, nil, err)
@@ -89,12 +89,12 @@ func Test_Delete(t *testing.T) {
id := "hello"
value := []byte("Hi there!")
store.data[id] = dataPoint{value, 0}
store.db[id] = entry{value, 0}
err := store.Delete(id)
utils.AssertEqual(t, nil, err)
_, found := store.data[id]
_, found := store.db[id]
utils.AssertEqual(t, false, found)
}
@@ -106,11 +106,11 @@ func Test_Clear(t *testing.T) {
id := "hello"
value := []byte("Hi there!")
store.data[id] = dataPoint{value, 0}
store.db[id] = entry{value, 0}
err := store.Clear()
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, make(map[string]dataPoint), store.data)
utils.AssertEqual(t, make(map[string]entry), store.db)
}
@@ -137,7 +137,7 @@ func Benchmark_Get(b *testing.B) {
id := "hello"
value := []byte("Hi there!")
store.data[id] = dataPoint{value, 0}
store.db[id] = entry{value, 0}
b.ResetTimer()