Skip to content

Commit

Permalink
fix #1553
Browse files Browse the repository at this point in the history
Former-commit-id: 5cb64a4121a664d5066312d385ebd36bb05151ce
  • Loading branch information
kataras committed Jul 19, 2020
1 parent a1991a0 commit c181061
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 94 deletions.
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ Other Improvements:

![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)

- Fix [#1553](https://github.com/kataras/iris/issues/1553).

- New `DirOptions.PushTargets` and `PushTargetsRegexp` to push index' assets to the client without additional requests. Inspirated by issue [#1562](https://github.com/kataras/iris/issues/1562). Example matching all `.js, .css and .ico` files (recursively):

```go
Expand Down
25 changes: 17 additions & 8 deletions sessions/sessiondb/redis/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package redis
import (
"crypto/tls"
"errors"
"strings"
"time"

"github.com/kataras/iris/v12/sessions"
Expand Down Expand Up @@ -133,11 +134,12 @@ func (db *Database) Config() *Config {
// 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 {
seconds, hasExpiration, found := db.c.Driver.TTL(sid)
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(sid, sid, int64(expires.Seconds())); err != nil {
if err := db.c.Driver.Set(key, sid, int64(expires.Seconds())); err != nil {
golog.Debug(err)
}

Expand All @@ -154,11 +156,14 @@ func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime
// 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(sid, int64(newExpires.Seconds()))
return db.c.Driver.UpdateTTLMany(db.makeKey(sid, ""), int64(newExpires.Seconds()))
}

func (db *Database) makeKey(sid, key string) string {
return sid + db.c.Delim + key
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.
Expand All @@ -183,20 +188,23 @@ func (db *Database) Get(sid string, key string) (value interface{}) {
return
}

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

if err = sessions.DefaultTranscoder.Unmarshal(data.([]byte), outPtr); err != nil {
golog.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(sid)
keys, err := db.c.Driver.GetKeys(db.makeKey(sid, ""))
if err != nil {
golog.Debugf("unable to get all redis keys of session '%s': %v", sid, err)
return nil
Expand All @@ -211,6 +219,7 @@ func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
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)
}
}
Expand Down Expand Up @@ -245,7 +254,7 @@ func (db *Database) Release(sid string) {
// clear all $sid-$key.
db.Clear(sid)
// and remove the $sid.
err := db.c.Driver.Delete(sid)
err := db.c.Driver.Delete(db.c.Prefix + sid)
if err != nil {
golog.Debugf("Database.Release.Driver.Delete: %s: %v", sid, err)
}
Expand Down
60 changes: 22 additions & 38 deletions sessions/sessiondb/redis/driver_radix.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,30 +149,24 @@ func (r *RadixDriver) CloseConnection() error {
// Set sets a key-value to the redis store.
// The expiration is setted by the secondsLifetime.
func (r *RadixDriver) Set(key string, value interface{}, secondsLifetime int64) error {
// fmt.Printf("%#+v. %T. %s\n", value, value, value)

// if vB, ok := value.([]byte); ok && secondsLifetime <= 0 {
// return r.pool.Do(radix.Cmd(nil, "MSET", r.Config.Prefix+key, string(vB)))
// }

var cmd radix.CmdAction
// if has expiration, then use the "EX" to delete the key automatically.
if secondsLifetime > 0 {
cmd = radix.FlatCmd(nil, "SETEX", r.Config.Prefix+key, secondsLifetime, value)
cmd = radix.FlatCmd(nil, "SETEX", key, secondsLifetime, value)
} else {
cmd = radix.FlatCmd(nil, "SET", r.Config.Prefix+key, value) // MSET same performance...
cmd = radix.FlatCmd(nil, "SET", key, value) // MSET same performance...
}

return r.pool.Do(cmd)
}

// Get returns value, err by its key
// returns nil and a filled error if something bad happened.
func (r *RadixDriver) Get(key string) (interface{}, error) {
func (r *RadixDriver) Get(key string /* full key */) (interface{}, error) {
var redisVal interface{}
mn := radix.MaybeNil{Rcv: &redisVal}

err := r.pool.Do(radix.Cmd(&mn, "GET", r.Config.Prefix+key))
err := r.pool.Do(radix.Cmd(&mn, "GET", key))
if err != nil {
return nil, err
}
Expand All @@ -186,7 +180,7 @@ func (r *RadixDriver) Get(key string) (interface{}, error) {
// Read more at: https://redis.io/commands/ttl
func (r *RadixDriver) TTL(key string) (seconds int64, hasExpiration bool, found bool) {
var redisVal interface{}
err := r.pool.Do(radix.Cmd(&redisVal, "TTL", r.Config.Prefix+key))
err := r.pool.Do(radix.Cmd(&redisVal, "TTL", key))
if err != nil {
return -2, false, false
}
Expand All @@ -198,9 +192,9 @@ func (r *RadixDriver) TTL(key string) (seconds int64, hasExpiration bool, found
return
}

func (r *RadixDriver) updateTTLConn(key string, newSecondsLifeTime int64) error {
func (r *RadixDriver) updateTTLConn(key string /* full key */, newSecondsLifeTime int64) error {
var reply int
err := r.pool.Do(radix.FlatCmd(&reply, "EXPIRE", r.Config.Prefix+key, newSecondsLifeTime))
err := r.pool.Do(radix.FlatCmd(&reply, "EXPIRE", key, newSecondsLifeTime))
if err != nil {
return err
}
Expand Down Expand Up @@ -229,8 +223,8 @@ func (r *RadixDriver) UpdateTTL(key string, newSecondsLifeTime int64) error {
// UpdateTTLMany like `UpdateTTL` but for all keys starting with that "prefix",
// it is a bit faster operation if you need to update all sessions keys (although it can be even faster if we used hash but this will limit other features),
// look the `sessions/Database#OnUpdateExpiration` for example.
func (r *RadixDriver) UpdateTTLMany(prefix string, newSecondsLifeTime int64) error {
keys, err := r.getKeys("0", prefix)
func (r *RadixDriver) UpdateTTLMany(prefix string /* prefix is the sid */, newSecondsLifeTime int64) error {
keys, err := r.getKeys("0", prefix, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -284,21 +278,26 @@ func (s *scanResult) UnmarshalRESP(br *bufio.Reader) error {
return (resp2.Any{I: &s.keys}).UnmarshalRESP(br)
}

func (r *RadixDriver) getKeys(cursor, prefix string) ([]string, error) {
func (r *RadixDriver) getKeys(cursor, prefix string, includeSID bool) ([]string, error) {
var res scanResult
err := r.pool.Do(radix.Cmd(&res, "SCAN", cursor, "MATCH", r.Config.Prefix+prefix+"*", "COUNT", "300000"))

if !includeSID {
prefix += r.Config.Delim // delim can be used for fast matching of only keys.
}
pattern := prefix + "*"

err := r.pool.Do(radix.Cmd(&res, "SCAN", cursor, "MATCH", pattern, "COUNT", "300000"))
if err != nil {
return nil, err
}

if len(res.keys) <= 1 {
if len(res.keys) == 0 {
return nil, nil
}

keys := res.keys[1:] // the first one is always the session id itself.

keys := res.keys[0:]
if res.cur != "0" {
moreKeys, err := r.getKeys(res.cur, prefix)
moreKeys, err := r.getKeys(res.cur, prefix, includeSID)
if err != nil {
return nil, err
}
Expand All @@ -312,26 +311,11 @@ func (r *RadixDriver) getKeys(cursor, prefix string) ([]string, error) {
// GetKeys returns all redis keys using the "SCAN" with MATCH command.
// Read more at: https://redis.io/commands/scan#the-match-option.
func (r *RadixDriver) GetKeys(prefix string) ([]string, error) {
return r.getKeys("0", prefix)
return r.getKeys("0", prefix, false)
}

// // GetBytes returns bytes representation of a value based on given "key".
// func (r *Service) GetBytes(key string) ([]byte, error) {
// var redisVal []byte
// mn := radix.MaybeNil{Rcv: &redisVal}
// err := r.pool.Do(radix.Cmd(&mn, "GET", r.Config.Prefix+key))
// if err != nil {
// return nil, err
// }
// if mn.Nil {
// return nil, ErrKeyNotFound.Format(key)
// }

// return redisVal, nil
// }

// Delete removes redis entry by specific key
func (r *RadixDriver) Delete(key string) error {
err := r.pool.Do(radix.Cmd(nil, "DEL", r.Config.Prefix+key))
err := r.pool.Do(radix.Cmd(nil, "DEL", key))
return err
}
43 changes: 24 additions & 19 deletions sessions/sessiondb/redis/driver_redigo.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ func (r *RedigoDriver) Set(key string, value interface{}, secondsLifetime int64)

// if has expiration, then use the "EX" to delete the key automatically.
if secondsLifetime > 0 {
_, err = c.Do("SETEX", r.Config.Prefix+key, secondsLifetime, value)
_, err = c.Do("SETEX", key, secondsLifetime, value)
} else {
_, err = c.Do("SET", r.Config.Prefix+key, value)
_, err = c.Do("SET", key, value)
}

return
Expand All @@ -78,7 +78,7 @@ func (r *RedigoDriver) Get(key string) (interface{}, error) {
return nil, err
}

redisVal, err := c.Do("GET", r.Config.Prefix+key)
redisVal, err := c.Do("GET", key)
if err != nil {
return nil, err
}
Expand All @@ -93,7 +93,7 @@ func (r *RedigoDriver) Get(key string) (interface{}, error) {
func (r *RedigoDriver) TTL(key string) (seconds int64, hasExpiration bool, found bool) {
c := r.pool.Get()
defer c.Close()
redisVal, err := c.Do("TTL", r.Config.Prefix+key)
redisVal, err := c.Do("TTL", key)
if err != nil {
return -2, false, false
}
Expand All @@ -106,7 +106,7 @@ func (r *RedigoDriver) TTL(key string) (seconds int64, hasExpiration bool, found
}

func (r *RedigoDriver) updateTTLConn(c redis.Conn, key string, newSecondsLifeTime int64) error {
reply, err := c.Do("EXPIRE", r.Config.Prefix+key, newSecondsLifeTime)
reply, err := c.Do("EXPIRE", key, newSecondsLifeTime)
if err != nil {
return err
}
Expand Down Expand Up @@ -143,14 +143,14 @@ func (r *RedigoDriver) UpdateTTL(key string, newSecondsLifeTime int64) error {
// UpdateTTLMany like `UpdateTTL` but for all keys starting with that "prefix",
// it is a bit faster operation if you need to update all sessions keys (although it can be even faster if we used hash but this will limit other features),
// look the `sessions/Database#OnUpdateExpiration` for example.
func (r *RedigoDriver) UpdateTTLMany(prefix string, newSecondsLifeTime int64) error {
func (r *RedigoDriver) UpdateTTLMany(prefix string /* prefix is the sid */, newSecondsLifeTime int64) error {
c := r.pool.Get()
defer c.Close()
if err := c.Err(); err != nil {
return err
}

keys, err := r.getKeysConn(c, 0, prefix)
keys, err := r.getKeysConn(c, 0, prefix, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -184,8 +184,13 @@ func (r *RedigoDriver) GetAll() (interface{}, error) {
return redisVal, nil
}

func (r *RedigoDriver) getKeysConn(c redis.Conn, cursor interface{}, prefix string) ([]string, error) {
if err := c.Send("SCAN", cursor, "MATCH", r.Config.Prefix+prefix+"*", "COUNT", 300000); err != nil {
func (r *RedigoDriver) getKeysConn(c redis.Conn, cursor interface{}, prefix string, includeSID bool) ([]string, error) {
if !includeSID {
prefix += r.Config.Delim // delim can be used for fast matching of only keys.
}
pattern := prefix + "*"

if err := c.Send("SCAN", cursor, "MATCH", pattern, "COUNT", 300000); err != nil {
return nil, err
}

Expand All @@ -205,23 +210,23 @@ func (r *RedigoDriver) getKeysConn(c redis.Conn, cursor interface{}, prefix stri
if len(replies) == 2 {
// take the second, it must contain the slice of keys.
if keysSliceAsBytes, ok := replies[1].([]interface{}); ok {
n := len(keysSliceAsBytes) - 1 // scan match returns the session id key too.
n := len(keysSliceAsBytes)
if n <= 0 {
return nil, nil
}

keys := make([]string, n)

for i, k := range keysSliceAsBytes {
key := fmt.Sprintf("%s", k)[len(r.Config.Prefix):]
if key == prefix {
continue // it's the session id itself.
}
keys[i] = key
// key := fmt.Sprintf("%s", k)[len(r.Config.Prefix):]
// if key == prefix {
// continue // it's the session id itself.
// }
keys[i] = fmt.Sprintf("%s", k)
}

if cur := fmt.Sprintf("%s", replies[0]); cur != "0" {
moreKeys, err := r.getKeysConn(c, cur, prefix)
moreKeys, err := r.getKeysConn(c, cur, prefix, includeSID)
if err != nil {
return nil, err
}
Expand All @@ -247,7 +252,7 @@ func (r *RedigoDriver) GetKeys(prefix string) ([]string, error) {
return nil, err
}

return r.getKeysConn(c, 0, prefix)
return r.getKeysConn(c, 0, prefix, false)
}

// GetBytes returns value, err by its key
Expand All @@ -260,7 +265,7 @@ func (r *RedigoDriver) GetBytes(key string) ([]byte, error) {
return nil, err
}

redisVal, err := c.Do("GET", r.Config.Prefix+key)
redisVal, err := c.Do("GET", key)
if err != nil {
return nil, err
}
Expand All @@ -276,7 +281,7 @@ func (r *RedigoDriver) Delete(key string) error {
c := r.pool.Get()
defer c.Close()

_, err := c.Do("DEL", r.Config.Prefix+key)
_, err := c.Do("DEL", key)
return err
}

Expand Down
Loading

0 comments on commit c181061

Please sign in to comment.