| 194 | } |
| 195 | |
| 196 | func newQueryCache(sizeInBytes int64) *queryCache { |
| 197 | if sizeInBytes <= 100 { |
| 198 | panic(fmt.Sprintf("invalid cache size should be greater than 100: %v", sizeInBytes)) |
| 199 | } |
| 200 | cache, err := ristretto.NewCache(&ristretto.Config{ |
| 201 | // Use 5% of cache memory for storing counters. Each counter takes roughly 3 bytes. |
| 202 | // Recommended value is 10x the number of items in cache when full. |
| 203 | // Tune this again based on metrics. |
| 204 | NumCounters: int64(float64(sizeInBytes) * 0.05 / 3), |
| 205 | MaxCost: int64(float64(sizeInBytes) * 0.95), |
| 206 | BufferItems: 64, |
| 207 | Metrics: true, |
| 208 | }) |
| 209 | if err != nil { |
| 210 | panic(err) |
| 211 | } |
| 212 | |
| 213 | metrics := observability.Must(meter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { |
| 214 | observer.ObserveInt64(queryCacheHitsCounter, int64(cache.Metrics.Hits())) |
| 215 | observer.ObserveInt64(queryCacheMissesCounter, int64(cache.Metrics.Misses())) |
| 216 | observer.ObserveInt64(queryCacheItemCountGauge, int64(cache.Metrics.KeysAdded()-cache.Metrics.KeysEvicted())) |
| 217 | observer.ObserveInt64(queryCacheSizeBytesGauge, int64(cache.Metrics.CostAdded()-cache.Metrics.CostEvicted())) |
| 218 | return nil |
| 219 | }, queryCacheHitsCounter, queryCacheMissesCounter, queryCacheItemCountGauge, queryCacheSizeBytesGauge)) |
| 220 | |
| 221 | return &queryCache{ |
| 222 | cache: cache, |
| 223 | singleflight: &singleflight.Group[string, any]{}, |
| 224 | metrics: metrics, |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | func (c *queryCache) close() error { |
| 229 | c.cache.Close() |