✏ refactor storage

This commit is contained in:
Fenny
2020-11-04 20:49:53 +01:00
parent b29989b82e
commit a6058cffb3
22 changed files with 705 additions and 614 deletions

View File

@@ -26,9 +26,7 @@ store := memcache.New()
// Initialize custom config // Initialize custom config
store := memcache.New(memcache.Config{ store := memcache.New(memcache.Config{
Servers: "localhost:11211", Servers: "localhost:11211",
Timeout: 100 * time.Millisecond,
MaxIdleConns: 10,
}) })
``` ```
@@ -40,19 +38,6 @@ type Config struct {
// //
// Optional. Default is "127.0.0.1:11211" // Optional. Default is "127.0.0.1:11211"
Servers string 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 ```go
var ConfigDefault = Config{ var ConfigDefault = Config{
Servers: "localhost:11211", Servers: "localhost:11211",
Timeout: 100 * time.Millisecond,
MaxIdleConns: 2,
} }
``` ```

View File

@@ -13,7 +13,7 @@ type Config struct {
// The socket read/write timeout. // The socket read/write timeout.
// //
// Optional. Default is 100 * time.Millisecond // Optional. Default is 100 * time.Millisecond
Timeout time.Duration timeout time.Duration
// The maximum number of idle connections that will be maintained per address. // 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. // be set to a number higher than your peak parallel requests.
// //
// Optional. Default is 2 // Optional. Default is 2
MaxIdleConns int maxIdleConns int
} }
// ConfigDefault is the default config // ConfigDefault is the default config
var ConfigDefault = Config{ var ConfigDefault = Config{
Servers: "localhost:11211", Servers: "localhost:11211",
Timeout: 100 * time.Millisecond, timeout: 100 * time.Millisecond,
MaxIdleConns: 2, maxIdleConns: 2,
} }
// Helper function to set default values // Helper function to set default values
@@ -45,12 +45,12 @@ func configDefault(config ...Config) Config {
if len(cfg.Servers) < 1 { if len(cfg.Servers) < 1 {
cfg.Servers = ConfigDefault.Servers cfg.Servers = ConfigDefault.Servers
} }
if int(cfg.Timeout) == 0 { // if int(cfg.Timeout) == 0 {
cfg.Timeout = ConfigDefault.Timeout // cfg.Timeout = ConfigDefault.Timeout
} // }
if cfg.MaxIdleConns == 0 { // if cfg.MaxIdleConns == 0 {
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns // cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
} // }
return cfg return cfg
} }

View File

@@ -1,6 +1,7 @@
package memcache package memcache
import ( import (
"errors"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -15,6 +16,9 @@ type Storage struct {
items *sync.Pool items *sync.Pool
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
// New creates a new storage // New creates a new storage
func New(config ...Config) *Storage { func New(config ...Config) *Storage {
// Set default config // Set default config
@@ -27,8 +31,8 @@ func New(config ...Config) *Storage {
db := mc.New(serverList...) db := mc.New(serverList...)
// Set options // Set options
db.Timeout = cfg.Timeout db.Timeout = cfg.timeout
db.MaxIdleConns = cfg.MaxIdleConns db.MaxIdleConns = cfg.maxIdleConns
// Ping database to ensure a connection has been made // Ping database to ensure a connection has been made
if err := db.Ping(); err != nil { if err := db.Ping(); err != nil {

View File

@@ -1,3 +1,105 @@
// +build memcache // +build memcache
package 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)
}

View File

@@ -1,6 +1,7 @@
package memory package memory
import ( import (
"errors"
"sync" "sync"
"time" "time"
) )
@@ -12,6 +13,9 @@ type Storage struct {
gcInterval time.Duration gcInterval time.Duration
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
type entry struct { type entry struct {
data []byte data []byte
expiry int64 expiry int64

View File

@@ -9,181 +9,104 @@ import (
"github.com/gofiber/utils" "github.com/gofiber/utils"
) )
func Test_Memory_Config(t *testing.T) { func Test_Redis_Set(t *testing.T) {
t.Parallel() var (
store = testStore
store := New(Config{}) key = "john"
val = []byte("doe")
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)
err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, entry{value, 0}, store.db[id])
} }
func Test_Memory_SetExpiry(t *testing.T) { func Test_Redis_Get(t *testing.T) {
t.Parallel() var (
store = testStore
store := New() key = "john"
val = []byte("doe")
id := "hello" )
value := []byte("Hi there!")
expiry := time.Second * 10
err := store.Set(id, value, expiry)
err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
now := time.Now().Unix() result, err := store.Get(key)
fromStore, found := store.db[id] utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, true, found) utils.AssertEqual(t, val, result)
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) { func Test_Redis_Set_Expiration(t *testing.T) {
t.Parallel() var (
store = testStore
key = "john"
val = []byte("doe")
exp = 500 * time.Millisecond
)
store := &Storage{ err := store.Set(key, val, exp)
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)
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) {
t.Parallel()
store := New()
id := "hello"
value := []byte("Hi there!")
store.db[id] = entry{value, 0}
err := store.Delete(id)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
_, found := store.db[id] time.Sleep(1 * time.Second)
utils.AssertEqual(t, false, found)
} }
func Test_Memory_Clear(t *testing.T) { func Test_Redis_Get_Expired(t *testing.T) {
t.Parallel() 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" func Test_Redis_Get_NotExist(t *testing.T) {
value := []byte("Hi there!") 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, 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" err = store.Set("john2", val, 0)
value := []byte("Hi there!") utils.AssertEqual(t, nil, err)
expiry := time.Duration(0)
b.ReportAllocs() err = store.Clear()
b.ResetTimer() utils.AssertEqual(t, nil, err)
b.RunParallel(func(pb *testing.PB) { result, err := store.Get("john1")
for pb.Next() { utils.AssertEqual(t, ErrNotExist, err)
_ = store.Set(id, value, expiry) utils.AssertEqual(t, true, len(result) == 0)
}
}) result, err = store.Get("john2")
} utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, len(result) == 0)
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)
}
})
} }

View File

@@ -2,6 +2,7 @@ package mongodb
import ( import (
"context" "context"
"errors"
"sync" "sync"
"time" "time"
@@ -18,6 +19,9 @@ type Storage struct {
items *sync.Pool items *sync.Pool
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
type item struct { type item struct {
ObjectID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` ObjectID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Key string `json:"key" bson:"key"` Key string `json:"key" bson:"key"`

View File

@@ -3,12 +3,11 @@
package mongodb package mongodb
import ( import (
"context"
"github.com/gofiber/utils"
"go.mongodb.org/mongo-driver/bson"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/gofiber/utils"
) )
const ( const (
@@ -27,141 +26,104 @@ func getConfig() Config {
} }
} }
func contains(arr []string, item string) bool { func Test_Redis_Set(t *testing.T) {
for _, i := range arr { var (
if i == item { store = testStore
return true key = "john"
} val = []byte("doe")
} )
return false
err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err)
} }
func Test_MongoDB_Set_Get(t *testing.T) { func Test_Redis_Get(t *testing.T) {
if uri == "" { var (
t.Skip() store = testStore
} key = "john"
store := New(getConfig()) val = []byte("doe")
defer func() { )
_ = store.db.Client().Disconnect(context.TODO())
}()
key := "example_key" err := store.Set(key, val, 0)
value := []byte("123")
err := store.Set(key, value, 0)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
getVal, getErr := store.Get(key) result, err := store.Get(key)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, nil, getErr) utils.AssertEqual(t, val, result)
utils.AssertEqual(t, value, getVal, "correctly set and get value")
} }
func Test_MongoDB_Get_Invalid(t *testing.T) { func Test_Redis_Set_Expiration(t *testing.T) {
if uri == "" { var (
t.Skip() store = testStore
} key = "john"
store := New(getConfig()) val = []byte("doe")
defer func() { exp = 500 * time.Millisecond
_ = store.db.Client().Disconnect(context.TODO()) )
}()
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) { func Test_Redis_Get_Expired(t *testing.T) {
if uri == "" { var (
t.Skip() store = testStore
} key = "john"
store := New(getConfig()) )
defer func() {
_ = store.db.Client().Disconnect(context.TODO())
}()
key := "replace_key" result, err := store.Get(key)
value1 := []byte("value1") utils.AssertEqual(t, ErrNotExist, err)
value2 := []byte("value2") utils.AssertEqual(t, true, len(result) == 0)
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")
} }
func Test_MongoDB_SetExpiry(t *testing.T) { func Test_Redis_Get_NotExist(t *testing.T) {
if uri == "" { var store = testStore
t.Skip()
}
store := New(getConfig())
defer func() {
_ = store.db.Client().Disconnect(context.TODO())
}()
key := "example_key_2" result, err := store.Get("notexist")
value := []byte("123") utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, len(result) == 0)
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")
} }
func Test_MongoDB_Delete(t *testing.T) { func Test_Redis_Delete(t *testing.T) {
if uri == "" { var (
t.Skip() store = testStore
} key = "john"
store := New(getConfig()) val = []byte("doe")
defer func() { )
_ = store.db.Client().Disconnect(context.TODO())
}()
key := "example_key_3" err := store.Set(key, val, 0)
value := []byte("123")
err := store.Set(key, value, 0)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
err = store.Delete(key) err = store.Delete(key)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
getVal, getErr := store.Get(key) result, err := store.Get(key)
utils.AssertEqual(t, true, getErr != nil) utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, getVal == nil, "correctly delete value") utils.AssertEqual(t, true, len(result) == 0)
} }
func Test_MongoDB_Clear(t *testing.T) { func Test_Redis_Clear(t *testing.T) {
if uri == "" { var (
t.Skip() store = testStore
} val = []byte("doe")
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{})
err := store.Set("john1", val, 0)
utils.AssertEqual(t, nil, err) 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{}) err = store.Clear()
utils.AssertEqual(t, nil, cErr) utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, false, contains(names2, colName), "do not have collection")
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)
} }

