[mirror] Scalable static site server for Git forges (like GitHub Pages)
1package git_pages
2
3import (
4 "context"
5 "time"
6
7 "github.com/maypok86/otter/v2"
8 "github.com/prometheus/client_golang/prometheus"
9)
10
11type weightedCacheEntry interface {
12 Weight() uint32
13}
14
15type trackedLoader[K comparable, V any] struct {
16 loader otter.Loader[K, V]
17 loaded bool
18 reloaded bool
19}
20
21func (l *trackedLoader[K, V]) Load(ctx context.Context, key K) (V, error) {
22 val, err := l.loader.Load(ctx, key)
23 l.loaded = true
24 return val, err
25}
26
27func (l *trackedLoader[K, V]) Reload(ctx context.Context, key K, oldValue V) (V, error) {
28 val, err := l.loader.Reload(ctx, key, oldValue)
29 l.reloaded = true
30 return val, err
31}
32
33type observedCacheMetrics struct {
34 HitNumberCounter prometheus.Counter
35 HitWeightCounter prometheus.Counter
36 MissNumberCounter prometheus.Counter
37 MissWeightCounter prometheus.Counter
38 EvictionNumberCounter prometheus.Counter
39 EvictionWeightCounter prometheus.Counter
40}
41
42type observedCache[K comparable, V weightedCacheEntry] struct {
43 Cache *otter.Cache[K, V]
44
45 metrics observedCacheMetrics
46}
47
48func newObservedCache[K comparable, V weightedCacheEntry](
49 options *otter.Options[K, V],
50 metrics observedCacheMetrics,
51) (*observedCache[K, V], error) {
52 c := &observedCache[K, V]{}
53 c.metrics = metrics
54
55 optionsCopy := *options
56 options = &optionsCopy
57 options.StatsRecorder = c
58
59 var err error
60 c.Cache, err = otter.New(options)
61 if err != nil {
62 return nil, err
63 }
64 return c, nil
65}
66
67func (c *observedCache[K, V]) Get(ctx context.Context, key K, loader otter.Loader[K, V]) (V, error) {
68 observedLoader := trackedLoader[K, V]{loader: loader}
69 val, err := c.Cache.Get(ctx, key, &observedLoader)
70 if err == nil {
71 if observedLoader.loaded {
72 if c.metrics.MissNumberCounter != nil {
73 c.metrics.MissNumberCounter.Inc()
74 }
75 if c.metrics.MissWeightCounter != nil {
76 c.metrics.MissWeightCounter.Add(float64(val.Weight()))
77 }
78 } else {
79 if c.metrics.HitNumberCounter != nil {
80 c.metrics.HitNumberCounter.Inc()
81 }
82 if c.metrics.HitWeightCounter != nil {
83 c.metrics.HitWeightCounter.Add(float64(val.Weight()))
84 }
85 }
86 }
87 return val, err
88}
89
90func (c *observedCache[K, V]) RecordHits(count int) {}
91func (c *observedCache[K, V]) RecordMisses(count int) {}
92func (c *observedCache[K, V]) RecordEviction(weight uint32) {
93 if c.metrics.EvictionNumberCounter != nil {
94 c.metrics.EvictionNumberCounter.Inc()
95 }
96 if c.metrics.EvictionWeightCounter != nil {
97 c.metrics.EvictionWeightCounter.Add(float64(weight))
98 }
99}
100func (c *observedCache[K, V]) RecordLoadSuccess(loadTime time.Duration) {}
101func (c *observedCache[K, V]) RecordLoadFailure(loadTime time.Duration) {}