mirror of
https://github.com/gofiber/storage.git
synced 2025-10-07 09:31:44 +08:00
👷 Improve memory storage
This commit is contained in:
@@ -15,7 +15,7 @@ var ConfigDefault = Config{
|
|||||||
GCInterval: 10 * time.Second,
|
GCInterval: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to set default values
|
// configDefault is a helper function to set default values
|
||||||
func configDefault(config ...Config) Config {
|
func configDefault(config ...Config) Config {
|
||||||
// Return default config if nothing provided
|
// Return default config if nothing provided
|
||||||
if len(config) < 1 {
|
if len(config) < 1 {
|
||||||
|
@@ -43,7 +43,7 @@ func (s *Storage) Get(key string) ([]byte, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.expiry <= time.Now().Unix() && v.expiry != 0 {
|
if v.expiry != 0 && v.expiry <= time.Now().Unix() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,15 +80,11 @@ func (s *Storage) Clear() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) gc() {
|
func (s *Storage) gc() {
|
||||||
tick := time.NewTicker(s.gcInterval)
|
for t := range time.NewTicker(s.gcInterval).C {
|
||||||
for {
|
now := t.Unix()
|
||||||
<-tick.C
|
|
||||||
|
|
||||||
s.mux.Lock()
|
s.mux.Lock()
|
||||||
|
|
||||||
now := time.Now().Unix()
|
|
||||||
for id, v := range s.db {
|
for id, v := range s.db {
|
||||||
if v.expiry < now && v.expiry != 0 {
|
if v.expiry != 0 && v.expiry < now {
|
||||||
delete(s.db, id)
|
delete(s.db, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,20 +7,31 @@ import (
|
|||||||
"github.com/gofiber/utils"
|
"github.com/gofiber/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Test_Memory_Config(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
store := New(Config{})
|
||||||
|
|
||||||
|
utils.AssertEqual(t, ConfigDefault.GCInterval, store.gcInterval)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_Memory_Set(t *testing.T) {
|
func Test_Memory_Set(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
store := New()
|
store := New()
|
||||||
|
|
||||||
id := "hello"
|
id := "hello"
|
||||||
value := []byte("Hi there!")
|
value := []byte("Hi there!")
|
||||||
|
|
||||||
store.Set(id, value, 0)
|
err := store.Set(id, value, 0)
|
||||||
|
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
utils.AssertEqual(t, entry{value, 0}, store.db[id])
|
utils.AssertEqual(t, entry{value, 0}, store.db[id])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Memory_SetExpiry(t *testing.T) {
|
func Test_Memory_SetExpiry(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
store := New()
|
store := New()
|
||||||
|
|
||||||
@@ -28,7 +39,9 @@ func Test_Memory_SetExpiry(t *testing.T) {
|
|||||||
value := []byte("Hi there!")
|
value := []byte("Hi there!")
|
||||||
expiry := time.Second * 10
|
expiry := time.Second * 10
|
||||||
|
|
||||||
store.Set(id, value, expiry)
|
err := store.Set(id, value, expiry)
|
||||||
|
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
fromStore, found := store.db[id]
|
fromStore, found := store.db[id]
|
||||||
@@ -44,45 +57,66 @@ func Test_Memory_SetExpiry(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func Test_Memory_GC(t *testing.T) {
|
func Test_Memory_GC(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
// // New() isn't being used here so the gcInterval can be set low
|
store := &Storage{
|
||||||
// store := &Storage{
|
db: make(map[string]entry),
|
||||||
// DB: make(map[string]entry),
|
gcInterval: time.Millisecond * 10,
|
||||||
// gcInterval: time.Second * 1,
|
}
|
||||||
// }
|
|
||||||
// go store.gc()
|
|
||||||
|
|
||||||
// id := "hello"
|
|
||||||
// value := []byte("Hi there!")
|
|
||||||
|
|
||||||
// expireAt := time.Now().Add(time.Second * 2).Unix()
|
|
||||||
|
|
||||||
// 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.db[id]
|
|
||||||
// utils.AssertEqual(t, false, found)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
func Test_Memory_Get(t *testing.T) {
|
|
||||||
|
|
||||||
store := New()
|
|
||||||
|
|
||||||
id := "hello"
|
id := "hello"
|
||||||
value := []byte("Hi there!")
|
value := []byte("Hi there!")
|
||||||
|
|
||||||
store.db[id] = entry{value, 0}
|
expireAt := time.Now().Add(-time.Second).Unix()
|
||||||
|
|
||||||
returnedValue, err := store.Get(id)
|
store.db[id] = entry{value, expireAt}
|
||||||
utils.AssertEqual(t, nil, err)
|
|
||||||
utils.AssertEqual(t, value, returnedValue)
|
|
||||||
|
|
||||||
|
go store.gc()
|
||||||
|
|
||||||
|
// The purpose of the long delay is to ensure the GC has time to run and delete the value
|
||||||
|
time.Sleep(time.Millisecond * 15)
|
||||||
|
|
||||||
|
store.mux.RLock()
|
||||||
|
_, found := store.db[id]
|
||||||
|
utils.AssertEqual(t, false, found)
|
||||||
|
store.mux.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Memory_Get(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
store := New()
|
||||||
|
|
||||||
|
t.Run("exist", func(t *testing.T) {
|
||||||
|
id := "hello"
|
||||||
|
value := []byte("Hi there!")
|
||||||
|
|
||||||
|
store.db[id] = entry{value, 0}
|
||||||
|
|
||||||
|
returnedValue, err := store.Get(id)
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
utils.AssertEqual(t, value, returnedValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("expired", func(t *testing.T) {
|
||||||
|
expired := "expired"
|
||||||
|
store.db[expired] = entry{[]byte{}, time.Now().Add(-time.Second).Unix()}
|
||||||
|
|
||||||
|
returnedValue, err := store.Get(expired)
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
utils.AssertEqual(t, true, returnedValue == nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non-exist", func(t *testing.T) {
|
||||||
|
returnedValue, err := store.Get("non-exist")
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
utils.AssertEqual(t, true, returnedValue == nil)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Memory_Delete(t *testing.T) {
|
func Test_Memory_Delete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
store := New()
|
store := New()
|
||||||
|
|
||||||
@@ -100,6 +134,7 @@ func Test_Memory_Delete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Memory_Clear(t *testing.T) {
|
func Test_Memory_Clear(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
store := New()
|
store := New()
|
||||||
|
|
||||||
@@ -122,12 +157,14 @@ func Benchmark_Memory_Set(b *testing.B) {
|
|||||||
value := []byte("Hi there!")
|
value := []byte("Hi there!")
|
||||||
expiry := time.Duration(0)
|
expiry := time.Duration(0)
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
store.Set(id, value, expiry)
|
for pb.Next() {
|
||||||
}
|
_ = store.Set(id, value, expiry)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Memory_Get(b *testing.B) {
|
func Benchmark_Memory_Get(b *testing.B) {
|
||||||
@@ -139,9 +176,12 @@ func Benchmark_Memory_Get(b *testing.B) {
|
|||||||
|
|
||||||
store.db[id] = entry{value, 0}
|
store.db[id] = entry{value, 0}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
store.Get(id)
|
for pb.Next() {
|
||||||
}
|
_, _ = store.Get(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user