Skip to content

Commit fedbf07

Browse files
authored
Merge pull request #43 from splitio/SDKS-9120-impressoion-per-toggle
[SDKS-9120] Toggle impressions
2 parents d46a130 + 6576ec4 commit fedbf07

File tree

12 files changed

+236
-116
lines changed

12 files changed

+236
-116
lines changed

CHANGES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
1.5.0 (Jan 17, 2025):
2+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs.
3+
14
1.4.0 (May 14, 2024):
25
- Updated go-split-commons to v6
36
- Added support for targeting rules based on semantic versions (https://semver.org/).

external/commons/mocks/impmanager.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@ func (m *ImpressionManagerMock) ProcessSingle(impression *dtos.Impression) bool
2222
return args.Bool(0)
2323
}
2424

25+
func (m *ImpressionManagerMock) Process(impressions []dtos.Impression, listenerEnabled bool) ([]dtos.Impression, []dtos.Impression) {
26+
args := m.Called(impressions)
27+
return args.Get(0).([]dtos.Impression), args.Get(1).([]dtos.Impression)
28+
}
29+
2530
var _ provisional.ImpressionManager = (*ImpressionManagerMock)(nil)

external/commons/mocks/splitstorage.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,8 @@ func (m *SplitStorageMock) GetAllFlagSetNames() []string {
6868
return args.Get(0).([]string)
6969
}
7070

71+
func (m *SplitStorageMock) LargeSegmentNames() *set.ThreadUnsafeSet {
72+
return m.Called().Get(0).(*set.ThreadUnsafeSet)
73+
}
74+
7175
var _ storage.SplitStorage = (*SplitStorageMock)(nil)

go.mod

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ module github.com/splitio/splitd
33
go 1.21
44

55
require (
6-
github.com/splitio/go-split-commons/v6 v6.0.0
6+
github.com/splitio/go-split-commons/v6 v6.1.0
77
github.com/splitio/go-toolkit/v5 v5.4.0
8-
github.com/stretchr/testify v1.8.1
8+
github.com/stretchr/testify v1.9.0
99
github.com/vmihailenco/msgpack/v5 v5.3.5
1010
golang.org/x/sync v0.3.0
1111
gopkg.in/yaml.v3 v3.0.1
1212
)
1313

1414
require (
15+
github.com/bits-and-blooms/bitset v1.3.1 // indirect
16+
github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect
1517
github.com/davecgh/go-spew v1.1.1 // indirect
1618
github.com/pmezard/go-difflib v1.0.0 // indirect
17-
github.com/stretchr/objx v0.5.0 // indirect
19+
github.com/stretchr/objx v0.5.2 // indirect
1820
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
1921
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
2022
)

go.sum

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
88
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
99
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
10-
github.com/splitio/go-split-commons/v6 v6.0.0 h1:qenr5qbXafjvM832C64CVpjtlShuQiWCwtR5I2h4ogM=
11-
github.com/splitio/go-split-commons/v6 v6.0.0/go.mod h1:TsvIh3XP7yjc7ly4vpj06AkoBND36SodPs5qfhb8rHc=
10+
github.com/splitio/go-split-commons/v6 v6.1.0 h1:k3mwr12DF6gbEaV8XXU/tSAQlPkIEuzIgTEneYhGg2I=
11+
github.com/splitio/go-split-commons/v6 v6.1.0/go.mod h1:D/XIY/9Hmfk9ivWsRsJVp439kEdmHbzUi3PKzQQDOXY=
1212
github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM=
1313
github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko=
1414
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15-
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
16-
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
17-
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
15+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
16+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
1817
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
19-
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
20-
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
21-
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
22-
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
18+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
19+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
20+
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
21+
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
2322
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
2423
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
2524
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=

splitio/link/protocol/v1/responses.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ type SplitNamesPayload struct {
3737
}
3838

3939
type SplitPayload struct {
40-
Name string `msgpack:"n"`
41-
TrafficType string `msgpack:"t"`
42-
Killed bool `msgpack:"k"`
43-
Treatments []string `msgpack:"s"`
44-
ChangeNumber int64 `msgpack:"c"`
45-
Configs map[string]string `msgpack:"f"`
46-
DefaultTreatment string `msgpack:"d"`
47-
Sets []string `msgpack:"e"`
40+
Name string `msgpack:"n"`
41+
TrafficType string `msgpack:"t"`
42+
Killed bool `msgpack:"k"`
43+
Treatments []string `msgpack:"s"`
44+
ChangeNumber int64 `msgpack:"c"`
45+
Configs map[string]string `msgpack:"f"`
46+
DefaultTreatment string `msgpack:"d"`
47+
Sets []string `msgpack:"e"`
48+
ImpressionsDisabled bool `msgpack:"i"`
4849
}
4950