View File

@@ -1,34 +1,56 @@
# MySQL # 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 ### Table of Contents
- [Signatures](#signatures)
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. - [Examples](#examples)
- [Config](#config)
- [Default Config](#default-config)
### Signatures
```go ```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{ store := mysql.New(mysql.Config{
DatabaseName: "myDb", GCInterval: 10 * time.Second,
TableName: "thisStore", Address: "127.0.0.1:3306",
Username: "user", TableName: "fiber",
Password: "yourPasswordHere", 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). ### Default Config
```go
### Running tests/benchmarks var ConfigDefault = Config{
GCInterval: 10 * time.Second,
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: Address: "127.0.0.1:3306",
TableName: "fiber",
| Name | Corresponding `Config` option | DatabaseName: "fiber",
| ---------------- | ----------------------------- | DropTable: false,
| `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.

View File

@@ -48,7 +48,7 @@ type Config struct {
// If n < 0, no idle connections are retained. // If n < 0, no idle connections are retained.
// //
// The default is 100. // The default is 100.
MaxIdleConns int maxIdleConns int
// The maximum number of open connections to the database. // 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. // If n < 0, then there is no limit on the number of open connections.
// //
// The default is 100. // The default is 100.
MaxOpenConns int maxOpenConns int
// The maximum amount of time a connection may be reused. // The maximum amount of time a connection may be reused.
// //
@@ -68,7 +68,7 @@ type Config struct {
// If d < 0, connections are reused forever. // If d < 0, connections are reused forever.
// //
// The default is 1 * time.Second // The default is 1 * time.Second
ConnMaxLifetime time.Duration connMaxLifetime time.Duration
} }
// ConfigDefault is the default config // ConfigDefault is the default config
@@ -78,9 +78,9 @@ var ConfigDefault = Config{
TableName: "fiber", TableName: "fiber",
DatabaseName: "fiber", DatabaseName: "fiber",
DropTable: false, DropTable: false,
MaxOpenConns: 100, maxOpenConns: 100,
MaxIdleConns: 100, maxIdleConns: 100,
ConnMaxLifetime: 1 * time.Second, connMaxLifetime: 1 * time.Second,
} }
// Helper function to set default values // Helper function to set default values
@@ -112,15 +112,15 @@ func configDefault(config ...Config) Config {
if cfg.DatabaseName == "" { if cfg.DatabaseName == "" {
cfg.DatabaseName = ConfigDefault.DatabaseName cfg.DatabaseName = ConfigDefault.DatabaseName
} }
if cfg.MaxOpenConns == 0 { // if cfg.MaxOpenConns == 0 {
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns // cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
} // }
if cfg.MaxIdleConns == 0 { // if cfg.MaxIdleConns == 0 {
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns // cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
} // }
if int(cfg.ConnMaxLifetime) == 0 { // if int(cfg.ConnMaxLifetime) == 0 {
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime // cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
} // }
return cfg return cfg
} }

View File

@@ -2,6 +2,7 @@ package mysql
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"time" "time"
@@ -21,6 +22,9 @@ type Storage struct {
sqlGC string sqlGC string
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
var ( var (
dropQuery = "DROP TABLE IF EXISTS %s;" dropQuery = "DROP TABLE IF EXISTS %s;"
initQuery = []string{ initQuery = []string{
@@ -41,9 +45,9 @@ func New(config ...Config) *Storage {
} }
// Set options // Set options
db.SetMaxOpenConns(cfg.MaxOpenConns) db.SetMaxOpenConns(cfg.maxOpenConns)
db.SetMaxIdleConns(cfg.MaxIdleConns) db.SetMaxIdleConns(cfg.maxIdleConns)
db.SetConnMaxLifetime(cfg.ConnMaxLifetime) db.SetConnMaxLifetime(cfg.connMaxLifetime)
// Ping database to ensure a connection has been made // Ping database to ensure a connection has been made
if err := db.Ping(); err != nil { if err := db.Ping(); err != nil {

View File

@@ -29,159 +29,104 @@ func init() {
} }
} }
func Test_MySQL_Set(t *testing.T) { func Test_Redis_Set(t *testing.T) {
store := New(storeConfig)
id := "hello"
value := []byte("Hi there!")
store.Set(id, value, 0)
var ( var (
returnedValue []byte store = testStore
exp int64 key = "john"
val = []byte("doe")
) )
store.db.QueryRow(store.sqlSelect, id).Scan(&returnedValue, &exp) err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, returnedValue, value)
utils.AssertEqual(t, exp, int64(0))
} }
func Test_MySQL_SetExpiry(t *testing.T) { func Test_Redis_Get(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()
var ( var (
returnedValue []byte store = testStore
exp int64 key = "john"
val = []byte("doe")
) )
store.db.QueryRow(store.sqlSelect, id).Scan(&returnedValue, &exp)
delta := exp - now err := store.Set(key, val, 0)
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)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
row := store.db.QueryRow(store.sqlSelect, id) result, err := store.Get(key)
err = row.Scan() utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, noRows, err.Error()) 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) err := store.Set(key, val, exp)
id := "hello"
value := []byte("Hi there!")
store.db.Exec(store.sqlInsert, id, utils.UnsafeString(value), 0)
err := store.Clear()
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
row := store.db.QueryRow(store.sqlSelect, id) time.Sleep(1 * time.Second)
err = row.Scan()
utils.AssertEqual(t, noRows, err.Error())
} }
func Benchmark_MySQL_Set(b *testing.B) { func Test_Redis_Get_Expired(t *testing.T) {
store := New(storeConfig) var (
store = testStore
key := "aaaa" key = "john"
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 Benchmark_MySQL_Get(b *testing.B) { func Test_Redis_Get_NotExist(t *testing.T) {
store := New(storeConfig) var store = testStore
key := "aaaa"
val := []byte("This is a value")
store.Set(key, val, 0)
b.ResetTimer()
for n := 0; n < b.N; n++ {
store.Get(key)
}
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)
} }

View File

@@ -32,10 +32,6 @@ store := postgres.New(postgres.Config{
Database: "fiber", Database: "fiber",
TableName: "fiber", TableName: "fiber",
DropTable: false, 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 // Optional. Default is false
DropTable bool 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", Database: "fiber",
TableName: "fiber", TableName: "fiber",
DropTable: false, DropTable: false,
Timeout: 30 * time.Second,
MaxOpenConns: 100,
MaxIdleConns: 100,
ConnMaxLifetime: 1 * time.Second,
} }
``` ```

View File

@@ -46,7 +46,7 @@ type Config struct {
// Maximum wait for connection, in seconds. Zero or // Maximum wait for connection, in seconds. Zero or
// n < 0 means wait indefinitely. // n < 0 means wait indefinitely.
Timeout time.Duration timeout time.Duration
// The maximum number of connections in the idle connection pool. // 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 // The default max idle connections is currently 2. This may change in
// a future release. // a future release.
MaxIdleConns int maxIdleConns int
// The maximum number of open connections to the database. // 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. // If n <= 0, then there is no limit on the number of open connections.
// The default is 0 (unlimited). // The default is 0 (unlimited).
MaxOpenConns int maxOpenConns int
// The maximum amount of time a connection may be reused. // The maximum amount of time a connection may be reused.
// //
// Expired connections may be closed lazily before reuse. // Expired connections may be closed lazily before reuse.
// //
// If d <= 0, connections are reused forever. // If d <= 0, connections are reused forever.
ConnMaxLifetime time.Duration connMaxLifetime time.Duration
} }
// ConfigDefault is the default config // ConfigDefault is the default config
@@ -85,10 +85,10 @@ var ConfigDefault = Config{
Database: "fiber", Database: "fiber",
TableName: "fiber", TableName: "fiber",
DropTable: false, DropTable: false,
Timeout: 30 * time.Second, timeout: 30 * time.Second,
MaxOpenConns: 100, maxOpenConns: 100,
MaxIdleConns: 100, maxIdleConns: 100,
ConnMaxLifetime: 1 * time.Second, connMaxLifetime: 1 * time.Second,
} }
// Helper function to set default values // Helper function to set default values
@@ -120,17 +120,17 @@ func configDefault(config ...Config) Config {
if cfg.TableName == "" { if cfg.TableName == "" {
cfg.TableName = ConfigDefault.TableName cfg.TableName = ConfigDefault.TableName
} }
if int(cfg.Timeout) == 0 { // if int(cfg.Timeout) == 0 {
cfg.Timeout = ConfigDefault.Timeout // cfg.Timeout = ConfigDefault.Timeout
} // }
if cfg.MaxOpenConns == 0 { // if cfg.MaxOpenConns == 0 {
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns // cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
} // }
if cfg.MaxIdleConns == 0 { // if cfg.MaxIdleConns == 0 {
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns // cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
} // }
if int(cfg.ConnMaxLifetime) == 0 { // if int(cfg.ConnMaxLifetime) == 0 {
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime // cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
} // }
return cfg return cfg
} }

View File

@@ -23,6 +23,9 @@ type Storage struct {
sqlGC string sqlGC string
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
var ( var (
dropQuery = `DROP TABLE IF EXISTS %s;` dropQuery = `DROP TABLE IF EXISTS %s;`
initQuery = []string{ initQuery = []string{
@@ -47,7 +50,7 @@ func New(config ...Config) *Storage {
url.QueryEscape(cfg.Host), url.QueryEscape(cfg.Host),
cfg.Port, cfg.Port,
url.QueryEscape(cfg.Database), url.QueryEscape(cfg.Database),
int64(cfg.Timeout.Seconds())) int64(cfg.timeout.Seconds()))
// Create db // Create db
db, err := sql.Open("postgres", dsn) db, err := sql.Open("postgres", dsn)
@@ -56,9 +59,9 @@ func New(config ...Config) *Storage {
} }
// Set database options // Set database options
db.SetMaxOpenConns(cfg.MaxOpenConns) db.SetMaxOpenConns(cfg.maxOpenConns)
db.SetMaxIdleConns(cfg.MaxIdleConns) db.SetMaxIdleConns(cfg.maxIdleConns)
db.SetConnMaxLifetime(cfg.ConnMaxLifetime) db.SetConnMaxLifetime(cfg.connMaxLifetime)
// Ping database // Ping database
if err := db.Ping(); err != nil { if err := db.Ping(); err != nil {

View File

@@ -1,3 +1,118 @@
// +build postgres // +build postgres
package 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)
}

View File

@@ -2,6 +2,7 @@ package redis
import ( import (
"context" "context"
"errors"
"time" "time"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
@@ -12,6 +13,9 @@ type Storage struct {
db *redis.Client db *redis.Client
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
// New creates a new redis storage // New creates a new redis storage
func New(config ...Config) *Storage { func New(config ...Config) *Storage {
// Set default config // Set default config
@@ -55,13 +59,10 @@ func New(config ...Config) *Storage {
// Get value by key // Get value by key
func (s *Storage) Get(key string) ([]byte, error) { func (s *Storage) Get(key string) ([]byte, error) {
val, err := s.db.Get(context.Background(), key).Bytes() val, err := s.db.Get(context.Background(), key).Bytes()
if err != nil { if err == redis.Nil {
if err != redis.Nil { return nil, ErrNotExist
return nil, err
}
return nil, nil
} }
return val, nil return val, err
} }
// Set key with value // Set key with value

View File

@@ -41,13 +41,12 @@ func Test_Redis_Get(t *testing.T) {
val = []byte("doe") val = []byte("doe")
) )
err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err)
result, err := store.Get(key) result, err := store.Get(key)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, val, result) 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) { 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) 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) utils.AssertEqual(t, true, len(result) == 0)
} }
@@ -90,7 +97,7 @@ func Test_Redis_Delete(t *testing.T) {
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
result, err := store.Get(key) result, err := store.Get(key)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, len(result) == 0) utils.AssertEqual(t, true, len(result) == 0)
} }
@@ -110,10 +117,10 @@ func Test_Redis_Clear(t *testing.T) {
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
result, err := store.Get("john1") result, err := store.Get("john1")
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, len(result) == 0) utils.AssertEqual(t, true, len(result) == 0)
result, err = store.Get("john2") result, err = store.Get("john2")
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, ErrNotExist, err)
utils.AssertEqual(t, true, len(result) == 0) utils.AssertEqual(t, true, len(result) == 0)
} }

