mirror of
https://github.com/gofiber/storage.git
synced 2025-11-03 10:50:58 +08:00
🎨 update memory storage
This commit is contained in:
@@ -5,5 +5,5 @@ An in-memory storage driver.
|
||||
### Creation
|
||||
|
||||
```go
|
||||
store := memoryStore.New()
|
||||
store := memory.New()
|
||||
```
|
||||
|
||||
21
memory/config.go
Normal file
21
memory/config.go
Normal 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
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
module github.com/gofiber/storage/memory
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/gofiber/utils v0.0.10
|
||||
@@ -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=
|
||||
103
memory/memory.go
103
memory/memory.go
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user