Skip to content

Commit 1780158

Browse files
FFM-11115 - Update Evaluation logic to be more efficient (#147)
* Update Evaluation logic to be more efficient
1 parent cfb1894 commit 1780158

File tree

5 files changed

+317
-217
lines changed

5 files changed

+317
-217
lines changed

analyticsservice/analytics.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const (
2626
variationValueAttribute string = "featureValue"
2727
targetAttribute string = "target"
2828
sdkVersionAttribute string = "SDK_VERSION"
29-
SdkVersion string = "0.1.19"
29+
SdkVersion string = "0.1.20"
3030
sdkTypeAttribute string = "SDK_TYPE"
3131
sdkType string = "server"
3232
sdkLanguageAttribute string = "SDK_LANGUAGE"

evaluation/evaluator.go

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,58 +85,44 @@ func NewEvaluator(query Query, postEvalCallback PostEvaluateCallback, logger log
8585
}
8686

8787
func (e Evaluator) evaluateClause(clause *rest.Clause, target *Target) bool {
88-
if clause == nil {
89-
return false
90-
}
91-
92-
values := clause.Values
93-
if len(values) == 0 {
94-
return false
95-
}
96-
value := values[0]
97-
98-
operator := clause.Op
99-
if operator == "" {
100-
e.logger.Warnf("Clause has no valid operator: Clause (%v)", clause)
88+
if clause == nil || len(clause.Values) == 0 || clause.Op == "" {
89+
e.logger.Debugf("Clause cannot be evaluated because operator is either nil, has no values or operation: Clause (%v)", clause)
10190
return false
10291
}
10392

93+
value := clause.Values[0]
10494
attrValue := getAttrValue(target, clause.Attribute)
105-
if operator != segmentMatchOperator && !attrValue.IsValid() {
106-
e.logger.Debugf("Operator is not a segment match and attribute value is not valid: Operator (%s), attributeVal (%s)", operator, attrValue.String())
95+
96+
if clause.Op != segmentMatchOperator && attrValue == "" {
97+
e.logger.Debugf("Operator is not a segment match and attribute value is not valid: Operator (%s), attributeVal (%s)", clause.Op, attrValue)
10798
return false
10899
}
109100

110-
object := reflectValueToString(attrValue)
111-
112-
switch operator {
101+
switch clause.Op {
113102
case startsWithOperator:
114-
return strings.HasPrefix(object, value)
103+
return strings.HasPrefix(attrValue, value)
115104
case endsWithOperator:
116-
return strings.HasSuffix(object, value)
105+
return strings.HasSuffix(attrValue, value)
117106
case matchOperator:
118-
found, err := regexp.MatchString(value, object)
119-
if err != nil || !found {
120-
return false
121-
}
122-
return true
107+
found, err := regexp.MatchString(value, attrValue)
108+
return err == nil && found
123109
case containsOperator:
124-
return strings.Contains(object, value)
110+
return strings.Contains(attrValue, value)
125111
case equalOperator:
126-
return strings.EqualFold(object, value)
112+
return strings.EqualFold(attrValue, value)
127113
case equalSensitiveOperator:
128-
return object == value
114+
return attrValue == value
129115
case inOperator:
130-
for _, val := range values {
131-
if val == object {
116+
for _, val := range clause.Values {
117+
if val == attrValue {
132118
return true
133119
}
134120
}
135121
return false
136122
case gtOperator:
137-
return object > value
123+
return attrValue > value
138124
case segmentMatchOperator:
139-
return e.isTargetIncludedOrExcludedInSegment(values, target)
125+
return e.isTargetIncludedOrExcludedInSegment(clause.Values, target)
140126
default:
141127
return false
142128
}

evaluation/evaluator_test.go

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,3 +1799,255 @@ func TestEvaluator_JSONVariation(t *testing.T) {
17991799
})
18001800
}
18011801
}
1802+
1803+
// BENCHMARK
1804+
func BenchmarkEvaluateClause_NilClause(b *testing.B) {
1805+
evaluator := Evaluator{}
1806+
var clause *rest.Clause = nil
1807+
target := &Target{
1808+
Identifier: "harness",
1809+
}
1810+
for i := 0; i < b.N; i++ {
1811+
evaluator.evaluateClause(clause, target)
1812+
}
1813+
}
1814+
1815+
func BenchmarkEvaluateClause_EmptyOperator(b *testing.B) {
1816+
evaluator := Evaluator{}
1817+
clause := &rest.Clause{
1818+
Op: "",
1819+
Values: []string{"harness"},
1820+
}
1821+
for i := 0; i < b.N; i++ {
1822+
evaluator.evaluateClause(clause, nil)
1823+
}
1824+
}
1825+
1826+
func BenchmarkEvaluateClause_NilValues(b *testing.B) {
1827+
evaluator := Evaluator{}
1828+
clause := &rest.Clause{
1829+
Values: nil,
1830+
}
1831+
for i := 0; i < b.N; i++ {
1832+
evaluator.evaluateClause(clause, nil)
1833+
}
1834+
}
1835+
1836+
func BenchmarkEvaluateClause_EmptyValues(b *testing.B) {
1837+
evaluator := Evaluator{}
1838+
clause := &rest.Clause{
1839+
Values: []string{},
1840+
}
1841+
for i := 0; i < b.N; i++ {
1842+
evaluator.evaluateClause(clause, nil)
1843+
}
1844+
}
1845+
1846+
func BenchmarkEvaluateClause_WrongOperator(b *testing.B) {
1847+
evaluator := Evaluator{}
1848+
clause := &rest.Clause{
1849+
Attribute: "identifier",
1850+
Op: "greaterthan",
1851+
Values: []string{"harness"},
1852+
}
1853+
target := &Target{
1854+
Identifier: "harness",
1855+
}
1856+
for i := 0; i < b.N; i++ {
1857+
evaluator.evaluateClause(clause, target)
1858+
}
1859+
}
1860+
1861+
func BenchmarkEvaluateClause_EmptyAttribute(b *testing.B) {
1862+
evaluator := Evaluator{}
1863+
clause := &rest.Clause{
1864+
Attribute: "",
1865+
Op: "equalOperator",
1866+
Values: []string{"harness"},
1867+
}
1868+
target := &Target{
1869+
Identifier: "harness",
1870+
}
1871+
for i := 0; i < b.N; i++ {
1872+
evaluator.evaluateClause(clause, target)
1873+
}
1874+
}
1875+
1876+
func BenchmarkEvaluateClause_MatchOperator(b *testing.B) {
1877+
evaluator := Evaluator{}
1878+
clause := &rest.Clause{
1879+
Attribute: "identifier",
1880+
Op: "matchOperator",
1881+
Values: []string{"^harness$"},
1882+
}
1883+
target := &Target{
1884+
Identifier: "harness",
1885+
}
1886+
for i := 0; i < b.N; i++ {
1887+
evaluator.evaluateClause(clause, target)
1888+
}
1889+
}
1890+
1891+
func BenchmarkEvaluateClause_MatchOperatorError(b *testing.B) {
1892+
evaluator := Evaluator{}
1893+
clause := &rest.Clause{
1894+
Attribute: "identifier",
1895+
Op: "matchOperator",
1896+
Values: []string{"^harness(wings$"},
1897+
}
1898+
target := &Target{
1899+
Identifier: "harness",
1900+
}
1901+
for i := 0; i < b.N; i++ {
1902+
evaluator.evaluateClause(clause, target)
1903+
}
1904+
}
1905+
1906+
func BenchmarkEvaluateClause_InOperator(b *testing.B) {
1907+
evaluator := Evaluator{}
1908+
clause := &rest.Clause{
1909+
Attribute: "identifier",
1910+
Op: "inOperator",
1911+
Values: []string{"harness", "wings-software"},
1912+
}
1913+
target := &Target{
1914+
Identifier: "harness",
1915+
}
1916+
for i := 0; i < b.N; i++ {
1917+
evaluator.evaluateClause(clause, target)
1918+
}
1919+
}
1920+
1921+
func BenchmarkEvaluateClause_InOperatorNotFound(b *testing.B) {
1922+
evaluator := Evaluator{}
1923+
clause := &rest.Clause{
1924+
Attribute: "identifier",
1925+
Op: "inOperator",
1926+
Values: []string{"harness1", "wings-software"},
1927+
}
1928+
target := &Target{
1929+
Identifier: "harness",
1930+
}
1931+
for i := 0; i < b.N; i++ {
1932+
evaluator.evaluateClause(clause, target)
1933+
}
1934+
}
1935+
1936+
func BenchmarkEvaluateClause_EqualOperator(b *testing.B) {
1937+
evaluator := Evaluator{}
1938+
clause := &rest.Clause{
1939+
Attribute: "identifier",
1940+
Op: "equalOperator",
1941+
Values: []string{"harness"},
1942+
}
1943+
target := &Target{
1944+
Identifier: "harness",
1945+
}
1946+
for i := 0; i < b.N; i++ {
1947+
evaluator.evaluateClause(clause, target)
1948+
}
1949+
}
1950+
1951+
func BenchmarkEvaluateClause_EqualSensitiveOperator(b *testing.B) {
1952+
evaluator := Evaluator{}
1953+
clause := &rest.Clause{
1954+
Attribute: "identifier",
1955+
Op: "equalSensitiveOperator",
1956+
Values: []string{"harness"},
1957+
}
1958+
target := &Target{
1959+
Identifier: "Harness",
1960+
}
1961+
for i := 0; i < b.N; i++ {
1962+
evaluator.evaluateClause(clause, target)
1963+
}
1964+
}
1965+
1966+
func BenchmarkEvaluateClause_GTOperator(b *testing.B) {
1967+
evaluator := Evaluator{}
1968+
clause := &rest.Clause{
1969+
Attribute: "identifier",
1970+
Op: "gtOperator",
1971+
Values: []string{"A"},
1972+
}
1973+
target := &Target{
1974+
Identifier: "B",
1975+
}
1976+
for i := 0; i < b.N; i++ {
1977+
evaluator.evaluateClause(clause, target)
1978+
}
1979+
}
1980+
1981+
func BenchmarkEvaluateClause_GTOperatorNegative(b *testing.B) {
1982+
evaluator := Evaluator{}
1983+
clause := &rest.Clause{
1984+
Attribute: "identifier",
1985+
Op: "gtOperator",
1986+
Values: []string{"B"},
1987+
}
1988+
target := &Target{
1989+
Identifier: "A",
1990+
}
1991+
for i := 0; i < b.N; i++ {
1992+
evaluator.evaluateClause(clause, target)
1993+
}
1994+
}
1995+
1996+
func BenchmarkEvaluateClause_StartsWithOperator(b *testing.B) {
1997+
evaluator := Evaluator{}
1998+
clause := &rest.Clause{
1999+
Attribute: "identifier",
2000+
Op: "startsWithOperator",
2001+
Values: []string{"harness"},
2002+
}
2003+
target := &Target{
2004+
Identifier: "harness - wings software",
2005+
}
2006+
for i := 0; i < b.N; i++ {
2007+
evaluator.evaluateClause(clause, target)
2008+
}
2009+
}
2010+
2011+
func BenchmarkEvaluateClause_EndsWithOperator(b *testing.B) {
2012+
evaluator := Evaluator{}
2013+
clause := &rest.Clause{
2014+
Attribute: "identifier",
2015+
Op: "endsWithOperator",
2016+
Values: []string{"harness"},
2017+
}
2018+
target := &Target{
2019+
Identifier: "wings software - harness",
2020+
}
2021+
for i := 0; i < b.N; i++ {
2022+
evaluator.evaluateClause(clause, target)
2023+
}
2024+
}
2025+
2026+
func BenchmarkEvaluateClause_ContainsOperator(b *testing.B) {
2027+
evaluator := Evaluator{}
2028+
clause := &rest.Clause{
2029+
Attribute: "identifier",
2030+
Op: "containsOperator",
2031+
Values: []string{"harness"},
2032+
}
2033+
target := &Target{
2034+
Identifier: "wings harness software",
2035+
}
2036+
for i := 0; i < b.N; i++ {
2037+
evaluator.evaluateClause(clause, target)
2038+
}
2039+
}
2040+
2041+
func BenchmarkEvaluateClause_SegmentMatchOperator(b *testing.B) {
2042+
evaluator := Evaluator{query: testRepo}
2043+
clause := &rest.Clause{
2044+
Op: "segmentMatchOperator",
2045+
Values: []string{"beta"},
2046+
}
2047+
target := &Target{
2048+
Identifier: "harness",
2049+
}
2050+
for i := 0; i < b.N; i++ {
2051+
evaluator.evaluateClause(clause, target)
2052+
}
2053+
}

0 commit comments

Comments
 (0)