mirror of
https://github.com/gofiber/storage.git
synced 2025-10-08 18:11:23 +08:00
✏ refactor storage
This commit is contained in:
@@ -27,8 +27,6 @@ store := memcache.New()
|
||||
// Initialize custom config
|
||||
store := memcache.New(memcache.Config{
|
||||
Servers: "localhost:11211",
|
||||
Timeout: 100 * time.Millisecond,
|
||||
MaxIdleConns: 10,
|
||||
})
|
||||
```
|
||||
|
||||
@@ -40,19 +38,6 @@ type Config struct {
|
||||
//
|
||||
// Optional. Default is "127.0.0.1:11211"
|
||||
Servers string
|
||||
|
||||
// The socket read/write timeout.
|
||||
//
|
||||
// Optional. Default is 100 * time.Millisecond
|
||||
Timeout time.Duration
|
||||
|
||||
// The maximum number of idle connections that will be maintained per address.
|
||||
//
|
||||
// Consider your expected traffic rates and latency carefully. This should
|
||||
// be set to a number higher than your peak parallel requests.
|
||||
//
|
||||
// Optional. Default is 2
|
||||
MaxIdleConns int
|
||||
}
|
||||
```
|
||||
|
||||
@@ -60,7 +45,5 @@ type Config struct {
|
||||
```go
|
||||
var ConfigDefault = Config{
|
||||
Servers: "localhost:11211",
|
||||
Timeout: 100 * time.Millisecond,
|
||||
MaxIdleConns: 2,
|
||||
}
|
||||
```
|
||||
|
@@ -13,7 +13,7 @@ type Config struct {
|
||||
// The socket read/write timeout.
|
||||
//
|
||||
// Optional. Default is 100 * time.Millisecond
|
||||
Timeout time.Duration
|
||||
timeout time.Duration
|
||||
|
||||
// The maximum number of idle connections that will be maintained per address.
|
||||
//
|
||||
@@ -21,14 +21,14 @@ type Config struct {
|
||||
// be set to a number higher than your peak parallel requests.
|
||||
//
|
||||
// Optional. Default is 2
|
||||
MaxIdleConns int
|
||||
maxIdleConns int
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
Servers: "localhost:11211",
|
||||
Timeout: 100 * time.Millisecond,
|
||||
MaxIdleConns: 2,
|
||||
timeout: 100 * time.Millisecond,
|
||||
maxIdleConns: 2,
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
@@ -45,12 +45,12 @@ func configDefault(config ...Config) Config {
|
||||
if len(cfg.Servers) < 1 {
|
||||
cfg.Servers = ConfigDefault.Servers
|
||||
}
|
||||
if int(cfg.Timeout) == 0 {
|
||||
cfg.Timeout = ConfigDefault.Timeout
|
||||
}
|
||||
if cfg.MaxIdleConns == 0 {
|
||||
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
}
|
||||
// if int(cfg.Timeout) == 0 {
|
||||
// cfg.Timeout = ConfigDefault.Timeout
|
||||
// }
|
||||
// if cfg.MaxIdleConns == 0 {
|
||||
// cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
// }
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -15,6 +16,9 @@ type Storage struct {
|
||||
items *sync.Pool
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
// New creates a new storage
|
||||
func New(config ...Config) *Storage {
|
||||
// Set default config
|
||||
@@ -27,8 +31,8 @@ func New(config ...Config) *Storage {
|
||||
db := mc.New(serverList...)
|
||||
|
||||
// Set options
|
||||
db.Timeout = cfg.Timeout
|
||||
db.MaxIdleConns = cfg.MaxIdleConns
|
||||
db.Timeout = cfg.timeout
|
||||
db.MaxIdleConns = cfg.maxIdleConns
|
||||
|
||||
// Ping database to ensure a connection has been made
|
||||
if err := db.Ping(); err != nil {
|
||||
|
@@ -1,3 +1,105 @@
|
||||
// +build memcache
|
||||
|
||||
package memcache
|
||||
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
}
|
||||
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -12,6 +13,9 @@ type Storage struct {
|
||||
gcInterval time.Duration
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
type entry struct {
|
||||
data []byte
|
||||
expiry int64
|
||||
|
@@ -9,181 +9,104 @@ import (
|
||||
"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) {
|
||||
t.Parallel()
|
||||
|
||||
store := New()
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
err := store.Set(id, value, 0)
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, entry{value, 0}, store.db[id])
|
||||
|
||||
}
|
||||
|
||||
func Test_Memory_SetExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
store := New()
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
expiry := time.Second * 10
|
||||
|
||||
err := store.Set(id, value, expiry)
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
now := time.Now().Unix()
|
||||
fromStore, found := store.db[id]
|
||||
utils.AssertEqual(t, true, found)
|
||||
|
||||
delta := fromStore.expiry - now
|
||||
upperBound := int64(expiry.Seconds())
|
||||
lowerBound := upperBound - 2
|
||||
|
||||
if !(delta <= upperBound && delta > lowerBound) {
|
||||
t.Fatalf("Test_SetExpiry: expiry delta out of bounds (is %d, must be %d<x<=%d)", delta, lowerBound, upperBound)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_Memory_GC(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
store := &Storage{
|
||||
db: make(map[string]entry),
|
||||
gcInterval: time.Millisecond * 10,
|
||||
}
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
expireAt := time.Now().Add(-time.Second).Unix()
|
||||
|
||||
store.db[id] = entry{value, expireAt}
|
||||
|
||||
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)
|
||||
result, err := store.Get(key)
|
||||
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)
|
||||
})
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_Memory_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
store := New()
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db[id] = entry{value, 0}
|
||||
|
||||
err := store.Delete(id)
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
_, found := store.db[id]
|
||||
utils.AssertEqual(t, false, found)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
func Test_Memory_Clear(t *testing.T) {
|
||||
t.Parallel()
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
store := New()
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
store.db[id] = entry{value, 0}
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
err := store.Clear()
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, make(map[string]entry), store.db)
|
||||
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Benchmark_Memory_Set(b *testing.B) {
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
store := New()
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
expiry := time.Duration(0)
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = store.Set(id, value, expiry)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Memory_Get(b *testing.B) {
|
||||
|
||||
store := New()
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db[id] = entry{value, 0}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_, _ = store.Get(id)
|
||||
}
|
||||
})
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -18,6 +19,9 @@ type Storage struct {
|
||||
items *sync.Pool
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
type item struct {
|
||||
ObjectID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
|
||||
Key string `json:"key" bson:"key"`
|
||||
|
@@ -3,12 +3,11 @@
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gofiber/utils"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -27,141 +26,104 @@ func getConfig() Config {
|
||||
}
|
||||
}
|
||||
|
||||
func contains(arr []string, item string) bool {
|
||||
for _, i := range arr {
|
||||
if i == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
}
|
||||
|
||||
func Test_MongoDB_Set_Get(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
key := "example_key"
|
||||
value := []byte("123")
|
||||
|
||||
err := store.Set(key, value, 0)
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
getVal, getErr := store.Get(key)
|
||||
|
||||
utils.AssertEqual(t, nil, getErr)
|
||||
utils.AssertEqual(t, value, getVal, "correctly set and get value")
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_MongoDB_Get_Invalid(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
key := "random_invalid_key"
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
getVal, getErr := store.Get(key)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
utils.AssertEqual(t, true, getErr != nil)
|
||||
utils.AssertEqual(t, true, getVal == nil, "get nil if key not found")
|
||||
}
|
||||
|
||||
func Test_MongoDB_Set_Replace(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
key := "replace_key"
|
||||
value1 := []byte("value1")
|
||||
value2 := []byte("value2")
|
||||
|
||||
setErr1 := store.Set(key, value1, 0)
|
||||
setErr2 := store.Set(key, value2, 0)
|
||||
val, getErr := store.Get(key)
|
||||
|
||||
utils.AssertEqual(t, true, setErr1 == nil)
|
||||
utils.AssertEqual(t, true, setErr2 == nil)
|
||||
utils.AssertEqual(t, true, getErr == nil)
|
||||
|
||||
utils.AssertEqual(t, value2, val, "replace value if key exists")
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_MongoDB_SetExpiry(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
key := "example_key_2"
|
||||
value := []byte("123")
|
||||
|
||||
setErr := store.Set(key, value, 1*time.Nanosecond)
|
||||
utils.AssertEqual(t, true, setErr == nil)
|
||||
|
||||
val, getErr := store.Get(key)
|
||||
utils.AssertEqual(t, true, getErr == nil)
|
||||
utils.AssertEqual(t, true, val == nil, "get nil if key is expire")
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_MongoDB_Delete(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
key := "example_key_3"
|
||||
value := []byte("123")
|
||||
|
||||
err := store.Set(key, value, 0)
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
getVal, getErr := store.Get(key)
|
||||
utils.AssertEqual(t, true, getErr != nil)
|
||||
utils.AssertEqual(t, true, getVal == nil, "correctly delete value")
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_MongoDB_Clear(t *testing.T) {
|
||||
if uri == "" {
|
||||
t.Skip()
|
||||
}
|
||||
store := New(getConfig())
|
||||
defer func() {
|
||||
_ = store.db.Client().Disconnect(context.TODO())
|
||||
}()
|
||||
|
||||
key := "example_key_4"
|
||||
value := []byte("123")
|
||||
|
||||
err := store.Set(key, value, 10)
|
||||
names, _ := store.db.ListCollectionNames(context.TODO(), bson.D{})
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, true, contains(names, colName), "has collection")
|
||||
|
||||
cErr := store.Clear()
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
names2, _ := store.db.ListCollectionNames(context.TODO(), bson.D{})
|
||||
utils.AssertEqual(t, nil, cErr)
|
||||
utils.AssertEqual(t, false, contains(names2, colName), "do not have collection")
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -1,34 +1,56 @@
|
||||
# MySQL
|
||||
|
||||
A MySQL storage driver using `database/sql` and [`go-sql-driver/mysql`](https://github.com/go-sql-driver/mysql).
|
||||
A MySQL storage driver using `database/sql` and [go-sql-driver/mysql](https://github.com/go-sql-driver/mysql).
|
||||
|
||||
### Creation
|
||||
|
||||
To create a new instance of the MySQL store, is is reccomended that you provide a database name, a table name, a username and a password. The database must exist beforehand, but the table will be created if it does not exist.
|
||||
### Table of Contents
|
||||
- [Signatures](#signatures)
|
||||
- [Examples](#examples)
|
||||
- [Config](#config)
|
||||
- [Default Config](#default-config)
|
||||
|
||||
### Signatures
|
||||
```go
|
||||
// import "github.com/gomodule/redigo/redis"
|
||||
func New(config ...Config) Storage
|
||||
```
|
||||
|
||||
### Examples
|
||||
Import the storage package.
|
||||
```go
|
||||
import "github.com/gofiber/storage/mysql"
|
||||
```
|
||||
|
||||
You can use the following possibilities to create a storage:
|
||||
```go
|
||||
// Initialize default config
|
||||
store := mysql.New()
|
||||
|
||||
// Initialize custom config
|
||||
store := mysql.New(mysql.Config{
|
||||
DatabaseName: "myDb",
|
||||
TableName: "thisStore",
|
||||
Username: "user",
|
||||
Password: "yourPasswordHere",
|
||||
GCInterval: 10 * time.Second,
|
||||
Address: "127.0.0.1:3306",
|
||||
TableName: "fiber",
|
||||
DatabaseName: "fiber",
|
||||
DropTable: false,
|
||||
})
|
||||
```
|
||||
|
||||
By default the store will connect to a database on `127.0.0.1:3306`. If you are using multiple MySQL stores in your application, it is strongly advised that you use different table names for each, to avoid data being overwritten or otherwise damaged.
|
||||
### Config
|
||||
```go
|
||||
type Config struct {
|
||||
// Time before deleting expired keys
|
||||
//
|
||||
// Default is 10 * time.Second
|
||||
GCInterval time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
A full list of configuration options and their defaults can be found [in `config.go`](/config.go).
|
||||
|
||||
### Running tests/benchmarks
|
||||
|
||||
Tests and benchmarks for this package require a running MySQL server, and assume you have one at `127.0.0.1:3306`. The following environment variables can be used to configure the tests:
|
||||
|
||||
| Name | Corresponding `Config` option |
|
||||
| ---------------- | ----------------------------- |
|
||||
| `MYSQL_ADDRESS` | `Address` |
|
||||
| `MYSQL_USERNAME` | `Username` |
|
||||
| `MYSQL_PASSWORD` | `Password` |
|
||||
| `MYSQL_DATABASE` | `DatabaseName` |
|
||||
|
||||
If a given environment variable is not set, the default value is used.
|
||||
### Default Config
|
||||
```go
|
||||
var ConfigDefault = Config{
|
||||
GCInterval: 10 * time.Second,
|
||||
Address: "127.0.0.1:3306",
|
||||
TableName: "fiber",
|
||||
DatabaseName: "fiber",
|
||||
DropTable: false,
|
||||
}
|
||||
```
|
||||
|
@@ -48,7 +48,7 @@ type Config struct {
|
||||
// If n < 0, no idle connections are retained.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxIdleConns int
|
||||
maxIdleConns int
|
||||
|
||||
// The maximum number of open connections to the database.
|
||||
//
|
||||
@@ -59,7 +59,7 @@ type Config struct {
|
||||
// If n < 0, then there is no limit on the number of open connections.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxOpenConns int
|
||||
maxOpenConns int
|
||||
|
||||
// The maximum amount of time a connection may be reused.
|
||||
//
|
||||
@@ -68,7 +68,7 @@ type Config struct {
|
||||
// If d < 0, connections are reused forever.
|
||||
//
|
||||
// The default is 1 * time.Second
|
||||
ConnMaxLifetime time.Duration
|
||||
connMaxLifetime time.Duration
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
@@ -78,9 +78,9 @@ var ConfigDefault = Config{
|
||||
TableName: "fiber",
|
||||
DatabaseName: "fiber",
|
||||
DropTable: false,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
maxOpenConns: 100,
|
||||
maxIdleConns: 100,
|
||||
connMaxLifetime: 1 * time.Second,
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
@@ -112,15 +112,15 @@ func configDefault(config ...Config) Config {
|
||||
if cfg.DatabaseName == "" {
|
||||
cfg.DatabaseName = ConfigDefault.DatabaseName
|
||||
}
|
||||
if cfg.MaxOpenConns == 0 {
|
||||
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
}
|
||||
if cfg.MaxIdleConns == 0 {
|
||||
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
}
|
||||
if int(cfg.ConnMaxLifetime) == 0 {
|
||||
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
}
|
||||
// if cfg.MaxOpenConns == 0 {
|
||||
// cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
// }
|
||||
// if cfg.MaxIdleConns == 0 {
|
||||
// cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
// }
|
||||
// if int(cfg.ConnMaxLifetime) == 0 {
|
||||
// cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
// }
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -21,6 +22,9 @@ type Storage struct {
|
||||
sqlGC string
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
var (
|
||||
dropQuery = "DROP TABLE IF EXISTS %s;"
|
||||
initQuery = []string{
|
||||
@@ -41,9 +45,9 @@ func New(config ...Config) *Storage {
|
||||
}
|
||||
|
||||
// Set options
|
||||
db.SetMaxOpenConns(cfg.MaxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.MaxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
||||
db.SetMaxOpenConns(cfg.maxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.maxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.connMaxLifetime)
|
||||
|
||||
// Ping database to ensure a connection has been made
|
||||
if err := db.Ping(); err != nil {
|
||||
|
@@ -29,159 +29,104 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MySQL_Set(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.Set(id, value, 0)
|
||||
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
returnedValue []byte
|
||||
exp int64
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
store.db.QueryRow(store.sqlSelect, id).Scan(&returnedValue, &exp)
|
||||
|
||||
utils.AssertEqual(t, returnedValue, value)
|
||||
utils.AssertEqual(t, exp, int64(0))
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
}
|
||||
|
||||
func Test_MySQL_SetExpiry(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
expiry := time.Second * 10
|
||||
|
||||
store.Set(id, value, expiry)
|
||||
|
||||
now := time.Now().Unix()
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
returnedValue []byte
|
||||
exp int64
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
store.db.QueryRow(store.sqlSelect, id).Scan(&returnedValue, &exp)
|
||||
|
||||
delta := exp - now
|
||||
upperBound := int64(expiry.Seconds())
|
||||
lowerBound := upperBound - 2
|
||||
|
||||
if !(delta <= upperBound && delta > lowerBound) {
|
||||
t.Fatalf("Test_SetExpiry: expiry delta out of bounds (is %d, must be %d<x<=%d)", delta, lowerBound, upperBound)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_MySQL_Get(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db.Exec(store.sqlInsert, id, utils.UnsafeString(value), 0)
|
||||
|
||||
returnedValue, err := store.Get(id)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, value, returnedValue)
|
||||
|
||||
}
|
||||
|
||||
func Test_MySQL_Get_NoRows(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
|
||||
returnedValue, err := store.Get(id)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, 0, len(returnedValue))
|
||||
|
||||
}
|
||||
|
||||
func Test_MySQL_Get_Expired(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db.Exec(store.sqlInsert, id, utils.UnsafeString(value), time.Now().Add(time.Minute*-1).Unix())
|
||||
|
||||
returnedValue, err := store.Get(id)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, 0, len(returnedValue))
|
||||
}
|
||||
|
||||
func Test_MySQL_Delete(t *testing.T) {
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db.Exec(store.sqlInsert, id, utils.UnsafeString(value), 0)
|
||||
|
||||
err := store.Delete(id)
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
row := store.db.QueryRow(store.sqlSelect, id)
|
||||
err = row.Scan()
|
||||
utils.AssertEqual(t, noRows, err.Error())
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_MySQL_Clear(t *testing.T) {
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
store := New(storeConfig)
|
||||
|
||||
id := "hello"
|
||||
value := []byte("Hi there!")
|
||||
|
||||
store.db.Exec(store.sqlInsert, id, utils.UnsafeString(value), 0)
|
||||
|
||||
err := store.Clear()
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
row := store.db.QueryRow(store.sqlSelect, id)
|
||||
err = row.Scan()
|
||||
utils.AssertEqual(t, noRows, err.Error())
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
func Benchmark_MySQL_Set(b *testing.B) {
|
||||
store := New(storeConfig)
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
key := "aaaa"
|
||||
val := []byte("This is a value")
|
||||
|
||||
expiry := time.Second * 60
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
store.Set(key, val, expiry)
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Benchmark_MySQL_Get(b *testing.B) {
|
||||
store := New(storeConfig)
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
key := "aaaa"
|
||||
val := []byte("This is a value")
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
store.Set(key, val, 0)
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
store.Get(key)
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -32,10 +32,6 @@ store := postgres.New(postgres.Config{
|
||||
Database: "fiber",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
Timeout: 30 * time.Second,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
})
|
||||
```
|
||||
|
||||
@@ -82,38 +78,6 @@ type Config struct {
|
||||
//
|
||||
// Optional. Default is false
|
||||
DropTable bool
|
||||
|
||||
// Maximum wait for connection, in seconds. Zero or
|
||||
// n < 0 means wait indefinitely.
|
||||
Timeout time.Duration
|
||||
|
||||
// The maximum number of connections in the idle connection pool.
|
||||
//
|
||||
// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns,
|
||||
// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit.
|
||||
//
|
||||
// If n <= 0, no idle connections are retained.
|
||||
//
|
||||
// The default max idle connections is currently 2. This may change in
|
||||
// a future release.
|
||||
MaxIdleConns int
|
||||
|
||||
// The maximum number of open connections to the database.
|
||||
//
|
||||
// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than
|
||||
// MaxIdleConns, then MaxIdleConns will be reduced to match the new
|
||||
// MaxOpenConns limit.
|
||||
//
|
||||
// If n <= 0, then there is no limit on the number of open connections.
|
||||
// The default is 0 (unlimited).
|
||||
MaxOpenConns int
|
||||
|
||||
// The maximum amount of time a connection may be reused.
|
||||
//
|
||||
// Expired connections may be closed lazily before reuse.
|
||||
//
|
||||
// If d <= 0, connections are reused forever.
|
||||
ConnMaxLifetime time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
@@ -126,9 +90,5 @@ var ConfigDefault = Config{
|
||||
Database: "fiber",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
Timeout: 30 * time.Second,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
}
|
||||
```
|
||||
|
@@ -46,7 +46,7 @@ type Config struct {
|
||||
|
||||
// Maximum wait for connection, in seconds. Zero or
|
||||
// n < 0 means wait indefinitely.
|
||||
Timeout time.Duration
|
||||
timeout time.Duration
|
||||
|
||||
// The maximum number of connections in the idle connection pool.
|
||||
//
|
||||
@@ -57,7 +57,7 @@ type Config struct {
|
||||
//
|
||||
// The default max idle connections is currently 2. This may change in
|
||||
// a future release.
|
||||
MaxIdleConns int
|
||||
maxIdleConns int
|
||||
|
||||
// The maximum number of open connections to the database.
|
||||
//
|
||||
@@ -67,14 +67,14 @@ type Config struct {
|
||||
//
|
||||
// If n <= 0, then there is no limit on the number of open connections.
|
||||
// The default is 0 (unlimited).
|
||||
MaxOpenConns int
|
||||
maxOpenConns int
|
||||
|
||||
// The maximum amount of time a connection may be reused.
|
||||
//
|
||||
// Expired connections may be closed lazily before reuse.
|
||||
//
|
||||
// If d <= 0, connections are reused forever.
|
||||
ConnMaxLifetime time.Duration
|
||||
connMaxLifetime time.Duration
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
@@ -85,10 +85,10 @@ var ConfigDefault = Config{
|
||||
Database: "fiber",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
Timeout: 30 * time.Second,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
timeout: 30 * time.Second,
|
||||
maxOpenConns: 100,
|
||||
maxIdleConns: 100,
|
||||
connMaxLifetime: 1 * time.Second,
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
@@ -120,17 +120,17 @@ func configDefault(config ...Config) Config {
|
||||
if cfg.TableName == "" {
|
||||
cfg.TableName = ConfigDefault.TableName
|
||||
}
|
||||
if int(cfg.Timeout) == 0 {
|
||||
cfg.Timeout = ConfigDefault.Timeout
|
||||
}
|
||||
if cfg.MaxOpenConns == 0 {
|
||||
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
}
|
||||
if cfg.MaxIdleConns == 0 {
|
||||
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
}
|
||||
if int(cfg.ConnMaxLifetime) == 0 {
|
||||
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
}
|
||||
// if int(cfg.Timeout) == 0 {
|
||||
// cfg.Timeout = ConfigDefault.Timeout
|
||||
// }
|
||||
// if cfg.MaxOpenConns == 0 {
|
||||
// cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
// }
|
||||
// if cfg.MaxIdleConns == 0 {
|
||||
// cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
// }
|
||||
// if int(cfg.ConnMaxLifetime) == 0 {
|
||||
// cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
// }
|
||||
return cfg
|
||||
}
|
||||
|
@@ -23,6 +23,9 @@ type Storage struct {
|
||||
sqlGC string
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
var (
|
||||
dropQuery = `DROP TABLE IF EXISTS %s;`
|
||||
initQuery = []string{
|
||||
@@ -47,7 +50,7 @@ func New(config ...Config) *Storage {
|
||||
url.QueryEscape(cfg.Host),
|
||||
cfg.Port,
|
||||
url.QueryEscape(cfg.Database),
|
||||
int64(cfg.Timeout.Seconds()))
|
||||
int64(cfg.timeout.Seconds()))
|
||||
|
||||
// Create db
|
||||
db, err := sql.Open("postgres", dsn)
|
||||
@@ -56,9 +59,9 @@ func New(config ...Config) *Storage {
|
||||
}
|
||||
|
||||
// Set database options
|
||||
db.SetMaxOpenConns(cfg.MaxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.MaxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
||||
db.SetMaxOpenConns(cfg.maxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.maxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.connMaxLifetime)
|
||||
|
||||
// Ping database
|
||||
if err := db.Ping(); err != nil {
|
||||
|
@@ -1,3 +1,118 @@
|
||||
// +build postgres
|
||||
|
||||
package postgres
|
||||
|
||||
var testStore *Storage
|
||||
|
||||
func init() {
|
||||
testConfig := ConfigDefault
|
||||
testConfig.Addr = "127.0.0.1:5432"
|
||||
|
||||
if v := os.Getenv("POSTGRES_ADDR"); v != "" {
|
||||
testConfig.Addr = v
|
||||
}
|
||||
|
||||
testStore = New(testConfig)
|
||||
}
|
||||
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
}
|
||||
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -12,6 +13,9 @@ type Storage struct {
|
||||
db *redis.Client
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
// New creates a new redis storage
|
||||
func New(config ...Config) *Storage {
|
||||
// Set default config
|
||||
@@ -55,13 +59,10 @@ func New(config ...Config) *Storage {
|
||||
// Get value by key
|
||||
func (s *Storage) Get(key string) ([]byte, error) {
|
||||
val, err := s.db.Get(context.Background(), key).Bytes()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
return nil, err
|
||||
if err == redis.Nil {
|
||||
return nil, ErrNotExist
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return val, nil
|
||||
return val, err
|
||||
}
|
||||
|
||||
// Set key with value
|
||||
|
@@ -41,13 +41,12 @@ func Test_Redis_Get(t *testing.T) {
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, val, result)
|
||||
|
||||
result, err = store.Get("doe")
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
@@ -72,7 +71,15 @@ func Test_Redis_Get_Expired(t *testing.T) {
|
||||
)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
@@ -90,7 +97,7 @@ func Test_Redis_Delete(t *testing.T) {
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
@@ -110,10 +117,10 @@ func Test_Redis_Clear(t *testing.T) {
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
@@ -30,9 +30,6 @@ store := sqlite3.New(sqlite3.Config{
|
||||
Database: "./fiber.sqlite3",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
})
|
||||
```
|
||||
|
||||
@@ -56,36 +53,6 @@ type Config struct {
|
||||
|
||||
// When set to true, this will Drop any existing table with the same name
|
||||
DropTable bool
|
||||
|
||||
// The maximum number of connections in the idle connection pool.
|
||||
//
|
||||
// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns,
|
||||
// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit.
|
||||
//
|
||||
// If n < 0, no idle connections are retained.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxIdleConns int
|
||||
|
||||
// The maximum number of open connections to the database.
|
||||
//
|
||||
// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than
|
||||
// MaxIdleConns, then MaxIdleConns will be reduced to match the new
|
||||
// MaxOpenConns limit.
|
||||
//
|
||||
// If n < 0, then there is no limit on the number of open connections.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxOpenConns int
|
||||
|
||||
// The maximum amount of time a connection may be reused.
|
||||
//
|
||||
// Expired connections may be closed lazily before reuse.
|
||||
//
|
||||
// If d < 0, connections are reused forever.
|
||||
//
|
||||
// The default is 1 * time.Second
|
||||
ConnMaxLifetime time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
@@ -96,8 +63,5 @@ var ConfigDefault = Config{
|
||||
Database: "./fiber.sqlite3",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
}
|
||||
```
|
||||
|
@@ -30,7 +30,7 @@ type Config struct {
|
||||
// If n < 0, no idle connections are retained.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxIdleConns int
|
||||
maxIdleConns int
|
||||
|
||||
// The maximum number of open connections to the database.
|
||||
//
|
||||
@@ -41,7 +41,7 @@ type Config struct {
|
||||
// If n < 0, then there is no limit on the number of open connections.
|
||||
//
|
||||
// The default is 100.
|
||||
MaxOpenConns int
|
||||
maxOpenConns int
|
||||
|
||||
// The maximum amount of time a connection may be reused.
|
||||
//
|
||||
@@ -50,7 +50,7 @@ type Config struct {
|
||||
// If d < 0, connections are reused forever.
|
||||
//
|
||||
// The default is 1 * time.Second
|
||||
ConnMaxLifetime time.Duration
|
||||
connMaxLifetime time.Duration
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
@@ -59,9 +59,9 @@ var ConfigDefault = Config{
|
||||
Database: "./fiber.sqlite3",
|
||||
TableName: "fiber",
|
||||
DropTable: false,
|
||||
MaxOpenConns: 100,
|
||||
MaxIdleConns: 100,
|
||||
ConnMaxLifetime: 1 * time.Second,
|
||||
maxOpenConns: 100,
|
||||
maxIdleConns: 100,
|
||||
connMaxLifetime: 1 * time.Second,
|
||||
}
|
||||
|
||||
// Helper function to set default values
|
||||
@@ -84,14 +84,14 @@ func configDefault(config ...Config) Config {
|
||||
if cfg.TableName == "" {
|
||||
cfg.TableName = ConfigDefault.TableName
|
||||
}
|
||||
if cfg.MaxOpenConns == 0 {
|
||||
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
}
|
||||
if cfg.MaxIdleConns == 0 {
|
||||
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
}
|
||||
if int(cfg.ConnMaxLifetime) == 0 {
|
||||
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
}
|
||||
// if cfg.MaxOpenConns == 0 {
|
||||
// cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
|
||||
// }
|
||||
// if cfg.MaxIdleConns == 0 {
|
||||
// cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
|
||||
// }
|
||||
// if int(cfg.ConnMaxLifetime) == 0 {
|
||||
// cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
|
||||
// }
|
||||
return cfg
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package sqlite3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -22,6 +23,9 @@ type Storage struct {
|
||||
sqlGC string
|
||||
}
|
||||
|
||||
// Common storage errors
|
||||
var ErrNotExist = errors.New("key does not exist")
|
||||
|
||||
var (
|
||||
dropQuery = `DROP TABLE IF EXISTS %s;`
|
||||
initQuery = []string{
|
||||
@@ -46,9 +50,9 @@ func New(config ...Config) *Storage {
|
||||
}
|
||||
|
||||
// Set database options
|
||||
db.SetMaxOpenConns(cfg.MaxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.MaxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
||||
db.SetMaxOpenConns(cfg.maxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.maxIdleConns)
|
||||
db.SetConnMaxLifetime(cfg.connMaxLifetime)
|
||||
|
||||
// Ping database
|
||||
if err := db.Ping(); err != nil {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build sqlite3
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
@@ -8,31 +10,113 @@ import (
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func Test_New(t *testing.T) {
|
||||
New()
|
||||
var testStore *Storage
|
||||
|
||||
func init() {
|
||||
testConfig := ConfigDefault
|
||||
testConfig.Database = ":memory:"
|
||||
|
||||
testStore = New(testConfig)
|
||||
}
|
||||
|
||||
func Test_Sqlite_Get_Set(t *testing.T) {
|
||||
s := New(Config{
|
||||
Database: ":memory:",
|
||||
})
|
||||
err := s.Set("fiber-10k-stars?", []byte("yes!"), time.Duration(time.Hour*1))
|
||||
utils.AssertEqual(t, nil, err)
|
||||
func Test_Redis_Set(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
b, err := s.Get("fiber-10k-stars?")
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, []byte("yes!"), b)
|
||||
}
|
||||
|
||||
func Test_Sqlite_Expiration(t *testing.T) {
|
||||
s := New(Config{
|
||||
Database: ":memory:",
|
||||
})
|
||||
func Test_Redis_Get(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := s.Set("fiber-20k-stars?", []byte("yes!"), time.Duration(time.Nanosecond/2))
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
b, err := s.Get("fiber-220k-stars?")
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, true, b == nil)
|
||||
utils.AssertEqual(t, val, result)
|
||||
}
|
||||
|
||||
func Test_Redis_Set_Expiration(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
exp = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
err := store.Set(key, val, exp)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
}
|
||||
|
||||
func Test_Redis_Get_Expired(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Get_NotExist(t *testing.T) {
|
||||
var store = testStore
|
||||
|
||||
result, err := store.Get("notexist")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Delete(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
key = "john"
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set(key, val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Delete(key)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get(key)
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
||||
func Test_Redis_Clear(t *testing.T) {
|
||||
var (
|
||||
store = testStore
|
||||
val = []byte("doe")
|
||||
)
|
||||
|
||||
err := store.Set("john1", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Set("john2", val, 0)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
err = store.Clear()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
result, err := store.Get("john1")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
|
||||
result, err = store.Get("john2")
|
||||
utils.AssertEqual(t, ErrNotExist, err)
|
||||
utils.AssertEqual(t, true, len(result) == 0)
|
||||
}
|
||||
|
Reference in New Issue
Block a user