iris/sessions/providers/redis/redisstore.go

190 lines
4.9 KiB
Go

package redis
import (
"sync"
"time"
"github.com/kataras/iris/sessions/store"
"github.com/kataras/iris/utils"
)
/*Notes only for me
--------
Here we are setting a structure which keeps the current session's values setted by store.Set(key,value)
this is the RedisValue struct.
if noexists
RedisValue := RedisValue{sessionid,values)
RedisValue.values[thekey]=thevalue
service.Set(store.sid,RedisValue)
because we are using the same redis service for all sessions, and this is the best way to separate them,
without prefix and all that which I tried and failed to deserialize them correctly if the value is string...
so again we will keep the current server's sessions into memory
and fetch them(the sessions) from the redis at each first session run. Yes this is the fastest way to get/set a session
and at the same time they are keep saved to the redis and the GC will cleanup the memory after a while like we are doing
with the memory provider. Or just have a values field inside the Store and use just it, yes better simpler approach.
Ok then, let's convert it again.
*/
// Values is just a type of a map[string]interface{}
type Values map[string]interface{}
// Store the redis session store
type Store struct {
sid string
lastAccessedTime time.Time
values Values
cookieLifeDuration time.Duration //used on .Set-> SETEX on redis
mu sync.Mutex
}
var _ store.IStore = &Store{}
// NewStore creates and returns a new store based on the session id(string) and the cookie life duration (time.Duration)
func NewStore(sid string, cookieLifeDuration time.Duration) *Store {
s := &Store{sid: sid, lastAccessedTime: time.Now(), cookieLifeDuration: cookieLifeDuration}
//fetch the values from this session id and copy-> store them
val, err := redis.GetBytes(sid)
if err == nil {
err = utils.DeserializeBytes(val, &s.values)
if err != nil {
//if deserialization failed
s.values = Values{}
}
}
if s.values == nil {
//if key/sid wasn't found or was found but no entries in it(L72)
s.values = Values{}
}
return s
}
// serialize the values to be stored as strings inside the Redis, we panic at any serialization error here
func serialize(values Values) []byte {
val, err := utils.SerializeBytes(values)
if err != nil {
panic("On redisstore.serialize: " + err.Error())
}
return val
}
// update updates the real redis store
func (s *Store) update() {
go redis.Set(s.sid, serialize(s.values), s.cookieLifeDuration.Seconds()) //set/update all the values, in goroutine
}
// GetAll returns all values
func (s *Store) GetAll() map[string]interface{} {
return s.values
}
// VisitAll loop each one entry and calls the callback function func(key,value)
func (s *Store) VisitAll(cb func(k string, v interface{})) {
for key := range s.values {
cb(key, s.values[key])
}
}
// Get returns the value of an entry by its key
func (s *Store) Get(key string) interface{} {
Provider.Update(s.sid)
if value, found := s.values[key]; found {
s.mu.Unlock()
return value
}
return nil
}
// GetString same as Get but returns as string, if nil then returns an empty string
func (s *Store) GetString(key string) string {
if value := s.Get(key); value != nil {
if v, ok := value.(string); ok {
return v
}
}
return ""
}
// GetInt same as Get but returns as int, if nil then returns -1
func (s *Store) GetInt(key string) int {
if value := s.Get(key); value != nil {
if v, ok := value.(int); ok {
return v
}
}
return -1
}
// Set fills the session with an entry, it receives a key and a value
// returns an error, which is always nil
func (s *Store) Set(key string, value interface{}) error {
s.mu.Lock()
s.values[key] = value
s.mu.Unlock()
Provider.Update(s.sid)
s.update()
return nil
}
// Delete removes an entry by its key
// returns an error, which is always nil
func (s *Store) Delete(key string) error {
s.mu.Lock()
delete(s.values, key)
s.mu.Unlock()
Provider.Update(s.sid)
s.update()
return nil
}
// Clear removes all entries
// returns an error, which is always nil
func (s *Store) Clear() error {
//we are not using the Redis.Delete, I made so work for nothing.. we wanted only the .Set at the end...
s.mu.Lock()
for key := range s.values {
delete(s.values, key)
}
s.mu.Unlock()
Provider.Update(s.sid)
s.update()
return nil
}
// ID returns the session id
func (s *Store) ID() string {
return s.sid
}
// LastAccessedTime returns the last time this session has been used
func (s *Store) LastAccessedTime() time.Time {
return s.lastAccessedTime
}
// SetLastAccessedTime updates the last accessed time
func (s *Store) SetLastAccessedTime(lastacc time.Time) {
s.lastAccessedTime = lastacc
}
// Destroy deletes entirely the session, from the memory, the client's cookie and the store
func (s *Store) Destroy() {
// remove the whole value which is the s.values from real redis
redis.Delete(s.sid)
s.mu.Lock()
for key := range s.values {
delete(s.values, key)
}
s.mu.Unlock()
}