-
Notifications
You must be signed in to change notification settings - Fork 8
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
Reduce treatment service memory usage #59
Changes from 4 commits
b1741ee
4826f53
b83afc2
01e2d4d
d753a9d
d076413
3c68735
fb285f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,7 @@ type ExperimentIndex struct { | |
stringSets map[string]*set.Set | ||
intSets map[string]*set.Set | ||
realSets map[string]*set.Set | ||
boolFlags map[string]bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added this new field to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :O Really? But the original implementation of func (i *ExperimentIndex) matchFlagSetSegment(segmentName string, value bool) MatchStrength {
// ...
for _, val := range i.Experiment.Segments[segmentName].GetValues() {
if val.GetBool() == value {
return MatchStrengthExact
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How? We are iterating over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohhh I see it! Oops my bad, I got carried away with the slightly different implementation of this function as compared to the other |
||
|
||
StartTime time.Time | ||
EndTime time.Time | ||
|
@@ -147,16 +148,12 @@ func (i *ExperimentIndex) MarshalJSON() ([]byte, error) { | |
} | ||
|
||
func (i *ExperimentIndex) matchFlagSetSegment(segmentName string, value bool) MatchStrength { | ||
if _, exists := i.Experiment.Segments[segmentName]; !exists || | ||
len(i.Experiment.Segments[segmentName].GetValues()) == 0 { | ||
// Optional segmenter | ||
if _, exists := i.boolFlags[segmentName]; !exists { | ||
return MatchStrengthWeak | ||
} | ||
|
||
for _, val := range i.Experiment.Segments[segmentName].GetValues() { | ||
if val.GetBool() == value { | ||
return MatchStrengthExact | ||
} | ||
if i.boolFlags[segmentName] == value { | ||
return MatchStrengthExact | ||
} | ||
|
||
return MatchStrengthNone | ||
|
@@ -204,8 +201,7 @@ func (i *ExperimentIndex) matchRealSetSegment(segmentName string, value float64) | |
func (i *ExperimentIndex) matchSegment(segmentName string, values []*_segmenters.SegmenterValue) Match { | ||
if len(values) == 0 { | ||
// We can either have an optional match on the experiment or none. | ||
if _, exists := i.Experiment.Segments[segmentName]; !exists || | ||
len(i.Experiment.Segments[segmentName].GetValues()) == 0 { | ||
if i.checkSegmentHasWeakMatch(segmentName) { | ||
return Match{Strength: MatchStrengthWeak, Value: nil} | ||
} | ||
} | ||
|
@@ -238,6 +234,22 @@ func (i *ExperimentIndex) isActive() bool { | |
return (i.StartTime.Before(time.Now()) || i.StartTime.Equal(time.Now())) && i.EndTime.After(time.Now()) | ||
} | ||
|
||
func (i *ExperimentIndex) checkSegmentHasWeakMatch(segmentName string) bool { | ||
if set, exists := i.stringSets[segmentName]; !exists || set.Len() == 0 { | ||
krithika369 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true | ||
} | ||
if set, exists := i.intSets[segmentName]; !exists || set.Len() == 0 { | ||
return true | ||
} | ||
if set, exists := i.realSets[segmentName]; !exists || set.Len() == 0 { | ||
return true | ||
} | ||
if _, exists := i.boolFlags[segmentName]; !exists { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new function is basically performing the same purpose as if _, exists := i.Experiment.Segments[segmentName]; !exists ||
len(i.Experiment.Segments[segmentName].GetValues()) == 0 {
// ...
} but has been refactored into a separate helper function and also uses the values within the sets of |
||
func (s *LocalStorage) InsertProjectSettings(projectSettings *pubsub.ProjectSettings) error { | ||
s.Lock() | ||
defer s.Unlock() | ||
|
@@ -362,6 +374,7 @@ func NewExperimentIndex(experiment *pubsub.Experiment) *ExperimentIndex { | |
stringSets := make(map[string]*set.Set) | ||
intSets := make(map[string]*set.Set) | ||
realSets := make(map[string]*set.Set) | ||
boolFlags := make(map[string]bool) | ||
|
||
for key, segment := range experiment.Segments { | ||
for _, val := range segment.Values { | ||
|
@@ -384,15 +397,22 @@ func NewExperimentIndex(experiment *pubsub.Experiment) *ExperimentIndex { | |
realSets[key] = set.New() | ||
} | ||
realSets[key].Insert(val.GetReal()) | ||
case *_segmenters.SegmenterValue_Bool: | ||
boolFlags[key] = val.GetBool() | ||
} | ||
} | ||
} | ||
|
||
// Delete all segments since they have already been converted to the various sets stored in ExperimentIndex, | ||
// and are no longer used by the Treatment Service | ||
experiment.Segments = nil | ||
krithika369 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return &ExperimentIndex{ | ||
Experiment: experiment, | ||
stringSets: stringSets, | ||
intSets: intSets, | ||
realSets: realSets, | ||
boolFlags: boolFlags, | ||
StartTime: time.Unix(experiment.StartTime.Seconds, 0).UTC(), | ||
EndTime: time.Unix(experiment.EndTime.Seconds, 0).UTC(), | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -570,6 +570,9 @@ func TestExperimentIndexMatchSegment(t *testing.T) { | |
realSets: map[string]*set.Set{ | ||
"realType": set.New(realSetsVal...), | ||
}, | ||
boolFlags: map[string]bool{ | ||
"flagType": true, | ||
}, | ||
Experiment: &_pubsub.Experiment{ | ||
Segments: map[string]*_segmenters.ListSegmenterValue{ | ||
"stringType": { | ||
|
@@ -672,7 +675,7 @@ func TestExperimentIndexMatchSegment(t *testing.T) { | |
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got := experimentIndex.matchSegment(tt.args.segmentName, tt.args.value) | ||
assert.Equal(t, got, tt.want) | ||
assert.Equal(t, tt.want, got) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flipped this around because |
||
}) | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a series of minor changes to initialise a fixed-length slice instead of a dynamic length slice to store the segmenter values so as to prevent repeated allocation of memory to copy-paste the slice values when
append
-ing.