// 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 TestSet(t *testing.T) { values := map[string][]byte{ "key1": []byte("value"), "key2": encoding.Int64Bytes(2), } for _, cache := range testDrivers() { for key, value := range values { if err := cache.Set(key, value, 0); err != nil { tests.FailMsg(t, cache, "Expecting `key1` to be `value`") } val, _, _ := cache.Get(key) if !reflect.DeepEqual(val, value) { tests.FailMsg(t, cache, "Value for key `"+key+"` does not match.") } } cache.Set("key1", []byte("value"), -1) _, _, err := cache.Get("key1") if err == nil { tests.FailMsg(t, cache, "key1 should be deleted with negative value") } } }
func TestDeleteMulti(t *testing.T) { for _, cache := range testDrivers() { items := map[string][]byte{ "item1": encoding.Int64Bytes(1), "item2": []byte("string"), } cache.SetMulti(items, 0) cache.Set("key1", []byte("value1"), 0) var keys []string for k := range items { keys = append(keys, k) } cache.DeleteMulti(keys) if _, _, err := cache.Get("item1"); err == nil { tests.FailMsg(t, cache, "`item1` should be deleted from the cache.") } if _, _, err := cache.Get("item2"); err == nil { tests.FailMsg(t, cache, "`item2` should be deleted from the cache.") } tests.Compare(t, cache, "key1", "value1") } }
func TestGetMulti(t *testing.T) { for _, cache := range testDrivers() { items := map[string][]byte{ "item1": encoding.Int64Bytes(1), "item2": []byte("string"), } cache.SetMulti(items, 0) var keys []string for k := range items { keys = append(keys, k) } values, tokens, errs := cache.GetMulti(keys) _, val := binary.Varint(values["item1"]) if val != 1 { tests.FailMsg(t, cache, "Expected `item1` to equal `1`") } if err, ok := errs["item1"]; !ok || err != nil { tests.FailMsg(t, cache, "Expected `item1` to be ok.") } if tokens["item1"] == "" { tests.FailMsg(t, cache, "Expected `item1` to have a valid token.") } if string(values["item2"]) != "string" { tests.FailMsg(t, cache, "Expected `item2` to equal `string`") } } }
// 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 TestSetMulti(t *testing.T) { for _, cache := range testDrivers() { items := map[string][]byte{ "item1": encoding.Int64Bytes(1), "item2": []byte("string"), } cache.SetMulti(items, 0) tests.Compare(t, cache, "item1", 1) tests.Compare(t, cache, "item2", "string") } }