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!: add default expiration (5m) for Redis #335

Merged
merged 1 commit into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions configs/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ open_saves_cloud: "gcp"
open_saves_bucket: ""
open_saves_project: ""
log_level: "info"
cache_default_ttl: "5m"

redis_address: "localhost:6379"
redis_max_idle: 500
Expand Down
3 changes: 2 additions & 1 deletion internal/app/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/googleforgames/open-saves/internal/pkg/blob"
"github.com/googleforgames/open-saves/internal/pkg/cache"
"github.com/googleforgames/open-saves/internal/pkg/cache/redis"
"github.com/googleforgames/open-saves/internal/pkg/config"
"github.com/googleforgames/open-saves/internal/pkg/metadb"
"github.com/googleforgames/open-saves/internal/pkg/metadb/blobref"
"github.com/googleforgames/open-saves/internal/pkg/metadb/blobref/chunkref"
Expand Down Expand Up @@ -64,7 +65,7 @@ func newCollector(ctx context.Context, cfg *Config) (*Collector, error) {
log.Fatalf("Failed to create a MetaDB instance: %v", err)
return nil, err
}
cache := cache.New(redis.NewRedis(cfg.Cache))
cache := cache.New(redis.NewRedis(cfg.Cache), &config.CacheConfig{})
c := &Collector{
blob: gcs,
metaDB: metadb,
Expand Down
5 changes: 3 additions & 2 deletions internal/app/server/open_saves.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import (
"bytes"
"context"
"fmt"
"github.com/googleforgames/open-saves/internal/pkg/config"
"io"

"github.com/googleforgames/open-saves/internal/pkg/config"

"github.com/google/uuid"
pb "github.com/googleforgames/open-saves/api"
"github.com/googleforgames/open-saves/internal/pkg/blob"
Expand Down Expand Up @@ -75,7 +76,7 @@ func newOpenSavesServer(ctx context.Context, cfg *config.ServiceConfig) (*openSa
log.Fatalf("Failed to create a MetaDB instance: %v", err)
return nil, err
}
cache := cache.New(redis.NewRedisWithConfig(&cfg.RedisConfig))
cache := cache.New(redis.NewRedisWithConfig(&cfg.RedisConfig), &cfg.CacheConfig)
server := &openSavesServer{
cloud: cfg.ServerConfig.Cloud,
blobStore: gcs,
Expand Down
11 changes: 8 additions & 3 deletions internal/pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package cache

import (
"context"
"time"

"github.com/googleforgames/open-saves/internal/pkg/config"
)

const defaultMaxSizeToCache int = 10 * 1024 * 1024 // 10 MB
Expand All @@ -24,12 +27,14 @@ const defaultMaxSizeToCache int = 10 * 1024 * 1024 // 10 MB
type Cache struct {
driver Driver
MaxSizeToCache int
Config *config.CacheConfig
}

func New(driver Driver) *Cache {
func New(driver Driver, config *config.CacheConfig) *Cache {
return &Cache{
driver: driver,
MaxSizeToCache: defaultMaxSizeToCache,
Config: config,
}
}

Expand All @@ -44,7 +49,7 @@ func (c *Cache) Set(ctx context.Context, object Cacheable) error {
if len(encoded) > c.MaxSizeToCache {
return c.Delete(ctx, object.CacheKey())
}
return c.driver.Set(ctx, object.CacheKey(), encoded)
return c.driver.Set(ctx, object.CacheKey(), encoded, c.Config.DefaultTTL)
}

// Get takes a key string, and a Cacheable object. It fetches an object from the cache,
Expand Down Expand Up @@ -81,7 +86,7 @@ type Cacheable interface {

// Driver interface defines common operations for the cache store.
type Driver interface {
Set(ctx context.Context, key string, value []byte) error
Set(ctx context.Context, key string, value []byte, expiration time.Duration) error
Get(ctx context.Context, key string) ([]byte, error)
Delete(ctx context.Context, key string) error
ListKeys(ctx context.Context) ([]string, error)
Expand Down
10 changes: 6 additions & 4 deletions internal/pkg/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import (
"bytes"
"context"
"testing"
"time"

"github.com/golang/mock/gomock"
mock_cache "github.com/googleforgames/open-saves/internal/pkg/cache/mock"
"github.com/googleforgames/open-saves/internal/pkg/config"
"github.com/stretchr/testify/assert"
)

Expand All @@ -32,14 +34,14 @@ func TestCache_Simple(t *testing.T) {
testBinary := []byte{0x42, 0x24, 0x00, 0x12}
ctx := context.Background()

cache := New(driver)
cache := New(driver, &config.CacheConfig{DefaultTTL: 42 * time.Second})
if cache == nil {
t.Fatal("cache.New returned nil")
}

cacheable.EXPECT().CacheKey().Return(testCacheKey)
cacheable.EXPECT().EncodeBytes().Return(testBinary, nil)
driver.EXPECT().Set(ctx, testCacheKey, testBinary).Return(nil)
driver.EXPECT().Set(ctx, testCacheKey, testBinary, 42*time.Second).Return(nil)

assert.NoError(t, cache.Set(ctx, cacheable))

Expand All @@ -62,7 +64,7 @@ func TestCache_TooBigToCache(t *testing.T) {
const testCacheKey = "testcache/key"
ctx := context.Background()

cache := New(driver)
cache := New(driver, &config.CacheConfig{})
if cache == nil {
t.Fatal("cache.New returned nil")
}
Expand All @@ -80,7 +82,7 @@ func TestCache_TooBigToCache(t *testing.T) {

cacheable.EXPECT().EncodeBytes().Return(testBytes, nil)
cacheable.EXPECT().CacheKey().Return(testCacheKey)
driver.EXPECT().Set(ctx, testCacheKey, testBytes).Return(nil)
driver.EXPECT().Set(ctx, testCacheKey, testBytes, time.Duration(0)).Return(nil)

assert.NoError(t, cache.Set(ctx, cacheable))

Expand Down
5 changes: 3 additions & 2 deletions internal/pkg/cache/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package redis

import (
"context"
"time"

"github.com/go-redis/redis/v8"
"github.com/googleforgames/open-saves/internal/pkg/config"
Expand Down Expand Up @@ -52,8 +53,8 @@ func NewRedisWithConfig(cfg *config.RedisConfig) *Redis {
}

// Set adds a key-value pair to the redis instance.
func (r *Redis) Set(ctx context.Context, key string, value []byte) error {
return r.c.Set(ctx, key, string(value), 0).Err()
func (r *Redis) Set(ctx context.Context, key string, value []byte, expiration time.Duration) error {
return r.c.Set(ctx, key, string(value), expiration).Err()
}

// Get retrieves the value for a given key.
Expand Down
11 changes: 10 additions & 1 deletion internal/pkg/cache/redis/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ package redis
import (
"context"
"testing"
"time"

redis "github.com/go-redis/redis/v8"
"github.com/stretchr/testify/assert"
)

Expand All @@ -39,12 +41,19 @@ func TestRedis_All(t *testing.T) {
assert.Error(t, err)

by := []byte("byte")
assert.NoError(t, r.Set(ctx, "hello", by))
assert.NoError(t, r.Set(ctx, "hello", by, 0))

val, err := r.Get(ctx, "hello")
assert.NoError(t, err)
assert.Equal(t, by, val)

// test with TTL. The resolution is one millisecond.
assert.NoError(t, r.Set(ctx, "withTTL", by, 1*time.Millisecond))
time.Sleep(2 * time.Millisecond)
val, err = r.Get(ctx, "withTTL")
assert.ErrorIs(t, redis.Nil, err)
assert.Nil(t, val)

keys, err = r.ListKeys(ctx)
assert.NoError(t, err)
assert.Equal(t, []string{"hello"}, keys)
Expand Down
19 changes: 19 additions & 0 deletions internal/pkg/config/loader.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
Expand Down Expand Up @@ -94,6 +108,10 @@ func Load(path string) (*ServiceConfig, error) {
serverConfig.Address = fmt.Sprintf(":%d", p)
}

cacheConfig := CacheConfig{
DefaultTTL: viper.GetDuration(CacheDefaultTTL),
}

// Redis configuration
redisConfig := RedisConfig{
Address: viper.GetString(RedisAddress),
Expand All @@ -107,6 +125,7 @@ func Load(path string) (*ServiceConfig, error) {

return &ServiceConfig{
ServerConfig: serverConfig,
CacheConfig: cacheConfig,
RedisConfig: redisConfig,
}, nil
}
23 changes: 23 additions & 0 deletions internal/pkg/config/model.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import "time"
Expand All @@ -9,6 +23,8 @@ const (
OpenSavesProject = "open_saves_project"
LogLevel = "log_level"

CacheDefaultTTL = "cache_default_ttl"

RedisAddress = "redis_address"
RedisMinIdleConns = "redis_min_idle_conns"
RedisPoolSize = "redis_pool_size"
Expand All @@ -20,6 +36,7 @@ const (

type ServiceConfig struct {
ServerConfig
CacheConfig
RedisConfig
}

Expand All @@ -32,6 +49,12 @@ type ServerConfig struct {
Project string
}

// CacheConfig has configurations for caching control (not Redis specific).
type CacheConfig struct {
// DefaultTTL is the default TTL for cached data.
DefaultTTL time.Duration
}

// RedisConfig as defined in https://pkg.go.dev/github.com/go-redis/redis/v8#Options
type RedisConfig struct {
Address string
Expand Down