Skip to content

Commit

Permalink
chore: use protos for caching evaluation rules
Browse files Browse the repository at this point in the history
  • Loading branch information
markphelps committed Nov 6, 2023
1 parent 8884282 commit 2365671
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 62 deletions.
2 changes: 1 addition & 1 deletion internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ type Cacher interface {
}

func Key(k string) string {
return fmt.Sprintf("flipt:%x", md5.Sum([]byte(k)))
return fmt.Sprintf("flipt:v1:%x", md5.Sum([]byte(k)))
}
26 changes: 26 additions & 0 deletions internal/cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cache

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestKey(t *testing.T) {
type args struct {
k string
}
tests := []struct {
name string
args args
want string
}{
{"empty", args{k: ""}, "flipt:v1:d41d8cd98f00b204e9800998ecf8427e"},
{"non-empty", args{k: "foo"}, "flipt:v1:acbd18db4cc2f85cedef654fccc4a4d8"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, Key(tt.args.k))
})
}
}
55 changes: 38 additions & 17 deletions internal/storage/cache/cache.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package cache

import (
"bytes"
"context"
"encoding/json"
"fmt"

"github.com/gogo/protobuf/jsonpb"
"go.flipt.io/flipt/internal/cache"
"go.flipt.io/flipt/internal/storage"
"go.uber.org/zap"
Expand All @@ -25,43 +27,62 @@ func NewStore(store storage.Store, cacher cache.Cacher, logger *zap.Logger) *Sto
return &Store{Store: store, cacher: cacher, logger: logger}
}

func (s *Store) set(ctx context.Context, key string, value any) {
cachePayload, err := json.Marshal(value)
if err != nil {
s.logger.Error("marshalling for storage cache", zap.Error(err))
return
func (s *Store) set(ctx context.Context, key string, values []*storage.EvaluationRule) {
marshaller := jsonpb.Marshaler{EmitDefaults: false}

var bytes bytes.Buffer

bytes.WriteByte('[')

for _, value := range values {
if err := marshaller.Marshal(&bytes, value); err != nil {
s.logger.Error("marshalling for storage cache", zap.Error(err))
return
}
}

err = s.cacher.Set(ctx, key, cachePayload)
if err != nil {
bytes.WriteByte(']')

if err := s.cacher.Set(ctx, key, bytes.Bytes()); err != nil {
s.logger.Error("setting in storage cache", zap.Error(err))
return
}
}

func (s *Store) get(ctx context.Context, key string, value any) bool {
func (s *Store) get(ctx context.Context, key string) (bool, []*storage.EvaluationRule) {
cachePayload, cacheHit, err := s.cacher.Get(ctx, key)
if err != nil {
s.logger.Error("getting from storage cache", zap.Error(err))
return false
return false, nil
} else if !cacheHit {
return false
return false, nil
}

err = json.Unmarshal(cachePayload, value)
var values []*storage.EvaluationRule
decoder := json.NewDecoder(bytes.NewReader(cachePayload))
_, err = decoder.Token() // read the opening bracket
if err != nil {
s.logger.Error("unmarshalling from storage cache", zap.Error(err))
return false
s.logger.Error("reading opening bracket from storage cache", zap.Error(err))
return false, nil
}

for decoder.More() {
value := &storage.EvaluationRule{}
if err := jsonpb.UnmarshalNext(decoder, value); err != nil {
s.logger.Error("unmarshalling from storage cache", zap.Error(err))
return false, nil
}

values = append(values, value)
}

return true
return true, values
}

func (s *Store) GetEvaluationRules(ctx context.Context, namespaceKey, flagKey string) ([]*storage.EvaluationRule, error) {
cacheKey := fmt.Sprintf(evaluationRulesCacheKeyFmt, namespaceKey, flagKey)

var rules []*storage.EvaluationRule

cacheHit := s.get(ctx, cacheKey, &rules)
cacheHit, rules := s.get(ctx, cacheKey)
if cacheHit {
return rules, nil
}
Expand Down
47 changes: 3 additions & 44 deletions internal/storage/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,15 @@ package cache

import (
"context"
"errors"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.flipt.io/flipt/internal/common"
"go.flipt.io/flipt/internal/storage"
"go.uber.org/zap/zaptest"
)

func TestSetHandleMarshalError(t *testing.T) {
var (
store = &common.StoreMock{}
cacher = &cacheSpy{}
logger = zaptest.NewLogger(t)
cachedStore = NewStore(store, cacher, logger)
)

cachedStore.set(context.TODO(), "key", make(chan int))
assert.Empty(t, cacher.cacheKey)
}

func TestGetHandleGetError(t *testing.T) {
var (
store = &common.StoreMock{}
cacher = &cacheSpy{getErr: errors.New("get error")}
logger = zaptest.NewLogger(t)
cachedStore = NewStore(store, cacher, logger)
)

value := make(map[string]string)
cacheHit := cachedStore.get(context.TODO(), "key", &value)
assert.False(t, cacheHit)
}

func TestGetHandleUnmarshalError(t *testing.T) {
var (
store = &common.StoreMock{}
cacher = &cacheSpy{
cached: true,
cachedValue: []byte(`{"invalid":"123"`),
}
logger = zaptest.NewLogger(t)
cachedStore = NewStore(store, cacher, logger)
)

value := make(map[string]string)
cacheHit := cachedStore.get(context.TODO(), "key", &value)
assert.False(t, cacheHit)
}

func TestGetEvaluationRules(t *testing.T) {
var (
expectedRules = []*storage.EvaluationRule{{Id: "123"}}
Expand All @@ -69,7 +28,7 @@ func TestGetEvaluationRules(t *testing.T) {
)

rules, err := cachedStore.GetEvaluationRules(context.TODO(), "ns", "flag-1")
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, expectedRules, rules)

assert.Equal(t, "s:er:ns:flag-1", cacher.cacheKey)
Expand All @@ -95,7 +54,7 @@ func TestGetEvaluationRulesCached(t *testing.T) {
)

rules, err := cachedStore.GetEvaluationRules(context.TODO(), "ns", "flag-1")
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, expectedRules, rules)
assert.Equal(t, "s:er:ns:flag-1", cacher.cacheKey)
}

0 comments on commit 2365671

Please sign in to comment.