Skip to content

Commit cb86779

Browse files
authored
Merge pull request #252 from splitio/FME-9987
[FME-9987] Implemented rule-based segment storage for redis
2 parents 5573c26 + 1295f9a commit cb86779

File tree

13 files changed

+977
-48
lines changed

13 files changed

+977
-48
lines changed

storage/inmemory/mutexmap/rulebasedsegment.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ func NewRuleBasedSegmentsStorage() *RuleBasedSegmentsStorageImpl {
2828
}
2929

3030
// Update atomically registers new rule-based segments, removes archived ones and updates the change number
31-
func (r *RuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) {
31+
func (r *RuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) error {
3232
r.mutex.Lock()
3333
defer r.mutex.Unlock()
3434
r.update(toAdd, toRemove, till)
35+
return nil
3536
}
3637

3738
// Update atomically registers new rule-based, removes archived ones and updates the change number
@@ -77,14 +78,14 @@ func (r *RuleBasedSegmentsStorageImpl) All() []dtos.RuleBasedSegmentDTO {
7778
}
7879

7980
// RuleBasedSegmentNames returns a slice with the names of all the current rule-baseds
80-
func (r *RuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() []string {
81+
func (r *RuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() ([]string, error) {
8182
r.mutex.RLock()
8283
defer r.mutex.RUnlock()
8384
ruleBasedNames := make([]string, 0)
8485
for key := range r.data {
8586
ruleBasedNames = append(ruleBasedNames, key)
8687
}
87-
return ruleBasedNames
88+
return ruleBasedNames, nil
8889
}
8990

9091
// SegmentNames returns a slice with the names of all segments referenced in rule-based
@@ -159,7 +160,6 @@ func (r *RuleBasedSegmentsStorageImpl) GetRuleBasedSegmentByName(name string) (*
159160
}
160161

161162
func (r *RuleBasedSegmentsStorageImpl) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error {
162-
// Get all current splits under read lock
163163
r.mutex.RLock()
164164
toRemove := make([]dtos.RuleBasedSegmentDTO, 0)
165165
for _, ruleBased := range r.data {

storage/inmemory/mutexmap/rulebasedsegment_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ func TestRuleBasedSegmentsStorage(t *testing.T) {
1717
changeNumber, _ := storage.ChangeNumber()
1818
assert.Equal(t, int64(-1), changeNumber)
1919
assert.Empty(t, storage.All())
20-
assert.Empty(t, storage.RuleBasedSegmentNames())
20+
names, _ := storage.RuleBasedSegmentNames()
21+
assert.Empty(t, names)
2122

2223
// Create test data
2324
ruleBased1 := dtos.RuleBasedSegmentDTO{
@@ -84,7 +85,7 @@ func TestRuleBasedSegmentsStorage(t *testing.T) {
8485
assert.Len(t, storage.All(), 2)
8586

8687
// Test RuleBasedSegmentNames
87-
names := storage.RuleBasedSegmentNames()
88+
names, _ = storage.RuleBasedSegmentNames()
8889
assert.Contains(t, names, "rule1")
8990
assert.Contains(t, names, "rule2")
9091

@@ -107,7 +108,8 @@ func TestRuleBasedSegmentsStorage(t *testing.T) {
107108
changeNumber, _ = storage.ChangeNumber()
108109
assert.Equal(t, int64(124), changeNumber)
109110
assert.Len(t, storage.All(), 1)
110-
assert.Contains(t, storage.RuleBasedSegmentNames(), "rule2")
111+
names, _ = storage.RuleBasedSegmentNames()
112+
assert.Contains(t, names, "rule2")
111113
}
112114

113115
func TestRuleBasedSegmentsStorageReplaceAll(t *testing.T) {
@@ -311,7 +313,7 @@ func TestRuleBasedSegmentsStorageConcurrent(t *testing.T) {
311313
go func() {
312314
defer wg.Done()
313315
_ = storage.All()
314-
_ = storage.RuleBasedSegmentNames()
316+
_, _ = storage.RuleBasedSegmentNames()
315317
_ = storage.Segments()
316318
_ = storage.Contains([]string{"segment1"})
317319
}()

storage/interfaces.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,15 +273,15 @@ type LargeSegmentsStorage interface {
273273
// RuleBasedSegmentStorageProducer interface should be implemented by all structs that offer writing rule-based segments
274274
type RuleBasedSegmentStorageProducer interface {
275275
SetChangeNumber(till int64) error
276-
Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64)
276+
Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) error
277277
ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error
278278
}
279279

280280
// RuleBasedStorageConsumer interface should be implemented by all structs that ofer reading rule-based segments
281281
type RuleBasedSegmentStorageConsumer interface {
282282
ChangeNumber() (int64, error)
283283
All() []dtos.RuleBasedSegmentDTO
284-
RuleBasedSegmentNames() []string
284+
RuleBasedSegmentNames() ([]string, error)
285285
Contains(ruleBasedSegmentNames []string) bool
286286
Segments() *set.ThreadUnsafeSet
287287
LargeSegments() *set.ThreadUnsafeSet

storage/mocks/rulebasedsegment.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func (m *MockRuleBasedSegmentStorage) All() []dtos.RuleBasedSegmentDTO {
2525
}
2626

2727
// RuleBasedSegmentNames mock
28-
func (m *MockRuleBasedSegmentStorage) RuleBasedSegmentNames() []string {
28+
func (m *MockRuleBasedSegmentStorage) RuleBasedSegmentNames() ([]string, error) {
2929
args := m.Called()
30-
return args.Get(0).([]string)
30+
return args.Get(0).([]string), args.Error(1)
3131
}
3232

3333
// Contains mock
@@ -61,8 +61,9 @@ func (m *MockRuleBasedSegmentStorage) SetChangeNumber(till int64) error {
6161
}
6262

6363
// Update mock
64-
func (m *MockRuleBasedSegmentStorage) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) {
65-
m.Called(toAdd, toRemove, till)
64+
func (m *MockRuleBasedSegmentStorage) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) error {
65+
args := m.Called(toAdd, toRemove, till)
66+
return args.Error(0)
6667
}
6768

6869
func (m *MockRuleBasedSegmentStorage) ReplaceAll(toAdd []dtos.RuleBasedSegmentDTO, changeNumber int64) error {

storage/redis/helpers.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,31 @@ func cleanPrefixedKeys(keys []string, toRemove string) []string {
8383

8484
return toReturn
8585
}
86+
87+
func updateRuleBasedSegments(pipeline redis.Pipeline, toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO) (map[string]error, []string) {
88+
failedToAdd := make(map[string]error)
89+
addedInPipe := make([]string, 0, len(toAdd))
90+
91+
for _, ruleBased := range toAdd {
92+
keyToStore := strings.Replace(KeyRuleBasedSegment, "{rbsegment}", ruleBased.Name, 1)
93+
raw, err := json.Marshal(ruleBased)
94+
if err != nil {
95+
failedToAdd[ruleBased.Name] = fmt.Errorf("failed to serialize rule-based segment: %w", err)
96+
continue
97+
}
98+
99+
// Attach each Feature Flag update into Redis Pipeline
100+
pipeline.Set(keyToStore, raw, 0)
101+
addedInPipe = append(addedInPipe, ruleBased.Name)
102+
}
103+
104+
if len(toRemove) > 0 {
105+
toRemoveKeys := make([]string, 0, len(toRemove))
106+
for idx := range toRemove {
107+
toRemoveKeys = append(toRemoveKeys, strings.Replace(KeyRuleBasedSegment, "{rbsegment}", toRemove[idx].Name, 1))
108+
}
109+
pipeline.Del(toRemoveKeys...)
110+
}
111+
112+
return failedToAdd, addedInPipe
113+
}

0 commit comments

Comments
 (0)