eko/gocache

Multiple caches, one metric

twolf-adc opened this issue · 2 comments

I currently have multiple caches, that I don't want to join as one. However, in my Prometheus metrics I want all of these caches to use the same metric aka namespace, where each cache has its uniquely identifying label. This way I can easily query Prometheus, showing me the metric over all caches or filtered to (via the label) the one cache I want.

What I tried
Attempting to call cachemetrics.NewPrometheus() multiple times with the same namespace but setting some label differently. This results in a runtime panic (panic: duplicate metrics collector registration attempted).

Other than that, I don't see a way to achieve this. Am I missing something, any suggestion?

Platforms:

alpine Linux

Versions:

v4.1.5

If this is not possible currently, is this a feature that you'd be willing to accept as a contribution from me?

If you use two different type of storage, they should uniquely identified by the store label.

# HELP cache_collector This represent the number of items in cache
# TYPE cache_collector gauge
cache_collector{metric="delete_error",service="cache-app",store="go-cache"} 0
cache_collector{metric="delete_error",service="cache-app",store="hazelcast"} 0
cache_collector{metric="delete_success",service="cache-app",store="go-cache"} 0
cache_collector{metric="delete_success",service="cache-app",store="hazelcast"} 0
cache_collector{metric="hit_count",service="cache-app",store="go-cache"} 0
cache_collector{metric="hit_count",service="cache-app",store="hazelcast"} 0
cache_collector{metric="invalidate_error",service="cache-app",store="go-cache"} 0
cache_collector{metric="invalidate_error",service="cache-app",store="hazelcast"} 0
cache_collector{metric="invalidate_success",service="cache-app",store="go-cache"} 0
cache_collector{metric="invalidate_success",service="cache-app",store="hazelcast"} 0
cache_collector{metric="miss_count",service="cache-app",store="go-cache"} 0
cache_collector{metric="miss_count",service="cache-app",store="hazelcast"} 0
cache_collector{metric="set_error",service="cache-app",store="go-cache"} 0
cache_collector{metric="set_error",service="cache-app",store="hazelcast"} 0
cache_collector{metric="set_success",service="cache-app",store="go-cache"} 0
cache_collector{metric="set_success",service="cache-app",store="hazelcast"} 0
# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler.
# TYPE promhttp_metric_handler_errors_total counter
promhttp_metric_handler_errors_total{cause="encoding"} 0
promhttp_metric_handler_errors_total{cause="gathering"} 0

If you use the same type of storage, it seems that the API doesn't provide direct support. However, there's a workaround you can use.

You can create a storage wrapper type to customize the store type modifying GetType function , so that the store value in the labels will be differentiated.

type StoreInterfaceWrapper struct {
	store.StoreInterface
	t string
}

func (c *StoreInterfaceWrapper) GetType() string {
	return fmt.Sprintf("%s-%s", c.StoreInterface.GetType(), c.t)
}
gocacheClientOne := gocache.New(5*time.Minute, 10*time.Minute)
gocacheStoreOne := gocache_store.NewGoCache(gocacheClientOne)
cacheManagerOne := cache.New[[]byte](&StoreInterfaceWrapper{StoreInterface: gocacheStoreOne, t: "one"})

gocacheClientTwo := gocache.New(5*time.Minute, 10*time.Minute)
gocacheStoreTwo := gocache_store.NewGoCache(gocacheClientTwo)
cacheManagerTwo := cache.New[[]byte](&StoreInterfaceWrapper{StoreInterface: gocacheStoreTwo, t: "two"})

reg := prometheus.NewRegistry()
p := metrics.NewPrometheus("cache-app", metrics.WithRegisterer(reg), metrics.WithNamespace("cache"))
go func() {
for {
	fmt.Println("sending metrics")
	p.RecordFromCodec(cacheManagerOne.GetCodec())
	p.RecordFromCodec(cacheManagerTwo.GetCodec())
	time.Sleep(time.Second * 5)
}
}()

http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
# HELP cache_collector This represent the number of items in cache
# TYPE cache_collector gauge
cache_collector{metric="delete_error",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="delete_error",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="delete_success",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="delete_success",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="hit_count",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="hit_count",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="invalidate_error",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="invalidate_error",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="invalidate_success",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="invalidate_success",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="miss_count",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="miss_count",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="set_error",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="set_error",service="cache-app",store="go-cache-two"} 0
cache_collector{metric="set_success",service="cache-app",store="go-cache-one"} 0
cache_collector{metric="set_success",service="cache-app",store="go-cache-two"} 0
# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler.
# TYPE promhttp_metric_handler_errors_total counter
promhttp_metric_handler_errors_total{cause="encoding"} 0
promhttp_metric_handler_errors_total{cause="gathering"} 0

@eko I think it should be supported in the API.