From dfba5f289e31d2052d587f3ab8f63e5a94321c0d Mon Sep 17 00:00:00 2001 From: Stefan Geisbacher Date: Sun, 9 Dec 2018 16:47:30 +0100 Subject: [PATCH] added atomic list-append operation --- cache.go | 21 +++++++++++++++++++++ cache_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/cache.go b/cache.go index db88d2f..7a5ca77 100644 --- a/cache.go +++ b/cache.go @@ -81,6 +81,27 @@ func (c *cache) set(k string, x interface{}, d time.Duration) { } } +func (c *cache) Append(k string, x string) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %q not found", k) + } + rv, ok := v.Object.([]string) + if !ok { + c.mu.Unlock() + return fmt.Errorf("The value for %s is not an []string", k) + } + nv := append(rv, x) + v.Object = nv + c.items[k] = v + // TODO: Calls to mu.Unlock are currently not deferred because defer + // adds ~200 ns (as of go1.) + c.mu.Unlock() + return nil +} + // Add an item to the cache, replacing any existing item, using the default // expiration. func (c *cache) SetDefault(k string, x interface{}) { diff --git a/cache_test.go b/cache_test.go index cb80b38..0e2e868 100644 --- a/cache_test.go +++ b/cache_test.go @@ -68,6 +68,53 @@ func TestCache(t *testing.T) { } } +func TestAppend(t *testing.T) { + tc := New(NoExpiration, 0) + tc.Set("label1", []string{}, NoExpiration) + tc.Set("label2", []string{}, NoExpiration) + tc.Set("label3", []string{}, NoExpiration) + + tc.Append("label1", "article1") + tc.Append("label1", "article2") + tc.Append("label2", "article1") + tc.Append("label2", "article3") + tc.Append("label2", "article4") + tc.Append("label3", "article2") + + label1Articles, found := tc.Get("label1") + if !found { + t.Error("could not find key 'label1' after appending") + } + label2Articles, found := tc.Get("label2") + if !found { + t.Error("could not find key 'label2' after appending") + } + label3Articles, found := tc.Get("label3") + if !found { + t.Error("could not find key 'label3' after appending") + } + + if l := len(label1Articles.([]string)); l != 2 { + t.Errorf("'label1' should have 2 articles but has: %d", l) + } + if l := len(label2Articles.([]string)); l != 3 { + t.Errorf("'label2' should have 3 articles but has: %d", l) + } + if l := len(label3Articles.([]string)); l != 1 { + t.Errorf("'label3' should have 1 articles but has: %d", l) + } + + if v := label2Articles.([]string)[0]; v != "article1" { + t.Errorf("first article of label2 should be article1, but is: %s", v) + } + if v := label2Articles.([]string)[1]; v != "article3" { + t.Errorf("second article of label2 should be article3, but is: %s", v) + } + if v := label2Articles.([]string)[2]; v != "article4" { + t.Errorf("third article of label2 should be article4, but is: %s", v) + } +} + func TestCacheTimes(t *testing.T) { var found bool