From 1d98e4827ee1f741b43d0d434b1715d2a836ce1f Mon Sep 17 00:00:00 2001 From: jkoberg Date: Sat, 5 Mar 2022 12:57:57 +0100 Subject: [PATCH] add caching Signed-off-by: jkoberg --- settings/pkg/store/metadata/cache.go | 124 +++++++++++++++++++++++++++ settings/pkg/store/metadata/store.go | 7 +- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 settings/pkg/store/metadata/cache.go diff --git a/settings/pkg/store/metadata/cache.go b/settings/pkg/store/metadata/cache.go new file mode 100644 index 0000000000..f2e80d8ca2 --- /dev/null +++ b/settings/pkg/store/metadata/cache.go @@ -0,0 +1,124 @@ +package store + +import ( + "context" + "path" + "strings" + "time" + + "github.com/ReneKroon/ttlcache/v2" +) + +var ( + cachettl = 0 + // these need to be global instances for now as the `Service` (and therefore the `Store`) are instantiated twice (for grpc and http) + // therefore caches need to cover both instances + dircache = initCache(cachettl) + filescache = initCache(cachettl) +) + +// CachedMDC is cache for the metadataclient +type CachedMDC struct { + next MetadataClient + + files *ttlcache.Cache + dirs *ttlcache.Cache +} + +// SimpleDownload caches the answer from SimpleDownload or returns the cached one +func (c *CachedMDC) SimpleDownload(ctx context.Context, id string) ([]byte, error) { + if b, err := c.files.Get(id); err == nil { + return b.([]byte), nil + } + b, err := c.next.SimpleDownload(ctx, id) + if err != nil { + return nil, err + } + + c.files.Set(id, b) + return b, nil +} + +// SimpleUpload caches the answer from SimpleUpload and invalidates the cache +func (c *CachedMDC) SimpleUpload(ctx context.Context, id string, content []byte) error { + b, err := c.files.Get(id) + if err == nil && string(b.([]byte)) == string(content) { + // no need to bug mdc + return nil + } + + err = c.next.SimpleUpload(ctx, id, content) + if err != nil { + return err + } + + // invalidate caches + err = c.dirs.Remove(path.Dir(id)) + err = c.files.Set(id, content) + return nil +} + +// Delete invalidates the cache when operation was successful +func (c *CachedMDC) Delete(ctx context.Context, id string) error { + err := c.next.Delete(ctx, id) + if err != nil { + return err + } + + // invalidate caches + _ = removePrefix(c.files, id) + _ = removePrefix(c.dirs, id) + return nil +} + +// ReadDir caches the response from ReadDir or returnes the cached one +func (c *CachedMDC) ReadDir(ctx context.Context, id string) ([]string, error) { + i, err := c.dirs.Get(id) + if err == nil { + return i.([]string), nil + } + + s, err := c.next.ReadDir(ctx, id) + if err != nil { + return nil, err + } + + return s, c.dirs.Set(id, s) +} + +// MakeDirIfNotExist invalidates the cache +func (c *CachedMDC) MakeDirIfNotExist(ctx context.Context, id string) error { + err := c.next.MakeDirIfNotExist(ctx, id) + if err != nil { + return err + } + + // invalidate caches + _ = c.dirs.Remove(path.Dir(id)) + return nil +} + +// Init instantiates the caches +func (c *CachedMDC) Init(ctx context.Context, id string) error { + c.dirs = dircache + c.files = filescache + return c.next.Init(ctx, id) +} + +func initCache(ttlSeconds int) *ttlcache.Cache { + cache := ttlcache.NewCache() + _ = cache.SetTTL(time.Duration(ttlSeconds) * time.Second) + cache.SkipTTLExtensionOnHit(true) + return cache +} + +func removePrefix(cache *ttlcache.Cache, prefix string) error { + for _, k := range cache.GetKeys() { + if strings.HasPrefix(k, prefix) { + if err := cache.Remove(k); err != nil { + return err + } + } + } + return nil +} diff --git a/settings/pkg/store/metadata/store.go b/settings/pkg/store/metadata/store.go index 6ba5fcab2c..e0106f6ca7 100644 --- a/settings/pkg/store/metadata/store.go +++ b/settings/pkg/store/metadata/store.go @@ -58,8 +58,13 @@ func (s *Store) Init() { s.l.Lock() defer s.l.Unlock() + if s.mdc != nil { + return + } + //s.init.Do(func() { - if err := s.initMetadataClient(NewMetadataClient(s.cfg.Metadata)); err != nil { + mdc := &CachedMDC{next: NewMetadataClient(s.cfg.Metadata)} + if err := s.initMetadataClient(mdc); err != nil { s.Logger.Error().Err(err).Msg("error initializing metadata client") } //})