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 }