mirror of
https://github.com/Luzifer/ots.git
synced 2024-10-01 01:06:09 -04:00
5e3d84df9b
Signed-off-by: Knut Ahlers <knut@ahlers.me>
359 lines
11 KiB
Go
359 lines
11 KiB
Go
package goredis
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
)
|
|
|
|
// BgRewriteAof Instruct Redis to start an Append Only File rewrite process.
|
|
// The rewrite will create a small optimized version of the current Append Only File.
|
|
func (r *Redis) BgRewriteAof() error {
|
|
_, err := r.ExecuteCommand("BGREWRITEAOF")
|
|
return err
|
|
}
|
|
|
|
// BgSave save the DB in background.
|
|
// The OK code is immediately returned.
|
|
// Redis forks, the parent continues to serve the clients, the child saves the DB on disk then exits.
|
|
// A client my be able to check if the operation succeeded using the LASTSAVE command.
|
|
func (r *Redis) BgSave() error {
|
|
_, err := r.ExecuteCommand("BGSAVE")
|
|
return err
|
|
}
|
|
|
|
// ClientKill closes a given client connection identified by ip:port.
|
|
// Due to the single-treaded nature of Redis,
|
|
// it is not possible to kill a client connection while it is executing a command.
|
|
// However, the client will notice the connection has been closed
|
|
// only when the next command is sent (and results in network error).
|
|
// Status code reply: OK if the connection exists and has been closed
|
|
func (r *Redis) ClientKill(ip string, port int) error {
|
|
rp, err := r.ExecuteCommand("CLIENT", "KILL", net.JoinHostPort(ip, strconv.Itoa(port)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// ClientList returns information and statistics
|
|
// about the client connections server in a mostly human readable format.
|
|
// Bulk reply: a unique string, formatted as follows:
|
|
// One client connection per line (separated by LF)
|
|
// Each line is composed of a succession of property=value fields separated by a space character.
|
|
func (r *Redis) ClientList() (string, error) {
|
|
rp, err := r.ExecuteCommand("CLIENT", "LIST")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rp.StringValue()
|
|
}
|
|
|
|
// ClientGetName returns the name of the current connection as set by CLIENT SETNAME.
|
|
// Since every new connection starts without an associated name,
|
|
// if no name was assigned a null bulk reply is returned.
|
|
func (r *Redis) ClientGetName() ([]byte, error) {
|
|
rp, err := r.ExecuteCommand("CLIENT", "GETNAME")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rp.BytesValue()
|
|
}
|
|
|
|
// ClientPause stops the server processing commands from clients for some time.
|
|
func (r *Redis) ClientPause(timeout uint64) error {
|
|
rp, err := r.ExecuteCommand("CLIENT", "PAUSE", timeout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// ClientSetName assigns a name to the current connection.
|
|
func (r *Redis) ClientSetName(name string) error {
|
|
rp, err := r.ExecuteCommand("CLIENT", "SETNAME", name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// ConfigGet is used to read the configuration parameters of a running Redis server.
|
|
// Not all the configuration parameters are supported in Redis 2.4,
|
|
// while Redis 2.6 can read the whole configuration of a server using this command.
|
|
// CONFIG GET takes a single argument, which is a glob-style pattern.
|
|
func (r *Redis) ConfigGet(parameter string) (map[string]string, error) {
|
|
rp, err := r.ExecuteCommand("CONFIG", "GET", parameter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rp.HashValue()
|
|
}
|
|
|
|
// ConfigRewrite rewrites the redis.conf file the server was started with,
|
|
// applying the minimal changes needed to make it reflecting the configuration currently used by the server,
|
|
// that may be different compared to the original one because of the use of the CONFIG SET command.
|
|
// Available since 2.8.0.
|
|
func (r *Redis) ConfigRewrite() error {
|
|
rp, err := r.ExecuteCommand("CONFIG", "REWRITE")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// ConfigSet is used in order to reconfigure the server at run time without the need to restart Redis.
|
|
// You can change both trivial parameters or switch from one to another persistence option using this command.
|
|
func (r *Redis) ConfigSet(parameter, value string) error {
|
|
rp, err := r.ExecuteCommand("CONFIG", "SET", parameter, value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// ConfigResetStat resets the statistics reported by Redis using the INFO command.
|
|
// These are the counters that are reset:
|
|
// Keyspace hits
|
|
// Keyspace misses
|
|
// Number of commands processed
|
|
// Number of connections received
|
|
// Number of expired keys
|
|
// Number of rejected connections
|
|
// Latest fork(2) time
|
|
// The aof_delayed_fsync counter
|
|
func (r *Redis) ConfigResetStat() error {
|
|
_, err := r.ExecuteCommand("CONFIG", "RESETSTAT")
|
|
return err
|
|
}
|
|
|
|
// DBSize return the number of keys in the currently-selected database.
|
|
func (r *Redis) DBSize() (int64, error) {
|
|
rp, err := r.ExecuteCommand("DBSIZE")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return rp.IntegerValue()
|
|
}
|
|
|
|
// DebugObject is a debugging command that should not be used by clients.
|
|
func (r *Redis) DebugObject(key string) (string, error) {
|
|
rp, err := r.ExecuteCommand("DEBUG", "OBJECT", key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rp.StatusValue()
|
|
}
|
|
|
|
// FlushAll delete all the keys of all the existing databases,
|
|
// not just the currently selected one.
|
|
// This command never fails.
|
|
func (r *Redis) FlushAll() error {
|
|
_, err := r.ExecuteCommand("FLUSHALL")
|
|
return err
|
|
}
|
|
|
|
// FlushDB delete all the keys of the currently selected DB.
|
|
// This command never fails.
|
|
func (r *Redis) FlushDB() error {
|
|
_, err := r.ExecuteCommand("FLUSHDB")
|
|
return err
|
|
}
|
|
|
|
// Info returns information and statistics about the server
|
|
// in a format that is simple to parse by computers and easy to read by humans.
|
|
// format document at http://redis.io/commands/info
|
|
func (r *Redis) Info(section string) (string, error) {
|
|
args := packArgs("INFO", section)
|
|
rp, err := r.ExecuteCommand(args...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rp.StringValue()
|
|
}
|
|
|
|
// LastSave return the UNIX TIME of the last DB save executed with success.
|
|
// A client may check if a BGSAVE command succeeded reading the LASTSAVE value,
|
|
// then issuing a BGSAVE command and checking at regular intervals every N seconds if LASTSAVE changed.
|
|
// Integer reply: an UNIX time stamp.
|
|
func (r *Redis) LastSave() (int64, error) {
|
|
rp, err := r.ExecuteCommand("LASTSAVE")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return rp.IntegerValue()
|
|
}
|
|
|
|
// MonitorCommand is a debugging command that streams back every command processed by the Redis server.
|
|
type MonitorCommand struct {
|
|
redis *Redis
|
|
conn *connection
|
|
}
|
|
|
|
// Monitor sned MONITOR command to redis server.
|
|
func (r *Redis) Monitor() (*MonitorCommand, error) {
|
|
c, err := r.pool.Get()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := c.SendCommand("MONITOR"); err != nil {
|
|
return nil, err
|
|
}
|
|
rp, err := c.RecvReply()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rp.OKValue(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &MonitorCommand{r, c}, nil
|
|
}
|
|
|
|
// Receive read from redis server and return the reply.
|
|
func (m *MonitorCommand) Receive() (string, error) {
|
|
rp, err := m.conn.RecvReply()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rp.StatusValue()
|
|
}
|
|
|
|
// Close closes current monitor command.
|
|
func (m *MonitorCommand) Close() error {
|
|
return m.conn.SendCommand("QUIT")
|
|
}
|
|
|
|
// Save performs a synchronous save of the dataset
|
|
// producing a point in time snapshot of all the data inside the Redis instance,
|
|
// in the form of an RDB file.
|
|
// You almost never want to call SAVE in production environments
|
|
// where it will block all the other clients. Instead usually BGSAVE is used.
|
|
func (r *Redis) Save() error {
|
|
rp, err := r.ExecuteCommand("SAVE")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// Shutdown behavior is the following:
|
|
// Stop all the clients.
|
|
// Perform a blocking SAVE if at least one save point is configured.
|
|
// Flush the Append Only File if AOF is enabled.
|
|
// Quit the server.
|
|
func (r *Redis) Shutdown(save, noSave bool) error {
|
|
args := packArgs("SHUTDOWN")
|
|
if save {
|
|
args = append(args, "SAVE")
|
|
} else if noSave {
|
|
args = append(args, "NOSAVE")
|
|
}
|
|
rp, err := r.ExecuteCommand(args...)
|
|
if err == io.EOF {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return errors.New(rp.Status)
|
|
}
|
|
|
|
// SlaveOf can change the replication settings of a slave on the fly.
|
|
// If a Redis server is already acting as slave, the command SLAVEOF NO ONE will turn off the replication,
|
|
// turning the Redis server into a MASTER.
|
|
// In the proper form SLAVEOF hostname port will make the server a slave of
|
|
// another server listening at the specified hostname and port.
|
|
//
|
|
// If a server is already a slave of some master,
|
|
// SLAVEOF hostname port will stop the replication against the old server
|
|
// and start the synchronization against the new one, discarding the old dataset.
|
|
// The form SLAVEOF NO ONE will stop replication, turning the server into a MASTER,
|
|
// but will not discard the replication.
|
|
// So, if the old master stops working,
|
|
// it is possible to turn the slave into a master and set the application to use this new master in read/write.
|
|
// Later when the other Redis server is fixed, it can be reconfigured to work as a slave.
|
|
func (r *Redis) SlaveOf(host, port string) error {
|
|
rp, err := r.ExecuteCommand("SLAVEOF", host, port)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// SlowLog is used in order to read and reset the Redis slow queries log.
|
|
type SlowLog struct {
|
|
ID int64
|
|
Timestamp int64
|
|
Microseconds int64
|
|
Command []string
|
|
}
|
|
|
|
// SlowLogGet returns slow logs.
|
|
func (r *Redis) SlowLogGet(n int) ([]*SlowLog, error) {
|
|
rp, err := r.ExecuteCommand("SLOWLOG", "GET", n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rp.Type == ErrorReply {
|
|
return nil, errors.New(rp.Error)
|
|
}
|
|
if rp.Type != MultiReply {
|
|
return nil, errors.New("slowlog get protocol error")
|
|
}
|
|
var slow []*SlowLog
|
|
for _, subrp := range rp.Multi {
|
|
if subrp.Multi == nil || len(subrp.Multi) != 4 {
|
|
return nil, errors.New("slowlog get protocol error")
|
|
}
|
|
id, err := subrp.Multi[0].IntegerValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
timestamp, err := subrp.Multi[1].IntegerValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
microseconds, err := subrp.Multi[2].IntegerValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
command, err := subrp.Multi[3].ListValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
slow = append(slow, &SlowLog{id, timestamp, microseconds, command})
|
|
}
|
|
return slow, nil
|
|
}
|
|
|
|
// SlowLogLen Obtaining the current length of the slow log
|
|
func (r *Redis) SlowLogLen() (int64, error) {
|
|
rp, err := r.ExecuteCommand("SLOWLOG", "LEN")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return rp.IntegerValue()
|
|
}
|
|
|
|
// SlowLogReset resetting the slow log.
|
|
// Once deleted the information is lost forever.
|
|
func (r *Redis) SlowLogReset() error {
|
|
rp, err := r.ExecuteCommand("SLOWLOG", "RESET")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rp.OKValue()
|
|
}
|
|
|
|
// Time returns a multi bulk reply containing two elements:
|
|
// unix time in seconds,
|
|
// microseconds.
|
|
func (r *Redis) Time() ([]string, error) {
|
|
rp, err := r.ExecuteCommand("TIME")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rp.ListValue()
|
|
}
|