├── Readme.md ├── hmget_test.go ├── redis-dump └── redis-dump.go ├── redis-load └── redis-load.go ├── redis.go └── redis_test.go /Readme.md: -------------------------------------------------------------------------------- 1 | ## goredis 2 | 3 | **NOTE:** This is a modified version of [redis.go](https://github.com/hoisie/redis.go) by [hoise](https://github.com/hoisie) to support the latest version of go. 4 | 5 | **NOTE:** This project was called redis.go and renamed to *goredis* because of [this](https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/dnOK9j5Fvn4). (In short, we can no longer use package names end in .go.) 6 | 7 | This package supports the latest release of Go. 8 | 9 | goredis is a client for the [redis](http://github.com/antirez/redis) key-value store. 10 | 11 | Some features include: 12 | 13 | * Designed for Redis 1.3.x. 14 | * Support for all redis types - strings, lists, sets, sorted sets, and hashes 15 | * Very simple usage 16 | * Connection pooling ( with configurable size ) 17 | * Support for concurrent access 18 | * Manages connections to the redis server, including dropped and timed out connections 19 | * Marshaling/Unmarshaling go types to hashes 20 | 21 | This library is stable and is used in production environments. However, some commands have not been tested as thoroughly as others. If you find any bugs please file an issue! 22 | 23 | ## Install 24 | 25 | go get github.com/monnand/goredis 26 | 27 | ## Examples 28 | 29 | Most of the examples connect to a redis database running in the default port -- 6367. 30 | 31 | ### Instantiating the client 32 | 33 | //connects to the default port (6379) 34 | var client goredis.Client 35 | 36 | //connects to port 8379, database 13 37 | var client2 goredis.Client 38 | client2.Addr = "127.0.0.1:8379" 39 | client2.Db = 13 40 | 41 | ### Strings 42 | 43 | var client goredis.Client 44 | client.Set("a", []byte("hello")) 45 | val, _ := client.Get("a") 46 | println(string(val)) 47 | client.Del("a") 48 | 49 | ### Lists 50 | 51 | var client goredis.Client 52 | vals := []string{"a", "b", "c", "d", "e"} 53 | for _, v := range vals { 54 | client.Rpush("l", []byte(v)) 55 | } 56 | dbvals,_ := client.Lrange("l", 0, 4) 57 | for i, v := range dbvals { 58 | println(i,":",string(v)) 59 | } 60 | client.Del("l") 61 | 62 | ### Publish/Subscribe 63 | sub := make(chan string, 1) 64 | sub <- "foo" 65 | messages := make(chan Message, 0) 66 | go client.Subscribe(sub, nil, nil, nil, messages) 67 | 68 | time.Sleep(10 * 1000 * 1000) 69 | client.Publish("foo", []byte("bar")) 70 | 71 | msg := <-messages 72 | println("received from:", msg.Channel, " message:", string(msg.Message)) 73 | 74 | close(sub) 75 | close(messages) 76 | 77 | 78 | More examples coming soon. See `redis_test.go` for more usage examples. 79 | 80 | ## (Known) Projects using this package 81 | 82 | - [uniqush](http://github.com/monnand/uniqush) 83 | 84 | 85 | ## Commands not supported yet 86 | 87 | * MULTI/EXEC/DISCARD/WATCH/UNWATCH 88 | * SORT 89 | * ZUNIONSTORE / ZINTERSTORE 90 | 91 | -------------------------------------------------------------------------------- /hmget_test.go: -------------------------------------------------------------------------------- 1 | package goredis 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestContainerToString(t *testing.T) { 9 | 10 | vals := make(map[string]string, 2) 11 | vals["f1"] = "hello" 12 | vals["f2"] = "world" 13 | 14 | args := make([]string, 0, 5) 15 | args = append(args, "myhash") 16 | args, _ = containerToString(reflect.ValueOf(vals), args) 17 | 18 | correct_args := make([]string, 5) 19 | correct_args[0] = "myhash" 20 | correct_args[1] = "f1" 21 | correct_args[2] = "hello" 22 | correct_args[3] = "f2" 23 | correct_args[4] = "world" 24 | 25 | for i, v := range correct_args { 26 | if args[i] != v { 27 | t.Fatalf("%dth argument should be %s, but it is %s", i, v, args[i]) 28 | } 29 | } 30 | 31 | } 32 | 33 | func TestHmset(t *testing.T) { 34 | var client Client 35 | client.Addr = "127.0.0.1:6379" 36 | client.Db = 13 37 | 38 | vals := make(map[string]string, 2) 39 | vals["f1"] = "hello" 40 | vals["f2"] = "world" 41 | key := "myhash" 42 | 43 | client.Hmset(key, vals) 44 | 45 | for f, v := range vals { 46 | value, err := client.Hget(key, f) 47 | if err != nil { 48 | t.Fatalf("Database error: %v", err) 49 | } 50 | str := string(value) 51 | if str != v { 52 | t.Fatalf("field %s should be %s but it is %s", f, v, str) 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /redis-dump/redis-dump.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "redis" 8 | "strconv" 9 | ) 10 | 11 | func dump_db(port int, db int, output io.Writer) { 12 | var client redis.Client 13 | 14 | if port != 0 { 15 | client.Addr = "127.0.0.1:" + strconv.Itoa(port) 16 | } 17 | 18 | if db != 0 { 19 | client.Db = db 20 | } 21 | 22 | fmt.Fprintf(output, "FLUSHDB\r\n") 23 | 24 | keys, err := client.Keys("*") 25 | 26 | if err != nil { 27 | println("Redis-dump failed", err.Error()) 28 | return 29 | } 30 | 31 | for _, key := range keys { 32 | typ, _ := client.Type(key) 33 | 34 | if typ == "string" { 35 | data, _ := client.Get(key) 36 | fmt.Fprintf(output, "SET %s %d\r\n%s\r\n", key, len(data), data) 37 | } else if typ == "list" { 38 | llen, _ := client.Llen(key) 39 | for i := 0; i < llen; i++ { 40 | data, _ := client.Lindex(key, i) 41 | fmt.Fprintf(output, "RPUSH %s %d\r\n%s\r\n", key, len(data), data) 42 | } 43 | } else if typ == "set" { 44 | members, _ := client.Smembers(key) 45 | for _, data := range members { 46 | fmt.Fprintf(output, "SADD %s %d\r\n%s\r\n", key, len(data), data) 47 | } 48 | } 49 | } 50 | 51 | } 52 | 53 | func usage() { println("redis-dump [-p port] [-db num]") } 54 | 55 | func main() { 56 | 57 | var err error 58 | 59 | db := 0 60 | port := 6379 61 | 62 | args := os.Args[1:] 63 | 64 | for i := 0; i < len(args); i++ { 65 | arg := args[i] 66 | if arg == "-p" && i < len(args)-1 { 67 | if port, err = strconv.Atoi(args[i+1]); err != nil { 68 | println(err.Error()) 69 | return 70 | } 71 | i += 1 72 | continue 73 | } else if arg == "-db" && i < len(args)-1 { 74 | if db, err = strconv.Atoi(args[i+1]); err != nil { 75 | println(err.Error()) 76 | return 77 | } 78 | i += 1 79 | continue 80 | } else { 81 | println("Invalid argument: ", arg) 82 | usage() 83 | return 84 | } 85 | } 86 | 87 | dump_db(port, db, os.Stdout) 88 | } 89 | -------------------------------------------------------------------------------- /redis-load/redis-load.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | ) 7 | import "fmt" 8 | import "net" 9 | import "os" 10 | import "strconv" 11 | 12 | func load_db(port int, db int, reader *bufio.Reader) { 13 | addr := "127.0.0.1:6379" 14 | 15 | if port != 0 { 16 | addr = "127.0.0.1:" + strconv.Itoa(port) 17 | } 18 | 19 | c, err := net.Dial("tcp", addr) 20 | 21 | if err != nil { 22 | println(err.Error()) 23 | return 24 | } 25 | 26 | if db != 0 { 27 | fmt.Fprintf(c, "SELECT %d\r\n", db) 28 | } 29 | 30 | for { 31 | line, err := reader.ReadBytes('\n') 32 | if err == io.EOF { 33 | break 34 | } 35 | println(string(line)) 36 | c.Write(line) 37 | } 38 | c.Write([]byte("QUIT\r\n")) 39 | buf := make([]byte, 512) 40 | 41 | for { 42 | n, err := c.Read(buf) 43 | if err != nil { 44 | break 45 | } 46 | println(string(buf[0:n])) 47 | } 48 | } 49 | 50 | func usage() { println("redis-load [-p port] [-db num]") } 51 | 52 | func main() { 53 | 54 | var err error 55 | 56 | db := 0 57 | port := 6379 58 | 59 | args := os.Args[1:] 60 | 61 | for i := 0; i < len(args); i++ { 62 | arg := args[i] 63 | if arg == "-p" && i < len(args)-1 { 64 | if port, err = strconv.Atoi(args[i+1]); err != nil { 65 | println(err.Error()) 66 | return 67 | } 68 | i += 1 69 | continue 70 | } else if arg == "-db" && i < len(args)-1 { 71 | if db, err = strconv.Atoi(args[i+1]); err != nil { 72 | println(err.Error()) 73 | return 74 | } 75 | i += 1 76 | continue 77 | } else { 78 | println("Invalid argument: ", arg) 79 | usage() 80 | return 81 | } 82 | } 83 | println("port", port, db) 84 | load_db(port, db, bufio.NewReader(os.Stdin)) 85 | 86 | } 87 | -------------------------------------------------------------------------------- /redis.go: -------------------------------------------------------------------------------- 1 | package goredis 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "net" 11 | "reflect" 12 | "strconv" 13 | "strings" 14 | "syscall" 15 | ) 16 | 17 | var MaxPoolSize = 50 18 | 19 | var defaultAddr = "127.0.0.1:6379" 20 | 21 | type Client struct { 22 | Addr string 23 | Db int 24 | Password string 25 | //the connection pool 26 | pool chan net.Conn 27 | } 28 | 29 | type RedisError string 30 | 31 | func (err RedisError) Error() string { return "Redis Error: " + string(err) } 32 | 33 | var doesNotExist = RedisError("Key does not exist ") 34 | 35 | func NewClient(poolsize int) (c *Client) { 36 | _c := new(Client) 37 | MaxPoolSize = poolsize 38 | _c.pool = make(chan net.Conn, MaxPoolSize) 39 | for i := 0; i < MaxPoolSize; i++ { 40 | _c.pool <- nil 41 | } 42 | return _c 43 | } 44 | 45 | // reads a bulk reply (i.e $5\r\nhello) 46 | func readBulk(reader *bufio.Reader, head string) ([]byte, error) { 47 | var err error 48 | var data []byte 49 | 50 | if head == "" { 51 | head, err = reader.ReadString('\n') 52 | if err != nil { 53 | return nil, err 54 | } 55 | } 56 | switch head[0] { 57 | case ':': 58 | data = []byte(strings.TrimSpace(head[1:])) 59 | 60 | case '$': 61 | size, err := strconv.Atoi(strings.TrimSpace(head[1:])) 62 | if err != nil { 63 | return nil, err 64 | } 65 | if size == -1 { 66 | return nil, doesNotExist 67 | } 68 | lr := io.LimitReader(reader, int64(size)) 69 | data, err = ioutil.ReadAll(lr) 70 | if err == nil { 71 | // read end of line 72 | _, err = reader.ReadString('\n') 73 | } 74 | default: 75 | return nil, RedisError("Expecting Prefix '$' or ':'") 76 | } 77 | 78 | return data, err 79 | } 80 | 81 | func writeRequest(writer io.Writer, cmd string, args ...string) error { 82 | b := commandBytes(cmd, args...) 83 | _, err := writer.Write(b) 84 | return err 85 | } 86 | 87 | func commandBytes(cmd string, args ...string) []byte { 88 | cmdbuf := bytes.NewBufferString(fmt.Sprintf("*%d\r\n$%d\r\n%s\r\n", len(args)+1, len(cmd), cmd)) 89 | for _, s := range args { 90 | cmdbuf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(s), s)) 91 | } 92 | return cmdbuf.Bytes() 93 | } 94 | 95 | func readResponse(reader *bufio.Reader) (interface{}, error) { 96 | 97 | var line string 98 | var err error 99 | 100 | //read until the first non-whitespace line 101 | for { 102 | line, err = reader.ReadString('\n') 103 | if len(line) == 0 || err != nil { 104 | return nil, err 105 | } 106 | line = strings.TrimSpace(line) 107 | if len(line) > 0 { 108 | break 109 | } 110 | } 111 | 112 | if line[0] == '+' { 113 | return strings.TrimSpace(line[1:]), nil 114 | } 115 | 116 | if line[0] == '-' { 117 | errmesg := strings.TrimSpace(line[1:]) 118 | return nil, RedisError(errmesg) 119 | } 120 | 121 | if line[0] == ':' { 122 | n, err := strconv.ParseInt(strings.TrimSpace(line[1:]), 10, 64) 123 | if err != nil { 124 | return nil, RedisError("Int reply is not a number") 125 | } 126 | return n, nil 127 | } 128 | 129 | if line[0] == '*' { 130 | size, err := strconv.Atoi(strings.TrimSpace(line[1:])) 131 | if err != nil { 132 | return nil, RedisError("MultiBulk reply expected a number") 133 | } 134 | if size <= 0 { 135 | return make([][]byte, 0), nil 136 | } 137 | res := make([][]byte, size) 138 | for i := 0; i < size; i++ { 139 | res[i], err = readBulk(reader, "") 140 | if err == doesNotExist { 141 | continue 142 | } 143 | if err != nil { 144 | return nil, err 145 | } 146 | // dont read end of line as might not have been bulk 147 | } 148 | return res, nil 149 | } 150 | return readBulk(reader, line) 151 | } 152 | 153 | // TODO: client is not needed here 154 | func (client *Client) rawSend(c net.Conn, cmd []byte) (interface{}, error) { 155 | _, err := c.Write(cmd) 156 | if err != nil { 157 | return nil, err 158 | } 159 | 160 | reader := bufio.NewReader(c) 161 | 162 | data, err := readResponse(reader) 163 | if err != nil { 164 | return nil, err 165 | } 166 | 167 | return data, nil 168 | } 169 | 170 | func (client *Client) openConnection() (c net.Conn, err error) { 171 | 172 | var addr = defaultAddr 173 | 174 | if client.Addr != "" { 175 | addr = client.Addr 176 | } 177 | c, err = net.Dial("tcp", addr) 178 | if err != nil { 179 | return 180 | } 181 | 182 | if client.Password != ""{ 183 | cmd := fmt.Sprintf("AUTH %s\r\n", client.Password) 184 | _, err = client.rawSend(c, []byte(cmd)) 185 | if err != nil { 186 | return 187 | } 188 | } 189 | 190 | if client.Db != 0 { 191 | cmd := fmt.Sprintf("SELECT %d\r\n", client.Db) 192 | _, err = client.rawSend(c, []byte(cmd)) 193 | if err != nil { 194 | return 195 | } 196 | } 197 | 198 | return 199 | } 200 | 201 | func (client *Client) sendCommand(cmd string, args ...string) (data interface{}, err error) { 202 | // grab a connection from the pool 203 | c, err := client.popCon() 204 | 205 | var b []byte 206 | if err != nil { 207 | //add the client back to the queue 208 | client.pushCon(c) 209 | return data, err 210 | } 211 | 212 | b = commandBytes(cmd, args...) 213 | data, err = client.rawSend(c, b) 214 | if err2, ok := err.(syscall.Errno); err == io.EOF || (ok && err2 == syscall.EPIPE) { 215 | c, err = client.openConnection() 216 | if err != nil { 217 | //add the client back to the queue 218 | client.pushCon(c) 219 | return data, err 220 | } 221 | 222 | data, err = client.rawSend(c, b) 223 | } 224 | 225 | //in case of "connection reset by peer" or "broken pipe" 226 | if err != nil { 227 | msg := err.Error() 228 | if strings.Contains(msg, "reset") || strings.Contains(msg, "broken"){ 229 | c.Close() 230 | client.pushCon(nil) 231 | return data, err 232 | } 233 | } 234 | 235 | //add the client back to the queue 236 | client.pushCon(c) 237 | 238 | return data, err 239 | } 240 | 241 | func (client *Client) sendCommands(cmdArgs <-chan []string, data chan<- interface{}) (err error) { 242 | // grab a connection from the pool 243 | c, err := client.popCon() 244 | var reader *bufio.Reader 245 | 246 | if err != nil { 247 | // Close client and synchronization issues are a nightmare to solve. 248 | c.Close() 249 | // Push nil back onto queue 250 | client.pushCon(nil) 251 | return err 252 | } 253 | 254 | reader = bufio.NewReader(c) 255 | 256 | // Ping first to verify connection is open 257 | err = writeRequest(c, "PING") 258 | 259 | // On first attempt permit a reconnection attempt 260 | if err == io.EOF || err == io.ErrClosedPipe { 261 | // Looks like we have to open a new connection 262 | c, err = client.openConnection() 263 | if err != nil { 264 | // Close client and synchronization issues are a nightmare to solve. 265 | c.Close() 266 | // Push nil back onto queue 267 | client.pushCon(nil) 268 | return err 269 | } 270 | reader = bufio.NewReader(c) 271 | } else { 272 | // Read Ping response 273 | pong, err := readResponse(reader) 274 | if pong != "PONG" { 275 | return RedisError("Unexpected response to PING.") 276 | } 277 | if err != nil { 278 | // Close client and synchronization issues are a nightmare to solve. 279 | c.Close() 280 | // Push nil back onto queue 281 | client.pushCon(nil) 282 | return err 283 | } 284 | } 285 | 286 | errs := make(chan error) 287 | 288 | go func() { 289 | for cmdArg := range cmdArgs { 290 | err = writeRequest(c, cmdArg[0], cmdArg[1:]...) 291 | if err != nil { 292 | errs <- err 293 | break 294 | } 295 | } 296 | close(errs) 297 | }() 298 | 299 | go func() { 300 | for { 301 | response, err := readResponse(reader) 302 | if err != nil { 303 | errs <- err 304 | break 305 | } 306 | data <- response 307 | } 308 | close(errs) 309 | }() 310 | 311 | // Block until errs channel closes 312 | for e := range errs { 313 | err = e 314 | } 315 | 316 | // Close client and synchronization issues are a nightmare to solve. 317 | c.Close() 318 | 319 | // Push nil back onto queue 320 | client.pushCon(nil) 321 | 322 | return err 323 | } 324 | 325 | func (client *Client) popCon() (net.Conn, error) { 326 | if client.pool == nil { 327 | client.pool = make(chan net.Conn, MaxPoolSize) 328 | for i := 0; i < MaxPoolSize; i++ { 329 | //add dummy values to the pool 330 | client.pool <- nil 331 | } 332 | } 333 | // grab a connection from the pool 334 | c := <-client.pool 335 | 336 | if c == nil { 337 | return client.openConnection() 338 | } 339 | return c, nil 340 | } 341 | 342 | func (client *Client) pushCon(c net.Conn) { 343 | client.pool <- c 344 | } 345 | 346 | // General Commands 347 | 348 | func (client *Client) Auth(password string) error { 349 | _, err := client.sendCommand("AUTH", password) 350 | if err != nil { 351 | return err 352 | } 353 | 354 | return nil 355 | } 356 | 357 | func (client *Client) Exists(key string) (bool, error) { 358 | res, err := client.sendCommand("EXISTS", key) 359 | if err != nil { 360 | return false, err 361 | } 362 | return res.(int64) == 1, nil 363 | } 364 | 365 | func (client *Client) Del(key string) (bool, error) { 366 | res, err := client.sendCommand("DEL", key) 367 | 368 | if err != nil { 369 | return false, err 370 | } 371 | 372 | return res.(int64) == 1, nil 373 | } 374 | 375 | func (client *Client) Type(key string) (string, error) { 376 | res, err := client.sendCommand("TYPE", key) 377 | 378 | if err != nil { 379 | return "", err 380 | } 381 | 382 | return res.(string), nil 383 | } 384 | 385 | func (client *Client) Keys(pattern string) ([]string, error) { 386 | res, err := client.sendCommand("KEYS", pattern) 387 | 388 | if err != nil { 389 | return nil, err 390 | } 391 | 392 | var ok bool 393 | var keydata [][]byte 394 | 395 | if keydata, ok = res.([][]byte); ok { 396 | // key data is already a double byte array 397 | } else { 398 | keydata = bytes.Fields(res.([]byte)) 399 | } 400 | ret := make([]string, len(keydata)) 401 | for i, k := range keydata { 402 | ret[i] = string(k) 403 | } 404 | return ret, nil 405 | } 406 | 407 | func (client *Client) Randomkey() (string, error) { 408 | res, err := client.sendCommand("RANDOMKEY") 409 | if err != nil { 410 | return "", err 411 | } 412 | return res.(string), nil 413 | } 414 | 415 | func (client *Client) Rename(src string, dst string) error { 416 | _, err := client.sendCommand("RENAME", src, dst) 417 | if err != nil { 418 | return err 419 | } 420 | return nil 421 | } 422 | 423 | func (client *Client) Renamenx(src string, dst string) (bool, error) { 424 | res, err := client.sendCommand("RENAMENX", src, dst) 425 | if err != nil { 426 | return false, err 427 | } 428 | return res.(int64) == 1, nil 429 | } 430 | 431 | func (client *Client) Dbsize() (int, error) { 432 | res, err := client.sendCommand("DBSIZE") 433 | if err != nil { 434 | return -1, err 435 | } 436 | 437 | return int(res.(int64)), nil 438 | } 439 | 440 | func (client *Client) Expire(key string, time int64) (bool, error) { 441 | res, err := client.sendCommand("EXPIRE", key, strconv.FormatInt(time, 10)) 442 | 443 | if err != nil { 444 | return false, err 445 | } 446 | 447 | return res.(int64) == 1, nil 448 | } 449 | 450 | func (client *Client) Ttl(key string) (int64, error) { 451 | res, err := client.sendCommand("TTL", key) 452 | if err != nil { 453 | return -1, err 454 | } 455 | 456 | return res.(int64), nil 457 | } 458 | 459 | func (client *Client) Move(key string, dbnum int) (bool, error) { 460 | res, err := client.sendCommand("MOVE", key, strconv.Itoa(dbnum)) 461 | 462 | if err != nil { 463 | return false, err 464 | } 465 | 466 | return res.(int64) == 1, nil 467 | } 468 | 469 | func (client *Client) Flush(all bool) error { 470 | var cmd string 471 | if all { 472 | cmd = "FLUSHALL" 473 | } else { 474 | cmd = "FLUSHDB" 475 | } 476 | _, err := client.sendCommand(cmd) 477 | if err != nil { 478 | return err 479 | } 480 | return nil 481 | } 482 | 483 | // String-related commands 484 | 485 | func (client *Client) Set(key string, val []byte) error { 486 | _, err := client.sendCommand("SET", key, string(val)) 487 | 488 | if err != nil { 489 | return err 490 | } 491 | 492 | return nil 493 | } 494 | 495 | func (client *Client) Get(key string) ([]byte, error) { 496 | res, err := client.sendCommand("GET", key) 497 | if err != nil { 498 | return nil, err 499 | } 500 | if res == nil { 501 | return []byte(""), nil 502 | } 503 | data := res.([]byte) 504 | return data, nil 505 | } 506 | 507 | func (client *Client) Getset(key string, val []byte) ([]byte, error) { 508 | res, err := client.sendCommand("GETSET", key, string(val)) 509 | 510 | if err != nil { 511 | return nil, err 512 | } 513 | 514 | data := res.([]byte) 515 | return data, nil 516 | } 517 | 518 | func (client *Client) Mget(keys ...string) ([][]byte, error) { 519 | res, err := client.sendCommand("MGET", keys...) 520 | if err != nil { 521 | return nil, err 522 | } 523 | 524 | data := res.([][]byte) 525 | return data, nil 526 | } 527 | 528 | func (client *Client) Setnx(key string, val []byte) (bool, error) { 529 | res, err := client.sendCommand("SETNX", key, string(val)) 530 | 531 | if err != nil { 532 | return false, err 533 | } 534 | if data, ok := res.(int64); ok { 535 | return data == 1, nil 536 | } 537 | return false, RedisError("Unexpected reply to SETNX") 538 | } 539 | 540 | func (client *Client) Setex(key string, time int64, val []byte) error { 541 | _, err := client.sendCommand("SETEX", key, strconv.FormatInt(time, 10), string(val)) 542 | 543 | if err != nil { 544 | return err 545 | } 546 | 547 | return nil 548 | } 549 | 550 | func (client *Client) Mset(mapping map[string][]byte) error { 551 | args := make([]string, len(mapping)*2) 552 | i := 0 553 | for k, v := range mapping { 554 | args[i] = k 555 | args[i+1] = string(v) 556 | i += 2 557 | } 558 | _, err := client.sendCommand("MSET", args...) 559 | if err != nil { 560 | return err 561 | } 562 | return nil 563 | } 564 | 565 | func (client *Client) Msetnx(mapping map[string][]byte) (bool, error) { 566 | args := make([]string, len(mapping)*2) 567 | i := 0 568 | for k, v := range mapping { 569 | args[i] = k 570 | args[i+1] = string(v) 571 | i += 2 572 | } 573 | res, err := client.sendCommand("MSETNX", args...) 574 | if err != nil { 575 | return false, err 576 | } 577 | if data, ok := res.(int64); ok { 578 | return data == 0, nil 579 | } 580 | return false, RedisError("Unexpected reply to MSETNX") 581 | } 582 | 583 | func (client *Client) Incr(key string) (int64, error) { 584 | res, err := client.sendCommand("INCR", key) 585 | if err != nil { 586 | return -1, err 587 | } 588 | 589 | return res.(int64), nil 590 | } 591 | 592 | func (client *Client) Incrby(key string, val int64) (int64, error) { 593 | res, err := client.sendCommand("INCRBY", key, strconv.FormatInt(val, 10)) 594 | if err != nil { 595 | return -1, err 596 | } 597 | 598 | return res.(int64), nil 599 | } 600 | 601 | func (client *Client) Decr(key string) (int64, error) { 602 | res, err := client.sendCommand("DECR", key) 603 | if err != nil { 604 | return -1, err 605 | } 606 | 607 | return res.(int64), nil 608 | } 609 | 610 | func (client *Client) Decrby(key string, val int64) (int64, error) { 611 | res, err := client.sendCommand("DECRBY", key, strconv.FormatInt(val, 10)) 612 | if err != nil { 613 | return -1, err 614 | } 615 | 616 | return res.(int64), nil 617 | } 618 | 619 | func (client *Client) Append(key string, val []byte) error { 620 | _, err := client.sendCommand("APPEND", key, string(val)) 621 | 622 | if err != nil { 623 | return err 624 | } 625 | 626 | return nil 627 | } 628 | 629 | func (client *Client) Substr(key string, start int, end int) ([]byte, error) { 630 | res, _ := client.sendCommand("SUBSTR", key, strconv.Itoa(start), strconv.Itoa(end)) 631 | 632 | if res == nil { 633 | return nil, RedisError("Key `" + key + "` does not exist") 634 | } 635 | 636 | data := res.([]byte) 637 | return data, nil 638 | } 639 | 640 | // List commands 641 | 642 | func (client *Client) Rpush(key string, val []byte) error { 643 | _, err := client.sendCommand("RPUSH", key, string(val)) 644 | 645 | if err != nil { 646 | return err 647 | } 648 | 649 | return nil 650 | } 651 | 652 | func (client *Client) Lpush(key string, val []byte) error { 653 | _, err := client.sendCommand("LPUSH", key, string(val)) 654 | 655 | if err != nil { 656 | return err 657 | } 658 | 659 | return nil 660 | } 661 | 662 | func (client *Client) Llen(key string) (int, error) { 663 | res, err := client.sendCommand("LLEN", key) 664 | if err != nil { 665 | return -1, err 666 | } 667 | 668 | return int(res.(int64)), nil 669 | } 670 | 671 | func (client *Client) Lrange(key string, start int, end int) ([][]byte, error) { 672 | res, err := client.sendCommand("LRANGE", key, strconv.Itoa(start), strconv.Itoa(end)) 673 | if err != nil { 674 | return nil, err 675 | } 676 | 677 | return res.([][]byte), nil 678 | } 679 | 680 | func (client *Client) Ltrim(key string, start int, end int) error { 681 | _, err := client.sendCommand("LTRIM", key, strconv.Itoa(start), strconv.Itoa(end)) 682 | if err != nil { 683 | return err 684 | } 685 | 686 | return nil 687 | } 688 | 689 | func (client *Client) Lindex(key string, index int) ([]byte, error) { 690 | res, err := client.sendCommand("LINDEX", key, strconv.Itoa(index)) 691 | if err != nil { 692 | return nil, err 693 | } 694 | 695 | return res.([]byte), nil 696 | } 697 | 698 | func (client *Client) Lset(key string, index int, value []byte) error { 699 | _, err := client.sendCommand("LSET", key, strconv.Itoa(index), string(value)) 700 | if err != nil { 701 | return err 702 | } 703 | 704 | return nil 705 | } 706 | 707 | func (client *Client) Lrem(key string, count int, value []byte) (int, error) { 708 | res, err := client.sendCommand("LREM", key, strconv.Itoa(count), string(value)) 709 | if err != nil { 710 | return -1, err 711 | } 712 | return int(res.(int64)), nil 713 | } 714 | 715 | func (client *Client) Lpop(key string) ([]byte, error) { 716 | res, err := client.sendCommand("LPOP", key) 717 | if err != nil { 718 | return nil, err 719 | } 720 | 721 | return res.([]byte), nil 722 | } 723 | 724 | func (client *Client) Rpop(key string) ([]byte, error) { 725 | res, err := client.sendCommand("RPOP", key) 726 | if err != nil { 727 | return nil, err 728 | } 729 | 730 | return res.([]byte), nil 731 | } 732 | 733 | func (client *Client) Blpop(keys []string, timeoutSecs uint) (*string, []byte, error) { 734 | return client.bpop("BLPOP", keys, timeoutSecs) 735 | } 736 | func (client *Client) Brpop(keys []string, timeoutSecs uint) (*string, []byte, error) { 737 | return client.bpop("BRPOP", keys, timeoutSecs) 738 | } 739 | 740 | func (client *Client) bpop(cmd string, keys []string, timeoutSecs uint) (*string, []byte, error) { 741 | args := append(keys, strconv.FormatUint(uint64(timeoutSecs), 10)) 742 | res, err := client.sendCommand(cmd, args...) 743 | if err != nil { 744 | return nil, nil, err 745 | } 746 | kv := res.([][]byte) 747 | // Check for timeout 748 | if len(kv) != 2 { 749 | return nil, nil, nil 750 | } 751 | k := string(kv[0]) 752 | v := kv[1] 753 | return &k, v, nil 754 | } 755 | 756 | func (client *Client) Rpoplpush(src string, dst string) ([]byte, error) { 757 | res, err := client.sendCommand("RPOPLPUSH", src, dst) 758 | if err != nil { 759 | return nil, err 760 | } 761 | 762 | return res.([]byte), nil 763 | } 764 | 765 | // Set commands 766 | 767 | func (client *Client) Sadd(key string, value []byte) (bool, error) { 768 | res, err := client.sendCommand("SADD", key, string(value)) 769 | 770 | if err != nil { 771 | return false, err 772 | } 773 | 774 | return res.(int64) == 1, nil 775 | } 776 | //Sadd mutiple members, with redis >=2.4 777 | func (client *Client) Smadd(key string, members []string ) (bool, error) { 778 | args := []string{key} 779 | args = append(args, members...) 780 | res, err := client.sendCommand("SADD", args...) 781 | 782 | if err != nil { 783 | return false, err 784 | } 785 | 786 | return res.(int64) >= 1, nil 787 | } 788 | 789 | func (client *Client) Srem(key string, value []byte) (bool, error) { 790 | res, err := client.sendCommand("SREM", key, string(value)) 791 | 792 | if err != nil { 793 | return false, err 794 | } 795 | 796 | return res.(int64) == 1, nil 797 | } 798 | 799 | func (client *Client) Spop(key string) ([]byte, error) { 800 | res, err := client.sendCommand("SPOP", key) 801 | if err != nil { 802 | return nil, err 803 | } 804 | 805 | if res == nil { 806 | return nil, RedisError("Spop failed") 807 | } 808 | 809 | data := res.([]byte) 810 | return data, nil 811 | } 812 | 813 | func (client *Client) Smove(src string, dst string, val []byte) (bool, error) { 814 | res, err := client.sendCommand("SMOVE", src, dst, string(val)) 815 | if err != nil { 816 | return false, err 817 | } 818 | 819 | return res.(int64) == 1, nil 820 | } 821 | 822 | func (client *Client) Scard(key string) (int, error) { 823 | res, err := client.sendCommand("SCARD", key) 824 | if err != nil { 825 | return -1, err 826 | } 827 | 828 | return int(res.(int64)), nil 829 | } 830 | 831 | func (client *Client) Sismember(key string, value []byte) (bool, error) { 832 | res, err := client.sendCommand("SISMEMBER", key, string(value)) 833 | 834 | if err != nil { 835 | return false, err 836 | } 837 | 838 | return res.(int64) == 1, nil 839 | } 840 | 841 | func (client *Client) Sinter(keys ...string) ([][]byte, error) { 842 | res, err := client.sendCommand("SINTER", keys...) 843 | if err != nil { 844 | return nil, err 845 | } 846 | 847 | return res.([][]byte), nil 848 | } 849 | 850 | func (client *Client) Sinterstore(dst string, keys ...string) (int, error) { 851 | args := make([]string, len(keys)+1) 852 | args[0] = dst 853 | copy(args[1:], keys) 854 | res, err := client.sendCommand("SINTERSTORE", args...) 855 | if err != nil { 856 | return 0, err 857 | } 858 | 859 | return int(res.(int64)), nil 860 | } 861 | 862 | func (client *Client) Sunion(keys ...string) ([][]byte, error) { 863 | res, err := client.sendCommand("SUNION", keys...) 864 | if err != nil { 865 | return nil, err 866 | } 867 | 868 | return res.([][]byte), nil 869 | } 870 | 871 | func (client *Client) Sunionstore(dst string, keys ...string) (int, error) { 872 | args := make([]string, len(keys)+1) 873 | args[0] = dst 874 | copy(args[1:], keys) 875 | res, err := client.sendCommand("SUNIONSTORE", args...) 876 | if err != nil { 877 | return 0, err 878 | } 879 | 880 | return int(res.(int64)), nil 881 | } 882 | 883 | func (client *Client) Sdiff(key1 string, keys []string) ([][]byte, error) { 884 | args := make([]string, len(keys)+1) 885 | args[0] = key1 886 | copy(args[1:], keys) 887 | res, err := client.sendCommand("SDIFF", args...) 888 | if err != nil { 889 | return nil, err 890 | } 891 | 892 | return res.([][]byte), nil 893 | } 894 | 895 | func (client *Client) Sdiffstore(dst string, key1 string, keys []string) (int, error) { 896 | args := make([]string, len(keys)+2) 897 | args[0] = dst 898 | args[1] = key1 899 | copy(args[2:], keys) 900 | res, err := client.sendCommand("SDIFFSTORE", args...) 901 | if err != nil { 902 | return 0, err 903 | } 904 | 905 | return int(res.(int64)), nil 906 | } 907 | 908 | func (client *Client) Smembers(key string) ([][]byte, error) { 909 | res, err := client.sendCommand("SMEMBERS", key) 910 | 911 | if err != nil { 912 | return nil, err 913 | } 914 | 915 | return res.([][]byte), nil 916 | } 917 | 918 | func (client *Client) Srandmember(key string) ([]byte, error) { 919 | res, err := client.sendCommand("SRANDMEMBER", key) 920 | if err != nil { 921 | return nil, err 922 | } 923 | 924 | return res.([]byte), nil 925 | } 926 | 927 | // sorted set commands 928 | 929 | func (client *Client) Zadd(key string, value []byte, score float64) (bool, error) { 930 | res, err := client.sendCommand("ZADD", key, strconv.FormatFloat(score, 'f', -1, 64), string(value)) 931 | if err != nil { 932 | return false, err 933 | } 934 | 935 | return res.(int64) == 1, nil 936 | } 937 | // zadd multiple elements. redis >=2.4 938 | func (client *Client) Zmadd(key string, values map[string]float64 ) (bool, error) { 939 | args := []string{key} 940 | for val,score := range values { 941 | args = append(args, strconv.FormatFloat(score, 'f', -1, 64)) 942 | args = append(args, val) 943 | } 944 | res, err := client.sendCommand("ZADD", args...) 945 | if err != nil { 946 | return false, err 947 | } 948 | 949 | return res.(int64) >= 1, nil 950 | } 951 | 952 | func (client *Client) Zrem(key string, value []byte) (bool, error) { 953 | res, err := client.sendCommand("ZREM", key, string(value)) 954 | if err != nil { 955 | return false, err 956 | } 957 | 958 | return res.(int64) == 1, nil 959 | } 960 | 961 | func (client *Client) Zincrby(key string, value []byte, score float64) (float64, error) { 962 | res, err := client.sendCommand("ZINCRBY", key, strconv.FormatFloat(score, 'f', -1, 64), string(value)) 963 | if err != nil { 964 | return 0, err 965 | } 966 | 967 | data := string(res.([]byte)) 968 | f, _ := strconv.ParseFloat(data, 64) 969 | return f, nil 970 | } 971 | 972 | func (client *Client) Zrank(key string, value []byte) (int, error) { 973 | res, err := client.sendCommand("ZRANK", key, string(value)) 974 | if err != nil { 975 | return 0, err 976 | } 977 | 978 | return int(res.(int64)), nil 979 | } 980 | 981 | func (client *Client) Zrevrank(key string, value []byte) (int, error) { 982 | res, err := client.sendCommand("ZREVRANK", key, string(value)) 983 | if err != nil { 984 | return 0, err 985 | } 986 | 987 | return int(res.(int64)), nil 988 | } 989 | 990 | func (client *Client) Zrange(key string, start int, end int,WITHSCORES ...string) ([][]byte, error) { 991 | args := []string{key,strconv.Itoa(start), strconv.Itoa(end)} 992 | if len(WITHSCORES) == 1 && WITHSCORES[0] == "WITHSCORES" { 993 | args = append(args,"WITHSCORES") 994 | } 995 | res, err := client.sendCommand("ZRANGE", args...) 996 | if err != nil { 997 | return nil, err 998 | } 999 | 1000 | return res.([][]byte), nil 1001 | } 1002 | 1003 | func (client *Client) Zrevrange(key string, start int, end int) ([][]byte, error) { 1004 | res, err := client.sendCommand("ZREVRANGE", key, strconv.Itoa(start), strconv.Itoa(end)) 1005 | if err != nil { 1006 | return nil, err 1007 | } 1008 | 1009 | return res.([][]byte), nil 1010 | } 1011 | 1012 | func (client *Client) Zrangebyscore(key string, start float64, end float64) ([][]byte, error) { 1013 | res, err := client.sendCommand("ZRANGEBYSCORE", key, strconv.FormatFloat(start, 'f', -1, 64), strconv.FormatFloat(end, 'f', -1, 64)) 1014 | if err != nil { 1015 | return nil, err 1016 | } 1017 | 1018 | return res.([][]byte), nil 1019 | } 1020 | 1021 | func (client *Client) Zcard(key string) (int, error) { 1022 | res, err := client.sendCommand("ZCARD", key) 1023 | if err != nil { 1024 | return -1, err 1025 | } 1026 | 1027 | return int(res.(int64)), nil 1028 | } 1029 | 1030 | func (client *Client) Zscore(key string, member []byte) (float64, error) { 1031 | res, err := client.sendCommand("ZSCORE", key, string(member)) 1032 | if err != nil { 1033 | return 0, err 1034 | } 1035 | 1036 | data := string(res.([]byte)) 1037 | f, _ := strconv.ParseFloat(data, 64) 1038 | return f, nil 1039 | } 1040 | 1041 | func (client *Client) Zremrangebyrank(key string, start int, end int) (int, error) { 1042 | res, err := client.sendCommand("ZREMRANGEBYRANK", key, strconv.Itoa(start), strconv.Itoa(end)) 1043 | if err != nil { 1044 | return -1, err 1045 | } 1046 | 1047 | return int(res.(int64)), nil 1048 | } 1049 | 1050 | func (client *Client) Zremrangebyscore(key string, start float64, end float64) (int, error) { 1051 | res, err := client.sendCommand("ZREMRANGEBYSCORE", key, strconv.FormatFloat(start, 'f', -1, 64), strconv.FormatFloat(end, 'f', -1, 64)) 1052 | if err != nil { 1053 | return -1, err 1054 | } 1055 | 1056 | return int(res.(int64)), nil 1057 | } 1058 | 1059 | // hash commands 1060 | 1061 | func (client *Client) Hset(key string, field string, val []byte) (bool, error) { 1062 | res, err := client.sendCommand("HSET", key, field, string(val)) 1063 | if err != nil { 1064 | return false, err 1065 | } 1066 | 1067 | return res.(int64) == 1, nil 1068 | } 1069 | 1070 | func (client *Client) Hget(key string, field string) ([]byte, error) { 1071 | res, err := client.sendCommand("HGET", key, field) 1072 | 1073 | if err != nil { 1074 | return nil, err 1075 | } 1076 | if res == nil { 1077 | return []byte(""), nil 1078 | } 1079 | data := res.([]byte) 1080 | return data, nil 1081 | } 1082 | 1083 | //pretty much copy the json code from here. 1084 | 1085 | func valueToString(v reflect.Value) (string, error) { 1086 | if !v.IsValid() { 1087 | return "null", nil 1088 | } 1089 | 1090 | switch v.Kind() { 1091 | case reflect.Ptr: 1092 | return valueToString(reflect.Indirect(v)) 1093 | case reflect.Interface: 1094 | return valueToString(v.Elem()) 1095 | case reflect.Bool: 1096 | x := v.Bool() 1097 | if x { 1098 | return "true", nil 1099 | } else { 1100 | return "false", nil 1101 | } 1102 | 1103 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1104 | return strconv.FormatInt(v.Int(), 10), nil 1105 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1106 | return strconv.FormatUint(v.Uint(), 10), nil 1107 | case reflect.UnsafePointer: 1108 | return strconv.FormatUint(uint64(v.Pointer()), 10), nil 1109 | 1110 | case reflect.Float32, reflect.Float64: 1111 | return strconv.FormatFloat(v.Float(), 'g', -1, 64), nil 1112 | 1113 | case reflect.String: 1114 | return v.String(), nil 1115 | 1116 | //This is kind of a rough hack to replace the old []byte 1117 | //detection with reflect.Uint8Type, it doesn't catch 1118 | //zero-length byte slices 1119 | case reflect.Slice: 1120 | typ := v.Type() 1121 | if typ.Elem().Kind() == reflect.Uint || typ.Elem().Kind() == reflect.Uint8 || typ.Elem().Kind() == reflect.Uint16 || typ.Elem().Kind() == reflect.Uint32 || typ.Elem().Kind() == reflect.Uint64 || typ.Elem().Kind() == reflect.Uintptr { 1122 | if v.Len() > 0 { 1123 | if v.Index(1).OverflowUint(257) { 1124 | return string(v.Interface().([]byte)), nil 1125 | } 1126 | } 1127 | } 1128 | } 1129 | return "", errors.New("Unsupported type") 1130 | } 1131 | 1132 | func containerToString(val reflect.Value, args []string) ([]string, error) { 1133 | switch v := val; v.Kind() { 1134 | case reflect.Ptr: 1135 | return containerToString(reflect.Indirect(v), args) 1136 | case reflect.Interface: 1137 | return containerToString(v.Elem(), args) 1138 | case reflect.Map: 1139 | if v.Type().Key().Kind() != reflect.String { 1140 | return nil, errors.New("Unsupported type - map key must be a string") 1141 | } 1142 | for _, k := range v.MapKeys() { 1143 | args = append(args, k.String()) 1144 | s, err := valueToString(v.MapIndex(k)) 1145 | if err != nil { 1146 | return nil, err 1147 | } 1148 | args = append(args, s) 1149 | } 1150 | case reflect.Struct: 1151 | st := v.Type() 1152 | for i := 0; i < st.NumField(); i++ { 1153 | ft := st.FieldByIndex([]int{i}) 1154 | args = append(args, ft.Name) 1155 | s, err := valueToString(v.FieldByIndex([]int{i})) 1156 | if err != nil { 1157 | return nil, err 1158 | } 1159 | args = append(args, s) 1160 | } 1161 | } 1162 | return args, nil 1163 | } 1164 | 1165 | func (client *Client) Hmset(key string, mapping interface{}) error { 1166 | args := make([]string, 0, 5) 1167 | args = append(args, key) 1168 | 1169 | args, err := containerToString(reflect.ValueOf(mapping), args) 1170 | if err != nil { 1171 | return err 1172 | } 1173 | _, err = client.sendCommand("HMSET", args...) 1174 | if err != nil { 1175 | return err 1176 | } 1177 | return nil 1178 | } 1179 | 1180 | func (client *Client) Hincrby(key string, field string, val int64) (int64, error) { 1181 | res, err := client.sendCommand("HINCRBY", key, field, strconv.FormatInt(val, 10)) 1182 | if err != nil { 1183 | return -1, err 1184 | } 1185 | 1186 | return res.(int64), nil 1187 | } 1188 | 1189 | func (client *Client) Hexists(key string, field string) (bool, error) { 1190 | res, err := client.sendCommand("HEXISTS", key, field) 1191 | if err != nil { 1192 | return false, err 1193 | } 1194 | return res.(int64) == 1, nil 1195 | } 1196 | 1197 | func (client *Client) Hdel(key string, field string) (bool, error) { 1198 | res, err := client.sendCommand("HDEL", key, field) 1199 | 1200 | if err != nil { 1201 | return false, err 1202 | } 1203 | 1204 | return res.(int64) == 1, nil 1205 | } 1206 | 1207 | func (client *Client) Hlen(key string) (int, error) { 1208 | res, err := client.sendCommand("HLEN", key) 1209 | if err != nil { 1210 | return -1, err 1211 | } 1212 | 1213 | return int(res.(int64)), nil 1214 | } 1215 | 1216 | func (client *Client) Hkeys(key string) ([]string, error) { 1217 | res, err := client.sendCommand("HKEYS", key) 1218 | 1219 | if err != nil { 1220 | return nil, err 1221 | } 1222 | 1223 | data := res.([][]byte) 1224 | ret := make([]string, len(data)) 1225 | for i, k := range data { 1226 | ret[i] = string(k) 1227 | } 1228 | return ret, nil 1229 | } 1230 | 1231 | func (client *Client) Hvals(key string) ([][]byte, error) { 1232 | res, err := client.sendCommand("HVALS", key) 1233 | 1234 | if err != nil { 1235 | return nil, err 1236 | } 1237 | return res.([][]byte), nil 1238 | } 1239 | 1240 | func writeTo(data []byte, val reflect.Value) error { 1241 | s := string(data) 1242 | switch v := val; v.Kind() { 1243 | // if we're writing to an interace value, just set the byte data 1244 | // TODO: should we support writing to a pointer? 1245 | case reflect.Interface: 1246 | v.Set(reflect.ValueOf(data)) 1247 | case reflect.Bool: 1248 | b, err := strconv.ParseBool(s) 1249 | if err != nil { 1250 | return err 1251 | } 1252 | v.SetBool(b) 1253 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1254 | i, err := strconv.ParseInt(s, 10, 64) 1255 | if err != nil { 1256 | return err 1257 | } 1258 | v.SetInt(i) 1259 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1260 | ui, err := strconv.ParseUint(s, 10, 64) 1261 | if err != nil { 1262 | return err 1263 | } 1264 | v.SetUint(ui) 1265 | case reflect.Float32, reflect.Float64: 1266 | f, err := strconv.ParseFloat(s, 64) 1267 | if err != nil { 1268 | return err 1269 | } 1270 | v.SetFloat(f) 1271 | 1272 | case reflect.String: 1273 | v.SetString(s) 1274 | case reflect.Slice: 1275 | typ := v.Type() 1276 | if typ.Elem().Kind() == reflect.Uint || typ.Elem().Kind() == reflect.Uint8 || typ.Elem().Kind() == reflect.Uint16 || typ.Elem().Kind() == reflect.Uint32 || typ.Elem().Kind() == reflect.Uint64 || typ.Elem().Kind() == reflect.Uintptr { 1277 | v.Set(reflect.ValueOf(data)) 1278 | } 1279 | } 1280 | return nil 1281 | } 1282 | 1283 | func writeToContainer(data [][]byte, val reflect.Value) error { 1284 | switch v := val; v.Kind() { 1285 | case reflect.Ptr: 1286 | return writeToContainer(data, reflect.Indirect(v)) 1287 | case reflect.Interface: 1288 | return writeToContainer(data, v.Elem()) 1289 | case reflect.Map: 1290 | if v.Type().Key().Kind() != reflect.String { 1291 | return errors.New("Invalid map type") 1292 | } 1293 | elemtype := v.Type().Elem() 1294 | for i := 0; i < len(data)/2; i++ { 1295 | mk := reflect.ValueOf(string(data[i*2])) 1296 | mv := reflect.New(elemtype).Elem() 1297 | writeTo(data[i*2+1], mv) 1298 | v.SetMapIndex(mk, mv) 1299 | } 1300 | case reflect.Struct: 1301 | for i := 0; i < len(data)/2; i++ { 1302 | name := string(data[i*2]) 1303 | field := v.FieldByName(name) 1304 | if !field.IsValid() { 1305 | continue 1306 | } 1307 | writeTo(data[i*2+1], field) 1308 | } 1309 | default: 1310 | return errors.New("Invalid container type") 1311 | } 1312 | return nil 1313 | } 1314 | 1315 | func (client *Client) Hgetall(key string, val interface{}) error { 1316 | res, err := client.sendCommand("HGETALL", key) 1317 | if err != nil { 1318 | return err 1319 | } 1320 | 1321 | data := res.([][]byte) 1322 | if data == nil || len(data) == 0 { 1323 | return RedisError("Key `" + key + "` does not exist") 1324 | } 1325 | err = writeToContainer(data, reflect.ValueOf(val)) 1326 | if err != nil { 1327 | return err 1328 | } 1329 | 1330 | return nil 1331 | } 1332 | 1333 | //Publish/Subscribe 1334 | 1335 | // Container for messages received from publishers on channels that we're subscribed to. 1336 | type Message struct { 1337 | ChannelMatched string 1338 | Channel string 1339 | Message []byte 1340 | } 1341 | 1342 | // Subscribe to redis serve channels, this method will block until one of the sub/unsub channels are closed. 1343 | // There are two pairs of channels subscribe/unsubscribe & psubscribe/punsubscribe. 1344 | // The former does an exact match on the channel, the later uses glob patterns on the redis channels. 1345 | // Closing either of these channels will unblock this method call. 1346 | // Messages that are received are sent down the messages channel. 1347 | func (client *Client) Subscribe(subscribe <-chan string, unsubscribe <-chan string, psubscribe <-chan string, punsubscribe <-chan string, messages chan<- Message) error { 1348 | cmds := make(chan []string, 0) 1349 | data := make(chan interface{}, 0) 1350 | 1351 | go func() { 1352 | for { 1353 | var channel string 1354 | var cmd string 1355 | 1356 | select { 1357 | case channel = <-subscribe: 1358 | cmd = "SUBSCRIBE" 1359 | case channel = <-unsubscribe: 1360 | cmd = "UNSUBSCRIBE" 1361 | case channel = <-psubscribe: 1362 | cmd = "PSUBSCRIBE" 1363 | case channel = <-punsubscribe: 1364 | cmd = "UNPSUBSCRIBE" 1365 | 1366 | } 1367 | if channel == "" { 1368 | break 1369 | } else { 1370 | cmds <- []string{cmd, channel} 1371 | } 1372 | } 1373 | close(cmds) 1374 | close(data) 1375 | }() 1376 | 1377 | go func() { 1378 | for response := range data { 1379 | db := response.([][]byte) 1380 | messageType := string(db[0]) 1381 | switch messageType { 1382 | case "message": 1383 | channel, message := string(db[1]), db[2] 1384 | messages <- Message{channel, channel, message} 1385 | case "subscribe": 1386 | // Ignore 1387 | case "unsubscribe": 1388 | // Ignore 1389 | case "pmessage": 1390 | channelMatched, channel, message := string(db[1]), string(db[2]), db[3] 1391 | messages <- Message{channelMatched, channel, message} 1392 | case "psubscribe": 1393 | // Ignore 1394 | case "punsubscribe": 1395 | // Ignore 1396 | 1397 | default: 1398 | // log.Printf("Unknown message '%s'", messageType) 1399 | } 1400 | } 1401 | }() 1402 | 1403 | err := client.sendCommands(cmds, data) 1404 | 1405 | return err 1406 | } 1407 | 1408 | // Publish a message to a redis server. 1409 | func (client *Client) Publish(channel string, val []byte) error { 1410 | _, err := client.sendCommand("PUBLISH", channel, string(val)) 1411 | if err != nil { 1412 | return err 1413 | } 1414 | return nil 1415 | } 1416 | 1417 | //Server commands 1418 | 1419 | func (client *Client) Save() error { 1420 | _, err := client.sendCommand("SAVE") 1421 | if err != nil { 1422 | return err 1423 | } 1424 | return nil 1425 | } 1426 | 1427 | func (client *Client) Bgsave() error { 1428 | _, err := client.sendCommand("BGSAVE") 1429 | if err != nil { 1430 | return err 1431 | } 1432 | return nil 1433 | } 1434 | 1435 | func (client *Client) Lastsave() (int64, error) { 1436 | res, err := client.sendCommand("LASTSAVE") 1437 | if err != nil { 1438 | return 0, err 1439 | } 1440 | 1441 | return res.(int64), nil 1442 | } 1443 | 1444 | func (client *Client) Bgrewriteaof() error { 1445 | _, err := client.sendCommand("BGREWRITEAOF") 1446 | if err != nil { 1447 | return err 1448 | } 1449 | return nil 1450 | } 1451 | 1452 | func (client *Client) Ping() (string, error) { 1453 | res, err := client.sendCommand("PING") 1454 | if err != nil { 1455 | return "", err 1456 | } 1457 | return res.(string), nil 1458 | } 1459 | 1460 | type Transaction struct { 1461 | c net.Conn 1462 | *Client 1463 | } 1464 | 1465 | func (client *Client) Transaction() (*Transaction, error) { 1466 | c, err := client.openConnection() 1467 | if err != nil { 1468 | return nil, err 1469 | } 1470 | return &Transaction{c, client}, nil 1471 | } 1472 | 1473 | func (t *Transaction) sendCommand(cmd string, args ...string) (data interface{}, err error) { 1474 | b := commandBytes(cmd, args...) 1475 | return t.Client.rawSend(t.c, b) 1476 | } 1477 | 1478 | func (t *Transaction) Watch(keys []string) error { 1479 | _, err := t.sendCommand("WATCH", keys...) 1480 | return err 1481 | } 1482 | 1483 | func (t *Transaction) Unwatch() error { 1484 | _, err := t.sendCommand("UNWATCH") 1485 | return err 1486 | } 1487 | 1488 | func (t *Transaction) Multi() error { 1489 | _, err := t.sendCommand("MULTI") 1490 | return err 1491 | } 1492 | 1493 | func (t *Transaction) Discard() error { 1494 | _, err := t.sendCommand("DISCARD") 1495 | return err 1496 | } 1497 | 1498 | func (t *Transaction) Exec() ([][]byte, error) { 1499 | res, err := t.sendCommand("EXEC") 1500 | if err != nil { 1501 | return nil, err 1502 | } 1503 | return res.([][]byte), nil 1504 | } 1505 | -------------------------------------------------------------------------------- /redis_test.go: -------------------------------------------------------------------------------- 1 | package goredis 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "reflect" 8 | "runtime" 9 | "strconv" 10 | "strings" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | const ( 16 | // the timeout config property in redis.conf. used to test 17 | // connection retrying 18 | serverTimeout = 5 19 | ) 20 | 21 | var client Client 22 | 23 | func init() { 24 | runtime.GOMAXPROCS(2) 25 | client.Addr = "127.0.0.1:6379" 26 | client.Db = 13 27 | } 28 | 29 | func TestBasic(t *testing.T) { 30 | 31 | var val []byte 32 | var err error 33 | 34 | err = client.Set("a", []byte("hello")) 35 | 36 | if err != nil { 37 | t.Fatal("set failed", err.Error()) 38 | } 39 | 40 | if val, err = client.Get("a"); err != nil || string(val) != "hello" { 41 | t.Fatal("get failed") 42 | } 43 | 44 | if typ, err := client.Type("a"); err != nil || typ != "string" { 45 | t.Fatal("type failed", typ) 46 | } 47 | 48 | //if keys, err := client.Keys("*"); err != nil || len(keys) != 1 { 49 | // t.Fatal("keys failed", keys) 50 | //} 51 | 52 | client.Del("a") 53 | 54 | if ok, _ := client.Exists("a"); ok { 55 | t.Fatal("Should be deleted") 56 | } 57 | } 58 | 59 | func setget(t *testing.T, i int) { 60 | 61 | s := strconv.Itoa(i) 62 | err := client.Set(s, []byte(s)) 63 | if err != nil { 64 | t.Fatal("Concurrent set", err.Error()) 65 | } 66 | 67 | s2, err := client.Get(s) 68 | 69 | if err != nil { 70 | t.Fatal("Concurrent get", err.Error()) 71 | } 72 | 73 | if s != string(s2) { 74 | t.Fatal("Concurrent: value not the same") 75 | } 76 | 77 | client.Del(s) 78 | } 79 | 80 | func TestEmptyGet(t *testing.T) { 81 | _, err := client.Get("failerer") 82 | 83 | if err == nil { 84 | t.Fatal("Expected an error") 85 | } 86 | client.Set("a", []byte("12")) 87 | 88 | vals, err := client.Mget("a", "b") 89 | 90 | if err != nil { 91 | t.Fatal(err.Error()) 92 | } 93 | if vals[0] == nil || vals[1] != nil { 94 | t.Fatal("TestEmptyGet failed") 95 | } 96 | } 97 | 98 | func TestConcurrent(t *testing.T) { 99 | for i := 0; i < 20; i++ { 100 | go setget(t, i) 101 | } 102 | } 103 | 104 | func TestSet(t *testing.T) { 105 | var err error 106 | 107 | vals := []string{"a", "b", "c", "d", "e"} 108 | 109 | for _, v := range vals { 110 | client.Sadd("s", []byte(v)) 111 | } 112 | 113 | var members [][]byte 114 | 115 | if members, err = client.Smembers("s"); err != nil || len(members) != 5 { 116 | if err != nil { 117 | t.Fatal("Set setup failed", err.Error()) 118 | } else { 119 | t.Fatalf("Expected %d members but got %d", 5, len(members)) 120 | } 121 | } 122 | 123 | for _, v := range vals { 124 | if ok, err := client.Sismember("s", []byte(v)); err != nil || !ok { 125 | t.Fatal("Sismember test failed") 126 | } 127 | } 128 | 129 | for _, v := range vals { 130 | if ok, err := client.Srem("s", []byte(v)); err != nil || !ok { 131 | t.Fatal("Sismember test failed") 132 | } 133 | } 134 | 135 | if members, err = client.Smembers("s"); err != nil || len(members) != 0 { 136 | if err != nil { 137 | t.Fatal("Set setup failed", err.Error()) 138 | } else { 139 | t.Fatalf("Expected %d members but got %d", 0, len(members)) 140 | } 141 | } 142 | 143 | client.Del("s") 144 | 145 | } 146 | 147 | func TestList(t *testing.T) { 148 | //var err os.Error 149 | 150 | vals := []string{"a", "b", "c", "d", "e"} 151 | 152 | for _, v := range vals { 153 | client.Rpush("l", []byte(v)) 154 | } 155 | 156 | if l, err := client.Llen("l"); err != nil || l != 5 { 157 | if err != nil { 158 | t.Fatal("Llen failed", err.Error()) 159 | } else { 160 | t.Fatal("Llen failed, list wrong length", l) 161 | } 162 | } 163 | 164 | for i := 0; i < len(vals); i++ { 165 | if val, err := client.Lindex("l", i); err != nil || string(val) != vals[i] { 166 | if err != nil { 167 | t.Fatal("Lindex failed", err.Error()) 168 | } else { 169 | t.Fatalf("Expected %s but got %s", vals[i], string(val)) 170 | } 171 | } 172 | } 173 | 174 | for i := 0; i < len(vals); i++ { 175 | if err := client.Lset("l", i, []byte("a")); err != nil { 176 | t.Fatal("Lset failed", err.Error()) 177 | } 178 | } 179 | 180 | for i := 0; i < len(vals); i++ { 181 | if val, err := client.Lindex("l", i); err != nil || string(val) != "a" { 182 | if err != nil { 183 | t.Fatal("Lindex failed", err.Error()) 184 | } else { 185 | t.Fatalf("Expected %s but got %s", "a", string(val)) 186 | } 187 | } 188 | } 189 | 190 | client.Del("l") 191 | 192 | } 193 | 194 | func TestBrpop(t *testing.T) { 195 | go func() { 196 | time.Sleep(100 * 1000) 197 | if err := client.Lpush("l", []byte("a")); err != nil { 198 | t.Fatal("Lpush failed", err.Error()) 199 | } 200 | }() 201 | key, value, err := client.Brpop([]string{"l"}, 1) 202 | if err != nil { 203 | t.Fatal("Brpop failed", err.Error()) 204 | } 205 | if *key != "l" { 206 | t.Fatalf("Expected %s but got %s", "l", *key) 207 | } 208 | if string(value) != "a" { 209 | t.Fatalf("Expected %s but got %s", "a", string(value)) 210 | } 211 | } 212 | 213 | func TestBlpop(t *testing.T) { 214 | go func() { 215 | time.Sleep(100 * 1000) 216 | if err := client.Lpush("l", []byte("a")); err != nil { 217 | t.Fatal("Lpush failed", err.Error()) 218 | } 219 | }() 220 | key, value, err := client.Blpop([]string{"l"}, 1) 221 | if err != nil { 222 | t.Fatal("Blpop failed", err.Error()) 223 | } 224 | if *key != "l" { 225 | t.Fatalf("Expected %s but got %s", "l", *key) 226 | } 227 | if string(value) != "a" { 228 | t.Fatalf("Expected %s but got %s", "a", string(value)) 229 | } 230 | } 231 | 232 | func TestBrpopTimeout(t *testing.T) { 233 | key, value, err := client.Brpop([]string{"l"}, 1) 234 | if err != nil { 235 | t.Fatal("BrpopTimeout failed", err.Error()) 236 | } 237 | if key != nil { 238 | t.Fatalf("Expected nil but got '%s'", *key) 239 | } 240 | if value != nil { 241 | t.Fatalf("Expected nil but got '%s'", value) 242 | } 243 | } 244 | 245 | func TestBlpopTimeout(t *testing.T) { 246 | key, value, err := client.Blpop([]string{"l"}, 1) 247 | if err != nil { 248 | t.Fatal("BlpopTimeout failed", err.Error()) 249 | } 250 | if key != nil { 251 | t.Fatalf("Expected nil but got '%s'", *key) 252 | } 253 | if value != nil { 254 | t.Fatalf("Expected nil but got '%s'", value) 255 | } 256 | } 257 | 258 | /* 259 | 260 | func TestSubscribe(t *testing.T) { 261 | subscribe := make(chan string, 0) 262 | unsubscribe := make(chan string, 0) 263 | psubscribe := make(chan string, 0) 264 | punsubscribe := make(chan string, 0) 265 | messages := make(chan Message, 0) 266 | 267 | defer func() { 268 | close(subscribe) 269 | close(unsubscribe) 270 | close(psubscribe) 271 | close(punsubscribe) 272 | close(messages) 273 | }() 274 | go func() { 275 | if err := client.Subscribe(subscribe, unsubscribe, psubscribe, punsubscribe, messages); err != nil { 276 | t.Fatal("Subscribed failed", err.String()) 277 | } 278 | }() 279 | subscribe <- "ccc" 280 | 281 | data := []byte("foo") 282 | quit := make(chan bool, 0) 283 | defer close(quit) 284 | go func() { 285 | tick := time.Tick(10 * 1000 * 1000) // 10ms 286 | timeout := time.Tick(100 * 1000 * 1000) // 100ms 287 | 288 | for { 289 | select { 290 | case <-quit: 291 | return 292 | case <-timeout: 293 | t.Fatal("TestSubscribe timeout") 294 | case <-tick: 295 | if err := client.Publish("ccc", data); err != nil { 296 | t.Fatal("Pubish failed", err.String()) 297 | } 298 | } 299 | } 300 | }() 301 | 302 | msg := <-messages 303 | quit <- true 304 | if msg.Channel != "ccc" { 305 | t.Fatal("Unexpected channel name") 306 | } 307 | if string(msg.Message) != string(data) { 308 | t.Fatalf("Expected %s but got %s", string(data), string(msg.Message)) 309 | } 310 | close(subscribe) 311 | } 312 | 313 | func TestSimpleSubscribe(t *testing.T) { 314 | sub := make(chan string, 1) 315 | messages := make(chan Message, 0) 316 | go client.Subscribe(sub, nil, nil, nil, messages) 317 | 318 | sub <- "foo" 319 | time.Sleep(10 * 1000 * 1000) // 10ms 320 | data := "bar" 321 | client.Publish("foo", []byte(data)) 322 | 323 | msg := <-messages 324 | if string(msg.Message) != data { 325 | t.Fatalf("Expected %s but got %s", data, string(msg.Message)) 326 | } 327 | 328 | close(sub) 329 | close(messages) 330 | } 331 | 332 | func TestUnsubscribe(t *testing.T) { 333 | subscribe := make(chan string, 0) 334 | unsubscribe := make(chan string, 0) 335 | psubscribe := make(chan string, 0) 336 | punsubscribe := make(chan string, 0) 337 | messages := make(chan Message, 0) 338 | 339 | defer func() { 340 | close(subscribe) 341 | close(unsubscribe) 342 | close(psubscribe) 343 | close(punsubscribe) 344 | close(messages) 345 | }() 346 | go func() { 347 | if err := client.Subscribe(subscribe, unsubscribe, psubscribe, punsubscribe, messages); err != nil { 348 | t.Fatal("Subscribed failed", err.String()) 349 | } 350 | }() 351 | subscribe <- "ccc" 352 | 353 | data := []byte("foo") 354 | quit := make(chan bool, 0) 355 | defer close(quit) 356 | go func() { 357 | tick := time.Tick(10 * 1000 * 1000) // 10ms 358 | 359 | for i := 0; i < 10; i++ { 360 | <-tick 361 | if err := client.Publish("ccc", data); err != nil { 362 | t.Fatal("Pubish failed", err.String()) 363 | } 364 | } 365 | quit <- true 366 | }() 367 | 368 | msgs := 0 369 | for { 370 | select { 371 | case msg := <-messages: 372 | if string(msg.Message) != string(data) { 373 | t.Fatalf("Expected %s but got %s", string(data), string(msg.Message)) 374 | } 375 | 376 | // Unsubscribe after first message 377 | if msgs == 0 { 378 | unsubscribe <- "ccc" 379 | } 380 | msgs++ 381 | case <-quit: 382 | // Allow for a little delay and extra async messages getting through 383 | if msgs > 3 { 384 | t.Fatalf("Expected to have unsubscribed after 1 message but received %d", msgs) 385 | } 386 | return 387 | } 388 | } 389 | } 390 | 391 | 392 | func TestPSubscribe(t *testing.T) { 393 | subscribe := make(chan string, 0) 394 | unsubscribe := make(chan string, 0) 395 | psubscribe := make(chan string, 0) 396 | punsubscribe := make(chan string, 0) 397 | messages := make(chan Message, 0) 398 | 399 | defer func() { 400 | close(subscribe) 401 | close(unsubscribe) 402 | close(psubscribe) 403 | close(punsubscribe) 404 | close(messages) 405 | }() 406 | go func() { 407 | if err := client.Subscribe(subscribe, unsubscribe, psubscribe, punsubscribe, messages); err != nil { 408 | t.Fatal("Subscribed failed", err.String()) 409 | } 410 | }() 411 | psubscribe <- "ccc.*" 412 | 413 | data := []byte("foo") 414 | quit := make(chan bool, 0) 415 | defer close(quit) 416 | go func() { 417 | tick := time.Tick(10 * 1000 * 1000) // 10ms 418 | timeout := time.Tick(100 * 1000 * 1000) // 100ms 419 | 420 | for { 421 | select { 422 | case <-quit: 423 | return 424 | case <-timeout: 425 | t.Fatal("TestSubscribe timeout") 426 | case <-tick: 427 | if err := client.Publish("ccc.foo", data); err != nil { 428 | t.Fatal("Pubish failed", err.String()) 429 | } 430 | } 431 | } 432 | }() 433 | 434 | msg := <-messages 435 | quit <- true 436 | if msg.Channel != "ccc.foo" { 437 | t.Fatal("Unexpected channel name") 438 | } 439 | if msg.ChannelMatched != "ccc.*" { 440 | t.Fatal("Unexpected channel name") 441 | } 442 | if string(msg.Message) != string(data) { 443 | t.Fatalf("Expected %s but got %s", string(data), string(msg.Message)) 444 | } 445 | close(subscribe) 446 | } 447 | */ 448 | func verifyHash(t *testing.T, key string, expected map[string][]byte) { 449 | //test Hget 450 | m1 := make(map[string][]byte) 451 | for k, _ := range expected { 452 | actual, err := client.Hget(key, k) 453 | if err != nil { 454 | t.Fatal("verifyHash Hget failed", err.Error()) 455 | } 456 | m1[k] = actual 457 | } 458 | if !reflect.DeepEqual(m1, expected) { 459 | t.Fatal("verifyHash Hget failed") 460 | } 461 | 462 | //test Hkeys 463 | keys, err := client.Hkeys(key) 464 | if err != nil { 465 | t.Fatal("verifyHash Hkeys failed", err.Error()) 466 | } 467 | if len(keys) != len(expected) { 468 | fmt.Printf("%v\n", keys) 469 | t.Fatal("verifyHash Hkeys failed - length not equal") 470 | } 471 | for _, key := range keys { 472 | if expected[key] == nil { 473 | t.Fatal("verifyHash Hkeys failed missing key", key) 474 | } 475 | } 476 | 477 | //test Hvals 478 | vals, err := client.Hvals(key) 479 | if err != nil { 480 | t.Fatal("verifyHash Hvals failed", err.Error()) 481 | } 482 | if len(vals) != len(expected) { 483 | t.Fatal("verifyHash Hvals failed") 484 | } 485 | 486 | m2 := map[string][]byte{} 487 | //test Hgetall 488 | err = client.Hgetall(key, m2) 489 | if err != nil { 490 | t.Fatal("verifyHash Hgetall failed", err.Error()) 491 | } 492 | if !reflect.DeepEqual(m2, expected) { 493 | t.Fatal("verifyHash Hgetall failed") 494 | } 495 | } 496 | 497 | func TestSortedSet(t *testing.T) { 498 | svals := []string{"a", "b", "c", "d", "e"} 499 | ranks := []float64{0.0, 1.0, 2.0, 3.0, 4.0} 500 | vals := make([][]byte, len(svals)) 501 | for i := 0; i < len(svals); i++ { 502 | vals[i] = []byte(svals[i]) 503 | _, err := client.Zadd("zs", vals[i], ranks[i]) 504 | if err != nil { 505 | t.Fatal("zdd failed" + err.Error()) 506 | } 507 | score, err := client.Zscore("zs", vals[i]) 508 | if err != nil { 509 | t.Fatal("zscore failed" + err.Error()) 510 | } 511 | if score != ranks[i] { 512 | t.Fatal("zscore failed") 513 | } 514 | } 515 | 516 | card, err := client.Zcard("zs") 517 | if err != nil { 518 | t.Fatal("zcard failed" + err.Error()) 519 | } 520 | if card != 5 { 521 | t.Fatal("zcard failed", card) 522 | } 523 | for i := 0; i <= 4; i++ { 524 | data, _ := client.Zrange("zs", 0, i) 525 | if !reflect.DeepEqual(data, vals[0:i+1]) { 526 | t.Fatal("zrange failed") 527 | } 528 | } 529 | for i := 0; i <= 4; i++ { 530 | data, _ := client.Zrangebyscore("zs", 0, float64(i)) 531 | if !reflect.DeepEqual(data, vals[0:i+1]) { 532 | t.Fatal("zrangebyscore failed") 533 | } 534 | } 535 | //incremement 536 | for i := 0; i <= 4; i++ { 537 | client.Zincrby("zs", vals[i], 1) 538 | 539 | score, err := client.Zscore("zs", vals[i]) 540 | if err != nil { 541 | t.Fatal("zscore failed" + err.Error()) 542 | } 543 | if score != ranks[i]+1 { 544 | t.Fatal("zscore failed") 545 | } 546 | } 547 | 548 | for i := 0; i <= 4; i++ { 549 | client.Zincrby("zs", vals[i], -1) 550 | } 551 | 552 | //clean up 553 | _, err = client.Zrem("zs", []byte("a")) 554 | if err != nil { 555 | t.Fatal("zrem failed" + err.Error()) 556 | } 557 | 558 | _, err = client.Zremrangebyrank("zs", 0, 1) 559 | if err != nil { 560 | t.Fatal("zremrangebynrank failed" + err.Error()) 561 | } 562 | 563 | _, err = client.Zremrangebyscore("zs", 3, 4) 564 | if err != nil { 565 | t.Fatal("zremrangebyscore failed" + err.Error()) 566 | } 567 | 568 | card, err = client.Zcard("zs") 569 | if err != nil { 570 | t.Fatal("zcard failed" + err.Error()) 571 | } 572 | if card != 0 { 573 | t.Fatal("zcard failed", card) 574 | } 575 | 576 | client.Del("zs") 577 | } 578 | 579 | type tt struct { 580 | A, B, C, D, E string 581 | } 582 | 583 | func TestHash(t *testing.T) { 584 | //test cast 585 | keys := []string{"a", "b", "c", "d", "e"} 586 | test := make(map[string][]byte) 587 | for _, v := range keys { 588 | test[v] = []byte(strings.Repeat(v, 5)) 589 | } 590 | 591 | //set with hset 592 | for k, v := range test { 593 | client.Hset("h", k, []byte(v)) 594 | } 595 | //test hset 596 | verifyHash(t, "h", test) 597 | 598 | //set with hmset 599 | client.Hmset("h2", test) 600 | //test hset 601 | verifyHash(t, "h2", test) 602 | 603 | test3 := tt{"aaaaa", "bbbbb", "ccccc", "ddddd", "eeeee"} 604 | 605 | client.Hmset("h3", test3) 606 | //verifyHash(t, "h3", test) 607 | 608 | var test4 tt 609 | //test Hgetall 610 | err := client.Hgetall("h3", &test4) 611 | if err != nil { 612 | t.Fatal("verifyHash Hgetall failed", err.Error()) 613 | } 614 | if !reflect.DeepEqual(test4, test3) { 615 | t.Fatal("verifyHash Hgetall failed") 616 | } 617 | 618 | //text extraneous fields 619 | client.Hset("h3", "f", []byte("ffffff")) 620 | var test5 tt 621 | err = client.Hgetall("h3", &test5) 622 | if err != nil { 623 | t.Fatal("verifyHash Hgetall failed", err.Error()) 624 | } 625 | if !reflect.DeepEqual(test5, test3) { 626 | t.Fatal("verifyHash Hgetall failed") 627 | } 628 | 629 | err = client.Hgetall("hdne", &test5) 630 | if err == nil { 631 | t.Fatal("should be an error") 632 | } 633 | 634 | test6 := make(map[string]interface{}) 635 | for _, v := range keys { 636 | test6[v] = []byte(strings.Repeat(v, 5)) 637 | } 638 | client.Hmset("h4", test6) 639 | 640 | //test Hgetall 641 | test7 := make(map[string]interface{}) 642 | err = client.Hgetall("h4", &test7) 643 | if err != nil { 644 | t.Fatal("verifyHash Hgetall failed", err.Error()) 645 | } 646 | if !reflect.DeepEqual(test6, test7) { 647 | t.Fatal("verifyHash Hgetall failed") 648 | } 649 | 650 | client.Del("h") 651 | client.Del("h2") 652 | client.Del("h3") 653 | client.Del("h4") 654 | } 655 | 656 | func BenchmarkMultipleGet(b *testing.B) { 657 | client.Set("bmg", []byte("hi")) 658 | for i := 0; i < b.N; i++ { 659 | client.Get("bmg") 660 | } 661 | client.Del("bmg") 662 | } 663 | 664 | func BenchmarkMGet(b *testing.B) { 665 | client.Set("bmg", []byte("hi")) 666 | vals := make([]string, b.N) 667 | for i := 0; i < b.N; i++ { 668 | vals = append(vals, "bmg") 669 | } 670 | client.Mget(vals...) 671 | client.Del("bmg") 672 | } 673 | 674 | type testType struct { 675 | A, B, C string 676 | D, E, F int64 677 | } 678 | 679 | var testObj = testType{"A", "B", "C", 1, 2, 3} 680 | 681 | func BenchmarkJsonSet(b *testing.B) { 682 | for i := 0; i < b.N; i++ { 683 | data, _ := json.Marshal(testObj) 684 | client.Set("tjs", data) 685 | } 686 | client.Del("tjs") 687 | } 688 | 689 | func BenchmarkHmset(b *testing.B) { 690 | for i := 0; i < b.N; i++ { 691 | client.Hmset("tjs", testObj) 692 | } 693 | client.Del("tjs") 694 | } 695 | 696 | func BenchmarkJsonGet(b *testing.B) { 697 | data, _ := json.Marshal(testObj) 698 | client.Set("tjs", data) 699 | 700 | for i := 0; i < b.N; i++ { 701 | var tt testType 702 | data, _ := client.Get("tjs") 703 | json.Unmarshal(data, &tt) 704 | } 705 | client.Del("tjs") 706 | } 707 | 708 | func BenchmarkHgetall(b *testing.B) { 709 | client.Hmset("tjs", testObj) 710 | for i := 0; i < b.N; i++ { 711 | var tt testType 712 | client.Hgetall("tjs", &tt) 713 | } 714 | client.Del("tjs") 715 | } 716 | 717 | func BenchmarkJsonMget(b *testing.B) { 718 | od, _ := json.Marshal(testObj) 719 | client.Set("tjs", od) 720 | 721 | vals := make([]string, b.N) 722 | for i := 0; i < b.N; i++ { 723 | vals = append(vals, "tjs") 724 | } 725 | 726 | data, _ := client.Mget(vals...) 727 | for _, val := range data { 728 | var tt testType 729 | json.Unmarshal(val, &tt) 730 | } 731 | 732 | client.Del("tjs") 733 | } 734 | 735 | func BenchmarkHset(b *testing.B) { 736 | client.Hmset("tjs", testObj) 737 | for i := 0; i < b.N; i++ { 738 | client.Hset("tjs", "a", []byte("z")) 739 | } 740 | client.Del("tjs") 741 | } 742 | 743 | func BenchmarkJsonFieldSet(b *testing.B) { 744 | data, _ := json.Marshal(testObj) 745 | client.Set("tjs", data) 746 | 747 | for i := 0; i < b.N; i++ { 748 | var tt testType 749 | data, _ := client.Get("tjs") 750 | json.Unmarshal(data, &tt) 751 | tt.A = "z" 752 | data, _ = json.Marshal(tt) 753 | client.Set("tjs", data) 754 | } 755 | client.Del("tjs") 756 | } 757 | 758 | func BenchmarkZadd(b *testing.B) { 759 | for i := 0; i < b.N; i++ { 760 | client.Zadd("zrs", []byte("hi"+strconv.Itoa(i)), float64(i)) 761 | } 762 | client.Del("zrs") 763 | } 764 | 765 | func BenchmarkRpush(b *testing.B) { 766 | for i := 0; i < b.N; i++ { 767 | client.Rpush("zrs", []byte("hi"+strconv.Itoa(i))) 768 | } 769 | client.Del("zrs") 770 | } 771 | 772 | /* 773 | func TestTimeout(t *testing.T) { 774 | client.Set("a", []byte("hello world")) 775 | 776 | time.Sleep((serverTimeout+10) * 1e9) 777 | val, err := client.Get("a") 778 | 779 | if err != nil { 780 | t.Fatal(err.String()) 781 | } 782 | 783 | println(string(val)) 784 | } 785 | */ 786 | --------------------------------------------------------------------------------