5051
type SplitsPayload struct {

splitio/sdk/helpers.go

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,58 @@ import (
1515
"github.com/splitio/go-split-commons/v6/provisional/strategy"
1616
"github.com/splitio/go-split-commons/v6/service/api"
1717
"github.com/splitio/go-split-commons/v6/storage"
18+
"github.com/splitio/go-split-commons/v6/storage/filter"
1819
"github.com/splitio/go-split-commons/v6/storage/inmemory"
1920
"github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap"
2021
"github.com/splitio/go-split-commons/v6/synchronizer"
2122
"github.com/splitio/go-split-commons/v6/synchronizer/worker/impressionscount"
2223
"github.com/splitio/go-split-commons/v6/synchronizer/worker/segment"
2324
"github.com/splitio/go-split-commons/v6/synchronizer/worker/split"
2425
"github.com/splitio/go-split-commons/v6/tasks"
26+
"github.com/splitio/go-split-commons/v6/telemetry"
2527
"github.com/splitio/go-toolkit/v5/logging"
2628
)
2729

30+
const (
31+
bfExpectedElemenets = 10000000
32+
bfFalsePositiveProbability = 0.01
33+
bfCleaningPeriod = 86400 // 24 hours
34+
uniqueKeysPeriodTaskInMemory = 900 // 15 min
35+
uniqueKeysPeriodTaskRedis = 300 // 5 min
36+
impressionsCountPeriodTaskInMemory = 1800 // 30 min
37+
impressionsCountPeriodTaskRedis = 300 // 5 min
38+
impressionsBulkSizeRedis = 100
39+
)
40+
2841
func setupWorkers(
2942
logger logging.LoggerInterface,
3043
api *api.SplitAPI,
3144
str *storages,
3245
hc application.MonitorProducerInterface,
3346
cfg *sdkConf.Config,
3447
flagSetsFilter flagsets.FlagSetFilter,
48+
md dtos.Metadata,
49+
impComponents impComponents,
3550
) *synchronizer.Workers {
3651
return &synchronizer.Workers{
37-
SplitUpdater: split.NewSplitUpdater(str.splits, api.SplitFetcher, logger, str.telemetry, hc, flagSetsFilter),
38-
SegmentUpdater: segment.NewSegmentUpdater(str.splits, str.segments, api.SegmentFetcher, logger, str.telemetry, hc),
39-
ImpressionRecorder: workers.NewImpressionsWorker(logger, str.telemetry, api.ImpressionRecorder, str.impressions, &cfg.Impressions),
40-
EventRecorder: workers.NewEventsWorker(logger, str.telemetry, api.EventRecorder, str.events, &cfg.Events),
52+
SplitUpdater: split.NewSplitUpdater(str.splits, api.SplitFetcher, logger, str.telemetry, hc, flagSetsFilter),
53+
SegmentUpdater: segment.NewSegmentUpdater(str.splits, str.segments, api.SegmentFetcher, logger, str.telemetry, hc),
54+
ImpressionRecorder: workers.NewImpressionsWorker(logger, str.telemetry, api.ImpressionRecorder, str.impressions, &cfg.Impressions),
55+
EventRecorder: workers.NewEventsWorker(logger, str.telemetry, api.EventRecorder, str.events, &cfg.Events),
56+
ImpressionsCountRecorder: impressionscount.NewRecorderSingle(impComponents.counter, api.ImpressionRecorder, md, logger, str.telemetry),
57+
TelemetryRecorder: telemetry.NewTelemetrySynchronizer(str.telemetry, api.TelemetryRecorder, str.splits, str.segments, logger, md, str.telemetry),
4158
}
4259
}
4360

4461
func setupTasks(
4562
cfg *sdkConf.Config,
46-
str *storages,
4763
logger logging.LoggerInterface,
4864
workers *synchronizer.Workers,
4965
impComponents impComponents,
50-
md dtos.Metadata,
51-
api *api.SplitAPI,
5266
) *synchronizer.SplitTasks {
5367
impCfg := cfg.Impressions
5468
evCfg := cfg.Events
69+
dummyHC := &application.Dummy{}
5570
tg := &synchronizer.SplitTasks{
5671
SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, int(cfg.Splits.SyncPeriod.Seconds()), logger),
5772
SegmentSyncTask: tasks.NewFetchSegmentsTask(
@@ -60,21 +75,18 @@ func setupTasks(
6075
cfg.Segments.WorkerCount,
6176
cfg.Segments.QueueSize,
6277
logger,
78+
dummyHC,
6379
),
64-
ImpressionSyncTask: tasks.NewRecordImpressionsTask(workers.ImpressionRecorder, int(impCfg.SyncPeriod.Seconds()), logger, 5000),
65-
EventSyncTask: tasks.NewRecordEventsTask(workers.EventRecorder, 5000, int(evCfg.SyncPeriod.Seconds()), logger),
66-
TelemetrySyncTask: &NoOpTask{},
67-
UniqueKeysTask: &NoOpTask{},
68-
CleanFilterTask: &NoOpTask{},
69-
ImpsCountConsumerTask: &NoOpTask{},
70-
}
71-
72-
if impCfg.Mode == "optimized" {
73-
tg.ImpressionsCountSyncTask = tasks.NewRecordImpressionsCountTask(
74-
impressionscount.NewRecorderSingle(impComponents.counter, api.ImpressionRecorder, md, logger, str.telemetry),
80+
ImpressionSyncTask: tasks.NewRecordImpressionsTask(workers.ImpressionRecorder, int(impCfg.SyncPeriod.Seconds()), logger, 5000),
81+
EventSyncTask: tasks.NewRecordEventsTask(workers.EventRecorder, 5000, int(evCfg.SyncPeriod.Seconds()), logger),
82+
TelemetrySyncTask: &NoOpTask{},
83+
UniqueKeysTask: tasks.NewRecordUniqueKeysTask(workers.TelemetryRecorder, *impComponents.tracker, uniqueKeysPeriodTaskInMemory, logger),
84+
CleanFilterTask: tasks.NewCleanFilterTask(*impComponents.filter, logger, bfCleaningPeriod),
85+
ImpsCountConsumerTask: tasks.NewRecordImpressionsCountTask(
86+
workers.ImpressionsCountRecorder,
7587
logger,
7688
int(impCfg.CountSyncPeriod.Seconds()),
77-
)
89+
),
7890
}
7991

8092
return tg
@@ -83,6 +95,8 @@ func setupTasks(
8395
type impComponents struct {
8496
manager provisional.ImpressionManager
8597
counter *strategy.ImpressionsCounter
98+
tracker *strategy.UniqueKeysTracker
99+
filter *storage.Filter
86100
}
87101

88102
func setupImpressionsComponents(c *sdkConf.Impressions, telemetry storage.TelemetryRuntimeProducer) (impComponents, error) {
@@ -92,20 +106,28 @@ func setupImpressionsComponents(c *sdkConf.Impressions, telemetry storage.Teleme
92106
return impComponents{}, fmt.Errorf("error building impressions observer: %w", err)
93107
}
94108

109+
counter := strategy.NewImpressionsCounter()
110+
bf := filter.NewBloomFilter(bfExpectedElemenets, bfFalsePositiveProbability)
111+
tracker := strategy.NewUniqueKeysTracker(bf)
112+
none := strategy.NewNoneImpl(counter, tracker, false)
113+
95114
var s strategy.ProcessStrategyInterface
96-
var counter *strategy.ImpressionsCounter
97115
switch c.Mode {
98116
case conf.ImpressionsModeDebug:
99117
s = strategy.NewDebugImpl(observer, false)
100118
case conf.ImpressionsModeNone:
119+
s = none
101120
default: // optimized
102-
counter = strategy.NewImpressionsCounter()
103121
s = strategy.NewOptimizedImpl(observer, counter, telemetry, false)
104122
}
105123

124+
impManager := provisional.NewImpressionManagerImp(none, s)
125+
106126
return impComponents{
107-
manager: provisional.NewImpressionManager(s),
127+
manager: impManager,
108128
counter: counter,
129+
tracker: &tracker,
130+
filter: &bf,
109131
}, nil
110132
}
111133

splitio/sdk/helpers_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ func TestSetupImpressionsComponents(t *testing.T) {
1616
ic, err := setupImpressionsComponents(&sdkCfg.Impressions, storages.telemetry)
1717
assert.Nil(t, err)
1818
assert.NotNil(t, ic.counter)
19+
assert.NotNil(t, ic.tracker)
20+
assert.NotNil(t, ic.filter)
1921

2022
sdkCfg.Impressions.Mode = "debug"
2123
ic, err = setupImpressionsComponents(&sdkCfg.Impressions, storages.telemetry)
2224
assert.Nil(t, err)
23-
assert.Nil(t, ic.counter)
25+
assert.NotNil(t, ic.counter)
26+
assert.NotNil(t, ic.tracker)
27+
assert.NotNil(t, ic.filter)
2428
}
2529

2630
func TestNoOpTask(t *testing.T) {

splitio/sdk/results.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ type EvaluationResult struct {
99
}
1010

1111
type SplitView struct {
12-
Name string
13-
TrafficType string
14-
Killed bool
15-
Treatments []string
16-
ChangeNumber int64
17-
Configs map[string]string
18-
DefaultTreatment string
19-
Sets []string
12+
Name string
13+
TrafficType string
14+
Killed bool
15+
Treatments []string
16+
ChangeNumber int64
17+
Configs map[string]string
18+
DefaultTreatment string
19+
Sets []string
20+
ImpressionsDisabled bool
2021
}

splitio/sdk/sdk.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ func New(logger logging.LoggerInterface, apikey string, c *conf.Config) (*Impl,
8484

8585
queueFullChan := make(chan string, 2)
8686
splitApi := api.NewSplitAPI(apikey, *advCfg, logger, md)
87-
workers := setupWorkers(logger, splitApi, stores, hc, c, flagSetsFilter)
88-
tasks := setupTasks(c, stores, logger, workers, impc, md, splitApi)
87+
workers := setupWorkers(logger, splitApi, stores, hc, c, flagSetsFilter, md, impc)
88+
tasks := setupTasks(c, logger, workers, impc)
8989
sync := synchronizer.NewSynchronizer(*advCfg, *tasks, *workers, logger, queueFullChan)
9090

9191
status := make(chan int, 10)
@@ -257,19 +257,20 @@ func (i *Impl) handleImpression(key string, bk *string, f string, r *evaluator.R
257257
label = r.Label
258258
}
259259

260-
imp := &dtos.Impression{
260+
imp := dtos.Impression{
261261
FeatureName: f,
262262
BucketingKey: common.StringFromRef(bk),
263263
ChangeNumber: r.SplitChangeNumber,
264264
KeyName: key,
265265
Label: label,
266266
Treatment: r.Treatment,
267267
Time: timeMillis(),
268+
Disabled: r.ImpressionsDisabled,
268269
}
269270

270-
shouldStore := i.iq.ProcessSingle(imp)
271-
if shouldStore {
272-
_, err := i.is.Push(cm, *imp)
271+
forLog, _ := i.iq.Process([]dtos.Impression{imp}, false)
272+
if len(forLog) == 1 {
273+
_, err := i.is.Push(cm, forLog[0])
273274
if err != nil {
274275
if err == storage.ErrQueueFull {
275276
select {
@@ -283,7 +284,7 @@ func (i *Impl) handleImpression(key string, bk *string, f string, r *evaluator.R
283284
}
284285
}
285286

286-
return imp
287+
return &imp
287288
}
288289

289290
func splitToView(s *dtos.SplitDTO) *SplitView {
@@ -296,14 +297,15 @@ func splitToView(s *dtos.SplitDTO) *SplitView {
296297
}
297298

298299
return &SplitView{
299-
Name: s.Name,
300-
TrafficType: s.TrafficTypeName,
301-
Killed: s.Killed,
302-
ChangeNumber: s.ChangeNumber,
303-
Configs: s.Configurations,
304-
Treatments: treatments,
305-
DefaultTreatment: s.DefaultTreatment,
306-
Sets: s.Sets,
300+
Name: s.Name,
301+
TrafficType: s.TrafficTypeName,
302+
Killed: s.Killed,
303+
ChangeNumber: s.ChangeNumber,
304+
Configs: s.Configurations,
305+
Treatments: treatments,
306+
DefaultTreatment: s.DefaultTreatment,
307+
Sets: s.Sets,
308+
ImpressionsDisabled: s.ImpressionsDisabled,
307309
}
308310
}
309311

0 commit comments

Comments
 (0)