1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-07 12:07:28 +00:00

Update to 8.2.0 | BoltDB session database, fix file sessiondb, faster, simpler and improvement Session Database API

Former-commit-id: 4034737a65b78a77277e4283fd9289c17f4a452e
This commit is contained in:
kataras
2017-08-07 06:04:35 +03:00
parent 36e6fb37b8
commit 48e352e1df
24 changed files with 1128 additions and 430 deletions

View File

@@ -1,103 +1,87 @@
package redis
import (
"bytes"
"encoding/gob"
"time"
"github.com/kataras/golog"
"github.com/kataras/iris/sessions"
"github.com/kataras/iris/sessions/sessiondb/redis/service"
)
// Database the redis back-end session database for the sessions.
type Database struct {
redis *service.Service
async bool
}
// New returns a new redis database.
func New(cfg ...service.Config) *Database {
return &Database{redis: service.New(cfg...)}
// Note: no need to clean up here, the redis should handle these automatically because of the "SETEX"
// but that expiration doesn't depend on the session, instead it depends on the `MaxAgeSeconds`
// of the redis database configuration.
}
// Config returns the configuration for the redis server bridge, you can change them.
func (d *Database) Config() *service.Config {
return d.redis.Config
func (db *Database) Config() *service.Config {
return db.redis.Config
}
// Async if true passed then it will use different
// go routines to update the redis storage.
func (db *Database) Async(useGoRoutines bool) *Database {
db.async = useGoRoutines
return db
}
// Load loads the values to the underline.
func (d *Database) Load(sid string) (datas map[string]interface{}, expireDate *time.Time) {
values := make(map[string]interface{})
func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
// values := make(map[string]interface{})
if !d.redis.Connected { //yes, check every first time's session for valid redis connection
d.redis.Connect()
_, err := d.redis.PingPong()
if !db.redis.Connected { //yes, check every first time's session for valid redis connection
db.redis.Connect()
_, err := db.redis.PingPong()
if err != nil {
golog.Errorf("redis database error on connect: %v", err)
return
}
}
//fetch the values from this session id and copy-> store them
val, err := d.redis.GetBytes(sid)
if err == nil {
// err removed because of previous TODO
DeserializeBytes(val, &values)
}
datas, _ = values["session-data"].(map[string]interface{})
dbExpireDateValue, exists := values["expire-date"]
if !exists {
return
}
expireDateValue, ok := dbExpireDateValue.(time.Time)
if !ok {
return
}
return datas, &expireDateValue
}
// serialize the values to be stored as strings inside the Redis, we panic at any serialization error here
func serialize(values map[string]interface{}) []byte {
val, err := SerializeBytes(values)
// fetch the values from this session id and copy-> store them
storeMaybe, err := db.redis.Get(sid)
if err != nil {
return nil
golog.Errorf("error while trying to load session values(%s) from redis: %v", sid, err)
return
}
return val
storeDB, ok := storeMaybe.(sessions.RemoteStore)
if !ok {
golog.Errorf(`error while trying to load session values(%s) from redis:
the retrieved value is not a sessions.RemoteStore type, please report that as bug, it should never occur`,
sid)
return
}
return
}
// Update updates the real redis store
func (d *Database) Update(sid string, newValues map[string]interface{}, expireDate *time.Time) {
if len(newValues) == 0 {
go d.redis.Delete(sid)
// Sync syncs the database.
func (db *Database) Sync(p sessions.SyncPayload) {
if db.async {
go db.sync(p)
} else {
datas := map[string]interface{}{"session-data": newValues}
if expireDate != nil {
datas["expire-date"] = *expireDate
}
db.sync(p)
}
}
//set/update all the values
go d.redis.Set(sid, serialize(datas))
func (db *Database) sync(p sessions.SyncPayload) {
if p.Action == sessions.ActionDestroy {
db.redis.Delete(p.SessionID)
return
}
storeB, err := p.Store.Serialize()
if err != nil {
golog.Error("error while encoding the remote session store")
return
}
}
// SerializeBytes serialize the "m" into bytes using the gob encoder and returns the result.
func SerializeBytes(m interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(m)
if err == nil {
return buf.Bytes(), nil
}
return nil, err
}
// DeserializeBytes converts the bytes to a go value and puts that to "m" using the gob decoder.
func DeserializeBytes(b []byte, m interface{}) error {
dec := gob.NewDecoder(bytes.NewBuffer(b))
return dec.Decode(m) //no reference here otherwise doesn't work because of go remote object
db.redis.Set(p.SessionID, storeB)
}

View File

@@ -42,21 +42,21 @@ func (r *Service) CloseConnection() error {
return ErrRedisClosed
}
// Set sets to the redis
// key string, value string, you can use utils.Serialize(&myobject{}) to convert an object to []byte
func (r *Service) Set(key string, value []byte) (err error) { // map[interface{}]interface{}) (err error) {
// Set sets a key-value to the redis store.
// The expiration is setted by the MaxAgeSeconds.
func (r *Service) Set(key string, value interface{}) error {
c := r.pool.Get()
defer c.Close()
if err = c.Err(); err != nil {
return
if c.Err() != nil {
return c.Err()
}
_, err = c.Do("SETEX", r.Config.Prefix+key, r.Config.MaxAgeSeconds, value)
return
_, err := c.Do("SETEX", r.Config.Prefix+key, r.Config.MaxAgeSeconds, value)
return err
}
// Get returns value, err by its key
// you can use utils.Deserialize((.Get("yourkey"),&theobject{})
//returns nil and a filled error if something wrong happens
//returns nil and a filled error if something bad happened.
func (r *Service) Get(key string) (interface{}, error) {
c := r.pool.Get()
defer c.Close()
@@ -75,6 +75,27 @@ func (r *Service) Get(key string) (interface{}, error) {
return redisVal, nil
}
// GetAll returns all redis entries using the "SCAN" command (2.8+).
func (r *Service) GetAll() (interface{}, error) {
c := r.pool.Get()
defer c.Close()
if err := c.Err(); err != nil {
return nil, err
}
redisVal, err := c.Do("SCAN", 0) // 0 -> cursor
if err != nil {
return nil, err
}
if redisVal == nil {
return nil, err
}
return redisVal, nil
}
// GetBytes returns value, err by its key
// you can use utils.Deserialize((.GetBytes("yourkey"),&theobject{})
//returns nil and a filled error if something wrong happens
@@ -97,95 +118,6 @@ func (r *Service) GetBytes(key string) ([]byte, error) {
return redis.Bytes(redisVal, err)
}
// GetString returns value, err by its key
// you can use utils.Deserialize((.GetString("yourkey"),&theobject{})
//returns empty string and a filled error if something wrong happens
func (r *Service) GetString(key string) (string, error) {
redisVal, err := r.Get(key)
if redisVal == nil {
return "", ErrKeyNotFound.Format(key)
}
sVal, err := redis.String(redisVal, err)
if err != nil {
return "", err
}
return sVal, nil
}
// GetInt returns value, err by its key
// you can use utils.Deserialize((.GetInt("yourkey"),&theobject{})
//returns -1 int and a filled error if something wrong happens
func (r *Service) GetInt(key string) (int, error) {
redisVal, err := r.Get(key)
if redisVal == nil {
return -1, ErrKeyNotFound.Format(key)
}
intVal, err := redis.Int(redisVal, err)
if err != nil {
return -1, err
}
return intVal, nil
}
// GetStringMap returns map[string]string, err by its key
//returns nil and a filled error if something wrong happens
func (r *Service) GetStringMap(key string) (map[string]string, error) {
redisVal, err := r.Get(key)
if redisVal == nil {
return nil, ErrKeyNotFound.Format(key)
}
_map, err := redis.StringMap(redisVal, err)
if err != nil {
return nil, err
}
return _map, nil
}
// GetAll returns all keys and their values from a specific key (map[string]string)
// returns a filled error if something bad happened
func (r *Service) GetAll(key string) (map[string]string, error) {
c := r.pool.Get()
defer c.Close()
if err := c.Err(); err != nil {
return nil, err
}
reply, err := c.Do("HGETALL", r.Config.Prefix+key)
if err != nil {
return nil, err
}
if reply == nil {
return nil, ErrKeyNotFound.Format(key)
}
return redis.StringMap(reply, err)
}
// GetAllKeysByPrefix returns all []string keys by a key prefix from the redis
func (r *Service) GetAllKeysByPrefix(prefix string) ([]string, error) {
c := r.pool.Get()
defer c.Close()
if err := c.Err(); err != nil {
return nil, err
}
reply, err := c.Do("KEYS", r.Config.Prefix+prefix)
if err != nil {
return nil, err
}
if reply == nil {
return nil, ErrKeyNotFound.Format(prefix)
}
return redis.Strings(reply, err)
}
// Delete removes redis entry by specific key
func (r *Service) Delete(key string) error {
c := r.pool.Get()