diff --git a/lib/cache/cache.go b/lib/cache/cache.go index 268ce4e88..9fe406f3c 100644 --- a/lib/cache/cache.go +++ b/lib/cache/cache.go @@ -3,6 +3,7 @@ package cache import ( + "strings" "sync" "time" ) @@ -119,6 +120,32 @@ func (c *Cache) GetMaybe(key string) (value interface{}, found bool) { return entry.value, found } +// Delete the entry passed in +// +// Returns true if the entry was found +func (c *Cache) Delete(key string) bool { + c.mu.Lock() + _, found := c.cache[key] + delete(c.cache, key) + c.mu.Unlock() + return found +} + +// DeletePrefix deletes all entries with the given prefix +// +// Returns number of entries deleted +func (c *Cache) DeletePrefix(prefix string) (deleted int) { + c.mu.Lock() + for k := range c.cache { + if strings.HasPrefix(k, prefix) { + delete(c.cache, k) + deleted++ + } + } + c.mu.Unlock() + return deleted +} + // Rename renames the item at oldKey to newKey. // // If there was an existing item at newKey then it takes precedence diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index d2811e66d..f5530cacc 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -28,6 +28,8 @@ func setup(t *testing.T) (*Cache, CreateFunc) { return "/file.txt", true, errCached case "/error": return nil, false, errSentinel + case "/err": + return nil, false, errSentinel } panic(fmt.Sprintf("Unknown path %q", path)) } @@ -225,6 +227,53 @@ func TestGetMaybe(t *testing.T) { assert.Nil(t, value) } +func TestDelete(t *testing.T) { + c, create := setup(t) + + assert.Equal(t, 0, len(c.cache)) + + _, err := c.Get("/", create) + require.NoError(t, err) + + assert.Equal(t, 1, len(c.cache)) + + assert.Equal(t, false, c.Delete("notfound")) + assert.Equal(t, 1, len(c.cache)) + + assert.Equal(t, true, c.Delete("/")) + assert.Equal(t, 0, len(c.cache)) + + assert.Equal(t, false, c.Delete("/")) + assert.Equal(t, 0, len(c.cache)) +} + +func TestDeletePrefix(t *testing.T) { + create := func(path string) (interface{}, bool, error) { + return path, true, nil + } + c := New() + + _, err := c.Get("remote:path", create) + require.NoError(t, err) + _, err = c.Get("remote:path2", create) + require.NoError(t, err) + _, err = c.Get("remote:", create) + require.NoError(t, err) + _, err = c.Get("remote", create) + require.NoError(t, err) + + assert.Equal(t, 4, len(c.cache)) + + assert.Equal(t, 3, c.DeletePrefix("remote:")) + assert.Equal(t, 1, len(c.cache)) + + assert.Equal(t, 1, c.DeletePrefix("")) + assert.Equal(t, 0, len(c.cache)) + + assert.Equal(t, 0, c.DeletePrefix("")) + assert.Equal(t, 0, len(c.cache)) +} + func TestCacheRename(t *testing.T) { c := New() create := func(path string) (interface{}, bool, error) {