// incrementOffset is a common incrementor method used between Increment and // Decrement. If the key isn't set before, we will set the initial value. If // there is a value present, we will add the given offset to that value and // update the value with the new TTL. func (c *Cache) incrementOffset(key string, initial, offset, ttl int64) error { c.client.Do("WATCH", key) if err := c.exists(key); err != nil { c.client.Do("MULTI") defer c.client.Do("EXEC") return c.Set(key, encoding.Int64Bytes(initial), ttl) } getValue, _, err := c.Get(key) if err != nil { return err } val, ok := encoding.BytesInt64(getValue) if !ok { return errors.NewEncoding(key) } // We are watching our key. With using a transaction, we can check that this // increment doesn't inflect with another concurrent request that might // happen. c.client.Do("MULTI") defer c.client.Do("EXEC") val += offset if val < 0 { return errors.NewValueBelowZero(key) } return c.Set(key, encoding.Int64Bytes(val), ttl) }
func TestCache_Increment(t *testing.T) { for _, cache := range testDrivers() { cache.Increment("key1", 0, 1, 0) cache.Increment("key1", 0, 1, 0) v, _, _ := cache.Get("key1") num, _ := encoding.BytesInt64(v) if num != 1 { tests.FailMsg(t, cache, "Expected the value to be 1, got %d", num) } cache.Set("key2", []byte("string value, not incrementable"), 0) err := cache.Increment("key2", 0, 5, 0) if err == nil { tests.FailMsg(t, cache, "Expected the error not to be nil") } } }
// incrementOffset is a common incrementor method used between Increment and // Decrement. If the key isn't set before, we will set the initial value. If // there is a value present, we will add the given offset to that value and // update the value with the new TTL. func (c *Cache) incrementOffset(key string, initial, offset, ttl int64) error { if err := c.exists(key); err != nil { return c.Set(key, encoding.Int64Bytes(initial), ttl) } val, ok := encoding.BytesInt64(c.items[key].value) if !ok { return errors.NewEncoding(key) } val += offset if val < 0 { return errors.NewValueBelowZero(key) } return c.Set(key, encoding.Int64Bytes(val), ttl) }
func ExampleDecrement() { cacher.Flush() cacher.Decrement("key1", 10, 1, 0) cacher.Decrement("key1", 10, 3, 0) v, _, _ := cacher.Get("key1") num, _ := encoding.BytesInt64(v) fmt.Println(num) cacher.Set("key2", []byte("string value, not decrementable"), 0) ok := cacher.Decrement("key2", 0, 5, 0) v2, _, _ := cacher.Get("key2") fmt.Println(ok, string(v2)) // Output: // 7 // Value for key `key2` could not be encoded. string value, not decrementable }
// Compare compares a cached value given by the cache and the key to the value // passed in as an interface. If the values do not match, the given test will // receive a `FailNow()` call and print an appropiate error message. func Compare(t *testing.T, cache cacher.Cacher, key string, value interface{}) { _, ok := value.(int) if ok { val := int64(value.(int)) v, _, _ := cache.Get(key) valInt, _ := encoding.BytesInt64(v) if valInt != val { msg := "Expected `" + key + "` to equal `" + strconv.FormatInt(val, 10) + "`, is `" + strconv.FormatInt(valInt, 10) + "`" FailMsg(t, cache, msg) } } else { value = value.(string) if v, _, _ := cache.Get(key); string(v) != value { msg := "Expected `" + key + "` to equal `" + value.(string) + "`" FailMsg(t, cache, msg) } } }