Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaime Martinez <jmartinez@gitlab.com>2021-09-15 09:23:58 +0300
committerJaime Martinez <jmartinez@gitlab.com>2021-09-15 09:23:58 +0300
commit9f74f03dd27ab0a5de5286a3e616ad14ae4528d5 (patch)
tree0f300464345663c07757be13273b065b93177b59
parent32f5682975cabde8923456e7948e17d0992c8fb7 (diff)
refactor: move lru cache to its own package
so it can be reused by different components like the zip vfs and the rate limiter. related to https://gitlab.com/gitlab-org/gitlab-pages/-/issues/626.
-rw-r--r--internal/lru/lru.go69
-rw-r--r--internal/vfs/zip/archive.go4
-rw-r--r--internal/vfs/zip/lru_cache.go62
-rw-r--r--internal/vfs/zip/vfs.go30
4 files changed, 94 insertions, 71 deletions
diff --git a/internal/lru/lru.go b/internal/lru/lru.go
new file mode 100644
index 00000000..21068175
--- /dev/null
+++ b/internal/lru/lru.go
@@ -0,0 +1,69 @@
+package lru
+
+import (
+ "time"
+
+ "github.com/karlseguin/ccache/v2"
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+// lruCacheGetPerPromote is a value that makes the item to be promoted
+// it is taken arbitrally as a sane value indicating that the item
+// was frequently picked
+// promotion moves the item to the front of the LRU list
+const getsPerPromote = 64
+
+// itemsToPruneDiv is a value that indicates how much items
+// needs to be pruned on OOM, this prunes 1/16 of items
+const itemsToPruneDiv = 16
+
+type lru struct {
+ op string
+ duration time.Duration
+ cache *ccache.Cache
+ metricCachedEntries *prometheus.GaugeVec
+ metricCacheRequests *prometheus.CounterVec
+}
+
+// New creates an LRU cache
+func New(op string, maxEntries int64, duration time.Duration, cachedEntriesMetric *prometheus.GaugeVec, cacheRequestsMetric *prometheus.CounterVec) *lru {
+ configuration := ccache.Configure()
+ configuration.MaxSize(maxEntries)
+ configuration.ItemsToPrune(uint32(maxEntries) / itemsToPruneDiv)
+ configuration.GetsPerPromote(getsPerPromote) // if item gets requested frequently promote it
+ configuration.OnDelete(func(*ccache.Item) {
+ cachedEntriesMetric.WithLabelValues(op).Dec()
+ })
+
+ return &lru{
+ op: op,
+ cache: ccache.New(configuration),
+ duration: duration,
+ metricCachedEntries: cachedEntriesMetric,
+ metricCacheRequests: cacheRequestsMetric,
+ }
+}
+
+// FindOrFetch will try to get the item from the cache if exists and is not expired.
+// If it can't find it, it will call fetchFn to retrieve the item and cache it.
+func (c *lru) FindOrFetch(cacheNamespace, key string, fetchFn func() (interface{}, error)) (interface{}, error) {
+ item := c.cache.Get(cacheNamespace + key)
+
+ if item != nil && !item.Expired() {
+ c.metricCacheRequests.WithLabelValues(c.op, "hit").Inc()
+ return item.Value(), nil
+ }
+
+ value, err := fetchFn()
+ if err != nil {
+ c.metricCacheRequests.WithLabelValues(c.op, "error").Inc()
+ return nil, err
+ }
+
+ c.metricCacheRequests.WithLabelValues(c.op, "miss").Inc()
+ c.metricCachedEntries.WithLabelValues(c.op).Inc()
+
+ c.cache.Set(cacheNamespace+key, value, c.duration)
+
+ return value, nil
+}
diff --git a/internal/vfs/zip/archive.go b/internal/vfs/zip/archive.go
index 3d6a9ff1..588fb76d 100644
--- a/internal/vfs/zip/archive.go
+++ b/internal/vfs/zip/archive.go
@@ -203,7 +203,7 @@ func (a *zipArchive) Open(ctx context.Context, name string) (vfs.File, error) {
return nil, errNotFile
}
- dataOffset, err := a.fs.dataOffsetCache.findOrFetch(a.cacheNamespace, name, func() (interface{}, error) {
+ dataOffset, err := a.fs.dataOffsetCache.FindOrFetch(a.cacheNamespace, name, func() (interface{}, error) {
return file.DataOffset()
})
if err != nil {
@@ -252,7 +252,7 @@ func (a *zipArchive) Readlink(ctx context.Context, name string) (string, error)
return "", errNotSymlink
}
- symlinkValue, err := a.fs.readlinkCache.findOrFetch(a.cacheNamespace, name, func() (interface{}, error) {
+ symlinkValue, err := a.fs.readlinkCache.FindOrFetch(a.cacheNamespace, name, func() (interface{}, error) {
rc, err := file.Open()
if err != nil {
return nil, err
diff --git a/internal/vfs/zip/lru_cache.go b/internal/vfs/zip/lru_cache.go
deleted file mode 100644
index 9810e245..00000000
--- a/internal/vfs/zip/lru_cache.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package zip
-
-import (
- "time"
-
- "github.com/karlseguin/ccache/v2"
-
- "gitlab.com/gitlab-org/gitlab-pages/metrics"
-)
-
-// lruCacheGetPerPromote is a value that makes the item to be promoted
-// it is taken arbitrally as a sane value indicating that the item
-// was frequently picked
-// promotion moves the item to the front of the LRU list
-const lruCacheGetsPerPromote = 64
-
-// lruCacheItemsToPruneDiv is a value that indicates how much items
-// needs to be pruned on OOM, this prunes 1/16 of items
-const lruCacheItemsToPruneDiv = 16
-
-type lruCache struct {
- op string
- duration time.Duration
- cache *ccache.Cache
-}
-
-func newLruCache(op string, maxEntries int64, duration time.Duration) *lruCache {
- configuration := ccache.Configure()
- configuration.MaxSize(maxEntries)
- configuration.ItemsToPrune(uint32(maxEntries) / lruCacheItemsToPruneDiv)
- configuration.GetsPerPromote(lruCacheGetsPerPromote) // if item gets requested frequently promote it
- configuration.OnDelete(func(*ccache.Item) {
- metrics.ZipCachedEntries.WithLabelValues(op).Dec()
- })
-
- return &lruCache{
- op: op,
- cache: ccache.New(configuration),
- duration: duration,
- }
-}
-
-func (c *lruCache) findOrFetch(cacheNamespace, key string, fetchFn func() (interface{}, error)) (interface{}, error) {
- item := c.cache.Get(cacheNamespace + key)
-
- if item != nil && !item.Expired() {
- metrics.ZipCacheRequests.WithLabelValues(c.op, "hit").Inc()
- return item.Value(), nil
- }
-
- value, err := fetchFn()
- if err != nil {
- metrics.ZipCacheRequests.WithLabelValues(c.op, "error").Inc()
- return nil, err
- }
-
- metrics.ZipCacheRequests.WithLabelValues(c.op, "miss").Inc()
- metrics.ZipCachedEntries.WithLabelValues(c.op).Inc()
-
- c.cache.Set(cacheNamespace+key, value, c.duration)
- return value, nil
-}
diff --git a/internal/vfs/zip/vfs.go b/internal/vfs/zip/vfs.go
index 1617c033..4118dd4a 100644
--- a/internal/vfs/zip/vfs.go
+++ b/internal/vfs/zip/vfs.go
@@ -8,13 +8,13 @@ import (
"sync"
"time"
- "gitlab.com/gitlab-org/gitlab-pages/internal/httpfs"
- "gitlab.com/gitlab-org/gitlab-pages/internal/httptransport"
-
"github.com/patrickmn/go-cache"
"gitlab.com/gitlab-org/gitlab-pages/internal/config"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/httpfs"
"gitlab.com/gitlab-org/gitlab-pages/internal/httprange"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/httptransport"
+ "gitlab.com/gitlab-org/gitlab-pages/internal/lru"
"gitlab.com/gitlab-org/gitlab-pages/internal/vfs"
"gitlab.com/gitlab-org/gitlab-pages/metrics"
)
@@ -35,6 +35,10 @@ var (
errAlreadyCached = errors.New("archive already cached")
)
+type lruCache interface {
+ FindOrFetch(cacheNamespace, key string, fetchFn func() (interface{}, error)) (interface{}, error)
+}
+
// zipVFS is a simple cached implementation of the vfs.VFS interface
type zipVFS struct {
cache *cache.Cache
@@ -45,8 +49,8 @@ type zipVFS struct {
cacheRefreshInterval time.Duration
cacheCleanupInterval time.Duration
- dataOffsetCache *lruCache
- readlinkCache *lruCache
+ dataOffsetCache lruCache
+ readlinkCache lruCache
// the `int64` needs to be 64bit aligned on some 32bit systems
// https://gitlab.com/gitlab-org/gitlab/-/issues/337261
@@ -80,8 +84,20 @@ func New(cfg *config.ZipServing) vfs.VFS {
zipVFS.resetCache()
// TODO: To be removed with https://gitlab.com/gitlab-org/gitlab-pages/-/issues/480
- zipVFS.dataOffsetCache = newLruCache("data-offset", defaultDataOffsetItems, defaultDataOffsetExpirationInterval)
- zipVFS.readlinkCache = newLruCache("readlink", defaultReadlinkItems, defaultReadlinkExpirationInterval)
+ zipVFS.dataOffsetCache = lru.New(
+ "data-offset",
+ defaultDataOffsetItems,
+ defaultDataOffsetExpirationInterval,
+ metrics.ZipCachedEntries,
+ metrics.ZipCacheRequests,
+ )
+ zipVFS.readlinkCache = lru.New(
+ "readlink",
+ defaultReadlinkItems,
+ defaultReadlinkExpirationInterval,
+ metrics.ZipCachedEntries,
+ metrics.ZipCacheRequests,
+ )
return zipVFS
}