src.dualinventive.com/go/lib/kv/rediskv/rediskv.go

149 lines
3.7 KiB
Go

package rediskv
import (
"errors"
"time"
"github.com/gomodule/redigo/redis"
)
// ErrNotFound is returned when an error is not found
var ErrNotFound = errors.New("not found")
// Rediskv stores the values that is set or retrieved in Redis
type Rediskv struct {
c redis.Conn
}
// Get returns the value from the requested key.
func (r Rediskv) Get(key string) (string, error) {
return redis.String(r.c.Do("GET", key))
}
// Set stores the value with the given key
func (r Rediskv) Set(key string, value interface{}) error {
_, err := r.c.Do("SET", key, value)
return err
}
// SetExp stores the value with the given key and expiration duration
func (r Rediskv) SetExp(key string, value interface{}, expiration time.Duration) error {
_, err := r.c.Do("SET", key, value, "EX", expiration.Seconds())
return err
}
// Del removes the keys matching the given key
// returns the number of keys removed
func (r Rediskv) Del(key string) (bool, error) {
removed, err := redis.Int(r.c.Do("DEL", key))
return removed == 1, err
}
//Exists return whether the key is existing or not
func (r Rediskv) Exists(key string) (bool, error) {
ok, err := redis.Bool(r.c.Do("EXISTS", key))
return ok, err
}
// Keys returns a list of keys matched by pattern
func (r Rediskv) Keys(pattern string) ([]string, error) {
// Note: maybe use scan instead of keys. Keys has a great performance impact
// See: https://redis.io/commands/keys, https://redis.io/commands/scan
return redis.Strings(r.c.Do("KEYS", pattern))
}
// HKeys returns all keys in a hash
func (r Rediskv) HKeys(key string) ([]string, error) {
return redis.Strings(r.c.Do("HKEYS", key))
}
// HGetAll returns all key-values in a map
func (r Rediskv) HGetAll(key string) (map[string]string, error) {
return redis.StringMap(r.c.Do("HGETALL", key))
}
// HGet retrieve the value from the given field in the given key
func (r Rediskv) HGet(key, field string) (string, error) {
ret, err := redis.String(r.c.Do("HGET", key, field))
if err == redis.ErrNil {
return ret, ErrNotFound
}
return ret, err
}
// HSet sets the value in the given field in the given key
func (r Rediskv) HSet(key, field string, value interface{}) error {
_, err := r.c.Do("HSET", key, field, value)
return err
}
// HDel removes the value in the given field in the given key
func (r Rediskv) HDel(key, field string) error {
_, err := r.c.Do("HDEL", key, field)
return err
}
// New creates a Redis key value instance
func New(hosts ...string) (*Rediskv, error) {
if len(hosts) == 0 {
return nil, errors.New("no hosts provided")
}
if c, err := newClusterConn(hosts); err == nil {
return &Rediskv{c: c}, nil
}
dial, err := redis.Dial("tcp", hosts[0])
if err != nil {
return nil, err
}
c := &ReconnectionDoer{Conn: dial, address: hosts[0]}
_, err = c.Do("PING")
if err != nil {
return nil, err
}
return &Rediskv{c: c}, nil
}
// IsClustered checks if redis is in clustered mode
func (r Rediskv) IsClustered() bool {
_, ok := r.c.(*clusterConn)
return ok
}
// Close closes the redis connection(s)
func (r Rediskv) Close() error {
return r.c.Close()
}
// ReconnectionDoer implements Do with reconnect
type ReconnectionDoer struct {
redis.Conn
address string
}
func (rc *ReconnectionDoer) redial() error {
conn, err := redis.Dial("tcp", rc.address)
if err != nil {
return err
}
rc.Conn = conn
return nil
}
//Do will execute redis commands, but redial and try again on error
func (rc *ReconnectionDoer) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
reply, err = rc.Conn.Do(commandName, args...)
if err != nil {
err = rc.redial()
if err != nil {
return nil, err
}
reply, err = rc.Conn.Do(commandName, args...)
return reply, err
}
return reply, err
}