Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support hash storage #1613

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 23 additions & 110 deletions sessions/sessiondb/redis/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package redis
import (
"crypto/tls"
"errors"
"strings"
"time"

"github.com/kataras/iris/v12/sessions"
Expand Down Expand Up @@ -77,15 +76,15 @@ func DefaultConfig() Config {

// Database the redis back-end session database for the sessions.
type Database struct {
c Config
logger *golog.Logger
driver DatabaseDriver
}

var _ sessions.Database = (*Database)(nil)

// New returns a new redis database.
func New(cfg ...Config) *Database {
c := DefaultConfig()
var driver DatabaseDriver
if len(cfg) > 0 {
c = cfg[0]

Expand Down Expand Up @@ -113,167 +112,81 @@ func New(cfg ...Config) *Database {
c.Driver = Redigo()
}
}

if err := c.Driver.Connect(c); err != nil {
panic(err)
if _, ok := c.Driver.(*RadixDriverHashed); ok {
driver = DatabaseDriverHashed(c)
} else {
driver = DatabaseDriverString(c)
}

db := &Database{c: c}
_, err := db.c.Driver.PingPong()
if err != nil {
if err := driver.Connect(c); err != nil {
panic(err)
}
// runtime.SetFinalizer(db, closeDB)

db := &Database{driver: driver}
return db
}

// Config returns the configuration for the redis server bridge, you can change them.
func (db *Database) Config() *Config {
return &db.c // 6 Aug 2019 - keep that for no breaking change.
return db.driver.Config() // 6 Aug 2019 - keep that for no breaking change.
}

// SetLogger sets the logger once before server ran.
// By default the Iris one is injected.
func (db *Database) SetLogger(logger *golog.Logger) {
db.logger = logger
db.driver.SetLogger(logger)
}

// Acquire receives a session's lifetime from the database,
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
key := db.makeKey(sid, "")
seconds, hasExpiration, found := db.c.Driver.TTL(key)
if !found {
// fmt.Printf("db.Acquire expires: %s. Seconds: %v\n", expires, expires.Seconds())
// not found, create an entry with ttl and return an empty lifetime, session manager will do its job.
if err := db.c.Driver.Set(key, sid, int64(expires.Seconds())); err != nil {
db.logger.Debug(err)
}

return sessions.LifeTime{} // session manager will handle the rest.
}

if !hasExpiration {
return sessions.LifeTime{}
}

return sessions.LifeTime{Time: time.Now().Add(time.Duration(seconds) * time.Second)}
return db.driver.Acquire(sid, expires)
}

// OnUpdateExpiration will re-set the database's session's entry ttl.
// https://redis.io/commands/expire#refreshing-expires
func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) error {
return db.c.Driver.UpdateTTLMany(db.makeKey(sid, ""), int64(newExpires.Seconds()))
return db.driver.OnUpdateExpiration(sid, newExpires)
}

func (db *Database) makeKey(sid, key string) string {
if key == "" {
return db.c.Prefix + sid
}
return db.c.Prefix + sid + db.c.Delim + key
}

// Set sets a key value of a specific session.
// Ignore the "immutable".
func (db *Database) Set(sid string, lifetime *sessions.LifeTime, key string, value interface{}, immutable bool) {
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
if err != nil {
db.logger.Error(err)
return
}

// fmt.Println("database.Set")
// fmt.Printf("lifetime.DurationUntilExpiration(): %s. Seconds: %v\n", lifetime.DurationUntilExpiration(), lifetime.DurationUntilExpiration().Seconds())
if err = db.c.Driver.Set(db.makeKey(sid, key), valueBytes, int64(lifetime.DurationUntilExpiration().Seconds())); err != nil {
db.logger.Debug(err)
}
db.driver.Set(sid, lifetime, key, value, immutable)
return
}

// Get retrieves a session value based on the key.
func (db *Database) Get(sid string, key string) (value interface{}) {
db.get(db.makeKey(sid, key), &value)
return
}

func (db *Database) get(key string, outPtr interface{}) error {
data, err := db.c.Driver.Get(key)
if err != nil {
// not found.
return err
}

if err = sessions.DefaultTranscoder.Unmarshal(data.([]byte), outPtr); err != nil {
db.logger.Debugf("unable to unmarshal value of key: '%s': %v", key, err)
return err
}

return nil
}

func (db *Database) keys(sid string) []string {
keys, err := db.c.Driver.GetKeys(db.makeKey(sid, ""))
if err != nil {
db.logger.Debugf("unable to get all redis keys of session '%s': %v", sid, err)
return nil
}

return keys
return db.driver.Get(sid, key)
}

// Visit loops through all session keys and values.
func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
keys := db.keys(sid)
for _, key := range keys {
var value interface{} // new value each time, we don't know what user will do in "cb".
db.get(key, &value)
key = strings.TrimPrefix(key, db.c.Prefix+sid+db.c.Delim)
cb(key, value)
}
db.driver.Visit(sid, cb)
return
}

// Len returns the length of the session's entries (keys).
func (db *Database) Len(sid string) (n int) {
return len(db.keys(sid))
return db.driver.Len(sid)
}

// Delete removes a session key value based on its key.
func (db *Database) Delete(sid string, key string) (deleted bool) {
err := db.c.Driver.Delete(db.makeKey(sid, key))
if err != nil {
db.logger.Error(err)
}
return err == nil
return db.driver.Delete(sid, key)
}

// Clear removes all session key values but it keeps the session entry.
func (db *Database) Clear(sid string) {
keys := db.keys(sid)
for _, key := range keys {
if err := db.c.Driver.Delete(key); err != nil {
db.logger.Debugf("unable to delete session '%s' value of key: '%s': %v", sid, key, err)
}
}
db.driver.Clear(sid)
}

// Release destroys the session, it clears and removes the session entry,
// session manager will create a new session ID on the next request after this call.
func (db *Database) Release(sid string) {
// clear all $sid-$key.
db.Clear(sid)
// and remove the $sid.
err := db.c.Driver.Delete(db.c.Prefix + sid)
if err != nil {
db.logger.Debugf("Database.Release.Driver.Delete: %s: %v", sid, err)
}
db.driver.Release(sid)
}

// Close terminates the redis connection.
func (db *Database) Close() error {
return closeDB(db)
}

func closeDB(db *Database) error {
return db.c.Driver.CloseConnection()
return db.driver.Close()
}

var (
Expand Down
28 changes: 28 additions & 0 deletions sessions/sessiondb/redis/database_driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package redis

import (
"github.com/kataras/iris/v12/sessions"
)

type DatabaseDriver interface {
sessions.Database

Config() *Config

Connect(c Config) error

Close() error
}

var (
_ DatabaseDriver = (*DatabaseString)(nil)
_ DatabaseDriver = (*DatabaseHashed)(nil)
)

func DatabaseDriverString(conf Config) *DatabaseString {
return &DatabaseString{c: conf}
}

func DatabaseDriverHashed(conf Config) *DatabaseHashed {
return &DatabaseHashed{c: conf}
}
Loading