// GetVoteComics returns two comics that should face off against each other in // a vote-duel. One comic is chosen randomly, the other is approximately chosen // from the 1% least voted-on comics. func GetVoteComics() (c1, c2 Comic, err error) { C := pool.Get() defer C.Close() var ( candidates []string votes int ) candidates, err = redis.Strings(C.Do("SRANDMEMBER", "strips", 100)) if err != nil { return } // Choose one comic at random (this is needed because Redis' SRANDMEMBER // function isn't random enough) idx := rand.Intn(len(candidates)) c1, err = GetComic(candidates[idx]) if err != nil { return } // Remove the chosen comic from the rotation, so we don't duel a comic // against itself candidates[idx] = candidates[len(candidates)-1] candidates = candidates[:len(candidates)-1] // Find the comic with the least totalvotes among the remaining candidates minVotes := 1<<31 - 1 bestStrip := "" for _, strip := range candidates { votes, err = redis.Int(C.Do("HGET", fmt.Sprintf(stripsFmt, strip), "totalvotes")) if votes < minVotes { bestStrip = strip minVotes = votes } } c2, err = GetComic(bestStrip) if err != nil { return } return }
// GetPage returns the contents of the requested page number func GetPage(page string) (List, error) { C := pool.Get() defer C.Close() p, err := strconv.Atoi(page) if err != nil || p < 1 { return List{}, errors.New("invalid page: " + page) } comics, err := redis.Strings(C.Do("ZREVRANGE", "ranked", (p-1)*pageSize, (p-1)*pageSize+pageSize-1)) if err != nil { return List{}, errors.New("could not retrieve comics for page " + page) } if len(comics) == 0 { return List{}, errors.New("invalid page: " + page) } numPages := 0 numComics, err := redis.Int(C.Do("SCARD", "strips")) if err != nil { numPages = p } else { numPages = numComics / pageSize if numComics%pageSize != 0 { numPages++ } } result := List{ CurPage: p, LastPage: numPages, ListStart: (p-1)*pageSize + 1, Comics: make([]Comic, 0, pageSize), } for _, c := range comics { comic, err := GetComic(c) if err == nil { result.Comics = append(result.Comics, comic) } } return result, nil }
// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. func zpop(c redis.Conn, key string) (result string, err error) { defer func() { // Return connection to normal state on error. if err != nil { c.Do("DISCARD") } }() // Loop until transaction is successful. for { if _, err := c.Do("WATCH", key); err != nil { return "", err } members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) if err != nil { return "", err } if len(members) != 1 { return "", redis.ErrNil } c.Send("MULTI") c.Send("ZREM", key, members[0]) queued, err := c.Do("EXEC") if err != nil { return "", err } if queued != nil { result = members[0] break } } return result, nil }
actual valueError expected valueError }{ { "ints([v1, v2])", ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), ve([]int{4, 5}, nil), }, { "ints(nil)", ve(redis.Ints(nil, nil)), ve([]int(nil), redis.ErrNil), }, { "strings([v1, v2])", ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), ve([]string{"v1", "v2"}, nil), }, { "strings(nil)", ve(redis.Strings(nil, nil)), ve([]string(nil), redis.ErrNil), }, { "values([v1, v2])", ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), }, { "values(nil)", ve(redis.Values(nil, nil)),