View File

@@ -30,9 +30,6 @@ store := sqlite3.New(sqlite3.Config{
Database: "./fiber.sqlite3", Database: "./fiber.sqlite3",
TableName: "fiber", TableName: "fiber",
DropTable: false, 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 // When set to true, this will Drop any existing table with the same name
DropTable bool 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", Database: "./fiber.sqlite3",
TableName: "fiber", TableName: "fiber",
DropTable: false, DropTable: false,
MaxOpenConns: 100,
MaxIdleConns: 100,
ConnMaxLifetime: 1 * time.Second,
} }
``` ```

View File

@@ -30,7 +30,7 @@ type Config struct {
// If n < 0, no idle connections are retained. // If n < 0, no idle connections are retained.
// //
// The default is 100. // The default is 100.
MaxIdleConns int maxIdleConns int
// The maximum number of open connections to the database. // 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. // If n < 0, then there is no limit on the number of open connections.
// //
// The default is 100. // The default is 100.
MaxOpenConns int maxOpenConns int
// The maximum amount of time a connection may be reused. // The maximum amount of time a connection may be reused.
// //
@@ -50,7 +50,7 @@ type Config struct {
// If d < 0, connections are reused forever. // If d < 0, connections are reused forever.
// //
// The default is 1 * time.Second // The default is 1 * time.Second
ConnMaxLifetime time.Duration connMaxLifetime time.Duration
} }
// ConfigDefault is the default config // ConfigDefault is the default config
@@ -59,9 +59,9 @@ var ConfigDefault = Config{
Database: "./fiber.sqlite3", Database: "./fiber.sqlite3",
TableName: "fiber", TableName: "fiber",
DropTable: false, DropTable: false,
MaxOpenConns: 100, maxOpenConns: 100,
MaxIdleConns: 100, maxIdleConns: 100,
ConnMaxLifetime: 1 * time.Second, connMaxLifetime: 1 * time.Second,
} }
// Helper function to set default values // Helper function to set default values
@@ -84,14 +84,14 @@ func configDefault(config ...Config) Config {
if cfg.TableName == "" { if cfg.TableName == "" {
cfg.TableName = ConfigDefault.TableName cfg.TableName = ConfigDefault.TableName
} }
if cfg.MaxOpenConns == 0 { // if cfg.MaxOpenConns == 0 {
cfg.MaxOpenConns = ConfigDefault.MaxOpenConns // cfg.MaxOpenConns = ConfigDefault.MaxOpenConns
} // }
if cfg.MaxIdleConns == 0 { // if cfg.MaxIdleConns == 0 {
cfg.MaxIdleConns = ConfigDefault.MaxIdleConns // cfg.MaxIdleConns = ConfigDefault.MaxIdleConns
} // }
if int(cfg.ConnMaxLifetime) == 0 { // if int(cfg.ConnMaxLifetime) == 0 {
cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime // cfg.ConnMaxLifetime = ConfigDefault.ConnMaxLifetime
} // }
return cfg return cfg
} }

