redis/v2
Vladimir Mihailenco bead8d1ea2 Improve pipelined example. 2013-11-04 09:47:36 +02:00
..
README.md v2: use time package primitives where possible. 2013-10-15 16:47:12 +03:00
command.go v2: use time package primitives where possible. 2013-10-15 16:47:12 +03:00
commands.go v2: use time package primitives where possible. 2013-10-15 16:47:12 +03:00
doc.go Remove examples from godoc. 2013-09-29 11:48:45 +03:00
example_test.go Improve pipelined example. 2013-11-04 09:47:36 +02:00
multi.go Add ErrTxFailed. Small tweaks. 2013-09-29 12:41:04 +03:00
parser.go Add ErrTxFailed. Small tweaks. 2013-09-29 12:41:04 +03:00
pipeline.go Add ErrTxFailed. Small tweaks. 2013-09-29 12:41:04 +03:00
pool.go Lower case few fields for consistency. 2013-09-29 11:11:18 +03:00
pubsub.go Lower case few fields for consistency. 2013-09-29 11:11:18 +03:00
redis.go Add ErrTxFailed. Small tweaks. 2013-09-29 12:41:04 +03:00
redis_test.go v2: use time package primitives where possible. 2013-10-15 16:47:12 +03:00
script.go Unexport as much as possible. 2013-09-29 11:06:49 +03:00

README.md

Redis client for Golang

Supports:

  • Redis 2.6 commands except QUIT, MONITOR, SLOWLOG and SYNC.
  • Pub/sub.
  • Transactions.
  • Pipelining.
  • Connection pool.
  • TLS connections.
  • Thread safety.
  • Timeouts.

API docs: http://godoc.org/github.com/vmihailenco/redis/v2

Installation

Install:

go get github.com/vmihailenco/redis/v2

Updgrading from previous version

Type system should catch most changes. But you have manually change SetEx, PSetEx, Expire and PExpire to use time.Duration instead of int64.

Getting started

Let's start with connecting to Redis using TCP:

client := redis.NewTCPClient(&redis.Options{
	Addr:     "localhost:6379",
	Password: "", // no password set
	DB:       0,  // use default DB
})
defer client.Close()

ping := client.Ping()
fmt.Println(ping.Val(), ping.Err())
// Output: PONG <nil>

or using Unix socket:

client := redis.NewUnixClient(&redis.Options{
	Addr: "/tmp/redis.sock",
})
defer client.Close()

ping := client.Ping()
fmt.Println(ping.Val(), ping.Err())
// Output: PONG <nil>

Then we can start sending commands:

set := client.Set("foo", "bar")
fmt.Println(set.Err(), set.Val())

get := client.Get("foo")
fmt.Println(get.Err(), get.Val())

// Output: <nil> OK
// <nil> bar

We can also pipeline two commands together:

pipeline := client.Pipeline()
set := pipeline.Set("key1", "hello1")
get := pipeline.Get("key2")
cmds, err := pipeline.Exec()
fmt.Println(cmds, err)
fmt.Println(set)
fmt.Println(get)
// Output: [SET key1 hello1: OK GET key2: (nil)] (nil)
// SET key1 hello1: OK
// GET key2: (nil)

or:

client := redis.NewTCPClient(&redis.Options{
	Addr: ":6379",
})
defer client.Close()

cmds, err := client.Pipelined(func(c *redis.Pipeline) {
	c.Set("key1", "hello1")
	c.Get("key2")
})
fmt.Println(cmds, err)
// Output: [SET key1 hello1: OK GET key2: (nil)] (nil)

We can also send several commands in transaction:

incr := func(tx *redis.Multi) ([]redis.Cmder, error) {
	get := tx.Get("key")
	if err := get.Err(); err != nil && err != redis.Nil {
		return nil, err
	}

	val, _ := strconv.ParseInt(get.Val(), 10, 64)

	return tx.Exec(func() {
		tx.Set("key", strconv.FormatInt(val+1, 10))
	})
}

client := redis.NewTCPClient(&redis.Options{
	Addr: ":6379",
})
defer client.Close()

client.Del("key")

tx := client.Multi()
defer tx.Close()

watch := tx.Watch("key")
_ = watch.Err()

for {
	cmds, err := incr(tx)
	if err == redis.TxFailedErr {
		continue
	} else if err != nil {
		panic(err)
	}
	fmt.Println(cmds, err)
	break
}

// Output: [SET key 1: OK] <nil>

To subscribe to the channel:

pubsub := client.PubSub()
defer pubsub.Close()

err := pubsub.Subscribe("mychannel")
_ = err

msg, err := pubsub.Receive()
fmt.Println(msg, err)

pub := client.Publish("mychannel", "hello")
_ = pub.Err()

msg, err = pubsub.Receive()
fmt.Println(msg, err)

// Output: &{subscribe mychannel 1} <nil>
// &{mychannel hello} <nil>

To use Lua scripting:

client := redis.NewTCPClient(&redis.Options{
	Addr: ":6379",
})
defer client.Close()

setnx := redis.NewScript(`
    if redis.call("get", KEYS[1]) == false then
        redis.call("set", KEYS[1], ARGV[1])
        return 1
    end
    return 0
`)

run1 := setnx.Run(client, []string{"keynx"}, []string{"foo"})
fmt.Println(run1.Val().(int64), run1.Err())

run2 := setnx.Run(client, []string{"keynx"}, []string{"bar"})
fmt.Println(run2.Val().(int64), run2.Err())

get := client.Get("keynx")
fmt.Println(get)

// Output: 1 <nil>
// 0 <nil>
// GET keynx: foo

You can also write custom commands:

Get := func(client *redis.Client, key string) *redis.StringCmd {
	cmd := redis.NewStringCmd("GET", key)
	client.Process(cmd)
	return cmd
}

client := redis.NewTCPClient(&redis.Options{
	Addr: ":6379",
})
defer client.Close()

get := Get(client, "key_does_not_exist")
fmt.Printf("%q %s", get.Val(), get.Err())
// Output: "" (nil)

Look and feel

Some corner cases:

SORT list LIMIT 0 2 ASC
client.Sort("list", redis.Sort{Offset: 0, Count: 2, Order: "ASC"})

ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{
    Min: "-inf",
    Max: "+inf",
    Offset: 0,
    Count: 2,
})

ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
client.ZInterStore("out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2")

EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, []string{"hello"})

Contributing

Configure Redis to allow maximum 10 clients:

maxclients 10

Run tests:

go test -gocheck.v

Run benchmarks:

go test -gocheck.b