Skip to content

v7 #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open

v7 #208

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:

- name: SonarQube Scan (Push)
if: ${{ github.event_name == 'push' }}
uses: SonarSource/sonarcloud-github-action@v4.0.0
uses: SonarSource/sonarcloud-github-action@v5.0.0
env:
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -46,7 +46,7 @@ jobs:

- name: SonarQube Scan (Pull Request)
if: ${{ github.event_name == 'pull_request' }}
uses: SonarSource/sonarcloud-github-action@v4.0.0
uses: SonarSource/sonarcloud-github-action@v5.0.0
env:
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
7.0.0 (Apr 24, 2025)
- BREAKING CHANGE:
- UniqueKeysTracker interface changed:
- Added Unique keys storage implementation.
- Added support for batch posting.
- Bump dependencies for vulnerability fixes.
- Updated flag specs handler.

6.1.0 (Jan 14, 2025)
- Added support for Impressions toggle.
- Bump dependencies for vulnerability fixes.
Expand Down
2 changes: 2 additions & 0 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ type AdvancedConfig struct {
EventsQueueSize int
ImpressionsQueueSize int
ImpressionsBulkSize int64
UniqueKeysQueueSize int64
UniqueKeysBulkSize int64
StreamingEnabled bool
AuthServiceURL string
StreamingServiceURL string
Expand Down
4 changes: 2 additions & 2 deletions dtos/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ type Stats struct {

// Key struct
type Key struct {
Feature string `json:"f,omitempty"`
Keys []string `json:"ks,omitempty"`
Feature string `json:"f,omitempty"`
Keys []interface{} `json:"ks,omitempty"`
}

// Uniques struct
Expand Down
8 changes: 6 additions & 2 deletions provisional/impmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"github.com/splitio/go-split-commons/v6/provisional/strategy"
"github.com/splitio/go-split-commons/v6/storage/filter"
"github.com/splitio/go-split-commons/v6/storage/inmemory"
"github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue"
"github.com/splitio/go-split-commons/v6/telemetry"
"github.com/splitio/go-toolkit/v5/logging"
)

func TestImpManagerInMemoryDebugListenerDisabled(t *testing.T) {
Expand Down Expand Up @@ -122,7 +124,8 @@ func TestImpManagerInMemoryOptimized(t *testing.T) {
func TestImpManagerInMemoryNone(t *testing.T) {
counter := strategy.NewImpressionsCounter()
filter := filter.NewBloomFilter(3000, 0.01)
uniqueTracker := strategy.NewUniqueKeysTracker(filter)
uniqueKeysStorage := mutexqueue.NewMQUniqueKeysStorage(100, make(chan string), logging.NewLogger(nil))
uniqueTracker := strategy.NewUniqueKeysTracker(filter, uniqueKeysStorage)
none := strategy.NewNoneImpl(counter, uniqueTracker, true)
impManager := NewImpressionManager(none)

Expand Down Expand Up @@ -190,7 +193,8 @@ func TestProcess(t *testing.T) {
observer, _ := strategy.NewImpressionObserver(5000)
debug := strategy.NewDebugImpl(observer, true)
filter := filter.NewBloomFilter(3000, 0.01)
uniqueTracker := strategy.NewUniqueKeysTracker(filter)
uniqueKeysStorage := mutexqueue.NewMQUniqueKeysStorage(100, make(chan string), logging.NewLogger(nil))
uniqueTracker := strategy.NewUniqueKeysTracker(filter, uniqueKeysStorage)
counter := strategy.NewImpressionsCounter()
none := strategy.NewNoneImpl(counter, uniqueTracker, false)

Expand Down
8 changes: 6 additions & 2 deletions provisional/strategy/none_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import (

"github.com/splitio/go-split-commons/v6/dtos"
"github.com/splitio/go-split-commons/v6/storage/filter"
"github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue"
"github.com/splitio/go-split-commons/v6/util"
"github.com/splitio/go-toolkit/v5/logging"
)

func TestNoneMode(t *testing.T) {
now := time.Now().UTC().UnixNano()
filter := filter.NewBloomFilter(1000, 0.01)
tracker := NewUniqueKeysTracker(filter)
uniqueKeysStorage := mutexqueue.NewMQUniqueKeysStorage(100, make(chan string), logging.NewLogger(nil))
tracker := NewUniqueKeysTracker(filter, uniqueKeysStorage)
counter := NewImpressionsCounter()
none := NewNoneImpl(counter, tracker, true)

Expand Down Expand Up @@ -52,7 +55,8 @@ func TestNoneMode(t *testing.T) {
func TestApplySingleNone(t *testing.T) {
now := time.Now().UTC().UnixNano()
filter := filter.NewBloomFilter(1000, 0.01)
tracker := NewUniqueKeysTracker(filter)
uniqueKeysStorage := mutexqueue.NewMQUniqueKeysStorage(100, make(chan string), logging.NewLogger(nil))
tracker := NewUniqueKeysTracker(filter, uniqueKeysStorage)
counter := NewImpressionsCounter()
none := NewNoneImpl(counter, tracker, true)

Expand Down
60 changes: 6 additions & 54 deletions provisional/strategy/uniquekeystracker.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
package strategy

import (
"sync"

"github.com/splitio/go-split-commons/v6/dtos"
"github.com/splitio/go-split-commons/v6/storage"
"github.com/splitio/go-toolkit/v5/datastructures/set"
)

// UniqueKeysTracker interface
type UniqueKeysTracker interface {
Track(featureName string, key string) bool
PopAll() dtos.Uniques
}

// UniqueKeysTrackerImpl description
type UniqueKeysTrackerImpl struct {
filter storage.Filter
cache map[string]*set.ThreadUnsafeSet
mutex *sync.RWMutex
filter storage.Filter
storage storage.UniqueKeysStorageProducer
}

// NewUniqueKeysTracker create new implementation
func NewUniqueKeysTracker(f storage.Filter) UniqueKeysTracker {
func NewUniqueKeysTracker(f storage.Filter, storage storage.UniqueKeysStorageProducer) UniqueKeysTracker {
return &UniqueKeysTrackerImpl{
filter: f,
cache: make(map[string]*set.ThreadUnsafeSet),
mutex: &sync.RWMutex{},
filter: f,
storage: storage,
}
}

Expand All @@ -37,49 +30,8 @@ func (t *UniqueKeysTrackerImpl) Track(featureName string, key string) bool {
return false
}

t.mutex.Lock()
defer t.mutex.Unlock()

t.filter.Add(fKey)
_, ok := t.cache[featureName]
if !ok {
t.cache[featureName] = set.NewSet()
}

t.cache[featureName].Add(key)
t.storage.Push(featureName, key)

return true
}

// PopAll returns all the elements stored in the cache and resets the cache
func (t *UniqueKeysTrackerImpl) PopAll() dtos.Uniques {
t.mutex.Lock()
defer t.mutex.Unlock()
toReturn := t.cache
t.cache = make(map[string]*set.ThreadUnsafeSet)

return getUniqueKeysDto(toReturn)
}

func getUniqueKeysDto(uniques map[string]*set.ThreadUnsafeSet) dtos.Uniques {
uniqueKeys := dtos.Uniques{
Keys: make([]dtos.Key, 0, len(uniques)),
}

for name, keys := range uniques {
list := keys.List()
keysDto := make([]string, 0, len(list))

for _, value := range list {
keysDto = append(keysDto, value.(string))
}
keyDto := dtos.Key{
Feature: name,
Keys: keysDto,
}

uniqueKeys.Keys = append(uniqueKeys.Keys, keyDto)
}

return uniqueKeys
}
8 changes: 5 additions & 3 deletions provisional/strategy/uniquekeystracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"testing"

"github.com/splitio/go-split-commons/v6/storage/filter"
"github.com/splitio/go-split-commons/v6/storage/inmemory/mutexqueue"
"github.com/splitio/go-toolkit/v5/logging"
)

func Test(t *testing.T) {
func TestUniqueKeysTracker(t *testing.T) {
bf := filter.NewBloomFilter(10000, 0.01)

tracker := NewUniqueKeysTracker(bf)
uniqueKeysStorage := mutexqueue.NewMQUniqueKeysStorage(100, make(chan string), logging.NewLogger(nil))
tracker := NewUniqueKeysTracker(bf, uniqueKeysStorage)

for i := 0; i < 10; i++ {
if !tracker.Track("feature-1", "key-"+fmt.Sprint(i)) {
Expand Down
8 changes: 4 additions & 4 deletions service/api/http_recorders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,11 @@ func TestJsonUniqueKeys(t *testing.T) {
Keys: []dtos.Key{
{
Feature: "feature-1",
Keys: []string{"key-1", "key-2"},
Keys: []interface{}{"key-1", "key-2"},
},
{
Feature: "feature-2",
Keys: []string{"key-10", "key-20"},
Keys: []interface{}{"key-10", "key-20"},
},
},
}
Expand Down Expand Up @@ -317,11 +317,11 @@ func TestPostUniqueKeys(t *testing.T) {
keys := []dtos.Key{
{
Feature: "feature-1",
Keys: []string{"key-1", "key-2"},
Keys: []interface{}{"key-1", "key-2"},
},
{
Feature: "feature-2",
Keys: []string{"key-3", "key-4"},
Keys: []interface{}{"key-3", "key-4"},
},
}
err := telemetryRecorder.RecordUniqueKeys(dtos.Uniques{
Expand Down
29 changes: 17 additions & 12 deletions service/api/specs/specversion.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
package specs

import "fmt"
import (
"fmt"

"golang.org/x/exp/slices"
)

const (
FLAG_V1_0 = "1.0"
FLAG_V1_1 = "1.1"
FLAG_V1_2 = "1.2"
FLAG_V1_0 = "1.0" // default
FLAG_V1_1 = "1.1" // Semver
FLAG_V1_2 = "1.2" // Large Segment
FLAG_V1_3 = "1.3" // Rule-based Segment
)

var flagSpecs = []string{FLAG_V1_0, FLAG_V1_1, FLAG_V1_2}
var Latest = flagSpecs[len(flagSpecs)-1]

// Match returns the spec version if it is valid, otherwise it returns nil
func Match(version string) *string {
switch version {
case FLAG_V1_0:
return &version
case FLAG_V1_1:
return &version
case FLAG_V1_2:
return &version
ok := slices.Contains(flagSpecs, version)
if !ok {
return nil
}
return nil

return &version
}

func ParseAndValidate(spec string) (string, error) {
Expand Down
25 changes: 12 additions & 13 deletions service/api/specs/splitversionfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,34 @@ import (
)

type SplitVersionFilter struct {
v1_0 map[string]bool
v1_1 map[string]bool
data map[string]map[string]bool
}

func NewSplitVersionFilter() SplitVersionFilter {
v1_1 := map[string]bool{matchers.MatcherTypeInLargeSegment: true}
v1_0 := mergeMaps(map[string]bool{
data := map[string]map[string]bool{
FLAG_V1_1: {matchers.MatcherTypeInLargeSegment: true},
}

data[FLAG_V1_0] = mergeMaps(map[string]bool{
matchers.MatcherEqualToSemver: true,
matchers.MatcherTypeLessThanOrEqualToSemver: true,
matchers.MatcherTypeGreaterThanOrEqualToSemver: true,
matchers.MatcherTypeBetweenSemver: true,
matchers.MatcherTypeInListSemver: true,
}, v1_1)
}, data[FLAG_V1_1])

return SplitVersionFilter{
v1_0: v1_0,
v1_1: v1_1,
data: data,
}
}

func (f *SplitVersionFilter) ShouldFilter(matcher string, apiVersion string) bool {
switch apiVersion {
case FLAG_V1_1:
return f.v1_1[matcher]
case FLAG_V1_0:
return f.v1_0[matcher]
matchers, ok := f.data[apiVersion]
if !ok {
return false
}

return false
return matchers[matcher]
}

func mergeMaps(versionMap map[string]bool, toMergeMap map[string]bool) map[string]bool {
Expand Down
2 changes: 1 addition & 1 deletion service/api/specs/splitversionfilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestParseAndValidate(t *testing.T) {
}
}

func TestsplitVersionFilter(t *testing.T) {
func TestSplitVersionFilter(t *testing.T) {
filter := NewSplitVersionFilter()
shouldFilter := filter.ShouldFilter(matchers.MatcherTypeBetweenSemver, FLAG_V1_0)
if !shouldFilter {
Expand Down
Loading
Loading