-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3987 from aduffeck/cache-space-indexes
Cache space indexes
- Loading branch information
Showing
7 changed files
with
323 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Enhancement: cache space indexes | ||
|
||
decomposedfs now caches the different space indexes in memory which greatly improves the performance of ListStorageSpaces on slow storages. | ||
|
||
https://github.com/cs3org/reva/pull/3987 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package mtimesyncedcache | ||
|
||
import "sync" | ||
|
||
type Map[K comparable, V any] struct { | ||
m sync.Map | ||
} | ||
|
||
func (m *Map[K, V]) Delete(key K) { m.m.Delete(key) } | ||
|
||
func (m *Map[K, V]) Load(key K) (value V, ok bool) { | ||
v, ok := m.m.Load(key) | ||
if !ok { | ||
return value, ok | ||
} | ||
return v.(V), ok | ||
} | ||
|
||
func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) { | ||
v, loaded := m.m.LoadAndDelete(key) | ||
if !loaded { | ||
return value, loaded | ||
} | ||
return v.(V), loaded | ||
} | ||
|
||
func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { | ||
a, loaded := m.m.LoadOrStore(key, value) | ||
return a.(V), loaded | ||
} | ||
|
||
func (m *Map[K, V]) Range(f func(key K, value V) bool) { | ||
m.m.Range(func(key, value any) bool { return f(key.(K), value.(V)) }) | ||
} | ||
|
||
func (m *Map[K, V]) Store(key K, value V) { m.m.Store(key, value) } |
59 changes: 59 additions & 0 deletions
59
pkg/storage/utils/decomposedfs/mtimesyncedcache/mtimesyncedcache.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package mtimesyncedcache | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
type Cache[K comparable, T any] struct { | ||
entries Map[K, *entry[T]] | ||
} | ||
|
||
type entry[T any] struct { | ||
mtime time.Time | ||
value T | ||
|
||
mu sync.Mutex | ||
} | ||
|
||
func New[K comparable, T any]() Cache[K, T] { | ||
return Cache[K, T]{ | ||
entries: Map[K, *entry[T]]{}, | ||
} | ||
} | ||
|
||
func (c *Cache[K, T]) Store(key K, mtime time.Time, value T) error { | ||
c.entries.Store(key, &entry[T]{ | ||
mtime: mtime, | ||
value: value, | ||
}) | ||
return nil | ||
} | ||
|
||
func (c *Cache[K, T]) Load(key K) (T, bool) { | ||
entry, ok := c.entries.Load(key) | ||
if !ok { | ||
var t T | ||
return t, false | ||
} | ||
return entry.value, true | ||
} | ||
|
||
func (c *Cache[K, T]) LoadOrStore(key K, mtime time.Time, f func() (T, error)) (T, error) { | ||
e, _ := c.entries.LoadOrStore(key, &entry[T]{}) | ||
|
||
e.mu.Lock() | ||
defer e.mu.Unlock() | ||
if mtime.After(e.mtime) { | ||
e.mtime = mtime | ||
v, err := f() | ||
if err != nil { | ||
var t T | ||
return t, err | ||
} | ||
e.value = v | ||
c.entries.Store(key, e) | ||
} | ||
|
||
return e.value, nil | ||
} |
13 changes: 13 additions & 0 deletions
13
pkg/storage/utils/decomposedfs/mtimesyncedcache/mtimesyncedcache_suite_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package mtimesyncedcache_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestMtimesyncedcache(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Mtimesyncedcache Suite") | ||
} |
119 changes: 119 additions & 0 deletions
119
pkg/storage/utils/decomposedfs/mtimesyncedcache/mtimesyncedcache_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package mtimesyncedcache_test | ||
|
||
import ( | ||
"errors" | ||
"time" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/mtimesyncedcache" | ||
) | ||
|
||
var _ = Describe("Mtimesyncedcache", func() { | ||
var ( | ||
cache mtimesyncedcache.Cache[string, string] | ||
|
||
key = "key" | ||
value = "value" | ||
) | ||
|
||
BeforeEach(func() { | ||
cache = mtimesyncedcache.New[string, string]() | ||
}) | ||
|
||
Describe("Store", func() { | ||
It("stores a value", func() { | ||
time := time.Now() | ||
|
||
err := cache.Store(key, time, value) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
}) | ||
|
||
Describe("Load", func() { | ||
It("loads the stored value", func() { | ||
err := cache.Store(key, time.Now(), value) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
v, ok := cache.Load(key) | ||
Expect(ok).To(BeTrue()) | ||
Expect(v).To(Equal(value)) | ||
}) | ||
|
||
It("reports when the key doesn't exist", func() { | ||
_, ok := cache.Load("doesnotexist") | ||
Expect(ok).To(BeFalse()) | ||
}) | ||
}) | ||
|
||
Describe("LoadOrStore", func() { | ||
It("does not update the cache if the cache is up to date", func() { | ||
cachedTime := time.Now().Add(-1 * time.Hour) | ||
err := cache.Store(key, cachedTime, value) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
newvalue := "yaaay" | ||
v, err := cache.LoadOrStore(key, cachedTime, func() (string, error) { | ||
return newvalue, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(value)) | ||
|
||
v, err = cache.LoadOrStore(key, time.Now().Add(-2*time.Hour), func() (string, error) { | ||
return newvalue, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(value)) | ||
}) | ||
|
||
It("updates the cache if the cache is outdated", func() { | ||
outdatedTime := time.Now().Add(-1 * time.Hour) | ||
err := cache.Store(key, outdatedTime, value) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
newvalue := "yaaay" | ||
v, err := cache.LoadOrStore(key, time.Now(), func() (string, error) { | ||
return newvalue, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(newvalue)) | ||
}) | ||
|
||
It("stores the value if the key doesn't exist yet", func() { | ||
newvalue := "yaaay" | ||
v, err := cache.LoadOrStore(key, time.Now(), func() (string, error) { | ||
return newvalue, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(newvalue)) | ||
}) | ||
|
||
It("sets the mtime when storing the value", func() { | ||
newTime := time.Now() | ||
|
||
newvalue := "yaaay" | ||
v, err := cache.LoadOrStore(key, newTime, func() (string, error) { | ||
return newvalue, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(newvalue)) | ||
|
||
newvalue2 := "asdfasdf" | ||
v, err = cache.LoadOrStore(key, newTime, func() (string, error) { | ||
return newvalue2, nil | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
Expect(v).To(Equal(newvalue)) | ||
}) | ||
|
||
It("passes on error from the store func", func() { | ||
v, err := cache.LoadOrStore(key, time.Now(), func() (string, error) { | ||
return "", errors.New("baa") | ||
}) | ||
Expect(v).To(Equal("")) | ||
Expect(err.Error()).To(Equal("baa")) | ||
|
||
}) | ||
}) | ||
}) |
Oops, something went wrong.