-
Notifications
You must be signed in to change notification settings - Fork 10
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(ARCO-283): remove freecache, combine methods with cache implementation #643
base: main
Are you sure you want to change the base?
Changes from all commits
a2b89c9
7fdcfe8
0e047e2
95d7f94
e331102
5aaa53a
d5e89e3
be7e65b
2b8f479
b3cddd5
cf3b643
e886abc
a130ce5
60353e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package cache | ||
|
||
import ( | ||
"errors" | ||
"sync" | ||
"time" | ||
) | ||
|
||
type MemoryStore struct { | ||
data sync.Map | ||
} | ||
|
||
func NewMemoryStore() *MemoryStore { | ||
return &MemoryStore{} | ||
} | ||
|
||
// Get retrieves a value by key and hash (if given) | ||
func (s *MemoryStore) Get(hash *string, key string) ([]byte, error) { | ||
if hash != nil { | ||
hashValue, found := s.data.Load(hash) | ||
if !found { | ||
return nil, ErrCacheNotFound | ||
} | ||
|
||
hashMap, ok := hashValue.(map[string][]byte) | ||
if !ok { | ||
return nil, ErrCacheFailedToGet | ||
} | ||
|
||
fieldValue, exists := hashMap[key] | ||
if !exists { | ||
return nil, ErrCacheNotFound | ||
} | ||
|
||
return fieldValue, nil | ||
} | ||
|
||
value, found := s.data.Load(key) | ||
if !found { | ||
return nil, ErrCacheNotFound | ||
} | ||
|
||
bytes, ok := value.([]byte) | ||
if !ok { | ||
return nil, ErrCacheFailedToGet | ||
} | ||
|
||
return bytes, nil | ||
} | ||
|
||
// Set stores a key-value pair, ignoring the ttl parameter. | ||
func (s *MemoryStore) Set(hash *string, key string, value []byte, _ time.Duration) error { | ||
if hash != nil { | ||
raw, _ := s.data.LoadOrStore(*hash, make(map[string][]byte)) | ||
|
||
hashMap, ok := raw.(map[string][]byte) | ||
if !ok { | ||
return ErrCacheFailedToSet | ||
} | ||
|
||
hashMap[key] = value | ||
|
||
s.data.Store(*hash, hashMap) | ||
return nil | ||
} | ||
|
||
s.data.Store(key, value) | ||
return nil | ||
} | ||
|
||
// Del removes a key from the store. | ||
func (s *MemoryStore) Del(hash *string, keys ...string) error { | ||
if hash != nil { | ||
hashValue, found := s.data.Load(*hash) | ||
if !found { | ||
return ErrCacheNotFound | ||
} | ||
|
||
hashMap, ok := hashValue.(map[string][]byte) | ||
if !ok { | ||
return errors.Join(ErrCacheFailedToDel, ErrCacheFailedToGet) | ||
} | ||
|
||
for _, k := range keys { | ||
delete(hashMap, k) | ||
} | ||
|
||
s.data.Store(*hash, hashMap) | ||
return nil | ||
} | ||
|
||
for _, k := range keys { | ||
s.data.Delete(k) | ||
} | ||
return nil | ||
} | ||
|
||
// GetAllForHash retrieves all key-value pairs for a specific hash. | ||
func (s *MemoryStore) GetAllForHash(hash string) (map[string][]byte, error) { | ||
hashValue, found := s.data.Load(hash) | ||
if !found { | ||
return nil, ErrCacheNotFound | ||
} | ||
|
||
hashMap, ok := hashValue.(map[string][]byte) | ||
if !ok { | ||
return nil, ErrCacheFailedToGet | ||
} | ||
|
||
return hashMap, nil | ||
} | ||
|
||
// CountElementsForHash returns the number of elements in a hash in memory. | ||
func (s *MemoryStore) CountElementsForHash(hash string) (int64, error) { | ||
hashMap, err := s.GetAllForHash(hash) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
return int64(len(hashMap)), nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,29 +22,54 @@ func NewRedisStore(ctx context.Context, c redis.UniversalClient) *RedisStore { | |
} | ||
} | ||
|
||
// Get retrieves a value by key. | ||
func (r *RedisStore) Get(key string) ([]byte, error) { | ||
result, err := r.client.Get(r.ctx, key).Result() | ||
// Get retrieves a value by key and hash (if given). | ||
func (r *RedisStore) Get(hash *string, key string) ([]byte, error) { | ||
var result string | ||
var err error | ||
|
||
if hash == nil { | ||
result, err = r.client.Get(r.ctx, key).Result() | ||
} else { | ||
result, err = r.client.HGet(r.ctx, *hash, key).Result() | ||
} | ||
Comment on lines
+25
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, we're only saving and returning a type RedisStore struct {
// ...
}
func (r *RedisStore) Get[T any](key string) (T, error) {
val, err := r.client.Get(r.ctx, key)
if err != nil {
return result, err
}
// Assume Redis values are stored as JSON strings
var result T
err = json.Unmarshal([]byte(val), &result)
if err != nil {
return result, ErrFailedToParseValue
}
return result, nil
}
func (r *RedisStore) Set[T any](key string, value T) error {
// Marshal the value into JSON
data, err := json.Marshal(value)
if err != nil {
return ErrFailedToMarshall
}
err = r.client.Set(r.ctx, key, string(data))
if err != nil {
return ErrCacheFailedToSet
}
return nil
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I'm going too far and this is not required, or maybe it's for another task, but I'm throwing the idea here. |
||
|
||
if errors.Is(err, redis.Nil) { | ||
return nil, ErrCacheNotFound | ||
} else if err != nil { | ||
return nil, errors.Join(ErrCacheFailedToGet, err) | ||
} | ||
|
||
return []byte(result), nil | ||
} | ||
|
||
// Set stores a value with a TTL. | ||
func (r *RedisStore) Set(key string, value []byte, ttl time.Duration) error { | ||
err := r.client.Set(r.ctx, key, value, ttl).Err() | ||
// Set stores a value with a TTL for a specific hash (if given). | ||
func (r *RedisStore) Set(hash *string, key string, value []byte, ttl time.Duration) error { | ||
var err error | ||
|
||
if hash == nil { | ||
err = r.client.Set(r.ctx, key, value, ttl).Err() | ||
} else { | ||
err = r.client.HSet(r.ctx, *hash, key, value).Err() | ||
} | ||
Comment on lines
+45
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're only passing in the |
||
|
||
if err != nil { | ||
return errors.Join(ErrCacheFailedToSet, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Del removes a value by key. | ||
func (r *RedisStore) Del(key string) error { | ||
result, err := r.client.Del(r.ctx, key).Result() | ||
func (r *RedisStore) Del(hash *string, keys ...string) error { | ||
var result int64 | ||
var err error | ||
|
||
if hash == nil { | ||
result, err = r.client.Del(r.ctx, keys...).Result() | ||
} else { | ||
result, err = r.client.HDel(r.ctx, *hash, keys...).Result() | ||
} | ||
|
||
if err != nil { | ||
return errors.Join(ErrCacheFailedToDel, err) | ||
} | ||
|
@@ -53,3 +78,26 @@ func (r *RedisStore) Del(key string) error { | |
} | ||
return nil | ||
} | ||
|
||
// GetAllForHash retrieves all key-value pairs for a specific hash. | ||
func (r *RedisStore) GetAllForHash(hash string) (map[string][]byte, error) { | ||
values, err := r.client.HGetAll(r.ctx, hash).Result() | ||
if err != nil { | ||
return nil, errors.Join(ErrCacheFailedToGet, err) | ||
} | ||
|
||
result := make(map[string][]byte) | ||
for field, value := range values { | ||
result[field] = []byte(value) | ||
} | ||
return result, nil | ||
} | ||
|
||
// CountElementsForHash returns the number of elements in a hash. | ||
func (r *RedisStore) CountElementsForHash(hash string) (int64, error) { | ||
count, err := r.client.HLen(r.ctx, hash).Result() | ||
if err != nil { | ||
return 0, errors.Join(ErrCacheFailedToGetCount, err) | ||
} | ||
return count, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
hash
is optional, as I understand, but because of that, in every method we have to checkif hash != nil
. Also, currently, we're always using a hash and never access the general store.What do you think about defining a list of
allowedHashSets
(e.g. incache.go
) with hashes of sets that we can use and only allow to use those HSets, instead of allowing using the general store? Or even better - just define those allowable sets in an Enum, which we can use everywhere in code.Then, you could also check if given hash exists in
allowedHashSets
(or enum) and if not - return an ErrNotAllowedSet or something like that. You wouldn't have to retrieve values in a different way depending on whether the hash exists or not and it would not have to be a pointer.