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(middleware): support caching new evaluation RPCs #1853

Merged
merged 3 commits into from
Jul 11, 2023
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
77 changes: 76 additions & 1 deletion internal/server/middleware/grpc/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"go.flipt.io/flipt/internal/server/metrics"
flipt "go.flipt.io/flipt/rpc/flipt"
fauth "go.flipt.io/flipt/rpc/flipt/auth"
"go.flipt.io/flipt/rpc/flipt/evaluation"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"google.golang.org/grpc"
Expand Down Expand Up @@ -227,6 +228,73 @@
if err := cache.Delete(ctx, flagCacheKey(keyer.GetNamespaceKey(), keyer.GetFlagKey())); err != nil {
logger.Error("deleting from cache", zap.Error(err))
}
case *evaluation.EvaluationRequest:
key, err := evaluationCacheKey(r)
if err != nil {
logger.Error("getting cache key", zap.Error(err))
return handler(ctx, req)
}

Check warning on line 236 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L234-L236

Added lines #L234 - L236 were not covered by tests

cached, ok, err := cache.Get(ctx, key)
if err != nil {
// if error, log and without cache
logger.Error("getting from cache", zap.Error(err))
return handler(ctx, req)
}

Check warning on line 243 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L240-L243

Added lines #L240 - L243 were not covered by tests

if ok {
resp := &evaluation.EvaluationResponse{}
if err := proto.Unmarshal(cached, resp); err != nil {
logger.Error("unmarshalling from cache", zap.Error(err))
return handler(ctx, req)
}

Check warning on line 250 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L246-L250

Added lines #L246 - L250 were not covered by tests

logger.Debug("evaluate cache hit", zap.Stringer("response", resp))
switch r := resp.Response.(type) {
case *evaluation.EvaluationResponse_VariantResponse:
return r.VariantResponse, nil
case *evaluation.EvaluationResponse_BooleanResponse:
return r.BooleanResponse, nil
default:
logger.Error("unexpected eval cache response type", zap.String("type", fmt.Sprintf("%T", resp.Response)))

Check warning on line 259 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L252-L259

Added lines #L252 - L259 were not covered by tests
}

return handler(ctx, req)

Check warning on line 262 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L262

Added line #L262 was not covered by tests
}

logger.Debug("evaluate cache miss")
resp, err := handler(ctx, req)
if err != nil {
return resp, err
}

Check warning on line 269 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L268-L269

Added lines #L268 - L269 were not covered by tests

evalResponse := &evaluation.EvaluationResponse{}
switch r := resp.(type) {
case *evaluation.VariantEvaluationResponse:
evalResponse.Type = evaluation.EvaluationResponseType_VARIANT_EVALUATION_RESPONSE_TYPE
evalResponse.Response = &evaluation.EvaluationResponse_VariantResponse{
VariantResponse: r,
}
case *evaluation.BooleanEvaluationResponse:
evalResponse.Type = evaluation.EvaluationResponseType_BOOLEAN_EVALUATION_RESPONSE_TYPE
evalResponse.Response = &evaluation.EvaluationResponse_BooleanResponse{
BooleanResponse: r,
}
}

// marshal response
data, merr := proto.Marshal(evalResponse)
if merr != nil {
logger.Error("marshalling for cache", zap.Error(err))
return resp, err
}

Check warning on line 290 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L288-L290

Added lines #L288 - L290 were not covered by tests

// set in cache
if cerr := cache.Set(ctx, key, data); cerr != nil {
logger.Error("setting in cache", zap.Error(err))
}

Check warning on line 295 in internal/server/middleware/grpc/middleware.go

View check run for this annotation

Codecov / codecov/patch

internal/server/middleware/grpc/middleware.go#L294-L295

Added lines #L294 - L295 were not covered by tests

return resp, err
}

return handler(ctx, req)
Expand Down Expand Up @@ -348,7 +416,14 @@
return fmt.Sprintf("flipt:%x", md5.Sum([]byte(k)))
}

func evaluationCacheKey(r *flipt.EvaluationRequest) (string, error) {
type evaluationRequest interface {
GetNamespaceKey() string
GetFlagKey() string
GetEntityId() string
GetContext() map[string]string
}

func evaluationCacheKey(r evaluationRequest) (string, error) {
out, err := json.Marshal(r.GetContext())
if err != nil {
return "", fmt.Errorf("marshalling req to json: %w", err)
Expand Down
Loading
Loading