mirror of
https://github.com/eosswedenorg/thalos
synced 2026-06-19 04:50:02 +02:00
rename app folder to internal.
This commit is contained in:
parent
afb90af1db
commit
9974bfe3fd
28 changed files with 23 additions and 23 deletions
31
internal/cache/cache.go
vendored
Normal file
31
internal/cache/cache.go
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
store Store
|
||||
prefix string
|
||||
}
|
||||
|
||||
// Create a new cache
|
||||
func NewCache(prefix string, store Store) *Cache {
|
||||
return &Cache{
|
||||
store: store,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *Cache) Get(ctx context.Context, key string, value any) error {
|
||||
return cache.store.Get(ctx, cache.key(key), value)
|
||||
}
|
||||
|
||||
func (cache *Cache) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
|
||||
return cache.store.Set(ctx, cache.key(key), value, ttl)
|
||||
}
|
||||
|
||||
func (cache *Cache) key(key string) string {
|
||||
return cache.prefix + "::" + key
|
||||
}
|
||||
61
internal/cache/memory_store.go
vendored
Normal file
61
internal/cache/memory_store.go
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Store time function in a variable.
|
||||
// Makes it easy to travel in time when testing.
|
||||
var now = time.Now
|
||||
|
||||
type memoryStoreItem struct {
|
||||
// Actual value stored.
|
||||
value any
|
||||
|
||||
// Cache expiration time.
|
||||
expired time.Time
|
||||
}
|
||||
|
||||
type MemoryStore struct {
|
||||
data map[string]memoryStoreItem
|
||||
}
|
||||
|
||||
func NewMemoryStore() *MemoryStore {
|
||||
return &MemoryStore{make(map[string]memoryStoreItem)}
|
||||
}
|
||||
|
||||
func (s *MemoryStore) Get(ctx context.Context, key string, value any) error {
|
||||
if item, ok := s.data[key]; ok {
|
||||
|
||||
if item.expired.Before(now()) {
|
||||
delete(s.data, key)
|
||||
return fmt.Errorf("key: %s does not exist", key)
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(value)
|
||||
if v.Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("value must be of pointer type, '%s' passed", v.Kind().String())
|
||||
}
|
||||
|
||||
v.Elem().Set(reflect.ValueOf(item.value))
|
||||
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("key: %s does not exist", key)
|
||||
}
|
||||
|
||||
func (s *MemoryStore) Has(ctx context.Context, key string) bool {
|
||||
_, hit := s.data[key]
|
||||
return hit
|
||||
}
|
||||
|
||||
func (s *MemoryStore) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
|
||||
s.data[key] = memoryStoreItem{
|
||||
value: value,
|
||||
expired: now().Add(ttl),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
88
internal/cache/memory_store_test.go
vendored
Normal file
88
internal/cache/memory_store_test.go
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type memoryTestItem struct {
|
||||
String string
|
||||
Num uint32
|
||||
Float float32
|
||||
}
|
||||
|
||||
func TestMemoryStore_Set(t *testing.T) {
|
||||
now = func() time.Time { return time.Unix(1581315270, 0) }
|
||||
|
||||
item := memoryTestItem{
|
||||
String: "MyString",
|
||||
Num: 23,
|
||||
Float: 3.14,
|
||||
}
|
||||
|
||||
expected := map[string]memoryStoreItem{
|
||||
"key1": {
|
||||
value: item,
|
||||
expired: now().Add(time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
store := NewMemoryStore()
|
||||
err := store.Set(context.Background(), "key1", item, time.Hour)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, store.data)
|
||||
}
|
||||
|
||||
func TestMemoryStore_GetMiss(t *testing.T) {
|
||||
store := NewMemoryStore()
|
||||
|
||||
var v any
|
||||
err := store.Get(context.Background(), "Key2", &v)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMemoryStore_GetHit(t *testing.T) {
|
||||
expected := memoryTestItem{
|
||||
String: "MyString",
|
||||
Num: 23,
|
||||
Float: 3.14,
|
||||
}
|
||||
|
||||
store := NewMemoryStore()
|
||||
err := store.Set(context.Background(), "key1", expected, time.Hour)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var actual memoryTestItem
|
||||
err = store.Get(context.Background(), "key1", &actual)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestMemoryStore_GetNonPointer(t *testing.T) {
|
||||
expected := memoryTestItem{
|
||||
String: "MyString",
|
||||
Num: 23,
|
||||
Float: 3.14,
|
||||
}
|
||||
|
||||
store := NewMemoryStore()
|
||||
err := store.Set(context.Background(), "key1", expected, time.Hour)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var actual string
|
||||
err = store.Get(context.Background(), "key1", actual)
|
||||
assert.EqualError(t, err, "value must be of pointer type, 'string' passed")
|
||||
}
|
||||
|
||||
func TestMemoryStore_Has(t *testing.T) {
|
||||
store := NewMemoryStore()
|
||||
err := store.Set(context.Background(), "key1", "value", time.Hour)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, store.Has(context.Background(), "key1"))
|
||||
assert.False(t, store.Has(context.Background(), "key2"))
|
||||
}
|
||||
35
internal/cache/redis_store.go
vendored
Normal file
35
internal/cache/redis_store.go
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/cache/v9"
|
||||
)
|
||||
|
||||
type RedisStore struct {
|
||||
c *cache.Cache
|
||||
}
|
||||
|
||||
func NewRedisStore(options *cache.Options) *RedisStore {
|
||||
return &RedisStore{
|
||||
c: cache.New(options),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RedisStore) Get(ctx context.Context, key string, value interface{}) error {
|
||||
return s.c.Get(ctx, key, value)
|
||||
}
|
||||
|
||||
func (s *RedisStore) Has(ctx context.Context, key string) bool {
|
||||
return s.c.Exists(ctx, key)
|
||||
}
|
||||
|
||||
func (s *RedisStore) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
|
||||
return s.c.Set(&cache.Item{
|
||||
Ctx: ctx,
|
||||
Key: key,
|
||||
Value: value,
|
||||
TTL: ttl,
|
||||
})
|
||||
}
|
||||
104
internal/cache/redis_store_test.go
vendored
Normal file
104
internal/cache/redis_store_test.go
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redismock/v9"
|
||||
|
||||
redis_cache "github.com/go-redis/cache/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testItem struct {
|
||||
Num uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
func TestRedisStore_Set(t *testing.T) {
|
||||
client, mock := redismock.NewClientMock()
|
||||
|
||||
store := NewRedisStore(&redis_cache.Options{
|
||||
Redis: client,
|
||||
})
|
||||
|
||||
expected := testItem{
|
||||
Num: 24,
|
||||
Name: "Some Name",
|
||||
}
|
||||
|
||||
bytes, err := store.c.Marshal(expected)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mock.ExpectSet("mykey", bytes, time.Minute).SetVal("OK")
|
||||
|
||||
err = store.Set(context.Background(), "mykey", expected, time.Minute)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
}
|
||||
|
||||
func TestRedisStore_GetMiss(t *testing.T) {
|
||||
client, mock := redismock.NewClientMock()
|
||||
|
||||
store := NewRedisStore(&redis_cache.Options{
|
||||
Redis: client,
|
||||
})
|
||||
|
||||
mock.ExpectGet("mykey").SetErr(redis_cache.ErrCacheMiss)
|
||||
|
||||
expected := testItem{}
|
||||
err := store.Get(context.Background(), "mykey", &expected)
|
||||
assert.ErrorIs(t, err, redis_cache.ErrCacheMiss)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
}
|
||||
|
||||
func TestRedisStore_GetHit(t *testing.T) {
|
||||
client, mock := redismock.NewClientMock()
|
||||
|
||||
store := NewRedisStore(&redis_cache.Options{
|
||||
Redis: client,
|
||||
})
|
||||
|
||||
expected := testItem{
|
||||
Num: 42,
|
||||
Name: "MyName",
|
||||
}
|
||||
|
||||
bytes, err := store.c.Marshal(expected)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mock.ExpectSet("mykey2", bytes, time.Second*20).SetVal("OK")
|
||||
mock.ExpectGet("mykey2").SetVal(string(bytes))
|
||||
|
||||
err = store.Set(context.Background(), "mykey2", expected, time.Second*20)
|
||||
assert.NoError(t, err)
|
||||
|
||||
actual := testItem{}
|
||||
err = store.Get(context.Background(), "mykey2", &actual)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
}
|
||||
|
||||
func TestRedisStore_Has(t *testing.T) {
|
||||
client, mock := redismock.NewClientMock()
|
||||
|
||||
store := NewRedisStore(&redis_cache.Options{
|
||||
Redis: client,
|
||||
})
|
||||
|
||||
bytes, err := store.c.Marshal("value")
|
||||
assert.NoError(t, err)
|
||||
|
||||
mock.ExpectSet("key1", bytes, time.Minute*15).SetVal("OK")
|
||||
mock.ExpectGet("key1").SetVal(string(bytes))
|
||||
mock.ExpectGet("key2").RedisNil()
|
||||
|
||||
err = store.Set(context.Background(), "key1", "value", time.Minute*15)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, store.Has(context.Background(), "key1"))
|
||||
assert.False(t, store.Has(context.Background(), "key2"))
|
||||
assert.NoError(t, mock.ExpectationsWereMet())
|
||||
}
|
||||
19
internal/cache/store.go
vendored
Normal file
19
internal/cache/store.go
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Interface to a cache storage.
|
||||
type Store interface {
|
||||
// Set an item in the store.
|
||||
Set(ctx context.Context, key string, value any, TTL time.Duration) error
|
||||
|
||||
// Get an item from the store.
|
||||
// returns an error if key is not found or there is other problems.
|
||||
Get(ctx context.Context, key string, value any) error
|
||||
|
||||
// Check if a key exist in the store.
|
||||
Has(ctx context.Context, key string) bool
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue