From 330171cbc45bfac92d50fabd1da11354c823f286 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 20 Oct 2025 11:52:03 -0300 Subject: [PATCH 1/8] Updated sotrages and logic when processFFChange --- engine/evaluator/evaluator_test.go | 13 +- storage/inmemory/mutexmap/rulebasedsegment.go | 28 ++- .../mutexmap/rulebasedsegment_test.go | 31 +-- storage/inmemory/mutexmap/splits.go | 20 +- storage/interfaces.go | 21 +- storage/mocks/rulebasedsegment.go | 19 +- storage/mocks/split.go | 56 +++-- storage/redis/splits.go | 22 +- synchronizer/synchronizer_test.go | 4 +- synchronizer/worker/segment/segment.go | 2 +- synchronizer/worker/segment/segment_test.go | 10 +- synchronizer/worker/split/split.go | 195 ++++++++++++------ synchronizer/worker/split/split_test.go | 21 +- 13 files changed, 306 insertions(+), 136 deletions(-) diff --git a/engine/evaluator/evaluator_test.go b/engine/evaluator/evaluator_test.go index 679a2207..c542a1ae 100644 --- a/engine/evaluator/evaluator_test.go +++ b/engine/evaluator/evaluator_test.go @@ -285,12 +285,13 @@ func (s *mockStorage) FetchMany( splits["mysplittest5"] = nil return splits } -func (s *mockStorage) All() []dtos.SplitDTO { return make([]dtos.SplitDTO, 0) } -func (s *mockStorage) SegmentNames() *set.ThreadUnsafeSet { return nil } -func (s *mockStorage) LargeSegmentNames() *set.ThreadUnsafeSet { return nil } -func (s *mockStorage) SplitNames() []string { return make([]string, 0) } -func (s *mockStorage) TrafficTypeExists(trafficType string) bool { return true } -func (s *mockStorage) ChangeNumber() (int64, error) { return 0, nil } +func (s *mockStorage) All() []dtos.SplitDTO { return make([]dtos.SplitDTO, 0) } +func (s *mockStorage) SegmentNames() *set.ThreadUnsafeSet { return nil } +func (s *mockStorage) LargeSegmentNames() *set.ThreadUnsafeSet { return nil } +func (s *mockStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { return nil } +func (s *mockStorage) SplitNames() []string { return make([]string, 0) } +func (s *mockStorage) TrafficTypeExists(trafficType string) bool { return true } +func (s *mockStorage) ChangeNumber() (int64, error) { return 0, nil } func (s *mockStorage) GetNamesByFlagSets(sets []string) map[string][]string { return make(map[string][]string) } diff --git a/storage/inmemory/mutexmap/rulebasedsegment.go b/storage/inmemory/mutexmap/rulebasedsegment.go index d247ab7a..13162ec5 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment.go +++ b/storage/inmemory/mutexmap/rulebasedsegment.go @@ -57,10 +57,10 @@ func (r *RuleBasedSegmentsStorageImpl) SetChangeNumber(till int64) error { } // ChangeNumber return the actual rule-based till -func (r *RuleBasedSegmentsStorageImpl) ChangeNumber() int64 { +func (r *RuleBasedSegmentsStorageImpl) ChangeNumber() (int64, error) { r.tillMutex.RLock() defer r.tillMutex.RUnlock() - return r.till + return r.till, nil } // All returns a list with a copy of each rule-based. @@ -87,7 +87,7 @@ func (r *RuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() []string { } // SegmentNames returns a slice with the names of all segments referenced in rule-based -func (r *RuleBasedSegmentsStorageImpl) GetSegments() *set.ThreadUnsafeSet { +func (r *RuleBasedSegmentsStorageImpl) Segments() *set.ThreadUnsafeSet { segments := set.NewSet() r.mutex.RLock() @@ -109,6 +109,28 @@ func (r *RuleBasedSegmentsStorageImpl) GetSegments() *set.ThreadUnsafeSet { return segments } +func (r *RuleBasedSegmentsStorageImpl) LargeSegments() *set.ThreadUnsafeSet { + largeSegments := set.NewSet() + + r.mutex.RLock() + defer r.mutex.RUnlock() + for _, ruleBased := range r.data { + for _, condition := range ruleBased.Conditions { + for _, matcher := range condition.MatcherGroup.Matchers { + if matcher.UserDefinedLargeSegment != nil { + largeSegments.Add(matcher.UserDefinedSegment.SegmentName) + } + } + } + for _, excluded := range ruleBased.Excluded.Segments { + if excluded.Type == dtos.TypeStandard { + largeSegments.Add(excluded.Name) + } + } + } + return largeSegments +} + // Contains returns true or false if all the rule-based segment names are present func (r *RuleBasedSegmentsStorageImpl) Contains(ruleBasedSegmentNames []string) bool { if len(ruleBasedSegmentNames) == 0 { diff --git a/storage/inmemory/mutexmap/rulebasedsegment_test.go b/storage/inmemory/mutexmap/rulebasedsegment_test.go index 9e7a7a7d..cd5d0811 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment_test.go +++ b/storage/inmemory/mutexmap/rulebasedsegment_test.go @@ -13,7 +13,8 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { storage := NewRuleBasedSegmentsStorage() // Test initial state - assert.Equal(t, int64(-1), storage.ChangeNumber()) + changeNumber, _ := storage.ChangeNumber() + assert.Equal(t, int64(-1), changeNumber) assert.Empty(t, storage.All()) assert.Empty(t, storage.RuleBasedSegmentNames()) @@ -62,7 +63,8 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { // Test Update storage.Update([]dtos.RuleBasedSegmentDTO{ruleBased1, ruleBased2}, nil, 123) - assert.Equal(t, int64(123), storage.ChangeNumber()) + changeNumber, _ = storage.ChangeNumber() + assert.Equal(t, int64(123), changeNumber) assert.Len(t, storage.All(), 2) // Test RuleBasedSegmentNames @@ -71,7 +73,7 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { assert.Contains(t, names, "rule2") // Test GetSegments - segments := storage.GetSegments() + segments := storage.Segments() // Print segments for debugging t.Logf("Segments in set: %v", segments.List()) assert.True(t, segments.Has("segment1"), "segment1 should be in segments") @@ -85,7 +87,8 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { // Test Remove storage.Update(nil, []dtos.RuleBasedSegmentDTO{ruleBased1}, 124) - assert.Equal(t, int64(124), storage.ChangeNumber()) + changeNumber, _ = storage.ChangeNumber() + assert.Equal(t, int64(124), changeNumber) assert.Len(t, storage.All(), 1) assert.Contains(t, storage.RuleBasedSegmentNames(), "rule2") @@ -142,7 +145,8 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { storage.ReplaceAll([]dtos.RuleBasedSegmentDTO{newRuleBased}, 200) // Verify change number was updated - assert.Equal(t, int64(200), storage.ChangeNumber()) + changeNumber, _ := storage.ChangeNumber() + assert.Equal(t, int64(200), changeNumber) // Verify old data was removed oldSegment, err := storage.GetRuleBasedSegmentByName("initial") @@ -156,7 +160,7 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { assert.Equal(t, "new", newSegment.Name) // Verify segments set - segments := storage.GetSegments() + segments := storage.Segments() assert.True(t, segments.Has("segment2")) assert.False(t, segments.Has("segment1")) @@ -165,7 +169,8 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { // Verify storage is empty assert.Empty(t, storage.All()) - assert.Equal(t, int64(300), storage.ChangeNumber()) + changeNumber, _ = storage.ChangeNumber() + assert.Equal(t, int64(300), changeNumber) // Test ReplaceAll with multiple segments ruleBased1 := dtos.RuleBasedSegmentDTO{ @@ -206,11 +211,12 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { // Verify multiple segments were added assert.Len(t, storage.All(), 2) - assert.Equal(t, int64(400), storage.ChangeNumber()) + changeNumber, _ = storage.ChangeNumber() + assert.Equal(t, int64(400), changeNumber) assert.True(t, storage.Contains([]string{"rule1", "rule2"})) // Verify segments set contains both segments - segments = storage.GetSegments() + segments = storage.Segments() assert.True(t, segments.Has("segment3")) assert.True(t, segments.Has("segment4")) } @@ -221,7 +227,8 @@ func TestRuleBasedSegmentsStorageEdgeCases(t *testing.T) { // Test SetChangeNumber explicitly err := storage.SetChangeNumber(100) assert.NoError(t, err) - assert.Equal(t, int64(100), storage.ChangeNumber()) + changeNumber, _ := storage.ChangeNumber() + assert.Equal(t, int64(100), changeNumber) // Test GetSegments with different segment types ruleBased := dtos.RuleBasedSegmentDTO{ @@ -241,7 +248,7 @@ func TestRuleBasedSegmentsStorageEdgeCases(t *testing.T) { } storage.Update([]dtos.RuleBasedSegmentDTO{ruleBased}, nil, 101) - segments := storage.GetSegments() + segments := storage.Segments() assert.True(t, segments.Has("excluded1")) assert.False(t, segments.Has("excluded2")) // Should not include non-standard segments @@ -288,7 +295,7 @@ func TestRuleBasedSegmentsStorageConcurrent(t *testing.T) { defer wg.Done() _ = storage.All() _ = storage.RuleBasedSegmentNames() - _ = storage.GetSegments() + _ = storage.Segments() _ = storage.Contains([]string{"segment1"}) }() } diff --git a/storage/inmemory/mutexmap/splits.go b/storage/inmemory/mutexmap/splits.go index 5de3f907..37172a8c 100644 --- a/storage/inmemory/mutexmap/splits.go +++ b/storage/inmemory/mutexmap/splits.go @@ -224,6 +224,23 @@ func (m *MMSplitStorage) LargeSegmentNames() *set.ThreadUnsafeSet { return largeSegments } +func (m *MMSplitStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + ruleBasedSegments := set.NewSet() + m.mutex.RLock() + defer m.mutex.RUnlock() + for _, split := range m.data { + for _, condition := range split.Conditions { + for _, matcher := range condition.MatcherGroup.Matchers { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == "IN_RULE_BASED_SEGMENT" { + ruleBasedSegments.Add(matcher.UserDefinedSegment.SegmentName) + } + + } + } + } + return ruleBasedSegments +} + // SetChangeNumber sets the till value belong to split func (m *MMSplitStorage) SetChangeNumber(till int64) error { m.tillMutex.Lock() @@ -270,7 +287,7 @@ func (m *MMSplitStorage) GetNamesByFlagSets(sets []string) map[string][]string { return toReturn } -func (m *MMSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { +func (m *MMSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error { // Get all current splits under read lock m.mutex.RLock() toRemove := make([]dtos.SplitDTO, 0) @@ -283,6 +300,7 @@ func (m *MMSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { m.mutex.Lock() defer m.mutex.Unlock() m.update(toAdd, toRemove, changeNumber) + return nil } var _ storage.SplitStorageConsumer = (*MMSplitStorage)(nil) diff --git a/storage/interfaces.go b/storage/interfaces.go index fd9d87f4..2382144c 100644 --- a/storage/interfaces.go +++ b/storage/interfaces.go @@ -13,7 +13,7 @@ type SplitStorageProducer interface { Update(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) KillLocally(splitName string, defaultTreatment string, changeNumber int64) SetChangeNumber(changeNumber int64) error - ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) + ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error } // SplitStorageConsumer should be implemented by structs that offer reading splits from storage @@ -21,8 +21,9 @@ type SplitStorageConsumer interface { ChangeNumber() (int64, error) All() []dtos.SplitDTO FetchMany(splitNames []string) map[string]*dtos.SplitDTO - SegmentNames() *set.ThreadUnsafeSet // Not in Spec - LargeSegmentNames() *set.ThreadUnsafeSet // Not in Spec + SegmentNames() *set.ThreadUnsafeSet // Not in Spec + LargeSegmentNames() *set.ThreadUnsafeSet // Not in Spec + RuleBasedSegmentNames() *set.ThreadUnsafeSet // Not in Spec Split(splitName string) *dtos.SplitDTO SplitNames() []string TrafficTypeExists(trafficType string) bool @@ -205,14 +206,15 @@ type SplitStorage interface { SetChangeNumber(changeNumber int64) error All() []dtos.SplitDTO FetchMany(splitNames []string) map[string]*dtos.SplitDTO - SegmentNames() *set.ThreadUnsafeSet // Not in Spec - LargeSegmentNames() *set.ThreadUnsafeSet // Not in Spec + SegmentNames() *set.ThreadUnsafeSet // Not in Spec + LargeSegmentNames() *set.ThreadUnsafeSet // Not in Spec + RuleBasedSegmentNames() *set.ThreadUnsafeSet // Not in Spec Split(splitName string) *dtos.SplitDTO SplitNames() []string TrafficTypeExists(trafficType string) bool GetNamesByFlagSets(sets []string) map[string][]string GetAllFlagSetNames() []string - ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) + ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error } // SegmentStorage wraps consumer and producer interfaces @@ -273,16 +275,17 @@ type RuleBasedSegmentStorageProducer interface { SetChangeNumber(till int64) error Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) Clear() - ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) + ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error } // RuleBasedStorageConsumer interface should be implemented by all structs that ofer reading rule-based segments type RuleBasedSegmentStorageConsumer interface { - ChangeNumber() int64 + ChangeNumber() (int64, error) All() []dtos.RuleBasedSegmentDTO RuleBasedSegmentNames() []string Contains(ruleBasedSegmentNames []string) bool - GetSegments() *set.ThreadUnsafeSet + Segments() *set.ThreadUnsafeSet + LargeSegments() *set.ThreadUnsafeSet GetRuleBasedSegmentByName(name string) (*dtos.RuleBasedSegmentDTO, error) } diff --git a/storage/mocks/rulebasedsegment.go b/storage/mocks/rulebasedsegment.go index e0c055d7..453e2520 100644 --- a/storage/mocks/rulebasedsegment.go +++ b/storage/mocks/rulebasedsegment.go @@ -13,9 +13,9 @@ type MockRuleBasedSegmentStorage struct { } // ChangeNumber mock -func (m *MockRuleBasedSegmentStorage) ChangeNumber() int64 { +func (m *MockRuleBasedSegmentStorage) ChangeNumber() (int64, error) { args := m.Called() - return args.Get(0).(int64) + return args.Get(0).(int64), nil } // All mock @@ -70,8 +70,19 @@ func (m *MockRuleBasedSegmentStorage) Clear() { m.Called() } -func (m *MockRuleBasedSegmentStorage) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) { - m.Called(toAdd, changeNumber) +func (m *MockRuleBasedSegmentStorage) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error { + args := m.Called(toAdd, changeNumber) + return args.Error(0) +} + +func (m *MockRuleBasedSegmentStorage) Segments() *set.ThreadUnsafeSet { + args := m.Called() + return args.Get(0).(*set.ThreadUnsafeSet) +} + +func (m *MockRuleBasedSegmentStorage) LargeSegments() *set.ThreadUnsafeSet { + args := m.Called() + return args.Get(0).(*set.ThreadUnsafeSet) } var _ storage.RuleBasedSegmentsStorage = (*MockRuleBasedSegmentStorage)(nil) diff --git a/storage/mocks/split.go b/storage/mocks/split.go index b618f122..0032b9c3 100644 --- a/storage/mocks/split.go +++ b/storage/mocks/split.go @@ -9,21 +9,22 @@ import ( // MockSplitStorage is a mocked implementation of Split Storage type MockSplitStorage struct { - AllCall func() []dtos.SplitDTO - ChangeNumberCall func() (int64, error) - FetchManyCall func(splitNames []string) map[string]*dtos.SplitDTO - KillLocallyCall func(splitName string, defaultTreatment string, changeNumber int64) - UpdateCall func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) - RemoveCall func(splitName string) - SegmentNamesCall func() *set.ThreadUnsafeSet - LargeSegmentNamesCall func() *set.ThreadUnsafeSet - SetChangeNumberCall func(changeNumber int64) error - SplitCall func(splitName string) *dtos.SplitDTO - SplitNamesCall func() []string - TrafficTypeExistsCall func(trafficType string) bool - GetNamesByFlagSetsCall func(sets []string) map[string][]string - GetAllFlagSetNamesCall func() []string - ReplaceAllCall func(toAdd []dtos.SplitDTO, changeNumber int64) + AllCall func() []dtos.SplitDTO + ChangeNumberCall func() (int64, error) + FetchManyCall func(splitNames []string) map[string]*dtos.SplitDTO + KillLocallyCall func(splitName string, defaultTreatment string, changeNumber int64) + UpdateCall func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) + RemoveCall func(splitName string) + SegmentNamesCall func() *set.ThreadUnsafeSet + LargeSegmentNamesCall func() *set.ThreadUnsafeSet + RuleBasedSegmentNamesCall func() *set.ThreadUnsafeSet + SetChangeNumberCall func(changeNumber int64) error + SplitCall func(splitName string) *dtos.SplitDTO + SplitNamesCall func() []string + TrafficTypeExistsCall func(trafficType string) bool + GetNamesByFlagSetsCall func(sets []string) map[string][]string + GetAllFlagSetNamesCall func() []string + ReplaceAllCall func(toAdd []dtos.SplitDTO, changeNumber int64) error } // All mock @@ -61,11 +62,16 @@ func (m MockSplitStorage) SegmentNames() *set.ThreadUnsafeSet { return m.SegmentNamesCall() } -// SegmentNames mock +// LargeSegmentNames mock func (m MockSplitStorage) LargeSegmentNames() *set.ThreadUnsafeSet { return m.LargeSegmentNamesCall() } +// RuleBasedSegmentNames mock +func (m MockSplitStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + return m.RuleBasedSegmentNamesCall() +} + // SetChangeNumber mock func (m MockSplitStorage) SetChangeNumber(changeNumber int64) error { return m.SetChangeNumberCall(changeNumber) @@ -95,8 +101,9 @@ func (m MockSplitStorage) GetAllFlagSetNames() []string { return m.GetAllFlagSetNamesCall() } -func (m MockSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { +func (m MockSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error { m.ReplaceAllCall(toAdd, changeNumber) + return nil } // SplitStorageMock is a mocked implementation of Split Storage with testify @@ -113,7 +120,7 @@ func (m *SplitStorageMock) All() []dtos.SplitDTO { // ChangeNumber mock func (m *SplitStorageMock) ChangeNumber() (int64, error) { args := m.Called() - return args.Get(0).(int64), args.Error(1) + return args.Get(0).(int64), nil } // FetchMany mock @@ -207,8 +214,17 @@ func (m *SplitStorageMock) GetAllFlagSetNames() []string { } // ReplaceAll mock -func (m *SplitStorageMock) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { - m.Called(toAdd, changeNumber) +func (m *SplitStorageMock) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error { + args := m.Called(toAdd, changeNumber) + return args.Error(0) +} + +func (m *SplitStorageMock) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).(*set.ThreadUnsafeSet) } var _ storage.SplitStorage = (*SplitStorageMock)(nil) diff --git a/storage/redis/splits.go b/storage/redis/splits.go index 8688ee0d..c8b68f7f 100644 --- a/storage/redis/splits.go +++ b/storage/redis/splits.go @@ -309,7 +309,7 @@ func (r *SplitStorage) SegmentNames() *set.ThreadUnsafeSet { for _, split := range splits { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil { + if matcher.UserDefinedSegment != nil && matcher.MatcherType != "IN_RULE_BASED_SEGMENT" { segmentNames.Add(matcher.UserDefinedSegment.SegmentName) } } @@ -325,6 +325,23 @@ func (r *SplitStorage) LargeSegmentNames() *set.ThreadUnsafeSet { return segments } +// RuleBasedSegmentNames returns a slice of strings with all the rule-baseed segment names +func (r *SplitStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + segmentNames := set.NewSet() + splits := r.All() + + for _, split := range splits { + for _, condition := range split.Conditions { + for _, matcher := range condition.MatcherGroup.Matchers { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == "IN_RULE_BASED_SEGMENT" { + segmentNames.Add(matcher.UserDefinedSegment.SegmentName) + } + } + } + } + return segmentNames +} + // SetChangeNumber sets the till value belong to segmentName func (r *SplitStorage) SetChangeNumber(changeNumber int64) error { return r.client.Set(KeySplitTill, changeNumber, 0) @@ -493,8 +510,9 @@ func (r *SplitStorage) splitKeysClusterMode() ([]string, error) { return result, nil } -func (r *SplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { +func (r *SplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) error { //to do + return nil } var _ storage.SplitStorage = (*SplitStorage)(nil) diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 5e67f98b..bce5481f 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -135,7 +135,7 @@ func TestSyncAllErrorInSegments(t *testing.T) { appMonitorMock := &hcMock.ApplicationMonitorMock{} appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(4) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -228,7 +228,7 @@ func TestSyncAllOk(t *testing.T) { appMonitorMock := &hcMock.ApplicationMonitorMock{} appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(4) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} diff --git a/synchronizer/worker/segment/segment.go b/synchronizer/worker/segment/segment.go index d313c083..1f8703d3 100644 --- a/synchronizer/worker/segment/segment.go +++ b/synchronizer/worker/segment/segment.go @@ -196,7 +196,7 @@ func (s *UpdaterImpl) SynchronizeSegment(name string, till *int64) (*UpdateResul // SynchronizeSegments syncs segments at once func (s *UpdaterImpl) SynchronizeSegments() (map[string]UpdateResult, error) { segmentNames := s.splitStorage.SegmentNames().List() - segmentNames = append(segmentNames, s.ruleBasedSegmentStorage.GetSegments().List()...) + segmentNames = append(segmentNames, s.ruleBasedSegmentStorage.Segments().List()...) s.logger.Debug("Segment Sync", segmentNames) s.hcMonitor.NotifyEvent(application.Segments) wg := sync.WaitGroup{} diff --git a/synchronizer/worker/segment/segment_test.go b/synchronizer/worker/segment/segment_test.go index d361e238..83f75791 100644 --- a/synchronizer/worker/segment/segment_test.go +++ b/synchronizer/worker/segment/segment_test.go @@ -71,7 +71,7 @@ func TestSegmentsSynchronizerError(t *testing.T) { }, } ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) segmentSync := NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, segmentMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock) @@ -184,7 +184,7 @@ func TestSegmentSynchronizer(t *testing.T) { }, } ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) segmentSync := NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, segmentMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock) @@ -259,7 +259,7 @@ func TestSegmentSyncUpdate(t *testing.T) { }, } ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) runtimeTelemetry, _ := inmemory.NewTelemetryStorage() segmentSync := NewSegmentUpdater(splitStorage, segmentStorage, ruleBasedSegmentMockStorage, segmentMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), runtimeTelemetry, appMonitorMock) @@ -376,7 +376,7 @@ func TestSegmentSyncProcess(t *testing.T) { }, } ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) runtimeTelemetry, _ := inmemory.NewTelemetryStorage() segmentSync := NewSegmentUpdater(splitStorage, segmentStorage, ruleBasedSegmentMockStorage, segmentMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), runtimeTelemetry, appMonitorMock) @@ -656,7 +656,7 @@ func TestSegmentSyncConcurrencyLimit(t *testing.T) { segmentStorage := mutexmap.NewMMSegmentStorage() runtimeTelemetry, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("Segments").Return(set.NewSet()) segmentSync := NewSegmentUpdater(splitStorage, segmentStorage, ruleBasedSegmentMockStorage, segmentMockFetcher, logging.NewLogger(nil), runtimeTelemetry, &application.Dummy{}) _, err := segmentSync.SynchronizeSegments() diff --git a/synchronizer/worker/split/split.go b/synchronizer/worker/split/split.go index ad66e630..cb14eaab 100644 --- a/synchronizer/worker/split/split.go +++ b/synchronizer/worker/split/split.go @@ -28,6 +28,7 @@ const ( UpdateTypeSplitChange = "SPLIT_UPDATE" UpdateTypeRuleBasedChange = "RB_SEGMENT_UPDATE" TypeStandard = "standard" + TypeLargeSegment = "large" onDemandFetchBackoffBase = int64(10) // backoff base starting at 10 seconds onDemandFetchBackoffMaxWait = 60 * time.Second // don't sleep for more than 1 minute onDemandFetchBackoffMaxRetries = 10 @@ -51,12 +52,13 @@ type Updater interface { // UpdateResult encapsulates information regarding the split update performed type UpdateResult struct { - UpdatedSplits []string - ReferencedSegments []string - ReferencedLargeSegments []string - NewChangeNumber int64 - NewRBChangeNumber int64 - RequiresFetch bool + UpdatedSplits []string + UpdatedRuleBasedSegments []string + ReferencedSegments []string + ReferencedLargeSegments []string + NewChangeNumber int64 + NewRBChangeNumber int64 + RequiresFetch bool } type internalSplitSync struct { @@ -122,11 +124,10 @@ func (s *UpdaterImpl) processUpdate(splitChanges dtos.FFResponse) { s.splitStorage.Update(activeSplits, inactiveSplits, splitChanges.FFTill()) } -func (s *UpdaterImpl) processRuleBasedUpdate(splitChanges dtos.FFResponse) []string { - activeRB, inactiveRB, segments := s.processRuleBasedSegmentChanges(splitChanges.RuleBasedSegments()) +func (s *UpdaterImpl) processRuleBasedUpdate(splitChanges dtos.FFResponse) { + activeRB, inactiveRB := s.processRuleBasedSegmentChanges(splitChanges.RuleBasedSegments()) // Add/Update active splits s.ruleBasedSegmentStorage.Update(activeRB, inactiveRB, splitChanges.RBTill()) - return segments } // fetchUntil Hit endpoint, update storage and return when since==till. @@ -134,6 +135,7 @@ func (s *UpdaterImpl) fetchUntil(fetchOptions *service.FlagRequestParams) (*Upda // just guessing sizes so the we don't realloc immediately segmentReferences := make([]string, 0, 10) updatedSplitNames := make([]string, 0, 10) + updatedRuleBasedsegmentNames := make([]string, 0, 10) largeSegmentReferences := make([]string, 0, 10) var err error var currentSince int64 @@ -141,7 +143,7 @@ func (s *UpdaterImpl) fetchUntil(fetchOptions *service.FlagRequestParams) (*Upda for { // Fetch until since==till currentSince, _ = s.splitStorage.ChangeNumber() - currentRBSince = s.ruleBasedSegmentStorage.ChangeNumber() + currentRBSince, _ = s.ruleBasedSegmentStorage.ChangeNumber() before := time.Now() var splitChanges dtos.FFResponse splitChanges, err = s.splitFetcher.Fetch(fetchOptions.WithChangeNumber(currentSince).WithChangeNumberRB(currentRBSince).WithSpecVersion(common.StringRef(s.specVersion))) @@ -158,21 +160,25 @@ func (s *UpdaterImpl) fetchUntil(fetchOptions *service.FlagRequestParams) (*Upda currentRBSince = splitChanges.RBTill() s.runtimeTelemetry.RecordSyncLatency(telemetry.SplitSync, time.Since(before)) s.processUpdate(splitChanges) - segmentReferences = s.processRuleBasedUpdate(splitChanges) + s.processRuleBasedUpdate(splitChanges) segmentReferences = appendSegmentNames(segmentReferences, splitChanges.FeatureFlags()) + segmentReferences = append(segmentReferences, s.getSegmentsFromRuleBasedSegments(splitChanges.RuleBasedSegments())...) updatedSplitNames = appendSplitNames(updatedSplitNames, splitChanges.FeatureFlags()) + updatedRuleBasedsegmentNames = appendRuleBasedNames(updatedRuleBasedsegmentNames, splitChanges.RuleBasedSegments()) largeSegmentReferences = appendLargeSegmentNames(largeSegmentReferences, splitChanges.FeatureFlags()) + largeSegmentReferences = append(largeSegmentReferences, s.getLargeSegmentsFromRuleBasedSegments(splitChanges.RuleBasedSegments())...) if splitChanges.NeedsAnotherFetch() { s.runtimeTelemetry.RecordSuccessfulSync(telemetry.SplitSync, time.Now().UTC()) break } } return &UpdateResult{ - UpdatedSplits: common.DedupeStringSlice(updatedSplitNames), - ReferencedSegments: common.DedupeStringSlice(segmentReferences), - NewChangeNumber: currentSince, - NewRBChangeNumber: currentRBSince, - ReferencedLargeSegments: common.DedupeStringSlice(largeSegmentReferences), + UpdatedSplits: common.DedupeStringSlice(updatedSplitNames), + UpdatedRuleBasedSegments: common.DedupeStringSlice(updatedRuleBasedsegmentNames), + ReferencedSegments: common.DedupeStringSlice(segmentReferences), + NewChangeNumber: currentSince, + NewRBChangeNumber: currentRBSince, + ReferencedLargeSegments: common.DedupeStringSlice(largeSegmentReferences), }, err } @@ -197,7 +203,7 @@ func (s *UpdaterImpl) attemptSplitSync(fetchOptions *service.FlagRequestParams, func (s *UpdaterImpl) synchronizeSplits(till *int64) (*UpdateResult, error) { s.hcMonitor.NotifyEvent(application.Splits) currentSince, _ := s.splitStorage.ChangeNumber() - currentRBSince := s.ruleBasedSegmentStorage.ChangeNumber() + currentRBSince, _ := s.ruleBasedSegmentStorage.ChangeNumber() if till != nil && *till < currentSince && *till < currentRBSince { // the passed till is less than change_number, no need to perform updates return &UpdateResult{}, nil } @@ -230,6 +236,7 @@ func (s *UpdaterImpl) attemptLatestSync() (*UpdateResult, error) { s.hcMonitor.NotifyEvent(application.Splits) // just guessing sizes so the we don't realloc immediately updatedSplitNames := make([]string, 0, 10) + updatedRuleBasedSegmentNames := make([]string, 0, 10) largeSegmentReferences := make([]string, 0, 10) currentSince := int64(-1) currentRBSince := int64(-1) @@ -256,13 +263,16 @@ func (s *UpdaterImpl) attemptLatestSync() (*UpdateResult, error) { segmentReferences := s.getSegmentsFromRuleBasedSegments(splitChanges.RuleBasedSegments()) segmentReferences = appendSegmentNames(segmentReferences, splitChanges.FeatureFlags()) updatedSplitNames = appendSplitNames(updatedSplitNames, splitChanges.FeatureFlags()) + updatedRuleBasedSegmentNames = appendRuleBasedNames(updatedRuleBasedSegmentNames, splitChanges.RuleBasedSegments()) largeSegmentReferences = appendLargeSegmentNames(largeSegmentReferences, splitChanges.FeatureFlags()) + largeSegmentReferences = append(largeSegmentReferences, s.getLargeSegmentsFromRuleBasedSegments(splitChanges.RuleBasedSegments())...) return &UpdateResult{ - UpdatedSplits: common.DedupeStringSlice(updatedSplitNames), - ReferencedSegments: common.DedupeStringSlice(segmentReferences), - NewChangeNumber: currentSince, - NewRBChangeNumber: currentRBSince, - ReferencedLargeSegments: common.DedupeStringSlice(largeSegmentReferences), + UpdatedSplits: common.DedupeStringSlice(updatedSplitNames), + UpdatedRuleBasedSegments: common.DedupeStringSlice(updatedRuleBasedSegmentNames), + ReferencedSegments: common.DedupeStringSlice(segmentReferences), + NewChangeNumber: currentSince, + NewRBChangeNumber: currentRBSince, + ReferencedLargeSegments: common.DedupeStringSlice(largeSegmentReferences), }, err } @@ -287,11 +297,12 @@ func (s *UpdaterImpl) SynchronizeSplits(till *int64) (*UpdateResult, error) { return untilTillSince, err } return &UpdateResult{ - UpdatedSplits: common.DedupeStringSlice(append(syncResult.UpdatedSplits, untilTillSince.UpdatedSplits...)), - ReferencedSegments: common.DedupeStringSlice(append(syncResult.ReferencedSegments, untilTillSince.ReferencedSegments...)), - NewChangeNumber: untilTillSince.NewChangeNumber, - NewRBChangeNumber: untilTillSince.NewRBChangeNumber, - ReferencedLargeSegments: common.DedupeStringSlice(append(syncResult.ReferencedLargeSegments, untilTillSince.ReferencedLargeSegments...)), + UpdatedSplits: common.DedupeStringSlice(append(syncResult.UpdatedSplits, untilTillSince.UpdatedSplits...)), + UpdatedRuleBasedSegments: common.DedupeStringSlice(append(syncResult.UpdatedRuleBasedSegments, untilTillSince.UpdatedRuleBasedSegments...)), + ReferencedSegments: common.DedupeStringSlice(append(syncResult.ReferencedSegments, untilTillSince.ReferencedSegments...)), + NewChangeNumber: untilTillSince.NewChangeNumber, + NewRBChangeNumber: untilTillSince.NewRBChangeNumber, + ReferencedLargeSegments: common.DedupeStringSlice(append(syncResult.ReferencedLargeSegments, untilTillSince.ReferencedLargeSegments...)), }, nil } else { if !errors.Is(err, ErrProxy) { @@ -311,6 +322,13 @@ func appendSplitNames(dst []string, featureFlags []dtos.SplitDTO) []string { return dst } +func appendRuleBasedNames(dst []string, ruleBasedSegments []dtos.RuleBasedSegmentDTO) []string { + for idx := range ruleBasedSegments { + dst = append(dst, ruleBasedSegments[idx].Name) + } + return dst +} + func appendSegmentNames(dst []string, featureFlags []dtos.SplitDTO) []string { for _, split := range featureFlags { for _, cond := range split.Conditions { @@ -345,9 +363,8 @@ func addIfNotExists(dst []string, seen map[string]struct{}, name string) []strin return dst } -func appendRuleBasedSegmentNames(dst []string, featureFlags []dtos.SplitDTO) []string { +func appendRuleBasedSegmentNamesReferenced(dst []string, featureFlags []dtos.SplitDTO) []string { seen := make(map[string]struct{}) - // Inicializamos el mapa con lo que ya tiene dst para no duplicar tampoco ahí for _, name := range dst { seen[name] = struct{}{} } @@ -399,32 +416,35 @@ func (s *UpdaterImpl) processFFChange(ffChange dtos.SplitChangeUpdate) *UpdateRe return &UpdateResult{RequiresFetch: true} } - // If we have a feature flag, update it - segmentReferences := make([]string, 0, 10) - updatedSplitNames := make([]string, 0, 1) - largeSegmentReferences := make([]string, 0, 10) - ruleBasedSegmentReferences := make([]string, 0, 10) - s.logger.Debug(fmt.Sprintf("updating feature flag %s", ffChange.FeatureFlag().Name)) - featureFlags := make([]dtos.SplitDTO, 0, 1) - featureFlags = append(featureFlags, *ffChange.FeatureFlag()) - activeFFs, inactiveFFs := s.processFeatureFlagChanges(featureFlags) - s.splitStorage.Update(activeFFs, inactiveFFs, ffChange.BaseUpdate.ChangeNumber()) - s.runtimeTelemetry.RecordUpdatesFromSSE(telemetry.SplitUpdate) - updatedSplitNames = append(updatedSplitNames, ffChange.FeatureFlag().Name) - segmentReferences = appendSegmentNames(segmentReferences, featureFlags) - largeSegmentReferences = appendLargeSegmentNames(largeSegmentReferences, featureFlags) - ruleBasedSegmentReferences = appendRuleBasedSegmentNames(ruleBasedSegmentReferences, featureFlags) - requiresFetch := false - if len(ruleBasedSegmentReferences) > 0 && !s.ruleBasedSegmentStorage.Contains(ruleBasedSegmentReferences) { - requiresFetch = true - } - return &UpdateResult{ - UpdatedSplits: updatedSplitNames, - ReferencedSegments: segmentReferences, - NewChangeNumber: ffChange.BaseUpdate.ChangeNumber(), - RequiresFetch: requiresFetch, - ReferencedLargeSegments: largeSegmentReferences, + if ffChange.FeatureFlag() != nil && *ffChange.PreviousChangeNumber() == changeNumber { + // If we have a feature flag, update it + segmentReferences := make([]string, 0, 10) + updatedSplitNames := make([]string, 0, 1) + largeSegmentReferences := make([]string, 0, 10) + ruleBasedSegmentReferences := make([]string, 0, 10) + s.logger.Debug(fmt.Sprintf("updating feature flag %s", ffChange.FeatureFlag().Name)) + featureFlags := make([]dtos.SplitDTO, 0, 1) + featureFlags = append(featureFlags, *ffChange.FeatureFlag()) + ruleBasedSegmentReferences = appendRuleBasedSegmentNamesReferenced(ruleBasedSegmentReferences, featureFlags) + if len(ruleBasedSegmentReferences) > 0 && !s.ruleBasedSegmentStorage.Contains(ruleBasedSegmentReferences) { + return &UpdateResult{RequiresFetch: true} + } + activeFFs, inactiveFFs := s.processFeatureFlagChanges(featureFlags) + s.splitStorage.Update(activeFFs, inactiveFFs, ffChange.BaseUpdate.ChangeNumber()) + s.runtimeTelemetry.RecordUpdatesFromSSE(telemetry.SplitUpdate) + updatedSplitNames = append(updatedSplitNames, ffChange.FeatureFlag().Name) + segmentReferences = appendSegmentNames(segmentReferences, featureFlags) + largeSegmentReferences = appendLargeSegmentNames(largeSegmentReferences, featureFlags) + return &UpdateResult{ + UpdatedSplits: updatedSplitNames, + ReferencedSegments: segmentReferences, + NewChangeNumber: ffChange.BaseUpdate.ChangeNumber(), + RequiresFetch: false, + ReferencedLargeSegments: largeSegmentReferences, + } } + s.logger.Debug("the feature flag was nil or the previous change number wasn't equal to the feature flag storage's change number") + return &UpdateResult{RequiresFetch: true} } func (s *UpdaterImpl) getSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) []string { @@ -448,20 +468,39 @@ func (s *UpdaterImpl) getSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) [] return segments } -func (s *UpdaterImpl) processRuleBasedSegmentChanges(ruleBasedSegments []dtos.RuleBasedSegmentDTO) ([]dtos.RuleBasedSegmentDTO, []dtos.RuleBasedSegmentDTO, []string) { +func (s *UpdaterImpl) getLargeSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) []string { + seen := make(map[string]struct{}) + largeSegments := make([]string, 0) + + for _, segment := range ruleBasedSegment.Excluded.Segments { + if segment.Type == TypeLargeSegment { + largeSegments = addIfNotExists(largeSegments, seen, segment.Name) + } + } + + for _, cond := range ruleBasedSegment.Conditions { + for _, matcher := range cond.MatcherGroup.Matchers { + if matcher.MatcherType == matcherTypeInLargeSegment { + largeSegments = addIfNotExists(largeSegments, seen, matcher.UserDefinedSegment.SegmentName) + } + } + } + + return largeSegments +} + +func (s *UpdaterImpl) processRuleBasedSegmentChanges(ruleBasedSegments []dtos.RuleBasedSegmentDTO) ([]dtos.RuleBasedSegmentDTO, []dtos.RuleBasedSegmentDTO) { toRemove := make([]dtos.RuleBasedSegmentDTO, 0, len(ruleBasedSegments)) toAdd := make([]dtos.RuleBasedSegmentDTO, 0, len(ruleBasedSegments)) - segments := make([]string, 0) for _, rbSegment := range ruleBasedSegments { if rbSegment.Status == Active { s.validator.ProcessRBMatchers(&rbSegment, s.logger) toAdd = append(toAdd, rbSegment) - segments = append(segments, s.getSegments(&rbSegment)...) } else { toRemove = append(toRemove, rbSegment) } } - return toAdd, toRemove, segments + return toAdd, toRemove } func (s *UpdaterImpl) getSegmentsFromRuleBasedSegments(ruleBasedSegments []dtos.RuleBasedSegmentDTO) []string { @@ -472,22 +511,42 @@ func (s *UpdaterImpl) getSegmentsFromRuleBasedSegments(ruleBasedSegments []dtos. return segments } +func (s *UpdaterImpl) getLargeSegmentsFromRuleBasedSegments(ruleBasedSegments []dtos.RuleBasedSegmentDTO) []string { + largeSegments := make([]string, 0) + for _, rbSegment := range ruleBasedSegments { + largeSegments = append(largeSegments, s.getLargeSegments(&rbSegment)...) + } + return largeSegments +} + func (s *UpdaterImpl) processRuleBasedChangeUpdate(ruleBasedChange dtos.SplitChangeUpdate) *UpdateResult { - changeNumber := s.ruleBasedSegmentStorage.ChangeNumber() + changeNumber, err := s.ruleBasedSegmentStorage.ChangeNumber() + if err != nil { + s.logger.Debug(fmt.Sprintf("problem getting change number from rule-based segments storage: %s", err.Error())) + return &UpdateResult{RequiresFetch: true} + } if changeNumber >= ruleBasedChange.BaseUpdate.ChangeNumber() { s.logger.Debug("the rule-based segment it's already updated") return &UpdateResult{RequiresFetch: true} } - ruleBasedSegments := make([]dtos.RuleBasedSegmentDTO, 0, 1) - ruleBasedSegments = append(ruleBasedSegments, *ruleBasedChange.RuleBasedSegment()) - toRemove, toAdd, segments := s.processRuleBasedSegmentChanges(ruleBasedSegments) - s.ruleBasedSegmentStorage.Update(toAdd, toRemove, ruleBasedChange.BaseUpdate.ChangeNumber()) - - return &UpdateResult{ - ReferencedSegments: segments, - NewRBChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), - RequiresFetch: false, + if ruleBasedChange.RuleBasedSegment() != nil && *ruleBasedChange.PreviousChangeNumber() == changeNumber { + ruleBasedSegments := make([]dtos.RuleBasedSegmentDTO, 0, 1) + ruleBasedSegments = append(ruleBasedSegments, *ruleBasedChange.RuleBasedSegment()) + toRemove, toAdd := s.processRuleBasedSegmentChanges(ruleBasedSegments) + segments := s.getSegmentsFromRuleBasedSegments(ruleBasedSegments) + //large segments + s.ruleBasedSegmentStorage.Update(toAdd, toRemove, ruleBasedChange.BaseUpdate.ChangeNumber()) + + return &UpdateResult{ + UpdatedRuleBasedSegments: []string{ruleBasedChange.RuleBasedSegment().Name}, + ReferencedSegments: segments, + //large segment referenced + NewRBChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), + RequiresFetch: false, + } } + s.logger.Debug("the rule-based segment was nil or the previous change number wasn't equal to the rule-based segment storage's change number") + return &UpdateResult{RequiresFetch: true} } func (s *UpdaterImpl) SynchronizeFeatureFlags(ffChange *dtos.SplitChangeUpdate) (*UpdateResult, error) { diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index b1bca597..cb7ee442 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -806,6 +806,9 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdate(t *testing.T) { ruleBasedSegmentMockStorage.AssertExpectations(t) appMonitorMock.AssertExpectations(t) }) +} + +func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { t.Run("Rule-based segment change number higher than current", func(t *testing.T) { ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} @@ -837,13 +840,25 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdate(t *testing.T) { }, }, } + response := &dtos.FFResponseV13{ + SplitChanges: dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Since: 3, + Till: 3, + }, + }, + } + pvChangeNumber := int64(100) baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") baseUpdate := dtos.NewBaseUpdate(baseMessage, changeNumber) - ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, nil, ruleBasedSegment) + ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, &pvChangeNumber, ruleBasedSegment) - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200)).Once() + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200)).Times(3) + splitMockStorage.On("ChangeNumber").Return(int64(200)).Twice() ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{*ruleBasedSegment}, []dtos.RuleBasedSegmentDTO{}, changeNumber).Return().Once() - + splitMockFetcher.On("Fetch", mock.Anything).Return(response, nil).Once() + splitMockStorage.On("Update", []dtos.SplitDTO{}, []dtos.SplitDTO{}, int64(3)).Once() + appMonitorMock.On("NotifyEvent", mock.Anything).Once() result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) assert.Nil(t, err) assert.False(t, result.RequiresFetch) From 2b389da7c06cc4bb8c168f06ce1a41cc08aef161 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 20 Oct 2025 15:17:06 -0300 Subject: [PATCH 2/8] Updated constants --- engine/engine_test.go | 16 +++--- engine/evaluator/evaluator_test.go | 17 ++++--- engine/grammar/condition_test.go | 3 +- engine/grammar/constants/constants.go | 51 +++++++++++++++++++ .../dependency_test/dependency_test.go | 17 ++++--- engine/grammar/inlargesegment_test.go | 17 ++++--- engine/grammar/matchers.go | 51 ------------------- engine/grammar/matchers_test.go | 7 +-- engine/grammar/rulebuilder.go | 49 +++++++++--------- engine/grammar/semver_test.go | 39 +++++++------- engine/grammar/split_test.go | 6 +-- engine/validator/matchers.go | 5 +- engine/validator/matchers_test.go | 31 +++++------ service/api/specs/splitversionfilter.go | 14 ++--- service/api/specs/splitversionfilter_test.go | 16 +++--- storage/inmemory/mutexmap/rulebasedsegment.go | 5 +- storage/inmemory/mutexmap/splits.go | 5 +- storage/redis/splits.go | 5 +- synchronizer/local_test.go | 17 ++++--- synchronizer/worker/split/split.go | 17 +++---- synchronizer/worker/split/split_test.go | 23 +++++---- tasks/splitsync_test.go | 17 ++++--- 22 files changed, 220 insertions(+), 208 deletions(-) diff --git a/engine/engine_test.go b/engine/engine_test.go index 981ebf2c..beff4799 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -16,14 +16,14 @@ import ( "github.com/splitio/go-toolkit/v5/logging" ) -var syncProxyFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} -var syncProxyRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} +var syncProxyFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} +var syncProxyRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} func TestProperHashFunctionIsUsed(t *testing.T) { eng := Engine{} diff --git a/engine/evaluator/evaluator_test.go b/engine/evaluator/evaluator_test.go index c542a1ae..44557cf7 100644 --- a/engine/evaluator/evaluator_test.go +++ b/engine/evaluator/evaluator_test.go @@ -7,6 +7,7 @@ import ( "github.com/splitio/go-split-commons/v7/engine" "github.com/splitio/go-split-commons/v7/engine/evaluator/impressionlabels" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/storage/inmemory/mutexmap" "github.com/splitio/go-split-commons/v7/storage/mocks" @@ -16,14 +17,14 @@ import ( "github.com/stretchr/testify/assert" ) -var syncProxyFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} -var syncProxyRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} +var syncProxyFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} +var syncProxyRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} type mockStorage struct{} diff --git a/engine/grammar/condition_test.go b/engine/grammar/condition_test.go index 05bf627b..c4fc14bf 100644 --- a/engine/grammar/condition_test.go +++ b/engine/grammar/condition_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/engine/grammar/datatypes" "github.com/splitio/go-toolkit/v5/logging" "github.com/stretchr/testify/assert" @@ -209,7 +210,7 @@ func TestConditionMatcherWithNilStringWrapperObject(t *testing.T) { Matchers: []dtos.MatcherDTO{ { Negate: false, - MatcherType: MatcherTypeStartsWith, + MatcherType: constants.MatcherTypeStartsWith, KeySelector: &dtos.KeySelectorDTO{ Attribute: nil, TrafficType: "something", diff --git a/engine/grammar/constants/constants.go b/engine/grammar/constants/constants.go index f3031837..6c7de375 100644 --- a/engine/grammar/constants/constants.go +++ b/engine/grammar/constants/constants.go @@ -12,3 +12,54 @@ const ( // SplitAlgoMurmur represents the murmur implementation of the hash funcion for bucketing SplitAlgoMurmur = 2 ) + +const ( + // MatcherTypeAllKeys string value + MatcherTypeAllKeys = "ALL_KEYS" + // MatcherTypeInSegment string value + MatcherTypeInSegment = "IN_SEGMENT" + // MatcherTypeWhitelist string value + MatcherTypeWhitelist = "WHITELIST" + // MatcherTypeEqualTo string value + MatcherTypeEqualTo = "EQUAL_TO" + // MatcherTypeGreaterThanOrEqualTo string value + MatcherTypeGreaterThanOrEqualTo = "GREATER_THAN_OR_EQUAL_TO" + // MatcherTypeLessThanOrEqualTo string value + MatcherTypeLessThanOrEqualTo = "LESS_THAN_OR_EQUAL_TO" + // MatcherTypeBetween string value + MatcherTypeBetween = "BETWEEN" + // MatcherTypeEqualToSet string value + MatcherTypeEqualToSet = "EQUAL_TO_SET" + // MatcherTypePartOfSet string value + MatcherTypePartOfSet = "PART_OF_SET" + // MatcherTypeContainsAllOfSet string value + MatcherTypeContainsAllOfSet = "CONTAINS_ALL_OF_SET" + // MatcherTypeContainsAnyOfSet string value + MatcherTypeContainsAnyOfSet = "CONTAINS_ANY_OF_SET" + // MatcherTypeStartsWith string value + MatcherTypeStartsWith = "STARTS_WITH" + // MatcherTypeEndsWith string value + MatcherTypeEndsWith = "ENDS_WITH" + // MatcherTypeContainsString string value + MatcherTypeContainsString = "CONTAINS_STRING" + // MatcherTypeInSplitTreatment string value + MatcherTypeInSplitTreatment = "IN_SPLIT_TREATMENT" + // MatcherTypeEqualToBoolean string value + MatcherTypeEqualToBoolean = "EQUAL_TO_BOOLEAN" + // MatcherTypeMatchesString string value + MatcherTypeMatchesString = "MATCHES_STRING" + // MatcherEqualToSemver string value + MatcherEqualToSemver = "EQUAL_TO_SEMVER" + // MatcherTypeGreaterThanOrEqualToSemver string value + MatcherTypeGreaterThanOrEqualToSemver = "GREATER_THAN_OR_EQUAL_TO_SEMVER" + // MatcherTypeLessThanOrEqualToSemver string value + MatcherTypeLessThanOrEqualToSemver = "LESS_THAN_OR_EQUAL_TO_SEMVER" + // MatcherTypeBetweenSemver string value + MatcherTypeBetweenSemver = "BETWEEN_SEMVER" + // MatcherTypeInListSemver string value + MatcherTypeInListSemver = "IN_LIST_SEMVER" + // MatcherInLargeSegment string value + MatcherTypeInLargeSegment = "IN_LARGE_SEGMENT" + // MatcherInRuleBasedSegment string value + MatcherTypeInRuleBasedSegment = "IN_RULE_BASED_SEGMENT" +) diff --git a/engine/grammar/dependency_test/dependency_test.go b/engine/grammar/dependency_test/dependency_test.go index 7a64c665..7f9520cf 100644 --- a/engine/grammar/dependency_test/dependency_test.go +++ b/engine/grammar/dependency_test/dependency_test.go @@ -11,20 +11,21 @@ import ( "github.com/splitio/go-split-commons/v7/engine" "github.com/splitio/go-split-commons/v7/engine/evaluator" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/logging" ) -var syncProxyFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} -var syncProxyRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} +var syncProxyFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} +var syncProxyRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} type mockEvaluator struct { t *testing.T diff --git a/engine/grammar/inlargesegment_test.go b/engine/grammar/inlargesegment_test.go index 7a064b52..c16a1b50 100644 --- a/engine/grammar/inlargesegment_test.go +++ b/engine/grammar/inlargesegment_test.go @@ -5,18 +5,19 @@ import ( "testing" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/logging" ) -var SyncProxyFeatureFlagsRules = []string{MatcherTypeAllKeys, MatcherTypeInSegment, MatcherTypeWhitelist, MatcherTypeEqualTo, MatcherTypeGreaterThanOrEqualTo, MatcherTypeLessThanOrEqualTo, MatcherTypeBetween, - MatcherTypeEqualToSet, MatcherTypePartOfSet, MatcherTypeContainsAllOfSet, MatcherTypeContainsAnyOfSet, MatcherTypeStartsWith, MatcherTypeEndsWith, MatcherTypeContainsString, MatcherTypeInSplitTreatment, - MatcherTypeEqualToBoolean, MatcherTypeMatchesString, MatcherEqualToSemver, MatcherTypeGreaterThanOrEqualToSemver, MatcherTypeLessThanOrEqualToSemver, MatcherTypeBetweenSemver, MatcherTypeInListSemver, MatcherTypeInLargeSegment, - MatcherTypeInRuleBasedSegment} -var SyncProxyRuleBasedSegmentRules = []string{MatcherTypeAllKeys, MatcherTypeInSegment, MatcherTypeWhitelist, MatcherTypeEqualTo, MatcherTypeGreaterThanOrEqualTo, MatcherTypeLessThanOrEqualTo, MatcherTypeBetween, - MatcherTypeEqualToSet, MatcherTypePartOfSet, MatcherTypeContainsAllOfSet, MatcherTypeContainsAnyOfSet, MatcherTypeStartsWith, MatcherTypeEndsWith, MatcherTypeContainsString, - MatcherTypeEqualToBoolean, MatcherTypeMatchesString, MatcherEqualToSemver, MatcherTypeGreaterThanOrEqualToSemver, MatcherTypeLessThanOrEqualToSemver, MatcherTypeBetweenSemver, MatcherTypeInListSemver, MatcherTypeInLargeSegment, - MatcherTypeInRuleBasedSegment} +var SyncProxyFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} +var SyncProxyRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} func TestInLargeSegmentMatcher(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) diff --git a/engine/grammar/matchers.go b/engine/grammar/matchers.go index 2f19ba57..3304e880 100644 --- a/engine/grammar/matchers.go +++ b/engine/grammar/matchers.go @@ -13,57 +13,6 @@ var ErrInvalidLTOESemver = errors.New("semver is required for LESS_THAN_OR_EQUAL var ErrInvalidLBetweenSemver = errors.New("semver is required for BETWEEN_SEMVER matcher type") var ErrInvalidLInListSemver = errors.New("semver is required for IN_LIST_SEMVER matcher type") -const ( - // MatcherTypeAllKeys string value - MatcherTypeAllKeys = "ALL_KEYS" - // MatcherTypeInSegment string value - MatcherTypeInSegment = "IN_SEGMENT" - // MatcherTypeWhitelist string value - MatcherTypeWhitelist = "WHITELIST" - // MatcherTypeEqualTo string value - MatcherTypeEqualTo = "EQUAL_TO" - // MatcherTypeGreaterThanOrEqualTo string value - MatcherTypeGreaterThanOrEqualTo = "GREATER_THAN_OR_EQUAL_TO" - // MatcherTypeLessThanOrEqualTo string value - MatcherTypeLessThanOrEqualTo = "LESS_THAN_OR_EQUAL_TO" - // MatcherTypeBetween string value - MatcherTypeBetween = "BETWEEN" - // MatcherTypeEqualToSet string value - MatcherTypeEqualToSet = "EQUAL_TO_SET" - // MatcherTypePartOfSet string value - MatcherTypePartOfSet = "PART_OF_SET" - // MatcherTypeContainsAllOfSet string value - MatcherTypeContainsAllOfSet = "CONTAINS_ALL_OF_SET" - // MatcherTypeContainsAnyOfSet string value - MatcherTypeContainsAnyOfSet = "CONTAINS_ANY_OF_SET" - // MatcherTypeStartsWith string value - MatcherTypeStartsWith = "STARTS_WITH" - // MatcherTypeEndsWith string value - MatcherTypeEndsWith = "ENDS_WITH" - // MatcherTypeContainsString string value - MatcherTypeContainsString = "CONTAINS_STRING" - // MatcherTypeInSplitTreatment string value - MatcherTypeInSplitTreatment = "IN_SPLIT_TREATMENT" - // MatcherTypeEqualToBoolean string value - MatcherTypeEqualToBoolean = "EQUAL_TO_BOOLEAN" - // MatcherTypeMatchesString string value - MatcherTypeMatchesString = "MATCHES_STRING" - // MatcherEqualToSemver string value - MatcherEqualToSemver = "EQUAL_TO_SEMVER" - // MatcherTypeGreaterThanOrEqualToSemver string value - MatcherTypeGreaterThanOrEqualToSemver = "GREATER_THAN_OR_EQUAL_TO_SEMVER" - // MatcherTypeLessThanOrEqualToSemver string value - MatcherTypeLessThanOrEqualToSemver = "LESS_THAN_OR_EQUAL_TO_SEMVER" - // MatcherTypeBetweenSemver string value - MatcherTypeBetweenSemver = "BETWEEN_SEMVER" - // MatcherTypeInListSemver string value - MatcherTypeInListSemver = "IN_LIST_SEMVER" - // MatcherInLargeSegment string value - MatcherTypeInLargeSegment = "IN_LARGE_SEGMENT" - // MatcherInRuleBasedSegment string value - MatcherTypeInRuleBasedSegment = "IN_RULE_BASED_SEGMENT" -) - // MatcherInterface should be implemented by all matchers type MatcherInterface interface { Match(key string, attributes map[string]interface{}, bucketingKey *string) bool diff --git a/engine/grammar/matchers_test.go b/engine/grammar/matchers_test.go index 9fb9d25f..d7c1e532 100644 --- a/engine/grammar/matchers_test.go +++ b/engine/grammar/matchers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-toolkit/v5/logging" "github.com/stretchr/testify/assert" ) @@ -18,7 +19,7 @@ func TestBuildMatcher_InRuleBasedSegment(t *testing.T) { { name: "valid rule-based segment matcher", dto: &dtos.MatcherDTO{ - MatcherType: MatcherTypeInRuleBasedSegment, + MatcherType: constants.MatcherTypeInRuleBasedSegment, Negate: false, UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", @@ -29,7 +30,7 @@ func TestBuildMatcher_InRuleBasedSegment(t *testing.T) { { name: "missing UserDefinedSegment", dto: &dtos.MatcherDTO{ - MatcherType: MatcherTypeInRuleBasedSegment, + MatcherType: constants.MatcherTypeInRuleBasedSegment, Negate: false, }, wantErr: true, @@ -38,7 +39,7 @@ func TestBuildMatcher_InRuleBasedSegment(t *testing.T) { { name: "with attribute name", dto: &dtos.MatcherDTO{ - MatcherType: MatcherTypeInRuleBasedSegment, + MatcherType: constants.MatcherTypeInRuleBasedSegment, Negate: true, UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", diff --git a/engine/grammar/rulebuilder.go b/engine/grammar/rulebuilder.go index 40cb8450..e70dd163 100644 --- a/engine/grammar/rulebuilder.go +++ b/engine/grammar/rulebuilder.go @@ -7,6 +7,7 @@ import ( "golang.org/x/exp/slices" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/engine/grammar/datatypes" "github.com/splitio/go-split-commons/v7/storage" "github.com/splitio/go-toolkit/v5/logging" @@ -53,11 +54,11 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error } switch dto.MatcherType { - case MatcherTypeAllKeys: + case constants.MatcherTypeAllKeys: r.logger.Debug(fmt.Sprintf("Building AllKeysMatcher with negate=%t", dto.Negate)) matcher = NewAllKeysMatcher(dto.Negate) - case MatcherTypeEqualTo: + case constants.MatcherTypeEqualTo: if dto.UnaryNumeric == nil { return nil, errors.New("UnaryNumeric is required for EQUAL_TO matcher type") } @@ -72,7 +73,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeInSegment: + case constants.MatcherTypeInSegment: if dto.UserDefinedSegment == nil { return nil, errors.New("UserDefinedSegment is required for IN_SEGMENT matcher type") } @@ -87,7 +88,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error r.segmentStorage, ) - case MatcherTypeWhitelist: + case constants.MatcherTypeWhitelist: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for WHITELIST matcher type") } @@ -101,7 +102,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeGreaterThanOrEqualTo: + case constants.MatcherTypeGreaterThanOrEqualTo: if dto.UnaryNumeric == nil { return nil, errors.New("UnaryNumeric is required for GREATER_THAN_OR_EQUAL_TO matcher type") } @@ -116,7 +117,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeLessThanOrEqualTo: + case constants.MatcherTypeLessThanOrEqualTo: if dto.UnaryNumeric == nil { return nil, errors.New("UnaryNumeric is required for LESS_THAN_OR_EQUAL_TO matcher type") } @@ -131,7 +132,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeBetween: + case constants.MatcherTypeBetween: if dto.Between == nil { return nil, errors.New("Between is required for BETWEEN matcher type") } @@ -147,7 +148,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeEqualToSet: + case constants.MatcherTypeEqualToSet: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for EQUAL_TO_SET matcher type") } @@ -161,7 +162,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypePartOfSet: + case constants.MatcherTypePartOfSet: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for PART_OF_SET matcher type") } @@ -175,7 +176,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeContainsAllOfSet: + case constants.MatcherTypeContainsAllOfSet: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for CONTAINS_ALL_OF_SET matcher type") } @@ -189,7 +190,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeContainsAnyOfSet: + case constants.MatcherTypeContainsAnyOfSet: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for CONTAINS_ANY_OF_SET matcher type") } @@ -203,7 +204,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeStartsWith: + case constants.MatcherTypeStartsWith: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for STARTS_WITH matcher type") } @@ -217,7 +218,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeEndsWith: + case constants.MatcherTypeEndsWith: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for ENDS_WITH matcher type") } @@ -231,7 +232,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeContainsString: + case constants.MatcherTypeContainsString: if dto.Whitelist == nil { return nil, errors.New("Whitelist is required for CONTAINS_STRING matcher type") } @@ -245,7 +246,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeInSplitTreatment: + case constants.MatcherTypeInSplitTreatment: if dto.Dependency == nil { return nil, errors.New("Dependency is required for IN_SPLIT_TREATMENT matcher type") } @@ -260,7 +261,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error r.dependencyEvaluator, ) - case MatcherTypeEqualToBoolean: + case constants.MatcherTypeEqualToBoolean: if dto.Boolean == nil { return nil, errors.New("Boolean is required for EQUAL_TO_BOOLEAN matcher type") } @@ -274,7 +275,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, ) - case MatcherTypeMatchesString: + case constants.MatcherTypeMatchesString: if dto.String == nil { return nil, errors.New("String is required for MATCHES_STRING matcher type") } @@ -287,7 +288,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error *dto.String, attributeName, ) - case MatcherEqualToSemver: + case constants.MatcherEqualToSemver: if dto.String == nil { return nil, ErrInvalidEqualSemver } @@ -301,7 +302,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.logger, ) - case MatcherTypeGreaterThanOrEqualToSemver: + case constants.MatcherTypeGreaterThanOrEqualToSemver: if dto.String == nil { return nil, ErrInvalidGTOESemver } @@ -315,7 +316,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.logger, ) - case MatcherTypeLessThanOrEqualToSemver: + case constants.MatcherTypeLessThanOrEqualToSemver: if dto.String == nil { return nil, ErrInvalidLTOESemver } @@ -329,7 +330,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.logger, ) - case MatcherTypeBetweenSemver: + case constants.MatcherTypeBetweenSemver: if dto.BetweenString.Start == nil || dto.BetweenString.End == nil { return nil, ErrInvalidLBetweenSemver } @@ -344,7 +345,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.logger, ) - case MatcherTypeInListSemver: + case constants.MatcherTypeInListSemver: if dto.Whitelist == nil { return nil, ErrInvalidLInListSemver } @@ -358,7 +359,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.logger, ) - case MatcherTypeInLargeSegment: + case constants.MatcherTypeInLargeSegment: if dto.UserDefinedLargeSegment == nil { return nil, errors.New("UserDefinedLargeSegment is required for IN_LARGE_SEGMENT matcher type") } @@ -372,7 +373,7 @@ func (r RuleBuilder) BuildMatcher(dto *dtos.MatcherDTO) (MatcherInterface, error attributeName, r.largeSegmentStorage, ) - case MatcherTypeInRuleBasedSegment: + case constants.MatcherTypeInRuleBasedSegment: if dto.UserDefinedSegment == nil { return nil, errors.New("UserDefinedSegment is required for IN_RULE_BASED_SEGMENT matcher type") } diff --git a/engine/grammar/semver_test.go b/engine/grammar/semver_test.go index ee1a908c..8b34da65 100644 --- a/engine/grammar/semver_test.go +++ b/engine/grammar/semver_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-toolkit/v5/logging" ) @@ -73,7 +74,7 @@ func TestEqualToSemverMatcher(t *testing.T) { attrName := "version" str := "1.0.0" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -99,7 +100,7 @@ func TestPatchDiffers(t *testing.T) { attrName := "version" str := "1.0.0" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -125,7 +126,7 @@ func TestPreReleaseShouldReturnTrueWhenVersionsAreEqual(t *testing.T) { attrName := "version" str := "1.2.3----RC-SNAPSHOT.12.9.1--.12.88" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -151,7 +152,7 @@ func TestPreReleaseShouldReturnFalseWhenSemverIsNil(t *testing.T) { attrName := "version" str := "1.2-SNAPSHOT" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -176,7 +177,7 @@ func TestPreReleaseShouldReturnFalseWhenVersionsDiffer(t *testing.T) { attrName := "version" str := "1.2.3----RC-SNAPSHOT.12.9.1--.12.88" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -202,7 +203,7 @@ func TestMetadataShouldReturnTrueWhenVersionsAreEqual(t *testing.T) { attrName := "version" str := "2.2.2-rc.2+metadata-lalala" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -228,7 +229,7 @@ func TestMetadataShouldReturnFalseWhenVersionsDiffer(t *testing.T) { attrName := "version" str := "2.2.2-rc.2+metadata-lalala" dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: &str, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -252,7 +253,7 @@ func TestMetadataShouldReturnFalseWhenVersionsDiffer(t *testing.T) { func TestShouldReturnErrorWithNilSemver(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) dto := &dtos.MatcherDTO{ - MatcherType: MatcherEqualToSemver, + MatcherType: constants.MatcherEqualToSemver, String: nil, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -273,7 +274,7 @@ func TestGreaterThanOrEqualToSemverMatcher(t *testing.T) { for _, twoSemvers := range semvers { dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeGreaterThanOrEqualToSemver, + MatcherType: constants.MatcherTypeGreaterThanOrEqualToSemver, String: &twoSemvers.semver1, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -307,7 +308,7 @@ func TestGreaterThanOrEqualToSemverMatcherWithNilSemver(t *testing.T) { KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, }, - MatcherType: MatcherTypeGreaterThanOrEqualToSemver, + MatcherType: constants.MatcherTypeGreaterThanOrEqualToSemver, String: &semvers, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -334,7 +335,7 @@ func TestLessThanOrEqualToSemverMatcher(t *testing.T) { for _, twoSemvers := range semvers { dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeLessThanOrEqualToSemver, + MatcherType: constants.MatcherTypeLessThanOrEqualToSemver, String: &twoSemvers.semver2, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, @@ -363,7 +364,7 @@ func TestLessThanOrEqualToSemverMatcher(t *testing.T) { func TestLessThanOrEqualToSemverMatcherWithInvalidSemver(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeLessThanOrEqualToSemver, + MatcherType: constants.MatcherTypeLessThanOrEqualToSemver, String: nil, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -382,7 +383,7 @@ func TestLessThanOrEqualToSemverMatcherWithNilSemver(t *testing.T) { KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, }, - MatcherType: MatcherTypeLessThanOrEqualToSemver, + MatcherType: constants.MatcherTypeLessThanOrEqualToSemver, String: &semvers, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -409,7 +410,7 @@ func TestBetweenSemverMatcher(t *testing.T) { for _, threeSemvers := range semvers { dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeBetweenSemver, + MatcherType: constants.MatcherTypeBetweenSemver, BetweenString: &dtos.BetweenStringMatcherDataDTO{ Start: &threeSemvers.semver1, End: &threeSemvers.semver3, @@ -441,7 +442,7 @@ func TestBetweenSemverMatcher(t *testing.T) { func TestBetweenSemverWithNilSemvers(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeBetweenSemver, + MatcherType: constants.MatcherTypeBetweenSemver, BetweenString: &dtos.BetweenStringMatcherDataDTO{ Start: nil, End: nil, @@ -461,7 +462,7 @@ func TestBetweenSemverWithInvalidSemvers(t *testing.T) { start := "1.alpha.2" end := "3.4.5" dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeBetweenSemver, + MatcherType: constants.MatcherTypeBetweenSemver, BetweenString: &dtos.BetweenStringMatcherDataDTO{ Start: &start, End: &end, @@ -495,7 +496,7 @@ func TestInListSemvers(t *testing.T) { KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, }, - MatcherType: MatcherTypeInListSemver, + MatcherType: constants.MatcherTypeInListSemver, Whitelist: &dtos.WhitelistMatcherDataDTO{Whitelist: semvers}, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -528,7 +529,7 @@ func TestInListSemversNotMatch(t *testing.T) { KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, }, - MatcherType: MatcherTypeInListSemver, + MatcherType: constants.MatcherTypeInListSemver, Whitelist: &dtos.WhitelistMatcherDataDTO{Whitelist: semvers}, } ruleBuilder := NewRuleBuilder(nil, nil, nil, SyncProxyFeatureFlagsRules, SyncProxyRuleBasedSegmentRules, logger, nil) @@ -559,7 +560,7 @@ func TestInListInvalidSemvers(t *testing.T) { semvers = append(semvers, "alpha.beta.1") semvers = append(semvers, "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788") dto := &dtos.MatcherDTO{ - MatcherType: MatcherTypeInListSemver, + MatcherType: constants.MatcherTypeInListSemver, Whitelist: &dtos.WhitelistMatcherDataDTO{Whitelist: semvers}, KeySelector: &dtos.KeySelectorDTO{ Attribute: &attrName, diff --git a/engine/grammar/split_test.go b/engine/grammar/split_test.go index 63ee1b58..ff6cef06 100644 --- a/engine/grammar/split_test.go +++ b/engine/grammar/split_test.go @@ -70,7 +70,7 @@ func TestSplitCreationWithConditionsMatcher(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "off", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Combiner: "AND", - Matchers: []dtos.MatcherDTO{{MatcherType: MatcherTypeAllKeys, Negate: false}}, + Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeAllKeys, Negate: false}}, }, }, { ConditionType: ConditionTypeWhitelist, @@ -78,7 +78,7 @@ func TestSplitCreationWithConditionsMatcher(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Combiner: "AND", - Matchers: []dtos.MatcherDTO{{MatcherType: MatcherTypeAllKeys, Negate: false}}, + Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeAllKeys, Negate: false}}, }, }}, DefaultTreatment: "def", @@ -148,7 +148,7 @@ func TestSplitCreationWithUnsupportedMatcher(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Combiner: "AND", - Matchers: []dtos.MatcherDTO{{MatcherType: MatcherTypeAllKeys, Negate: false}}, + Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeAllKeys, Negate: false}}, }, }}, DefaultTreatment: "def", diff --git a/engine/validator/matchers.go b/engine/validator/matchers.go index 1767ea01..76f8e53c 100644 --- a/engine/validator/matchers.go +++ b/engine/validator/matchers.go @@ -5,6 +5,7 @@ import ( "github.com/splitio/go-split-commons/v7/engine/evaluator" "github.com/splitio/go-split-commons/v7/engine/evaluator/impressionlabels" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/engine/grammar/datatypes" "github.com/splitio/go-toolkit/v5/logging" ) @@ -26,7 +27,7 @@ var unsupportedMatcherConditionReplacement []dtos.ConditionDTO = []dtos.Conditio Partitions: []dtos.PartitionDTO{{Treatment: evaluator.Control, Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Combiner: "AND", - Matchers: []dtos.MatcherDTO{{MatcherType: grammar.MatcherTypeAllKeys, Negate: false}}, + Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeAllKeys, Negate: false}}, }, }} @@ -35,7 +36,7 @@ var unsupportedMatcherRBConditionReplacement []dtos.RuleBasedConditionDTO = []dt ConditionType: grammar.ConditionTypeWhitelist, MatcherGroup: dtos.MatcherGroupDTO{ Combiner: "AND", - Matchers: []dtos.MatcherDTO{{MatcherType: grammar.MatcherTypeAllKeys, Negate: false}}, + Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeAllKeys, Negate: false}}, }, }} diff --git a/engine/validator/matchers_test.go b/engine/validator/matchers_test.go index c272d6ac..d0377db7 100644 --- a/engine/validator/matchers_test.go +++ b/engine/validator/matchers_test.go @@ -5,18 +5,19 @@ import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" ) -var goClientFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} -var goClientRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} +var goClientFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} +var goClientRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} func TestProcessRBMatchers(t *testing.T) { // Test case 1: Rule-based segment with unsupported matcher @@ -42,7 +43,7 @@ func TestProcessRBMatchers(t *testing.T) { if ruleBased.Conditions[0].ConditionType != grammar.ConditionTypeWhitelist { t.Error("ConditionType should be WHITELIST") } - if ruleBased.Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeAllKeys { + if ruleBased.Conditions[0].MatcherGroup.Matchers[0].MatcherType != constants.MatcherTypeAllKeys { t.Error("MatcherType should be ALL_KEYS") } @@ -55,7 +56,7 @@ func TestProcessRBMatchers(t *testing.T) { ConditionType: grammar.ConditionTypeRollout, MatcherGroup: dtos.MatcherGroupDTO{ Matchers: []dtos.MatcherDTO{ - {MatcherType: grammar.MatcherTypeEndsWith, KeySelector: nil, String: common.StringRef("test")}, + {MatcherType: constants.MatcherTypeEndsWith, KeySelector: nil, String: common.StringRef("test")}, }, }, }, @@ -68,7 +69,7 @@ func TestProcessRBMatchers(t *testing.T) { if ruleBased.Conditions[0].ConditionType != grammar.ConditionTypeRollout { t.Error("ConditionType should be ROLLOUT") } - if ruleBased.Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeEndsWith { + if ruleBased.Conditions[0].MatcherGroup.Matchers[0].MatcherType != constants.MatcherTypeEndsWith { t.Error("MatcherType should be ENDS_WITH") } } @@ -81,7 +82,7 @@ func TestProcessMatchers(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Matchers: []dtos.MatcherDTO{ - {MatcherType: grammar.MatcherTypeEndsWith, KeySelector: nil, String: common.StringRef("test")}, + {MatcherType: constants.MatcherTypeEndsWith, KeySelector: nil, String: common.StringRef("test")}, }, }, }, @@ -104,7 +105,7 @@ func TestProcessMatchers(t *testing.T) { if split.Conditions[0].ConditionType != grammar.ConditionTypeWhitelist { t.Error("ConditionType should be WHITELIST") } - if split.Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeAllKeys { + if split.Conditions[0].MatcherGroup.Matchers[0].MatcherType != constants.MatcherTypeAllKeys { t.Error("MatcherType should be ALL_KEYS") } @@ -115,7 +116,7 @@ func TestProcessMatchers(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Matchers: []dtos.MatcherDTO{ - {MatcherType: grammar.MatcherTypeAllKeys, KeySelector: nil}, + {MatcherType: constants.MatcherTypeAllKeys, KeySelector: nil}, }, }, }, @@ -126,7 +127,7 @@ func TestProcessMatchers(t *testing.T) { if split.Conditions[0].ConditionType != grammar.ConditionTypeRollout { t.Error("ConditionType should be ROLLOUT") } - if split.Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeAllKeys { + if split.Conditions[0].MatcherGroup.Matchers[0].MatcherType != constants.MatcherTypeAllKeys { t.Error("MatcherType should be ALL_KEYS") } } diff --git a/service/api/specs/splitversionfilter.go b/service/api/specs/splitversionfilter.go index be2e401e..aff894e6 100644 --- a/service/api/specs/splitversionfilter.go +++ b/service/api/specs/splitversionfilter.go @@ -1,7 +1,7 @@ package specs import ( - "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" ) type SplitVersionFilter struct { @@ -10,13 +10,13 @@ type SplitVersionFilter struct { } func NewSplitVersionFilter() SplitVersionFilter { - v1_1 := map[string]bool{grammar.MatcherTypeInLargeSegment: true} + v1_1 := map[string]bool{constants.MatcherTypeInLargeSegment: true} v1_0 := mergeMaps(map[string]bool{ - grammar.MatcherEqualToSemver: true, - grammar.MatcherTypeLessThanOrEqualToSemver: true, - grammar.MatcherTypeGreaterThanOrEqualToSemver: true, - grammar.MatcherTypeBetweenSemver: true, - grammar.MatcherTypeInListSemver: true, + constants.MatcherEqualToSemver: true, + constants.MatcherTypeLessThanOrEqualToSemver: true, + constants.MatcherTypeGreaterThanOrEqualToSemver: true, + constants.MatcherTypeBetweenSemver: true, + constants.MatcherTypeInListSemver: true, }, v1_1) return SplitVersionFilter{ diff --git a/service/api/specs/splitversionfilter_test.go b/service/api/specs/splitversionfilter_test.go index 43680447..30d00854 100644 --- a/service/api/specs/splitversionfilter_test.go +++ b/service/api/specs/splitversionfilter_test.go @@ -3,7 +3,7 @@ package specs import ( "testing" - "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" ) func TestParseAndValidate(t *testing.T) { @@ -30,37 +30,37 @@ func TestParseAndValidate(t *testing.T) { func TestSplitVersionFilter(t *testing.T) { filter := NewSplitVersionFilter() - shouldFilter := filter.ShouldFilter(grammar.MatcherTypeBetweenSemver, FLAG_V1_0) + shouldFilter := filter.ShouldFilter(constants.MatcherTypeBetweenSemver, FLAG_V1_0) if !shouldFilter { t.Error("It should filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeEqualTo, FLAG_V1_0) + shouldFilter = filter.ShouldFilter(constants.MatcherTypeEqualTo, FLAG_V1_0) if shouldFilter { t.Error("It should not filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeBetweenSemver, FLAG_V1_1) + shouldFilter = filter.ShouldFilter(constants.MatcherTypeBetweenSemver, FLAG_V1_1) if shouldFilter { t.Error("It should not filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeInLargeSegment, FLAG_V1_0) + shouldFilter = filter.ShouldFilter(constants.MatcherTypeInLargeSegment, FLAG_V1_0) if !shouldFilter { t.Error("It should filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeInLargeSegment, FLAG_V1_1) + shouldFilter = filter.ShouldFilter(constants.MatcherTypeInLargeSegment, FLAG_V1_1) if !shouldFilter { t.Error("It should filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeInLargeSegment, FLAG_V1_2) + shouldFilter = filter.ShouldFilter(constants.MatcherTypeInLargeSegment, FLAG_V1_2) if shouldFilter { t.Error("It should not filtered") } - shouldFilter = filter.ShouldFilter(grammar.MatcherTypeInLargeSegment, "4.3") + shouldFilter = filter.ShouldFilter(constants.MatcherTypeInLargeSegment, "4.3") if shouldFilter { t.Error("It should not filtered") } diff --git a/storage/inmemory/mutexmap/rulebasedsegment.go b/storage/inmemory/mutexmap/rulebasedsegment.go index 13162ec5..cee1d095 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment.go +++ b/storage/inmemory/mutexmap/rulebasedsegment.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-toolkit/v5/datastructures/set" ) @@ -95,7 +96,7 @@ func (r *RuleBasedSegmentsStorageImpl) Segments() *set.ThreadUnsafeSet { for _, ruleBased := range r.data { for _, condition := range ruleBased.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType != "IN_RULE_BASED_SEGMENT" { + if matcher.UserDefinedSegment != nil && matcher.MatcherType != constants.MatcherTypeInRuleBasedSegment { segments.Add(matcher.UserDefinedSegment.SegmentName) } } @@ -123,7 +124,7 @@ func (r *RuleBasedSegmentsStorageImpl) LargeSegments() *set.ThreadUnsafeSet { } } for _, excluded := range ruleBased.Excluded.Segments { - if excluded.Type == dtos.TypeStandard { + if excluded.Type == dtos.TypeLarge { largeSegments.Add(excluded.Name) } } diff --git a/storage/inmemory/mutexmap/splits.go b/storage/inmemory/mutexmap/splits.go index 37172a8c..ac45852f 100644 --- a/storage/inmemory/mutexmap/splits.go +++ b/storage/inmemory/mutexmap/splits.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/storage" "github.com/splitio/go-toolkit/v5/datastructures/set" @@ -197,7 +198,7 @@ func (m *MMSplitStorage) SegmentNames() *set.ThreadUnsafeSet { for _, split := range m.data { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType != "IN_RULE_BASED_SEGMENT" { + if matcher.UserDefinedSegment != nil && matcher.MatcherType != constants.MatcherTypeInRuleBasedSegment { segments.Add(matcher.UserDefinedSegment.SegmentName) } @@ -231,7 +232,7 @@ func (m *MMSplitStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { for _, split := range m.data { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType == "IN_RULE_BASED_SEGMENT" { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == constants.MatcherTypeInRuleBasedSegment { ruleBasedSegments.Add(matcher.UserDefinedSegment.SegmentName) } diff --git a/storage/redis/splits.go b/storage/redis/splits.go index c8b68f7f..86d69f97 100644 --- a/storage/redis/splits.go +++ b/storage/redis/splits.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/storage" "github.com/splitio/go-toolkit/v5/datastructures/set" @@ -309,7 +310,7 @@ func (r *SplitStorage) SegmentNames() *set.ThreadUnsafeSet { for _, split := range splits { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType != "IN_RULE_BASED_SEGMENT" { + if matcher.UserDefinedSegment != nil && matcher.MatcherType != constants.MatcherTypeInRuleBasedSegment { segmentNames.Add(matcher.UserDefinedSegment.SegmentName) } } @@ -333,7 +334,7 @@ func (r *SplitStorage) RuleBasedSegmentNames() *set.ThreadUnsafeSet { for _, split := range splits { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType == "IN_RULE_BASED_SEGMENT" { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == constants.MatcherTypeInRuleBasedSegment { segmentNames.Add(matcher.UserDefinedSegment.SegmentName) } } diff --git a/synchronizer/local_test.go b/synchronizer/local_test.go index 6fb044fb..8ed5a912 100644 --- a/synchronizer/local_test.go +++ b/synchronizer/local_test.go @@ -7,6 +7,7 @@ import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" "github.com/splitio/go-split-commons/v7/service" @@ -22,14 +23,14 @@ import ( "github.com/stretchr/testify/mock" ) -var goClientFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} -var goClientRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} +var goClientFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} +var goClientRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} func TestLocalSyncAllError(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) diff --git a/synchronizer/worker/split/split.go b/synchronizer/worker/split/split.go index cb14eaab..765ec90c 100644 --- a/synchronizer/worker/split/split.go +++ b/synchronizer/worker/split/split.go @@ -8,6 +8,7 @@ import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/engine/validator" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/healthcheck/application" @@ -22,12 +23,8 @@ import ( ) const ( - matcherTypeInSegment = "IN_SEGMENT" - matcherTypeInLargeSegment = "IN_LARGE_SEGMENT" - matcherTypeInRuleBasedSegment = "IN_RULE_BASED_SEGMENT" UpdateTypeSplitChange = "SPLIT_UPDATE" UpdateTypeRuleBasedChange = "RB_SEGMENT_UPDATE" - TypeStandard = "standard" TypeLargeSegment = "large" onDemandFetchBackoffBase = int64(10) // backoff base starting at 10 seconds onDemandFetchBackoffMaxWait = 60 * time.Second // don't sleep for more than 1 minute @@ -333,7 +330,7 @@ func appendSegmentNames(dst []string, featureFlags []dtos.SplitDTO) []string { for _, split := range featureFlags { for _, cond := range split.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { - if matcher.MatcherType == matcherTypeInSegment && matcher.UserDefinedSegment != nil { + if matcher.MatcherType == constants.MatcherTypeInSegment && matcher.UserDefinedSegment != nil { dst = append(dst, matcher.UserDefinedSegment.SegmentName) } } @@ -346,7 +343,7 @@ func appendLargeSegmentNames(dst []string, featureFlags []dtos.SplitDTO) []strin for _, split := range featureFlags { for _, cond := range split.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { - if matcher.MatcherType == matcherTypeInLargeSegment && matcher.UserDefinedLargeSegment != nil { + if matcher.MatcherType == constants.MatcherTypeInLargeSegment && matcher.UserDefinedLargeSegment != nil { dst = append(dst, matcher.UserDefinedLargeSegment.LargeSegmentName) } } @@ -372,7 +369,7 @@ func appendRuleBasedSegmentNamesReferenced(dst []string, featureFlags []dtos.Spl for _, split := range featureFlags { for _, cond := range split.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { - if matcher.MatcherType == matcherTypeInRuleBasedSegment && matcher.UserDefinedSegment != nil { + if matcher.MatcherType == constants.MatcherTypeInRuleBasedSegment && matcher.UserDefinedSegment != nil { dst = addIfNotExists(dst, seen, matcher.UserDefinedSegment.SegmentName) } } @@ -452,14 +449,14 @@ func (s *UpdaterImpl) getSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) [] segments := make([]string, 0) for _, segment := range ruleBasedSegment.Excluded.Segments { - if segment.Type == TypeStandard { + if segment.Type == dtos.TypeStandard { segments = addIfNotExists(segments, seen, segment.Name) } } for _, cond := range ruleBasedSegment.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { - if matcher.MatcherType == matcherTypeInSegment && matcher.UserDefinedSegment != nil { + if matcher.MatcherType == constants.MatcherTypeInSegment && matcher.UserDefinedSegment != nil { segments = addIfNotExists(segments, seen, matcher.UserDefinedSegment.SegmentName) } } @@ -480,7 +477,7 @@ func (s *UpdaterImpl) getLargeSegments(ruleBasedSegment *dtos.RuleBasedSegmentDT for _, cond := range ruleBasedSegment.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { - if matcher.MatcherType == matcherTypeInLargeSegment { + if matcher.MatcherType == constants.MatcherTypeInLargeSegment { largeSegments = addIfNotExists(largeSegments, seen, matcher.UserDefinedSegment.SegmentName) } } diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index cb7ee442..0504736b 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -8,6 +8,7 @@ import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" "github.com/splitio/go-split-commons/v7/service" @@ -25,14 +26,14 @@ import ( "github.com/stretchr/testify/mock" ) -var syncProxyFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} -var syncProxyRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, - grammar.MatcherTypeInRuleBasedSegment} +var syncProxyFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} +var syncProxyRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, constants.MatcherTypeInLargeSegment, + constants.MatcherTypeInRuleBasedSegment} func TestSplitSynchronizerError(t *testing.T) { splitMockStorage := &mocks.SplitStorageMock{} @@ -903,7 +904,7 @@ func TestProcessMatchers(t *testing.T) { Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, MatcherGroup: dtos.MatcherGroupDTO{ Matchers: []dtos.MatcherDTO{ - {MatcherType: grammar.MatcherTypeAllKeys, KeySelector: nil}, + {MatcherType: constants.MatcherTypeAllKeys, KeySelector: nil}, }, }, }, @@ -911,9 +912,9 @@ func TestProcessMatchers(t *testing.T) { }} toAdd, _ := splitUpdater.processFeatureFlagChanges(featureFlags) assert.Equal(t, grammar.ConditionTypeWhitelist, toAdd[0].Conditions[0].ConditionType) - assert.Equal(t, grammar.MatcherTypeAllKeys, toAdd[0].Conditions[0].MatcherGroup.Matchers[0].MatcherType) + assert.Equal(t, constants.MatcherTypeAllKeys, toAdd[0].Conditions[0].MatcherGroup.Matchers[0].MatcherType) assert.Equal(t, grammar.ConditionTypeRollout, toAdd[1].Conditions[0].ConditionType) - assert.Equal(t, grammar.MatcherTypeAllKeys, toAdd[1].Conditions[0].MatcherGroup.Matchers[0].MatcherType) + assert.Equal(t, constants.MatcherTypeAllKeys, toAdd[1].Conditions[0].MatcherGroup.Matchers[0].MatcherType) } func TestSplitProxyDowngrade(t *testing.T) { diff --git a/tasks/splitsync_test.go b/tasks/splitsync_test.go index 8adc752c..cf78a597 100644 --- a/tasks/splitsync_test.go +++ b/tasks/splitsync_test.go @@ -6,6 +6,7 @@ import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/engine/grammar" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" "github.com/splitio/go-split-commons/v7/service/api/specs" @@ -19,14 +20,14 @@ import ( "github.com/stretchr/testify/mock" ) -var goClientFeatureFlagsRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, grammar.MatcherTypeInSplitTreatment, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} -var goClientRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar.MatcherTypeInSegment, grammar.MatcherTypeWhitelist, grammar.MatcherTypeEqualTo, grammar.MatcherTypeGreaterThanOrEqualTo, grammar.MatcherTypeLessThanOrEqualTo, grammar.MatcherTypeBetween, - grammar.MatcherTypeEqualToSet, grammar.MatcherTypePartOfSet, grammar.MatcherTypeContainsAllOfSet, grammar.MatcherTypeContainsAnyOfSet, grammar.MatcherTypeStartsWith, grammar.MatcherTypeEndsWith, grammar.MatcherTypeContainsString, - grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, - grammar.MatcherTypeInRuleBasedSegment} +var goClientFeatureFlagsRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, constants.MatcherTypeInSplitTreatment, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} +var goClientRuleBasedSegmentRules = []string{constants.MatcherTypeAllKeys, constants.MatcherTypeInSegment, constants.MatcherTypeWhitelist, constants.MatcherTypeEqualTo, constants.MatcherTypeGreaterThanOrEqualTo, constants.MatcherTypeLessThanOrEqualTo, constants.MatcherTypeBetween, + constants.MatcherTypeEqualToSet, constants.MatcherTypePartOfSet, constants.MatcherTypeContainsAllOfSet, constants.MatcherTypeContainsAnyOfSet, constants.MatcherTypeStartsWith, constants.MatcherTypeEndsWith, constants.MatcherTypeContainsString, + constants.MatcherTypeEqualToBoolean, constants.MatcherTypeMatchesString, constants.MatcherEqualToSemver, constants.MatcherTypeGreaterThanOrEqualToSemver, constants.MatcherTypeLessThanOrEqualToSemver, constants.MatcherTypeBetweenSemver, constants.MatcherTypeInListSemver, + constants.MatcherTypeInRuleBasedSegment} func TestSplitSyncTask(t *testing.T) { mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} From 2736845caaa257265a0390e8c5d312302907c68b Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 20 Oct 2025 15:52:16 -0300 Subject: [PATCH 3/8] Updated conditions --- storage/inmemory/mutexmap/rulebasedsegment.go | 4 ++-- storage/inmemory/mutexmap/splits.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/inmemory/mutexmap/rulebasedsegment.go b/storage/inmemory/mutexmap/rulebasedsegment.go index cee1d095..48e39f02 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment.go +++ b/storage/inmemory/mutexmap/rulebasedsegment.go @@ -96,7 +96,7 @@ func (r *RuleBasedSegmentsStorageImpl) Segments() *set.ThreadUnsafeSet { for _, ruleBased := range r.data { for _, condition := range ruleBased.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType != constants.MatcherTypeInRuleBasedSegment { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == constants.MatcherTypeInSegment { segments.Add(matcher.UserDefinedSegment.SegmentName) } } @@ -119,7 +119,7 @@ func (r *RuleBasedSegmentsStorageImpl) LargeSegments() *set.ThreadUnsafeSet { for _, condition := range ruleBased.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { if matcher.UserDefinedLargeSegment != nil { - largeSegments.Add(matcher.UserDefinedSegment.SegmentName) + largeSegments.Add(matcher.UserDefinedLargeSegment.LargeSegmentName) } } } diff --git a/storage/inmemory/mutexmap/splits.go b/storage/inmemory/mutexmap/splits.go index ac45852f..9905a0b4 100644 --- a/storage/inmemory/mutexmap/splits.go +++ b/storage/inmemory/mutexmap/splits.go @@ -198,7 +198,7 @@ func (m *MMSplitStorage) SegmentNames() *set.ThreadUnsafeSet { for _, split := range m.data { for _, condition := range split.Conditions { for _, matcher := range condition.MatcherGroup.Matchers { - if matcher.UserDefinedSegment != nil && matcher.MatcherType != constants.MatcherTypeInRuleBasedSegment { + if matcher.UserDefinedSegment != nil && matcher.MatcherType == constants.MatcherTypeInSegment { segments.Add(matcher.UserDefinedSegment.SegmentName) } From 3f87bfc878c09ba59ce078cbd2e020ab336f2b6d Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Mon, 20 Oct 2025 15:58:12 -0300 Subject: [PATCH 4/8] fixed test --- synchronizer/worker/split/split_test.go | 27 +++++++------------------ 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index 0504736b..8c4702df 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -811,7 +811,7 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdate(t *testing.T) { func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { - t.Run("Rule-based segment change number higher than current", func(t *testing.T) { + t.Run("Rule-based segment pcn matches", func(t *testing.T) { ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} splitMockStorage := &mocks.SplitStorageMock{} splitMockFetcher := &fetcherMock.MockSplitFetcher{} @@ -828,10 +828,9 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder, false, specs.FLAG_V1_3) - changeNumber := int64(300) ruleBasedSegment := &dtos.RuleBasedSegmentDTO{ Name: "test-segment", - ChangeNumber: changeNumber, + ChangeNumber: int64(300), Conditions: []dtos.RuleBasedConditionDTO{ { ConditionType: "WHITELIST", @@ -841,29 +840,17 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { }, }, } - response := &dtos.FFResponseV13{ - SplitChanges: dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{ - Since: 3, - Till: 3, - }, - }, - } - pvChangeNumber := int64(100) + pvChangeNumber := int64(200) baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") - baseUpdate := dtos.NewBaseUpdate(baseMessage, changeNumber) + baseUpdate := dtos.NewBaseUpdate(baseMessage, 300) ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, &pvChangeNumber, ruleBasedSegment) - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200)).Times(3) - splitMockStorage.On("ChangeNumber").Return(int64(200)).Twice() - ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{*ruleBasedSegment}, []dtos.RuleBasedSegmentDTO{}, changeNumber).Return().Once() - splitMockFetcher.On("Fetch", mock.Anything).Return(response, nil).Once() - splitMockStorage.On("Update", []dtos.SplitDTO{}, []dtos.SplitDTO{}, int64(3)).Once() - appMonitorMock.On("NotifyEvent", mock.Anything).Once() + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200), nil).Times(1) + ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{*ruleBasedSegment}, []dtos.RuleBasedSegmentDTO{}, int64(300)).Return().Once() result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) assert.Nil(t, err) assert.False(t, result.RequiresFetch) - assert.Equal(t, changeNumber, result.NewRBChangeNumber) + assert.Equal(t, int64(300), result.NewRBChangeNumber) splitMockFetcher.AssertExpectations(t) splitMockStorage.AssertExpectations(t) From 6dbf5625bf5ec82fc567889d71a1d61cf45b0854 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Mon, 20 Oct 2025 16:05:08 -0300 Subject: [PATCH 5/8] added pcn not cn --- synchronizer/worker/split/split_test.go | 68 +++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index 8c4702df..da28a086 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -816,11 +816,7 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { splitMockStorage := &mocks.SplitStorageMock{} splitMockFetcher := &fetcherMock.MockSplitFetcher{} - telemetryMockStorage := mocks.MockTelemetryStorage{ - RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, - RecordSuccessfulSyncCall: func(resource int, timestamp time.Time) {}, - RecordSyncErrorCall: func(resource, status int) {}, - } + telemetryMockStorage := mocks.MockTelemetryStorage{} appMonitorMock := &hcMock.ApplicationMonitorMock{} largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -857,6 +853,68 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { ruleBasedSegmentMockStorage.AssertExpectations(t) appMonitorMock.AssertExpectations(t) }) + + t.Run("Rule-based segment pcn does not match", func(t *testing.T) { + ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(200), nil).Times(2) + splitMockStorage.On("Update", []dtos.SplitDTO{}, []dtos.SplitDTO{}, int64(300)).Return().Once() + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.FFResponseV13{ + SplitChanges: dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Splits: []dtos.SplitDTO{}, + Since: 300, + Till: 300, + }, + RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ + RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, + Since: 300, + Till: 300, + }, + }, + }, nil).Once() + + telemetryMockStorage := mocks.MockTelemetryStorage{ + RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, + RecordSuccessfulSyncCall: func(resource int, timestamp time.Time) {}, + RecordSyncErrorCall: func(resource, status int) {}, + } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) + + splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder, false, specs.FLAG_V1_3) + + ruleBasedSegment := &dtos.RuleBasedSegmentDTO{ + Name: "test-segment", + ChangeNumber: int64(300), + Conditions: []dtos.RuleBasedConditionDTO{ + { + ConditionType: "WHITELIST", + MatcherGroup: dtos.MatcherGroupDTO{ + Matchers: []dtos.MatcherDTO{}, + }, + }, + }, + } + pvChangeNumber := int64(100) + baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") + baseUpdate := dtos.NewBaseUpdate(baseMessage, 300) + ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, &pvChangeNumber, ruleBasedSegment) + + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200), nil).Times(3) + ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{}, []dtos.RuleBasedSegmentDTO{}, int64(300)).Return().Once() + result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) + assert.Nil(t, err) + assert.True(t, result.RequiresFetch) + + splitMockFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + }) } func TestProcessMatchers(t *testing.T) { From 6c57997eaa8cd31278df92c3b080d058d80bc6c1 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Mon, 20 Oct 2025 16:24:24 -0300 Subject: [PATCH 6/8] fixed tests due lack o matcher type --- .../mutexmap/rulebasedsegment_test.go | 28 +++++++++++++++++-- synchronizer/worker/segment/segment_test.go | 4 +++ synchronizer/worker/split/split_test.go | 10 +++---- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/storage/inmemory/mutexmap/rulebasedsegment_test.go b/storage/inmemory/mutexmap/rulebasedsegment_test.go index cd5d0811..7d4cb6b9 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment_test.go +++ b/storage/inmemory/mutexmap/rulebasedsegment_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/stretchr/testify/assert" ) @@ -29,10 +30,16 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, }, + { + MatcherGroup: dtos.MatcherGroupDTO{ + Matchers: []dtos.MatcherDTO{ + {UserDefinedLargeSegment: &dtos.UserDefinedLargeSegmentMatcherDataDTO{LargeSegmentName: "ls1"}, MatcherType: constants.MatcherTypeInLargeSegment}}}, + }, }, Excluded: dtos.ExcludedDTO{ Segments: []dtos.ExcludedSegmentDTO{ @@ -40,6 +47,14 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { Name: "excluded1", Type: dtos.TypeStandard, }, + { + Name: "excluded2", + Type: dtos.TypeRuleBased, + }, + { + Name: "excluded3", + Type: dtos.TypeLarge, + }, }, }, } @@ -54,6 +69,7 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment2", }, + MatcherType: constants.MatcherTypeInRuleBasedSegment, }, }, }, @@ -74,12 +90,13 @@ func TestRuleBasedSegmentsStorage(t *testing.T) { // Test GetSegments segments := storage.Segments() - // Print segments for debugging - t.Logf("Segments in set: %v", segments.List()) assert.True(t, segments.Has("segment1"), "segment1 should be in segments") - assert.True(t, segments.Has("segment2"), "segment2 should be in segments") assert.True(t, segments.Has("excluded1"), "excluded1 should be in segments") + ls := storage.LargeSegments() + assert.True(t, ls.Has("excluded3"), "excluded3 should be in large segments") + assert.True(t, ls.Has("ls1"), "ls1 should be in large segments") + // Test Contains assert.True(t, storage.Contains([]string{"rule1", "rule2"}), "should contain rule1 and rule2") assert.True(t, storage.Contains([]string{"rule1"}), "should contain rule1") @@ -113,6 +130,7 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -134,6 +152,7 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment2", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -183,6 +202,7 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment3", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -200,6 +220,7 @@ func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment4", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -278,6 +299,7 @@ func TestRuleBasedSegmentsStorageConcurrent(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, diff --git a/synchronizer/worker/segment/segment_test.go b/synchronizer/worker/segment/segment_test.go index 83f75791..007f5f80 100644 --- a/synchronizer/worker/segment/segment_test.go +++ b/synchronizer/worker/segment/segment_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/engine/grammar/constants" "github.com/splitio/go-split-commons/v7/flagsets" "github.com/splitio/go-split-commons/v7/healthcheck/application" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" @@ -225,6 +226,7 @@ func TestSegmentSyncUpdate(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -322,6 +324,7 @@ func TestSegmentSyncProcess(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment1", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, @@ -341,6 +344,7 @@ func TestSegmentSyncProcess(t *testing.T) { UserDefinedSegment: &dtos.UserDefinedSegmentMatcherDataDTO{ SegmentName: "segment2", }, + MatcherType: constants.MatcherTypeInSegment, }, }, }, diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index da28a086..df28875c 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -908,7 +908,7 @@ func TestSynchronizeFeatureFlagsRuleBasedUpdateSecond(t *testing.T) { ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{}, []dtos.RuleBasedSegmentDTO{}, int64(300)).Return().Once() result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) assert.Nil(t, err) - assert.True(t, result.RequiresFetch) + assert.False(t, result.RequiresFetch) splitMockFetcher.AssertExpectations(t) splitMockStorage.AssertExpectations(t) @@ -1057,10 +1057,10 @@ func TestSplitProxyDowngrade(t *testing.T) { splitMockStorage := &mocks.SplitStorageMock{} splitMockStorage.On("ChangeNumber").Return(int64(3), nil).Times(2) - splitMockStorage.On("ReplaceAll", []dtos.SplitDTO{mockedSplit1}, int64(3)).Once() + splitMockStorage.On("ReplaceAll", []dtos.SplitDTO{mockedSplit1}, int64(3)).Once().Return(nil) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(3)).Times(2) - ruleBasedSegmentMockStorage.On("ReplaceAll", []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, int64(3)).Once() + ruleBasedSegmentMockStorage.On("ReplaceAll", []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, int64(3)).Once().Return(nil) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} splitMockFetcher := &fetcherMock.MockSplitFetcher{} splitMockFetcher.On("Fetch", service.MakeFlagRequestParams().WithSpecVersion(common.StringRef(specs.FLAG_V1_3)).WithChangeNumber(-1).WithChangeNumberRB(-1)).Return(response, nil).Once() @@ -1125,11 +1125,11 @@ func TestSplitProxyDowngrade(t *testing.T) { splitMockStorage := &mocks.SplitStorageMock{} splitMockStorage.On("ChangeNumber").Return(int64(3), nil).Times(2) - splitMockStorage.On("ReplaceAll", []dtos.SplitDTO{mockedSplit1}, int64(3)).Once() + splitMockStorage.On("ReplaceAll", []dtos.SplitDTO{mockedSplit1}, int64(3)).Once().Return(nil) splitMockStorage.On("Update", []dtos.SplitDTO{}, []dtos.SplitDTO{}, int64(3)).Once() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(3)).Times(2) - ruleBasedSegmentMockStorage.On("ReplaceAll", []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, int64(3)).Once() + ruleBasedSegmentMockStorage.On("ReplaceAll", []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, int64(3)).Once().Return(nil) ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{}, []dtos.RuleBasedSegmentDTO{}, int64(3)).Once().Return(3) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} splitMockFetcher := &fetcherMock.MockSplitFetcher{} From 1d8038d22be6f2bb3704ce3a138a8388491bf1cc Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Mon, 20 Oct 2025 16:34:47 -0300 Subject: [PATCH 7/8] small changes --- synchronizer/worker/split/split.go | 35 +++++++++--------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/synchronizer/worker/split/split.go b/synchronizer/worker/split/split.go index 765ec90c..0744db8e 100644 --- a/synchronizer/worker/split/split.go +++ b/synchronizer/worker/split/split.go @@ -352,25 +352,12 @@ func appendLargeSegmentNames(dst []string, featureFlags []dtos.SplitDTO) []strin return dst } -func addIfNotExists(dst []string, seen map[string]struct{}, name string) []string { - if _, exists := seen[name]; !exists { - seen[name] = struct{}{} - dst = append(dst, name) - } - return dst -} - func appendRuleBasedSegmentNamesReferenced(dst []string, featureFlags []dtos.SplitDTO) []string { - seen := make(map[string]struct{}) - for _, name := range dst { - seen[name] = struct{}{} - } - for _, split := range featureFlags { for _, cond := range split.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { if matcher.MatcherType == constants.MatcherTypeInRuleBasedSegment && matcher.UserDefinedSegment != nil { - dst = addIfNotExists(dst, seen, matcher.UserDefinedSegment.SegmentName) + dst = append(dst, matcher.UserDefinedSegment.SegmentName) } } } @@ -445,19 +432,18 @@ func (s *UpdaterImpl) processFFChange(ffChange dtos.SplitChangeUpdate) *UpdateRe } func (s *UpdaterImpl) getSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) []string { - seen := make(map[string]struct{}) segments := make([]string, 0) for _, segment := range ruleBasedSegment.Excluded.Segments { if segment.Type == dtos.TypeStandard { - segments = addIfNotExists(segments, seen, segment.Name) + segments = append(segments, segment.Name) } } for _, cond := range ruleBasedSegment.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { if matcher.MatcherType == constants.MatcherTypeInSegment && matcher.UserDefinedSegment != nil { - segments = addIfNotExists(segments, seen, matcher.UserDefinedSegment.SegmentName) + segments = append(segments, matcher.UserDefinedSegment.SegmentName) } } } @@ -466,19 +452,18 @@ func (s *UpdaterImpl) getSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) [] } func (s *UpdaterImpl) getLargeSegments(ruleBasedSegment *dtos.RuleBasedSegmentDTO) []string { - seen := make(map[string]struct{}) largeSegments := make([]string, 0) for _, segment := range ruleBasedSegment.Excluded.Segments { if segment.Type == TypeLargeSegment { - largeSegments = addIfNotExists(largeSegments, seen, segment.Name) + largeSegments = append(largeSegments, segment.Name) } } for _, cond := range ruleBasedSegment.Conditions { for _, matcher := range cond.MatcherGroup.Matchers { if matcher.MatcherType == constants.MatcherTypeInLargeSegment { - largeSegments = addIfNotExists(largeSegments, seen, matcher.UserDefinedSegment.SegmentName) + largeSegments = append(largeSegments, matcher.UserDefinedSegment.SegmentName) } } } @@ -531,15 +516,15 @@ func (s *UpdaterImpl) processRuleBasedChangeUpdate(ruleBasedChange dtos.SplitCha ruleBasedSegments = append(ruleBasedSegments, *ruleBasedChange.RuleBasedSegment()) toRemove, toAdd := s.processRuleBasedSegmentChanges(ruleBasedSegments) segments := s.getSegmentsFromRuleBasedSegments(ruleBasedSegments) - //large segments + largeSegments := s.getLargeSegmentsFromRuleBasedSegments(ruleBasedSegments) s.ruleBasedSegmentStorage.Update(toAdd, toRemove, ruleBasedChange.BaseUpdate.ChangeNumber()) return &UpdateResult{ UpdatedRuleBasedSegments: []string{ruleBasedChange.RuleBasedSegment().Name}, - ReferencedSegments: segments, - //large segment referenced - NewRBChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), - RequiresFetch: false, + ReferencedSegments: common.DedupeStringSlice(segments), + ReferencedLargeSegments: common.DedupeStringSlice(largeSegments), + NewRBChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), + RequiresFetch: false, } } s.logger.Debug("the rule-based segment was nil or the previous change number wasn't equal to the rule-based segment storage's change number") From 0c5ee482203be90395725b876fad28503204c592 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 20 Oct 2025 18:12:10 -0300 Subject: [PATCH 8/8] Updated ReplaceAll --- storage/inmemory/mutexmap/rulebasedsegment.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/inmemory/mutexmap/rulebasedsegment.go b/storage/inmemory/mutexmap/rulebasedsegment.go index 48e39f02..325cf75d 100644 --- a/storage/inmemory/mutexmap/rulebasedsegment.go +++ b/storage/inmemory/mutexmap/rulebasedsegment.go @@ -168,7 +168,7 @@ func (r *RuleBasedSegmentsStorageImpl) GetRuleBasedSegmentByName(name string) (* return nil, fmt.Errorf("rule-based segment %s not found in storage", name) } -func (r *RuleBasedSegmentsStorageImpl) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) { +func (r *RuleBasedSegmentsStorageImpl) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error { // Get all current splits under read lock r.mutex.RLock() toRemove := make([]dtos.RuleBasedSegmentDTO, 0) @@ -181,4 +181,5 @@ func (r *RuleBasedSegmentsStorageImpl) ReplaceAll(toAdd []dtos.RuleBasedSegmentD r.mutex.Lock() defer r.mutex.Unlock() r.update(toAdd, toRemove, changeNumber) + return nil }