1 package evaluation
2
3 import (
4 "fmt"
5
6 "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
7 "github.com/launchdarkly/go-sdk-common/v3/ldreason"
8 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
9 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
10 )
11
12 func makeBigSegmentRef(s *ldmodel.Segment) string {
13
14
15
16 return fmt.Sprintf("%s.g%d", s.Key, s.Generation.IntValue())
17 }
18
19 func (es *evaluationScope) segmentContainsContext(s *ldmodel.Segment, stack evaluationStack) (bool, error) {
20
21 for _, visitedKey := range stack.segmentChain {
22 if visitedKey == s.Key {
23 return false, circularSegmentReferenceError(s.Key)
24 }
25 }
26
27
28
29 stack.segmentChain = append(stack.segmentChain, s.Key)
30
31
32 if s.Unbounded {
33 if !s.Generation.IsDefined() {
34
35
36
37
38 es.bigSegmentsStatus = ldreason.BigSegmentsNotConfigured
39 return false, nil
40 }
41
42
43 key, ok := getApplicableContextKeyByKind(&es.context, s.UnboundedContextKind)
44 if !ok {
45 return false, nil
46 }
47
48
49
50 membership, wasCached := es.bigSegmentsMemberships[key]
51 if !wasCached {
52 if es.owner.bigSegmentProvider == nil {
53
54
55 es.bigSegmentsStatus = ldreason.BigSegmentsNotConfigured
56 } else {
57 var thisQueryStatus ldreason.BigSegmentsStatus
58 membership, thisQueryStatus = es.owner.bigSegmentProvider.GetMembership(key)
59
60
61
62
63
64 if es.bigSegmentsMemberships == nil {
65 es.bigSegmentsMemberships = make(map[string]BigSegmentMembership)
66 }
67 es.bigSegmentsMemberships[key] = membership
68 es.bigSegmentsStatus = computeUpdatedBigSegmentsStatus(es.bigSegmentsStatus, thisQueryStatus)
69 }
70 }
71 if membership != nil {
72 included := membership.CheckMembership(makeBigSegmentRef(s))
73 if included.IsDefined() {
74 return included.BoolValue(), nil
75 }
76 }
77 } else {
78
79 defaultKindKey, hasDefaultKindKey := getApplicableContextKeyByKind(&es.context, ldcontext.DefaultKind)
80 isOnlyDefaultKind := es.context.Kind() == ldcontext.DefaultKind
81 if hasDefaultKindKey && ldmodel.EvaluatorAccessors.SegmentFindKeyInIncluded(s, defaultKindKey) {
82 return true, nil
83 }
84 if !isOnlyDefaultKind {
85 for i := range s.IncludedContexts {
86 if es.segmentTargetMatchesContext(&s.IncludedContexts[i]) {
87 return true, nil
88 }
89 }
90 }
91 if hasDefaultKindKey && ldmodel.EvaluatorAccessors.SegmentFindKeyInExcluded(s, defaultKindKey) {
92 return false, nil
93 }
94 if !isOnlyDefaultKind {
95 for i := range s.ExcludedContexts {
96 if es.segmentTargetMatchesContext(&s.ExcludedContexts[i]) {
97 return false, nil
98 }
99 }
100 }
101 }
102
103
104 for _, rule := range s.Rules {
105
106 match, err := es.segmentRuleMatchesContext(&rule, stack, s.Key, s.Salt)
107 if err != nil {
108 return false, malformedSegmentError{SegmentKey: s.Key, Err: err}
109 }
110 if match {
111 return true, nil
112 }
113 }
114
115 return false, nil
116 }
117
118 func (es *evaluationScope) segmentTargetMatchesContext(t *ldmodel.SegmentTarget) bool {
119 if key, ok := getApplicableContextKeyByKind(&es.context, t.ContextKind); ok {
120 return ldmodel.EvaluatorAccessors.SegmentTargetFindKey(t, key)
121 }
122 return false
123 }
124
125 func (es *evaluationScope) segmentRuleMatchesContext(
126 r *ldmodel.SegmentRule,
127 stack evaluationStack,
128 key, salt string,
129 ) (bool, error) {
130 for i := range r.Clauses {
131
132 match, err := es.clauseMatchesContext(&r.Clauses[i], stack)
133 if !match || err != nil {
134 return false, err
135 }
136 }
137
138
139 if !r.Weight.IsDefined() {
140 return true, nil
141 }
142
143
144
145
146
147
148 bucket, failReason, err := es.computeBucketValue(
149 false,
150 ldvalue.OptionalInt{},
151 r.RolloutContextKind,
152 key,
153 r.BucketBy,
154 salt,
155 )
156 if err != nil {
157
158 return false, err
159 }
160 if failReason == bucketingFailureContextLacksDesiredKind {
161
162
163
164
165
166 return false, nil
167 }
168 weight := float32(r.Weight.IntValue()) / 100000.0
169 return bucket < weight, nil
170 }
171
172 func computeUpdatedBigSegmentsStatus(old, new ldreason.BigSegmentsStatus) ldreason.BigSegmentsStatus {
173
174
175
176 if old != "" && getBigSegmentsStatusPriority(old) > getBigSegmentsStatusPriority(new) {
177 return old
178 }
179 return new
180 }
181
182 func getBigSegmentsStatusPriority(status ldreason.BigSegmentsStatus) int {
183 switch status {
184 case ldreason.BigSegmentsStale:
185 return 1
186 case ldreason.BigSegmentsStoreError:
187 return 2
188 case ldreason.BigSegmentsNotConfigured:
189
190
191 return 3
192 default:
193 return 0
194 }
195 }
196
View as plain text