Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added new cache package for query service #6733

Merged
merged 20 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cache
vikrantgupta25 marked this conversation as resolved.
Show resolved Hide resolved

import (
"os"
"time"

"go.signoz.io/signoz/pkg/cache/entity"
"go.signoz.io/signoz/pkg/cache/status"
"go.signoz.io/signoz/pkg/cache/strategy/memory"
"go.signoz.io/signoz/pkg/cache/strategy/redis"
"gopkg.in/yaml.v2"
)

type Options struct {
Name string `yaml:"-"`
Provider string `yaml:"provider"`
Redis *redis.Options `yaml:"redis,omitempty"`
Memory *memory.Options `yaml:"memory,omitempty"`
}

type Cache interface {
Connect() error
Store(cacheKey string, data entity.CacheableEntity, ttl time.Duration) error
Retrieve(cacheKey string, dest entity.CacheableEntity, allowExpired bool) (status.RetrieveStatus, error)
SetTTL(cacheKey string, ttl time.Duration)
Remove(cacheKey string)
BulkRemove(cacheKeys []string)
Close() error
}

// LoadFromYAMLCacheConfig loads the cache options from the given YAML config bytes
func LoadFromYAMLCacheConfig(yamlConfig []byte) (*Options, error) {
var options Options
err := yaml.Unmarshal(yamlConfig, &options)
if err != nil {
return nil, err
}
return &options, nil
}

// LoadFromYAMLCacheConfigFile loads the cache options from the given YAML config file
func LoadFromYAMLCacheConfigFile(configFile string) (*Options, error) {
bytes, err := os.ReadFile(configFile)
if err != nil {
return nil, err
}
return LoadFromYAMLCacheConfig(bytes)
}

func NewCache(opts *Options) Cache {
switch opts.Provider {
case "memory":
return memory.New(opts.Memory)
case "redis":
return redis.New(opts.Redis)
default:
return nil
}
}
25 changes: 25 additions & 0 deletions pkg/cache/entity/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package entity
vikrantgupta25 marked this conversation as resolved.
Show resolved Hide resolved

import (
"encoding"
"fmt"
"reflect"
)

type CacheableEntity interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
}

func WrapCacheableEntityErrors(rt reflect.Type, caller string) error {
if rt == nil {
return fmt.Errorf("%s: (nil)", caller)
}

if rt.Kind() != reflect.Pointer {
return fmt.Errorf("%s: (non-pointer \"%s\")", caller, rt.String())
}

return fmt.Errorf("%s: (nil \"%s\")", caller, rt.String())

}
33 changes: 33 additions & 0 deletions pkg/cache/status/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package status
vikrantgupta25 marked this conversation as resolved.
Show resolved Hide resolved

// RetrieveStatus defines the possible status of a cache lookup
type RetrieveStatus int

const (
RetrieveStatusHit = RetrieveStatus(iota)
RetrieveStatusPartialHit
RetrieveStatusRangeMiss
RetrieveStatusKeyMiss
RetrieveStatusRevalidated

RetrieveStatusError
)

func (s RetrieveStatus) String() string {
switch s {
case RetrieveStatusHit:
return "hit"
case RetrieveStatusPartialHit:
return "partial hit"
case RetrieveStatusRangeMiss:
return "range miss"
case RetrieveStatusKeyMiss:
return "key miss"
case RetrieveStatusRevalidated:
return "revalidated"
case RetrieveStatusError:
return "error"
default:
return "unknown"
}
}
100 changes: 100 additions & 0 deletions pkg/cache/strategy/memory/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package memory

import (
"fmt"
"reflect"
"time"

go_cache "github.com/patrickmn/go-cache"
"go.signoz.io/signoz/pkg/cache/entity"
"go.signoz.io/signoz/pkg/cache/status"
)

type cache struct {
cc *go_cache.Cache
}

func New(opts *Options) *cache {
if opts == nil {
opts = defaultOptions()
}

return &cache{cc: go_cache.New(opts.TTL, opts.CleanupInterval)}
}

// Connect does nothing
func (c *cache) Connect() error {
return nil
}

// Store stores the data in the cache
func (c *cache) Store(cacheKey string, data entity.CacheableEntity, ttl time.Duration) error {
// check if the data being passed is a pointer and is not nil
rv := reflect.ValueOf(data)
if rv.Kind() != reflect.Pointer || rv.IsNil() {
return entity.WrapCacheableEntityErrors(reflect.TypeOf(data), "inmemory")
}

c.cc.Set(cacheKey, data, ttl)
return nil
}

// Retrieve retrieves the data from the cache
func (c *cache) Retrieve(cacheKey string, dest entity.CacheableEntity, allowExpired bool) (status.RetrieveStatus, error) {
// check if the destination being passed is a pointer and is not nil
dstv := reflect.ValueOf(dest)
if dstv.Kind() != reflect.Pointer || dstv.IsNil() {
return status.RetrieveStatusError, entity.WrapCacheableEntityErrors(reflect.TypeOf(dest), "inmemory")
}

// check if the destination value is settable
if !dstv.Elem().CanSet() {
return status.RetrieveStatusError, fmt.Errorf("destination value is not settable, %s", dstv.Elem())
}

data, found := c.cc.Get(cacheKey)
if !found {
return status.RetrieveStatusKeyMiss, nil
}

// check the type compatbility between the src and dest
srcv := reflect.ValueOf(data)
if !srcv.Type().AssignableTo(dstv.Type()) {
return status.RetrieveStatusError, fmt.Errorf("src type is not assignable to dst type")
}

// set the value to from src to dest
dstv.Elem().Set(srcv.Elem())
return status.RetrieveStatusHit, nil
}

// SetTTL sets the TTL for the cache entry
func (c *cache) SetTTL(cacheKey string, ttl time.Duration) {
item, found := c.cc.Get(cacheKey)
if !found {
return
}
c.cc.Replace(cacheKey, item, ttl)
}

// Remove removes the cache entry
func (c *cache) Remove(cacheKey string) {
c.cc.Delete(cacheKey)
}

// BulkRemove removes the cache entries
func (c *cache) BulkRemove(cacheKeys []string) {
for _, cacheKey := range cacheKeys {
c.cc.Delete(cacheKey)
}
}

// Close does nothing
func (c *cache) Close() error {
return nil
}

// Configuration returns the cache configuration
func (c *cache) Configuration() *Options {
return nil
}
Loading
Loading