Newer
Older
Provides cache access and manipulation methods for redis server
Implements cacher interface.
Official docs -
1. redis client - https://github.com/go-redis/redis
2. redis server - https://redis.io/
Note - corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl must be initialized before use
*/
import (
"encoding/json"
"time"
"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
"github.com/go-redis/redis"
)
const (
noExp time.Duration = 0
// NotOK refers to a unsuccessfull operations
NotOK int64 = 0
// OK refers to a successfull operations
OK int64 = 1
keySplitter = ":"
// RedisCache represents a Redis client with provided configuration. Do not change configuration at runtime.
cli *redis.Client // represents redis client
opt *redis.Options //
keyStr string // "<Prefix>:"
addPrefix bool //
connected bool // will be enabled if redis client connects to server
Addr string // redis server address, default "127.0.0.1:6379"
DB int // redis DB on provided server, default 0
Password string //
Expiration time.Duration // this duration will be used for Set() method
Prefix string // this will be used for storing keys for provided project
// Setup initializes redis cache for application. Must be called only once.
func (rc *RedisCache) Setup(addr, password, prefix string, db int, exp time.Duration) {
if rc == nil {
rc = new(RedisCache)
}
rc.Addr = addr
rc.Password = password
rc.DB = db
rc.Expiration = exp
rc.Prefix = prefix
opt := redis.Options{
Addr: addr,
Password: password,
DB: db,
}
rc.opt = &opt
rc.cli = redis.NewClient(&opt)
if _, err := rc.cli.Ping().Result(); err != nil {
// exit if connection to redis server fails
loggermdl.LogError("connection to redis server failed: ", err)
log.Fatal("connection to redis server failed: ", err)
if prefix != "" {
rc.keyStr = contcat(rc.Prefix, keySplitter)
rc.addPrefix = true
}
}
// Set marshalls provided value and stores against provided key. Errors will be logged to initialized logger.
func (rc *RedisCache) Set(key string, val interface{}) {
ba, err := marshalWithTypeCheck(val)
if err != nil {
loggermdl.LogError("error setting key ", key, " error: ", err)
return
}
rc.cli.Set(rc.key(key), ba, rc.Expiration)
}
// SetWithExpiration marshalls provided value and stores against provided key for given duration. Errors will be logged to initialized logger.
func (rc *RedisCache) SetWithExpiration(key string, val interface{}, exp time.Duration) {
ba, err := marshalWithTypeCheck(val)
if err != nil {
loggermdl.LogError("error setting key ", key, " error: ", err)
return
}
rc.cli.Set(rc.key(key), ba, exp)
}
// SetNoExpiration marshalls provided value and stores against provided key.
// Errors will be logged to initialized logger.
func (rc *RedisCache) SetNoExpiration(key string, val interface{}) {
ba, err := marshalWithTypeCheck(val)
if err != nil {
loggermdl.LogError("error setting key ", key, " error: ", err)
return
}
rc.cli.Set(rc.key(key), ba, noExp)
}
// Get returns data against provided key. The final result is parsed with gjson. Returns false if not present.
func (rc *RedisCache) Get(key string) (interface{}, bool) {
// exists, err := rc.cli.Exists(key).Result()
// if err != nil {
// loggermdl.LogError("error checking key ", key, " error: ", err)
// return nil, false
// }
// if exists == NotOK {
// return nil, false
// }
// Get returns error if key is not present.
val, err := rc.cli.Get(rc.key(key)).Result()
loggermdl.LogError("error getting key", key, "from redis cache with error:", err)
return nil, false
}
}
// Delete -
func (rc *RedisCache) Delete(key string) {
rc.cli.Del(rc.key(key)).Result()
}
// GetItemsCount -
func (rc *RedisCache) GetItemsCount() int {
pattern := "*"
keys, err := rc.cli.Keys(pattern).Result()
if err != nil {
loggermdl.LogError("error getting item count for ", pattern, " error: ", err)
return 0
}
return len(keys)
}
func (rc *RedisCache) flushDB() (string, error) {
return rc.cli.FlushDB().Result()
}
// Purge deletes for current redis db
func (rc *RedisCache) Purge() {
_, err := rc.flushDB()
if err != nil {
loggermdl.LogError("error purging redis cache for db ", rc.Addr, "/", rc.DB, " error: ", err)
}
}
func marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
// marshalWithTypeCheck checks type before marshsal. Save allocations and time significantly if the existing data is string or []byte
func marshalWithTypeCheck(v interface{}) ([]byte, error) {
switch d := v.(type) {
default:
return json.Marshal(v)
case string:
return []byte(d), nil
case []byte:
return d, nil
}
}
func contcat(s ...string) string {
sb := strings.Builder{}
for i := range s {
sb.WriteString(s[i])
}
return sb.String()
}
func (rc *RedisCache) key(key string) string {
// prepare in format "<Prefix>:<key>"
if rc.addPrefix {
return contcat(rc.keyStr, key)
}
return key
}
func (rc *RedisCache) Type() int {
return TypeRedisCache
}