github.com/gogf/gf@v1.16.9/os/gsession/gsession_storage_redis_hashtable.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gsession
     8  
     9  import (
    10  	"context"
    11  	"time"
    12  
    13  	"github.com/gogf/gf/container/gmap"
    14  	"github.com/gogf/gf/database/gredis"
    15  	"github.com/gogf/gf/internal/intlog"
    16  	"github.com/gogf/gf/util/gconv"
    17  )
    18  
    19  // StorageRedisHashTable implements the Session Storage interface with redis hash table.
    20  type StorageRedisHashTable struct {
    21  	redis  *gredis.Redis // Redis client for session storage.
    22  	prefix string        // Redis key prefix for session id.
    23  }
    24  
    25  // NewStorageRedisHashTable creates and returns a redis hash table storage object for session.
    26  func NewStorageRedisHashTable(redis *gredis.Redis, prefix ...string) *StorageRedisHashTable {
    27  	if redis == nil {
    28  		panic("redis instance for storage cannot be empty")
    29  		return nil
    30  	}
    31  	s := &StorageRedisHashTable{
    32  		redis: redis,
    33  	}
    34  	if len(prefix) > 0 && prefix[0] != "" {
    35  		s.prefix = prefix[0]
    36  	}
    37  	return s
    38  }
    39  
    40  // New creates a session id.
    41  // This function can be used for custom session creation.
    42  func (s *StorageRedisHashTable) New(ctx context.Context, ttl time.Duration) (id string, err error) {
    43  	return "", ErrorDisabled
    44  }
    45  
    46  // Get retrieves session value with given key.
    47  // It returns nil if the key does not exist in the session.
    48  func (s *StorageRedisHashTable) Get(ctx context.Context, id string, key string) (value interface{}, err error) {
    49  	value, err = s.redis.Ctx(ctx).Do("HGET", s.key(id), key)
    50  	if value != nil {
    51  		value = gconv.String(value)
    52  	}
    53  	return
    54  }
    55  
    56  // GetMap retrieves all key-value pairs as map from storage.
    57  func (s *StorageRedisHashTable) GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) {
    58  	r, err := s.redis.Ctx(ctx).DoVar("HGETALL", s.key(id))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	data = make(map[string]interface{})
    63  	array := r.Interfaces()
    64  	for i := 0; i < len(array); i += 2 {
    65  		if array[i+1] != nil {
    66  			data[gconv.String(array[i])] = gconv.String(array[i+1])
    67  		} else {
    68  			data[gconv.String(array[i])] = array[i+1]
    69  		}
    70  	}
    71  	return data, nil
    72  }
    73  
    74  // GetSize retrieves the size of key-value pairs from storage.
    75  func (s *StorageRedisHashTable) GetSize(ctx context.Context, id string) (size int, err error) {
    76  	r, err := s.redis.Ctx(ctx).DoVar("HLEN", s.key(id))
    77  	if err != nil {
    78  		return -1, err
    79  	}
    80  	return r.Int(), nil
    81  }
    82  
    83  // Set sets key-value session pair to the storage.
    84  // The parameter `ttl` specifies the TTL for the session id (not for the key-value pair).
    85  func (s *StorageRedisHashTable) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error {
    86  	_, err := s.redis.Ctx(ctx).Do("HSET", s.key(id), key, value)
    87  	return err
    88  }
    89  
    90  // SetMap batch sets key-value session pairs with map to the storage.
    91  // The parameter `ttl` specifies the TTL for the session id(not for the key-value pair).
    92  func (s *StorageRedisHashTable) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error {
    93  	array := make([]interface{}, len(data)*2+1)
    94  	array[0] = s.key(id)
    95  
    96  	index := 1
    97  	for k, v := range data {
    98  		array[index] = k
    99  		array[index+1] = v
   100  		index += 2
   101  	}
   102  	_, err := s.redis.Ctx(ctx).Do("HMSET", array...)
   103  	return err
   104  }
   105  
   106  // Remove deletes key with its value from storage.
   107  func (s *StorageRedisHashTable) Remove(ctx context.Context, id string, key string) error {
   108  	_, err := s.redis.Ctx(ctx).Do("HDEL", s.key(id), key)
   109  	return err
   110  }
   111  
   112  // RemoveAll deletes all key-value pairs from storage.
   113  func (s *StorageRedisHashTable) RemoveAll(ctx context.Context, id string) error {
   114  	_, err := s.redis.Ctx(ctx).Do("DEL", s.key(id))
   115  	return err
   116  }
   117  
   118  // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
   119  //
   120  // The parameter `ttl` specifies the TTL for this session, and it returns nil if the TTL is exceeded.
   121  // The parameter `data` is the current old session data stored in memory,
   122  // and for some storage it might be nil if memory storage is disabled.
   123  //
   124  // This function is called ever when session starts.
   125  func (s *StorageRedisHashTable) GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
   126  	intlog.Printf(ctx, "StorageRedisHashTable.GetSession: %s, %v", id, ttl)
   127  	r, err := s.redis.Ctx(ctx).DoVar("EXISTS", s.key(id))
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	if r.Bool() {
   132  		return gmap.NewStrAnyMap(true), nil
   133  	}
   134  	return nil, nil
   135  }
   136  
   137  // SetSession updates the data map for specified session id.
   138  // This function is called ever after session, which is changed dirty, is closed.
   139  // This copy all session data map from memory to storage.
   140  func (s *StorageRedisHashTable) SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error {
   141  	intlog.Printf(ctx, "StorageRedisHashTable.SetSession: %s, %v", id, ttl)
   142  	_, err := s.redis.Ctx(ctx).Do("EXPIRE", s.key(id), int64(ttl.Seconds()))
   143  	return err
   144  }
   145  
   146  // UpdateTTL updates the TTL for specified session id.
   147  // This function is called ever after session, which is not dirty, is closed.
   148  // It just adds the session id to the async handling queue.
   149  func (s *StorageRedisHashTable) UpdateTTL(ctx context.Context, id string, ttl time.Duration) error {
   150  	intlog.Printf(ctx, "StorageRedisHashTable.UpdateTTL: %s, %v", id, ttl)
   151  	_, err := s.redis.Do("EXPIRE", s.key(id), int64(ttl.Seconds()))
   152  	return err
   153  }
   154  
   155  func (s *StorageRedisHashTable) key(id string) string {
   156  	return s.prefix + id
   157  }