Skip to content

Commit

Permalink
FFM-11332 - Refactor evaluation logic to remove inefficiencies in the…
Browse files Browse the repository at this point in the history
… GetAttr(ibute) function (#155)

* Refactor to remove further inefficiencies in getAttr func
  • Loading branch information
stephenmcconkey committed Apr 29, 2024
1 parent 47f888f commit 3cececf
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 79 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

2 changes: 0 additions & 2 deletions evaluation/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,13 @@ func NewEvaluator(query Query, postEvalCallback PostEvaluateCallback, logger log

func (e Evaluator) evaluateClause(clause *rest.Clause, target *Target) bool {
if clause == nil || len(clause.Values) == 0 || clause.Op == "" {
e.logger.Debugf("Clause cannot be evaluated because operator is either nil, has no values or operation: Clause (%v)", clause)
return false
}

value := clause.Values[0]
attrValue := getAttrValue(target, clause.Attribute)

if clause.Op != segmentMatchOperator && attrValue == "" {
e.logger.Debugf("Operator is not a segment match and attribute value is not valid: Operator (%s), attributeVal (%s)", clause.Op, attrValue)
return false
}

Expand Down
36 changes: 18 additions & 18 deletions evaluation/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1988,7 +1988,7 @@ func TestEvaluator_JSONVariation(t *testing.T) {

// BENCHMARK
func BenchmarkEvaluateClause_NilClause(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
var clause *rest.Clause = nil
target := &Target{
Identifier: "harness",
Expand All @@ -1999,7 +1999,7 @@ func BenchmarkEvaluateClause_NilClause(b *testing.B) {
}

func BenchmarkEvaluateClause_EmptyOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Op: "",
Values: []string{"harness"},
Expand All @@ -2010,7 +2010,7 @@ func BenchmarkEvaluateClause_EmptyOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_NilValues(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Values: nil,
}
Expand All @@ -2020,7 +2020,7 @@ func BenchmarkEvaluateClause_NilValues(b *testing.B) {
}

func BenchmarkEvaluateClause_EmptyValues(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Values: []string{},
}
Expand All @@ -2030,7 +2030,7 @@ func BenchmarkEvaluateClause_EmptyValues(b *testing.B) {
}

func BenchmarkEvaluateClause_WrongOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "greaterthan",
Expand All @@ -2045,7 +2045,7 @@ func BenchmarkEvaluateClause_WrongOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_EmptyAttribute(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "",
Op: "equalOperator",
Expand All @@ -2060,7 +2060,7 @@ func BenchmarkEvaluateClause_EmptyAttribute(b *testing.B) {
}

func BenchmarkEvaluateClause_MatchOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "matchOperator",
Expand All @@ -2075,7 +2075,7 @@ func BenchmarkEvaluateClause_MatchOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_MatchOperatorError(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "matchOperator",
Expand All @@ -2090,7 +2090,7 @@ func BenchmarkEvaluateClause_MatchOperatorError(b *testing.B) {
}

func BenchmarkEvaluateClause_InOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "inOperator",
Expand All @@ -2105,7 +2105,7 @@ func BenchmarkEvaluateClause_InOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_InOperatorNotFound(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "inOperator",
Expand All @@ -2120,7 +2120,7 @@ func BenchmarkEvaluateClause_InOperatorNotFound(b *testing.B) {
}

func BenchmarkEvaluateClause_EqualOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "equalOperator",
Expand All @@ -2135,7 +2135,7 @@ func BenchmarkEvaluateClause_EqualOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_EqualSensitiveOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "equalSensitiveOperator",
Expand All @@ -2150,7 +2150,7 @@ func BenchmarkEvaluateClause_EqualSensitiveOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_GTOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "gtOperator",
Expand All @@ -2165,7 +2165,7 @@ func BenchmarkEvaluateClause_GTOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_GTOperatorNegative(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "gtOperator",
Expand All @@ -2180,7 +2180,7 @@ func BenchmarkEvaluateClause_GTOperatorNegative(b *testing.B) {
}

func BenchmarkEvaluateClause_StartsWithOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "startsWithOperator",
Expand All @@ -2195,7 +2195,7 @@ func BenchmarkEvaluateClause_StartsWithOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_EndsWithOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "endsWithOperator",
Expand All @@ -2210,7 +2210,7 @@ func BenchmarkEvaluateClause_EndsWithOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_ContainsOperator(b *testing.B) {
evaluator := Evaluator{}
evaluator := Evaluator{logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Attribute: "identifier",
Op: "containsOperator",
Expand All @@ -2225,7 +2225,7 @@ func BenchmarkEvaluateClause_ContainsOperator(b *testing.B) {
}

func BenchmarkEvaluateClause_SegmentMatchOperator(b *testing.B) {
evaluator := Evaluator{query: testRepo}
evaluator := Evaluator{query: testRepo, logger: logger.NoOpLogger{}}
clause := &rest.Clause{
Op: "segmentMatchOperator",
Values: []string{"beta"},
Expand Down
26 changes: 23 additions & 3 deletions evaluation/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package evaluation

import (
"fmt"
"strconv"
"strings"

jsoniter "github.com/json-iterator/go"

"github.com/harness/ff-golang-server-sdk/sdk_codes"

"github.com/harness/ff-golang-server-sdk/log"
Expand All @@ -12,19 +15,36 @@ import (
)

func getAttrValue(target *Target, attr string) string {
if target == nil {
if target == nil || attr == "" {
return ""
}

switch strings.ToLower(attr) {
switch attr {
case "identifier":
return target.Identifier
case "name":
return target.Name
default:
if target.Attributes != nil {
if val, ok := (*target.Attributes)[attr]; ok {
return fmt.Sprint(val)
switch v := val.(type) {
case string:
return v
case int:
return strconv.Itoa(v)
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
case bool:
return strconv.FormatBool(v)
case map[string]interface{}:
marshalledValue, err := jsoniter.MarshalToString(v)
if err != nil {
return fmt.Sprint(v)
}
return marshalledValue
default:
return fmt.Sprint(v)
}
}
}
}
Expand Down
92 changes: 92 additions & 0 deletions evaluation/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,95 @@ func Test_isTargetInList(t *testing.T) {
})
}
}

// Benchmark scenarios
func BenchmarkGetAttrValueNilTarget(b *testing.B) {
for i := 0; i < b.N; i++ {
getAttrValue(nil, "identifier")
}
}

func BenchmarkGetAttrValueEmptyAttribute(b *testing.B) {
var target Target
for i := 0; i < b.N; i++ {
getAttrValue(&target, "")
}
}

func BenchmarkGetAttrValueIdentifier(b *testing.B) {
var target = Target{Identifier: "1234"}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "identifier")
}
}

func BenchmarkGetAttrValueName(b *testing.B) {
var target = Target{Name: "targetName"}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "name")
}
}

func BenchmarkGetAttrValueUnmatched(b *testing.B) {
var target = Target{}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "unmatched")
}
}

func BenchmarkGetAttrValueStringAttr(b *testing.B) {
attributes := map[string]interface{}{"city": "New York"}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "city")
}
}

func BenchmarkGetAttrValueIntegerAttr(b *testing.B) {
attributes := map[string]interface{}{"age": 30}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "age")
}
}

func BenchmarkGetAttrValueFloatAttr(b *testing.B) {
attributes := map[string]interface{}{"temperature": 98.6}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "temperature")
}
}

func BenchmarkGetAttrValueBoolAttr(b *testing.B) {
attributes := map[string]interface{}{"active": true}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "active")
}
}

func BenchmarkGetAttrValueBasicJSON(b *testing.B) {
basicJSON := map[string]interface{}{
"myName": "Stephen",
}
attributes := map[string]interface{}{"basicJson": basicJSON}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "basicJson")
}
}

func BenchmarkGetAttrValueComplexJSON(b *testing.B) {
complexJSON := map[string]interface{}{
"config": map[string]interface{}{
"setting": "on",
"level": 5,
},
}
attributes := map[string]interface{}{"complexJSON": complexJSON}
var target = Target{Attributes: &attributes}
for i := 0; i < b.N; i++ {
getAttrValue(&target, "complexJSON")
}
}

0 comments on commit 3cececf

Please sign in to comment.