View File

@@ -2,6 +2,7 @@ package sqlite3
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"time" "time"
@@ -22,6 +23,9 @@ type Storage struct {
sqlGC string sqlGC string
} }
// Common storage errors
var ErrNotExist = errors.New("key does not exist")
var ( var (
dropQuery = `DROP TABLE IF EXISTS %s;` dropQuery = `DROP TABLE IF EXISTS %s;`
initQuery = []string{ initQuery = []string{
@@ -46,9 +50,9 @@ func New(config ...Config) *Storage {
} }
// Set database options // Set database options
db.SetMaxOpenConns(cfg.MaxOpenConns) db.SetMaxOpenConns(cfg.maxOpenConns)
db.SetMaxIdleConns(cfg.MaxIdleConns) db.SetMaxIdleConns(cfg.maxIdleConns)
db.SetConnMaxLifetime(cfg.ConnMaxLifetime) db.SetConnMaxLifetime(cfg.connMaxLifetime)
// Ping database // Ping database
if err := db.Ping(); err != nil { if err := db.Ping(); err != nil {

View File

@@ -1,3 +1,5 @@
// +build sqlite3
package sqlite3 package sqlite3
import ( import (
@@ -8,31 +10,113 @@ import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
func Test_New(t *testing.T) { var testStore *Storage
New()
func init() {
testConfig := ConfigDefault
testConfig.Database = ":memory:"
testStore = New(testConfig)
} }
func Test_Sqlite_Get_Set(t *testing.T) { func Test_Redis_Set(t *testing.T) {
s := New(Config{ var (
Database: ":memory:", store = testStore
}) key = "john"
err := s.Set("fiber-10k-stars?", []byte("yes!"), time.Duration(time.Hour*1)) val = []byte("doe")
utils.AssertEqual(t, nil, err) )
b, err := s.Get("fiber-10k-stars?") err := store.Set(key, val, 0)
utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, []byte("yes!"), b)
} }
func Test_Sqlite_Expiration(t *testing.T) { func Test_Redis_Get(t *testing.T) {
s := New(Config{ var (
Database: ":memory:", 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) 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, 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)
} }