From 5e4d9e1efce5e1abd9cfcd7d3a88129dc4e89003 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 22 Oct 2025 14:10:20 -0300 Subject: [PATCH 01/36] updated deps with commons v8 --- go.mod | 6 +- go.sum | 12 +- splitio/admin/common/config.go | 2 +- splitio/admin/controllers/helpers.go | 4 +- .../admin/controllers/observability_test.go | 12 +- splitio/admin/controllers/snapshot_test.go | 6 +- splitio/commitversion.go | 2 +- splitio/common/conf/advanced.go | 2 +- splitio/common/conf/validators.go | 2 +- splitio/common/conf/validators_test.go | 3 +- splitio/common/impressionlistener/listener.go | 3 +- .../impressionlistener/listener_test.go | 2 +- .../impressionlistener/mocks/listener.go | 3 +- splitio/common/runtime.go | 8 +- splitio/common/sync/sync.go | 8 +- splitio/producer/conf/sections.go | 3 +- splitio/producer/initialization.go | 40 +++--- splitio/producer/initialization_test.go | 27 ++--- splitio/producer/storage/mocks/telemetry.go | 2 +- splitio/producer/storage/telemetry.go | 7 +- splitio/producer/storage/telemetry_test.go | 6 +- splitio/producer/task/events.go | 7 +- splitio/producer/task/events_test.go | 7 +- splitio/producer/task/impressions.go | 9 +- splitio/producer/task/impressions_test.go | 13 +- splitio/producer/task/pipelined.go | 3 +- splitio/producer/task/uniquekeys.go | 6 +- splitio/producer/task/uniquekeys_test.go | 6 +- splitio/producer/util.go | 19 +-- splitio/producer/worker/impcounts.go | 4 +- splitio/producer/worker/telemetry.go | 7 +- splitio/producer/worker/telemetry_test.go | 9 +- .../healthcheck/application/monitor.go | 5 +- .../healthcheck/application/monitor_test.go | 5 +- .../services/counter/bypercentage.go | 7 +- .../observability/segment_wrapper.go | 3 +- .../observability/segment_wrapper_test.go | 2 +- .../observability/split_wrapper.go | 6 +- .../observability/split_wrapper_test.go | 6 +- splitio/proxy/caching/caching.go | 5 +- splitio/proxy/caching/caching_test.go | 4 +- splitio/proxy/caching/mocks/mock.go | 41 ++++++- splitio/proxy/caching/workers.go | 27 +++-- splitio/proxy/caching/workers_test.go | 10 +- splitio/proxy/conf/sections.go | 5 +- splitio/proxy/controllers/events.go | 23 ++-- splitio/proxy/controllers/events_test.go | 8 +- splitio/proxy/controllers/sdk.go | 29 +++-- splitio/proxy/controllers/sdk_test.go | 114 +++++++++--------- splitio/proxy/controllers/telemetry.go | 23 ++-- splitio/proxy/controllers/telemetry_test.go | 8 +- splitio/proxy/controllers/util.go | 5 +- splitio/proxy/initialization.go | 23 ++-- splitio/proxy/initialization_test.go | 6 +- splitio/proxy/internal/dtos.go | 2 +- splitio/proxy/proxy.go | 10 +- splitio/proxy/proxy_test.go | 8 +- splitio/proxy/storage/mocks/mocks.go | 7 +- splitio/proxy/storage/optimized/historic.go | 2 +- .../proxy/storage/optimized/historic_test.go | 4 +- .../proxy/storage/optimized/mocks/mocks.go | 2 +- splitio/proxy/storage/persistent/splits.go | 2 +- .../proxy/storage/persistent/splits_test.go | 2 +- splitio/proxy/storage/segments.go | 10 +- splitio/proxy/storage/splits.go | 22 ++-- splitio/proxy/storage/splits_test.go | 4 +- splitio/proxy/storage/telemetry.go | 6 +- splitio/proxy/storage/telemetryts.go | 2 +- splitio/proxy/tasks/deferred.go | 2 +- splitio/proxy/tasks/events.go | 6 +- splitio/proxy/tasks/impcount.go | 6 +- splitio/proxy/tasks/impressions.go | 6 +- splitio/proxy/tasks/telemetry.go | 6 +- splitio/util/tls.go | 4 +- splitio/util/utils.go | 5 +- 75 files changed, 405 insertions(+), 328 deletions(-) diff --git a/go.mod b/go.mod index 05b0939b..7b6e93da 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v6 v6.1.0 - github.com/splitio/go-toolkit/v5 v5.4.0 - github.com/stretchr/testify v1.10.0 + github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f + github.com/splitio/go-toolkit/v5 v5.4.1 + github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 golang.org/x/exp v0.0.0-20231006140011-7918f672742d ) diff --git a/go.sum b/go.sum index 1b002341..0bc211a1 100644 --- a/go.sum +++ b/go.sum @@ -74,10 +74,10 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v6 v6.1.0 h1:k3mwr12DF6gbEaV8XXU/tSAQlPkIEuzIgTEneYhGg2I= -github.com/splitio/go-split-commons/v6 v6.1.0/go.mod h1:D/XIY/9Hmfk9ivWsRsJVp439kEdmHbzUi3PKzQQDOXY= -github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM= -github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko= +github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f h1:2o8Hu3G4jAoF6Y0Ceptr4Bwp3x9wFDenp494Cu/V5nU= +github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= +github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -87,8 +87,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= diff --git a/splitio/admin/common/config.go b/splitio/admin/common/config.go index 275f8ed3..b576cc96 100644 --- a/splitio/admin/common/config.go +++ b/splitio/admin/common/config.go @@ -1,6 +1,6 @@ package common -import "github.com/splitio/go-split-commons/v6/storage" +import "github.com/splitio/go-split-commons/v8/storage" // Storages wraps storages in one struct type Storages struct { diff --git a/splitio/admin/controllers/helpers.go b/splitio/admin/controllers/helpers.go index 6454ab9f..4aa784f3 100644 --- a/splitio/admin/controllers/helpers.go +++ b/splitio/admin/controllers/helpers.go @@ -5,8 +5,8 @@ import ( "strings" "time" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/telemetry" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/telemetry" "github.com/splitio/split-synchronizer/v5/splitio/admin/views/dashboard" "github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc" diff --git a/splitio/admin/controllers/observability_test.go b/splitio/admin/controllers/observability_test.go index e5f3e305..c92d57c5 100644 --- a/splitio/admin/controllers/observability_test.go +++ b/splitio/admin/controllers/observability_test.go @@ -7,14 +7,16 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage/mocks" - "github.com/splitio/go-toolkit/v5/datastructures/set" - "github.com/splitio/go-toolkit/v5/logging" adminCommon "github.com/splitio/split-synchronizer/v5/splitio/admin/common" "github.com/splitio/split-synchronizer/v5/splitio/provisional/observability" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage/mocks" + "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) func TestSyncObservabilityEndpoint(t *testing.T) { diff --git a/splitio/admin/controllers/snapshot_test.go b/splitio/admin/controllers/snapshot_test.go index c966d9d4..28d0e69c 100644 --- a/splitio/admin/controllers/snapshot_test.go +++ b/splitio/admin/controllers/snapshot_test.go @@ -7,10 +7,12 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/common/snapshot" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" + + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) func TestDownloadProxySnapshot(t *testing.T) { diff --git a/splitio/commitversion.go b/splitio/commitversion.go index d8b6bd07..019c8f13 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "567e14c" +const CommitVersion = "28e4d5c" diff --git a/splitio/common/conf/advanced.go b/splitio/common/conf/advanced.go index 36063ad7..8c786f12 100644 --- a/splitio/common/conf/advanced.go +++ b/splitio/common/conf/advanced.go @@ -3,7 +3,7 @@ package conf import ( "os" - "github.com/splitio/go-split-commons/v6/conf" + "github.com/splitio/go-split-commons/v8/conf" ) // InitAdvancedOptions initializes an advanced config with default values + overriden urls. diff --git a/splitio/common/conf/validators.go b/splitio/common/conf/validators.go index 8a69a1ea..fbafb216 100644 --- a/splitio/common/conf/validators.go +++ b/splitio/common/conf/validators.go @@ -3,7 +3,7 @@ package conf import ( "strings" - "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v8/flagsets" ) type FlagSetValidationError struct { diff --git a/splitio/common/conf/validators_test.go b/splitio/common/conf/validators_test.go index 456dbe36..ab46165a 100644 --- a/splitio/common/conf/validators_test.go +++ b/splitio/common/conf/validators_test.go @@ -3,7 +3,8 @@ package conf import ( "testing" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/stretchr/testify/assert" ) diff --git a/splitio/common/impressionlistener/listener.go b/splitio/common/impressionlistener/listener.go index e45ccd4a..e0e43e7f 100644 --- a/splitio/common/impressionlistener/listener.go +++ b/splitio/common/impressionlistener/listener.go @@ -7,8 +7,7 @@ import ( "fmt" "net/http" - "github.com/splitio/go-split-commons/v6/dtos" - + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-toolkit/v5/struct/traits/lifecycle" ) diff --git a/splitio/common/impressionlistener/listener_test.go b/splitio/common/impressionlistener/listener_test.go index 0a178ed0..f190b839 100644 --- a/splitio/common/impressionlistener/listener_test.go +++ b/splitio/common/impressionlistener/listener_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "testing" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" ) func TestImpressionListener(t *testing.T) { diff --git a/splitio/common/impressionlistener/mocks/listener.go b/splitio/common/impressionlistener/mocks/listener.go index 561d39bd..7a478a03 100644 --- a/splitio/common/impressionlistener/mocks/listener.go +++ b/splitio/common/impressionlistener/mocks/listener.go @@ -1,8 +1,9 @@ package mocks import ( - "github.com/splitio/go-split-commons/v6/dtos" "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" + + "github.com/splitio/go-split-commons/v8/dtos" ) type ImpressionBulkListenerMock struct { diff --git a/splitio/common/runtime.go b/splitio/common/runtime.go index 5bba5677..b6dd09b5 100644 --- a/splitio/common/runtime.go +++ b/splitio/common/runtime.go @@ -7,14 +7,14 @@ import ( "syscall" "time" - "github.com/splitio/go-split-commons/v6/synchronizer" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/go-toolkit/v5/sync" - "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/log" "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/application" "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/services" + + "github.com/splitio/go-split-commons/v8/synchronizer" + "github.com/splitio/go-toolkit/v5/logging" + "github.com/splitio/go-toolkit/v5/sync" ) // ErrShutdownAlreadyRegistered is returned when trying to register the shutdown handler more than once diff --git a/splitio/common/sync/sync.go b/splitio/common/sync/sync.go index 66effc97..051096c3 100644 --- a/splitio/common/sync/sync.go +++ b/splitio/common/sync/sync.go @@ -1,12 +1,10 @@ package sync import ( + "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/synchronizer" + "github.com/splitio/go-split-commons/v8/tasks" "github.com/splitio/go-toolkit/v5/logging" - - "github.com/splitio/go-split-commons/v6/conf" - - "github.com/splitio/go-split-commons/v6/synchronizer" - "github.com/splitio/go-split-commons/v6/tasks" ) // WSync is a wrapper for the Regular synchronizer that handles both local telemetry diff --git a/splitio/producer/conf/sections.go b/splitio/producer/conf/sections.go index 9c98850c..b306e71f 100644 --- a/splitio/producer/conf/sections.go +++ b/splitio/producer/conf/sections.go @@ -1,8 +1,9 @@ package conf import ( - cconf "github.com/splitio/go-split-commons/v6/conf" "github.com/splitio/split-synchronizer/v5/splitio/common/conf" + + cconf "github.com/splitio/go-split-commons/v8/conf" ) // Main configuration options diff --git a/splitio/producer/initialization.go b/splitio/producer/initialization.go index 77cb05f0..22fba294 100644 --- a/splitio/producer/initialization.go +++ b/splitio/producer/initialization.go @@ -5,22 +5,6 @@ import ( "fmt" "time" - cconf "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/flagsets" - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/service/api" - "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/redis" - "github.com/splitio/go-split-commons/v6/synchronizer" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/impressionscount" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/segment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/split" - "github.com/splitio/go-split-commons/v6/tasks" - "github.com/splitio/go-split-commons/v6/telemetry" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/admin" adminCommon "github.com/splitio/split-synchronizer/v5/splitio/admin/common" "github.com/splitio/split-synchronizer/v5/splitio/common" @@ -35,6 +19,24 @@ import ( hcServices "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/services" "github.com/splitio/split-synchronizer/v5/splitio/provisional/observability" "github.com/splitio/split-synchronizer/v5/splitio/util" + + cconf "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/grammar" + "github.com/splitio/go-split-commons/v8/flagsets" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/service/api" + "github.com/splitio/go-split-commons/v8/service/api/specs" + "github.com/splitio/go-split-commons/v8/storage/filter" + "github.com/splitio/go-split-commons/v8/storage/inmemory" + "github.com/splitio/go-split-commons/v8/storage/redis" + "github.com/splitio/go-split-commons/v8/synchronizer" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/impressionscount" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/split" + "github.com/splitio/go-split-commons/v8/tasks" + "github.com/splitio/go-split-commons/v8/telemetry" + "github.com/splitio/go-toolkit/v5/logging" ) const ( @@ -125,8 +127,10 @@ func Start(logger logging.LoggerInterface, cfg *conf.Main) error { eventEvictionMonitor := evcalc.New(1) workers := synchronizer.Workers{ - SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter), - SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, splitAPI.SegmentFetcher, + // TODO add ruleBasedSegmentStorage, ruleBuilder, sdkOverrides + SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, nil, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specs.FLAG_V1_3), + // TODO add ruleBasedSegmentStorage + SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, nil, splitAPI.SegmentFetcher, logger, syncTelemetryStorage, appMonitor), ImpressionsCountRecorder: impressionscount.NewRecorderSingle(impressionsCounter, splitAPI.ImpressionRecorder, metadata, logger, syncTelemetryStorage), diff --git a/splitio/producer/initialization_test.go b/splitio/producer/initialization_test.go index fb1d71ba..e696fd17 100644 --- a/splitio/producer/initialization_test.go +++ b/splitio/producer/initialization_test.go @@ -10,15 +10,16 @@ import ( "strings" "testing" - config "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/service" - "github.com/splitio/go-split-commons/v6/service/mocks" - predis "github.com/splitio/go-split-commons/v6/storage/redis" - "github.com/splitio/go-toolkit/v5/logging" cconf "github.com/splitio/split-synchronizer/v5/splitio/common/conf" "github.com/splitio/split-synchronizer/v5/splitio/producer/conf" "github.com/splitio/split-synchronizer/v5/splitio/util" + + config "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/service/mocks" + predis "github.com/splitio/go-split-commons/v8/storage/redis" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/stretchr/testify/mock" ) func TestHashApiKey(t *testing.T) { @@ -47,11 +48,8 @@ func TestIsApikeyValidOk(t *testing.T) { os.Setenv("SPLITIO_SDK_URL", ts.URL) os.Setenv("SPLITIO_EVENTS_URL", ts.URL) - httpSplitFetcher := mocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - return nil, nil - }, - } + httpSplitFetcher := &mocks.MockSplitFetcher{} + httpSplitFetcher.On("Fetch", mock.Anything).Return(nil, nil) if !isValidApikey(httpSplitFetcher) { t.Error("APIKEY should be valid.") @@ -67,11 +65,8 @@ func TestIsApikeyValidNotOk(t *testing.T) { os.Setenv("SPLITIO_SDK_URL", ts.URL) os.Setenv("SPLITIO_EVENTS_URL", ts.URL) - httpSplitFetcher := mocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - return nil, errors.New("Some") - }, - } + httpSplitFetcher := &mocks.MockSplitFetcher{} + httpSplitFetcher.On("Fetch", mock.Anything).Return(nil, errors.New("Some")) if isValidApikey(httpSplitFetcher) { t.Error("APIKEY should be invalid.") diff --git a/splitio/producer/storage/mocks/telemetry.go b/splitio/producer/storage/mocks/telemetry.go index beb484aa..8466f75c 100644 --- a/splitio/producer/storage/mocks/telemetry.go +++ b/splitio/producer/storage/mocks/telemetry.go @@ -1,7 +1,7 @@ package mocks import ( - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" ) // RedisTelemetryConsumerMultiMock is a mock diff --git a/splitio/producer/storage/telemetry.go b/splitio/producer/storage/telemetry.go index 66742583..13244f05 100644 --- a/splitio/producer/storage/telemetry.go +++ b/splitio/producer/storage/telemetry.go @@ -7,12 +7,11 @@ import ( "strconv" "strings" + "github.com/splitio/go-split-commons/v8/dtos" + redisSt "github.com/splitio/go-split-commons/v8/storage/redis" + "github.com/splitio/go-split-commons/v8/telemetry" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/redis" - - "github.com/splitio/go-split-commons/v6/dtos" - redisSt "github.com/splitio/go-split-commons/v6/storage/redis" - "github.com/splitio/go-split-commons/v6/telemetry" ) const ( diff --git a/splitio/producer/storage/telemetry_test.go b/splitio/producer/storage/telemetry_test.go index 69943a89..d88eca61 100644 --- a/splitio/producer/storage/telemetry_test.go +++ b/splitio/producer/storage/telemetry_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/dtos" - redisSt "github.com/splitio/go-split-commons/v6/storage/redis" - "github.com/splitio/go-split-commons/v6/telemetry" + "github.com/splitio/go-split-commons/v8/dtos" + redisSt "github.com/splitio/go-split-commons/v8/storage/redis" + "github.com/splitio/go-split-commons/v8/telemetry" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/redis" ) diff --git a/splitio/producer/task/events.go b/splitio/producer/task/events.go index 709200fa..7c578cfe 100644 --- a/splitio/producer/task/events.go +++ b/splitio/producer/task/events.go @@ -8,10 +8,11 @@ import ( "sync" "time" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/logging" ) const ( diff --git a/splitio/producer/task/events_test.go b/splitio/producer/task/events_test.go index 407067d1..e09ac79d 100644 --- a/splitio/producer/task/events_test.go +++ b/splitio/producer/task/events_test.go @@ -11,10 +11,11 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage/mocks" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage/mocks" + "github.com/splitio/go-toolkit/v5/logging" ) type eventTrackingAllocator struct { diff --git a/splitio/producer/task/impressions.go b/splitio/producer/task/impressions.go index d8fc2298..f402d9aa 100644 --- a/splitio/producer/task/impressions.go +++ b/splitio/producer/task/impressions.go @@ -8,12 +8,13 @@ import ( "sync" "time" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/provisional" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/provisional" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/logging" ) const ( diff --git a/splitio/producer/task/impressions_test.go b/splitio/producer/task/impressions_test.go index e25b78a3..3fe8832a 100644 --- a/splitio/producer/task/impressions_test.go +++ b/splitio/producer/task/impressions_test.go @@ -11,13 +11,14 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/provisional" - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/storage/inmemory" - "github.com/splitio/go-split-commons/v6/storage/mocks" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/producer/evcalc" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/provisional" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/storage/inmemory" + "github.com/splitio/go-split-commons/v8/storage/mocks" + "github.com/splitio/go-toolkit/v5/logging" ) type trackingAllocator struct { diff --git a/splitio/producer/task/pipelined.go b/splitio/producer/task/pipelined.go index 79b1f910..5ed0b5a0 100644 --- a/splitio/producer/task/pipelined.go +++ b/splitio/producer/task/pipelined.go @@ -8,10 +8,9 @@ import ( "sync" "time" - tsync "github.com/splitio/go-toolkit/v5/sync" - "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" + tsync "github.com/splitio/go-toolkit/v5/sync" ) const ( diff --git a/splitio/producer/task/uniquekeys.go b/splitio/producer/task/uniquekeys.go index f28b6fb1..0b26d61d 100644 --- a/splitio/producer/task/uniquekeys.go +++ b/splitio/producer/task/uniquekeys.go @@ -6,9 +6,9 @@ import ( "fmt" "net/http" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/producer/task/uniquekeys_test.go b/splitio/producer/task/uniquekeys_test.go index f47e2507..3d01effc 100644 --- a/splitio/producer/task/uniquekeys_test.go +++ b/splitio/producer/task/uniquekeys_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/storage/mocks" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/storage/mocks" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/producer/util.go b/splitio/producer/util.go index ee1931b4..f4781d59 100644 --- a/splitio/producer/util.go +++ b/splitio/producer/util.go @@ -5,25 +5,26 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "log" "net/url" + "os" "strconv" "strings" "time" - config "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/provisional" - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/service" - storageCommon "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/storage/redis" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/producer/conf" hcAppCounter "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/application/counter" hcServicesCounter "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/services/counter" "github.com/splitio/split-synchronizer/v5/splitio/util" + + config "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/provisional" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/service" + storageCommon "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/storage/redis" + "github.com/splitio/go-toolkit/v5/logging" ) const ( @@ -44,7 +45,7 @@ func parseTLSConfig(opt *conf.Redis) (*tls.Config, error) { if len(opt.TLSCACertificates) > 0 { certPool := x509.NewCertPool() for _, cacert := range opt.TLSCACertificates { - pemData, err := ioutil.ReadFile(cacert) + pemData, err := os.ReadFile(cacert) if err != nil { return nil, fmt.Errorf("failed to load root certificate: %w", err) } diff --git a/splitio/producer/worker/impcounts.go b/splitio/producer/worker/impcounts.go index e4121e4c..99e7eaf8 100644 --- a/splitio/producer/worker/impcounts.go +++ b/splitio/producer/worker/impcounts.go @@ -1,8 +1,8 @@ package worker import ( - "github.com/splitio/go-split-commons/v6/provisional/strategy" - "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v8/provisional/strategy" + "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/producer/worker/telemetry.go b/splitio/producer/worker/telemetry.go index 8476a33e..10eebc1b 100644 --- a/splitio/producer/worker/telemetry.go +++ b/splitio/producer/worker/telemetry.go @@ -3,10 +3,11 @@ package worker import ( "fmt" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/service" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/producer/storage" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/service" + "github.com/splitio/go-toolkit/v5/logging" ) const ( diff --git a/splitio/producer/worker/telemetry_test.go b/splitio/producer/worker/telemetry_test.go index 3644b5d5..16ba9fcc 100644 --- a/splitio/producer/worker/telemetry_test.go +++ b/splitio/producer/worker/telemetry_test.go @@ -3,12 +3,13 @@ package worker import ( "testing" - "github.com/splitio/go-split-commons/v6/dtos" - serviceMocks "github.com/splitio/go-split-commons/v6/service/mocks" - "github.com/splitio/go-split-commons/v6/telemetry" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/producer/storage" storageMocks "github.com/splitio/split-synchronizer/v5/splitio/producer/storage/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + serviceMocks "github.com/splitio/go-split-commons/v8/service/mocks" + "github.com/splitio/go-split-commons/v8/telemetry" + "github.com/splitio/go-toolkit/v5/logging" ) func makeBucket(index int, count int64) []int64 { diff --git a/splitio/provisional/healthcheck/application/monitor.go b/splitio/provisional/healthcheck/application/monitor.go index f3de9a5d..913bd5dd 100644 --- a/splitio/provisional/healthcheck/application/monitor.go +++ b/splitio/provisional/healthcheck/application/monitor.go @@ -5,10 +5,11 @@ import ( "sync" "time" - hc "github.com/splitio/go-split-commons/v6/healthcheck/application" + "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/application/counter" + + hc "github.com/splitio/go-split-commons/v8/healthcheck/application" "github.com/splitio/go-toolkit/v5/logging" toolkitsync "github.com/splitio/go-toolkit/v5/sync" - "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/application/counter" ) // MonitorIterface monitor interface diff --git a/splitio/provisional/healthcheck/application/monitor_test.go b/splitio/provisional/healthcheck/application/monitor_test.go index ff3ad16f..085c3237 100644 --- a/splitio/provisional/healthcheck/application/monitor_test.go +++ b/splitio/provisional/healthcheck/application/monitor_test.go @@ -4,9 +4,10 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/healthcheck/application" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/provisional/healthcheck/application/counter" + + "github.com/splitio/go-split-commons/v8/healthcheck/application" + "github.com/splitio/go-toolkit/v5/logging" ) func assertItemsHealthy(t *testing.T, items []ItemDto, splitsExpected bool, segmentsExpected bool, errorsExpected bool) { diff --git a/splitio/provisional/healthcheck/services/counter/bypercentage.go b/splitio/provisional/healthcheck/services/counter/bypercentage.go index d29977e2..df15c9fc 100644 --- a/splitio/provisional/healthcheck/services/counter/bypercentage.go +++ b/splitio/provisional/healthcheck/services/counter/bypercentage.go @@ -6,10 +6,9 @@ import ( "sync" "time" - "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/service/api" - + "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/service/api" "github.com/splitio/go-toolkit/v5/asynctask" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/provisional/observability/segment_wrapper.go b/splitio/provisional/observability/segment_wrapper.go index aae49fa3..17c1fb8a 100644 --- a/splitio/provisional/observability/segment_wrapper.go +++ b/splitio/provisional/observability/segment_wrapper.go @@ -5,8 +5,7 @@ import ( "fmt" "sync" - "github.com/splitio/go-split-commons/v6/storage" - + "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/provisional/observability/segment_wrapper_test.go b/splitio/provisional/observability/segment_wrapper_test.go index cff78827..62e55d6b 100644 --- a/splitio/provisional/observability/segment_wrapper_test.go +++ b/splitio/provisional/observability/segment_wrapper_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/splitio/go-split-commons/v6/storage/mocks" + "github.com/splitio/go-split-commons/v8/storage/mocks" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/provisional/observability/split_wrapper.go b/splitio/provisional/observability/split_wrapper.go index fa935f5e..f6e9f19d 100644 --- a/splitio/provisional/observability/split_wrapper.go +++ b/splitio/provisional/observability/split_wrapper.go @@ -4,9 +4,9 @@ import ( "errors" "sync" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/storage/redis" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/storage/redis" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/provisional/observability/split_wrapper_test.go b/splitio/provisional/observability/split_wrapper_test.go index 92bc87b6..e0a6f300 100644 --- a/splitio/provisional/observability/split_wrapper_test.go +++ b/splitio/provisional/observability/split_wrapper_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage/mocks" - "github.com/splitio/go-split-commons/v6/storage/redis" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage/mocks" + "github.com/splitio/go-split-commons/v8/storage/redis" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/proxy/caching/caching.go b/splitio/proxy/caching/caching.go index 66807f2f..958b7dca 100644 --- a/splitio/proxy/caching/caching.go +++ b/splitio/proxy/caching/caching.go @@ -3,9 +3,10 @@ package caching import ( "strings" - "github.com/gin-gonic/gin" "github.com/splitio/gincache" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" + + "github.com/gin-gonic/gin" ) const ( diff --git a/splitio/proxy/caching/caching_test.go b/splitio/proxy/caching/caching_test.go index 98eb9f8f..62f02b2f 100644 --- a/splitio/proxy/caching/caching_test.go +++ b/splitio/proxy/caching/caching_test.go @@ -5,8 +5,10 @@ import ( "net/url" "testing" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/stretchr/testify/assert" ) diff --git a/splitio/proxy/caching/mocks/mock.go b/splitio/proxy/caching/mocks/mock.go index d3ef093d..24a01556 100644 --- a/splitio/proxy/caching/mocks/mock.go +++ b/splitio/proxy/caching/mocks/mock.go @@ -2,12 +2,13 @@ package mocks import ( "github.com/splitio/gincache" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/largesegment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/segment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/split" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/largesegment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/split" "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/stretchr/testify/mock" ) @@ -59,31 +60,50 @@ func (s *SplitStorageMock) ChangeNumber() (int64, error) { func (*SplitStorageMock) FetchMany(splitNames []string) map[string]*dtos.SplitDTO { panic("unimplemented") } + func (*SplitStorageMock) GetNamesByFlagSets(sets []string) map[string][]string { panic("unimplemented") } + func (*SplitStorageMock) GetAllFlagSetNames() []string { panic("unimplemented") } + func (*SplitStorageMock) KillLocally(splitName string, defaultTreatment string, changeNumber int64) { panic("unimplemented") } + func (s *SplitStorageMock) SegmentNames() *set.ThreadUnsafeSet { return s.Called().Get(0).(*set.ThreadUnsafeSet) } + func (s *SplitStorageMock) LargeSegmentNames() *set.ThreadUnsafeSet { return s.Called().Get(0).(*set.ThreadUnsafeSet) } + func (s *SplitStorageMock) SetChangeNumber(changeNumber int64) error { return s.Called(changeNumber).Error(0) } + func (*SplitStorageMock) Split(splitName string) *dtos.SplitDTO { panic("unimplemented") } func (*SplitStorageMock) SplitNames() []string { panic("unimplemented") } func (*SplitStorageMock) TrafficTypeExists(trafficType string) bool { panic("unimplemented") } + func (*SplitStorageMock) Update(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { panic("unimplemented") } +func (s *SplitStorageMock) ReplaceAll(splits []dtos.SplitDTO, changeNumber int64) error { + args := s.Called(splits, changeNumber) + return args.Error(0) +} + +func (s *SplitStorageMock) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + return s.Called().Get(0).(*set.ThreadUnsafeSet) +} + +// --- + type SegmentUpdaterMock struct { mock.Mock } @@ -108,6 +128,7 @@ type SegmentStorageMock struct { func (*SegmentStorageMock) SetChangeNumber(segmentName string, till int64) error { panic("unimplemented") } + func (s *SegmentStorageMock) Update(name string, toAdd *set.ThreadUnsafeSet, toRemove *set.ThreadUnsafeSet, changeNumber int64) error { return s.Called(name, toAdd, toRemove, changeNumber).Error(0) } @@ -119,9 +140,11 @@ func (s *SegmentStorageMock) ChangeNumber(segmentName string) (int64, error) { } func (*SegmentStorageMock) Keys(segmentName string) *set.ThreadUnsafeSet { panic("unimplemented") } + func (*SegmentStorageMock) SegmentContainsKey(segmentName string, key string) (bool, error) { panic("unimplemented") } + func (*SegmentStorageMock) SegmentKeysCount() int64 { panic("unimplemented") } // --- @@ -137,21 +160,26 @@ func (s *LargeSegmentStorageMock) SetChangeNumber(name string, till int64) { func (s *LargeSegmentStorageMock) Update(name string, userKeys []string, till int64) { s.Called(name, userKeys, till) } + func (s *LargeSegmentStorageMock) ChangeNumber(name string) int64 { args := s.Called(name) return args.Get(0).(int64) } + func (s *LargeSegmentStorageMock) Count() int { args := s.Called() return args.Get(0).(int) } + func (s *LargeSegmentStorageMock) LargeSegmentsForUser(userKey string) []string { return []string{} } + func (s *LargeSegmentStorageMock) IsInLargeSegment(name string, key string) (bool, error) { args := s.Called(name, key) return args.Get(0).(bool), args.Error(1) } + func (s *LargeSegmentStorageMock) TotalKeys(name string) int { return s.Called(name).Get(0).(int) } @@ -166,13 +194,16 @@ func (u *LargeSegmentUpdaterMock) SynchronizeLargeSegment(name string, till *int args := u.Called(name, till) return args.Get(0).(*int64), args.Error(1) } + func (u *LargeSegmentUpdaterMock) SynchronizeLargeSegments() (map[string]*int64, error) { args := u.Called() return args.Get(0).(map[string]*int64), args.Error(1) } + func (u *LargeSegmentUpdaterMock) IsCached(name string) bool { return u.Called().Get(0).(bool) } + func (u *LargeSegmentUpdaterMock) SynchronizeLargeSegmentUpdate(lsRFDResponseDTO *dtos.LargeSegmentRFDResponseDTO) (*int64, error) { args := u.Called(lsRFDResponseDTO) return args.Get(0).(*int64), args.Error(1) diff --git a/splitio/proxy/caching/workers.go b/splitio/proxy/caching/workers.go index 60f7388a..300d1f3c 100644 --- a/splitio/proxy/caching/workers.go +++ b/splitio/proxy/caching/workers.go @@ -1,17 +1,18 @@ package caching import ( - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/flagsets" - "github.com/splitio/go-split-commons/v6/healthcheck/application" - "github.com/splitio/go-split-commons/v6/service" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/largesegment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/segment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/split" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/gincache" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/grammar" + "github.com/splitio/go-split-commons/v8/flagsets" + "github.com/splitio/go-split-commons/v8/healthcheck/application" + "github.com/splitio/go-split-commons/v8/service" + "github.com/splitio/go-split-commons/v8/service/api/specs" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/largesegment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/split" + "github.com/splitio/go-toolkit/v5/logging" ) // CacheAwareSplitSynchronizer wraps a SplitSynchronizer and flushes cache when an update happens @@ -32,7 +33,8 @@ func NewCacheAwareSplitSync( flagSetsFilter flagsets.FlagSetFilter, ) *CacheAwareSplitSynchronizer { return &CacheAwareSplitSynchronizer{ - wrapped: split.NewSplitUpdater(splitStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter), + // TODO add ruleBasedSegmentStorage, ruleBuilder + wrapped: split.NewSplitUpdater(splitStorage, nil, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specs.FLAG_V1_3), splitStorage: splitStorage, cacheFlusher: cacheFlusher, } @@ -86,7 +88,8 @@ func NewCacheAwareSegmentSync( appMonitor application.MonitorProducerInterface, ) *CacheAwareSegmentSynchronizer { return &CacheAwareSegmentSynchronizer{ - wrapped: segment.NewSegmentUpdater(splitStorage, segmentStorage, segmentFetcher, logger, runtimeTelemetry, appMonitor), + // TODO add ruleBasedSegmentStorage + wrapped: segment.NewSegmentUpdater(splitStorage, segmentStorage, nil, segmentFetcher, logger, runtimeTelemetry, appMonitor), cacheFlusher: cacheFlusher, splitStorage: splitStorage, segmentStorage: segmentStorage, diff --git a/splitio/proxy/caching/workers_test.go b/splitio/proxy/caching/workers_test.go index 57cda98b..206832e7 100644 --- a/splitio/proxy/caching/workers_test.go +++ b/splitio/proxy/caching/workers_test.go @@ -3,11 +3,13 @@ package caching import ( "testing" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/segment" - "github.com/splitio/go-split-commons/v6/synchronizer/worker/split" - "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/split-synchronizer/v5/splitio/proxy/caching/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" + "github.com/splitio/go-split-commons/v8/synchronizer/worker/split" + "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/stretchr/testify/assert" ) diff --git a/splitio/proxy/conf/sections.go b/splitio/proxy/conf/sections.go index 5ffb8b8d..2e288b09 100644 --- a/splitio/proxy/conf/sections.go +++ b/splitio/proxy/conf/sections.go @@ -1,9 +1,10 @@ package conf import ( - cconf "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/service/api/specs" "github.com/splitio/split-synchronizer/v5/splitio/common/conf" + + cconf "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/service/api/specs" ) // Main configuration options diff --git a/splitio/proxy/controllers/events.go b/splitio/proxy/controllers/events.go index b37d8ce3..447525bb 100644 --- a/splitio/proxy/controllers/events.go +++ b/splitio/proxy/controllers/events.go @@ -2,16 +2,17 @@ package controllers import ( "encoding/json" - "io/ioutil" + "io" "net/http" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) // EventsServerController bundles all request handler for sdk-server apis @@ -66,7 +67,7 @@ func (c *EventsServerController) Register(regular gin.IRouter, beacon gin.IRoute func (c *EventsServerController) TestImpressionsBulk(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) impressionsMode := parseImpressionsMode(ctx.Request.Header.Get("SplitSDKImpressionsMode")) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -99,7 +100,7 @@ func (c *EventsServerController) TestImpressionsBeacon(ctx *gin.Context) { return } - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error("Error reading testImpressions/beacon request body: ", err) ctx.JSON(http.StatusInternalServerError, nil) @@ -135,7 +136,7 @@ func (c *EventsServerController) TestImpressionsBeacon(ctx *gin.Context) { func (c *EventsServerController) TestImpressionsCount(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error("Error reading request body in testImpressions/count endpoint: ", err) ctx.JSON(http.StatusInternalServerError, nil) @@ -162,7 +163,7 @@ func (c *EventsServerController) TestImpressionsCountBeacon(ctx *gin.Context) { return } - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -199,7 +200,7 @@ func (c *EventsServerController) TestImpressionsCountBeacon(ctx *gin.Context) { // EventsBulk accepts incoming event bulks func (c *EventsServerController) EventsBulk(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error("Error reading request body when accepting an event bulk: ", err) ctx.JSON(http.StatusInternalServerError, nil) @@ -225,7 +226,7 @@ func (c *EventsServerController) EventsBulkBeacon(ctx *gin.Context) { return } - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) diff --git a/splitio/proxy/controllers/events_test.go b/splitio/proxy/controllers/events_test.go index aa3637c6..4d48678f 100644 --- a/splitio/proxy/controllers/events_test.go +++ b/splitio/proxy/controllers/events_test.go @@ -7,14 +7,16 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" ilMock "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener/mocks" mw "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers/middleware" "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) func TestPostImpressionsbulk(t *testing.T) { diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index 87855863..45fc52db 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -7,18 +7,19 @@ import ( "strconv" "strings" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/engine/validator" - "github.com/splitio/go-split-commons/v6/service" - "github.com/splitio/go-split-commons/v6/service/api/specs" - cmnStorage "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/logging" - "golang.org/x/exp/slices" - "github.com/splitio/split-synchronizer/v5/splitio/proxy/caching" "github.com/splitio/split-synchronizer/v5/splitio/proxy/flagsets" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/validator" + "github.com/splitio/go-split-commons/v8/service" + "github.com/splitio/go-split-commons/v8/service/api/specs" + cmnStorage "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" + "golang.org/x/exp/slices" ) // SdkServerController bundles all request handler for sdk-server apis @@ -195,7 +196,15 @@ func (c *SdkServerController) fetchSplitChangesSince(since int64, sets []string) // perform a fetch to the BE using the supplied `since`, have the storage process it's response &, retry // TODO(mredolatti): implement basic collapsing here to avoid flooding the BE with requests fetchOptions := service.MakeFlagRequestParams().WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted - return c.fetcher.Fetch(fetchOptions) + ruleChanges, err := c.fetcher.Fetch(fetchOptions) + if err != nil { + return nil, err + } + return &dtos.SplitChangesDTO{ + Since: ruleChanges.FFSince(), + Till: ruleChanges.FFTill(), + Splits: ruleChanges.FeatureFlags(), + }, nil } func (c *SdkServerController) shouldOverrideSplitCondition(split *dtos.SplitDTO, version string) bool { diff --git a/splitio/proxy/controllers/sdk_test.go b/splitio/proxy/controllers/sdk_test.go index a4b96933..f66cfd2a 100644 --- a/splitio/proxy/controllers/sdk_test.go +++ b/splitio/proxy/controllers/sdk_test.go @@ -8,21 +8,23 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/engine/evaluator/impressionlabels" - "github.com/splitio/go-split-commons/v6/engine/grammar" - "github.com/splitio/go-split-commons/v6/engine/grammar/matchers" - "github.com/splitio/go-split-commons/v6/service" - "github.com/splitio/go-split-commons/v6/service/api/specs" - cmnStorage "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/splitio/split-synchronizer/v5/splitio/proxy/flagsets" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" psmocks "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/evaluator/impressionlabels" + "github.com/splitio/go-split-commons/v8/engine/grammar" + "github.com/splitio/go-split-commons/v8/engine/grammar/constants" + "github.com/splitio/go-split-commons/v8/service" + "github.com/splitio/go-split-commons/v8/service/api/specs" + "github.com/splitio/go-split-commons/v8/service/mocks" + cmnStorage "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestSplitChangesImpressionsDisabled(t *testing.T) { @@ -33,7 +35,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE", ImpressionsDisabled: true}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -42,7 +44,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -83,7 +85,7 @@ func TestSplitChangesRecentSince(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -92,7 +94,7 @@ func TestSplitChangesRecentSince(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -131,10 +133,13 @@ func TestSplitChangesOlderSince(t *testing.T) { Return((*dtos.SplitChangesDTO)(nil), storage.ErrSinceParamTooOld). Once() - var splitFetcher splitFetcherMock - splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))). - Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). - Once() + splitFetcher := &mocks.MockSplitFetcher{} + splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))).Return( + &dtos.FFResponseLegacy{ + SplitChanges: dtos.SplitChangesDTO{ + Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}, + }, + }, nil).Once() var largeSegmentStorageMock largeSegmentStorageMock @@ -146,7 +151,7 @@ func TestSplitChangesOlderSince(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -185,9 +190,9 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { Return((*dtos.SplitChangesDTO)(nil), storage.ErrSinceParamTooOld). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))). - Return((*dtos.SplitChangesDTO)(nil), errors.New("something")). + Return(nil, errors.New("something")). Once() var largeSegmentStorageMock largeSegmentStorageMock @@ -200,7 +205,7 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -229,7 +234,7 @@ func TestSplitChangesWithFlagSets(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -240,7 +245,7 @@ func TestSplitChangesWithFlagSets(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -278,7 +283,7 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -289,7 +294,7 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(true, []string{"a", "c"}), @@ -333,12 +338,12 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { Status: "ACTIVE", Conditions: []dtos.ConditionDTO{ { - MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: matchers.MatcherTypeEndsWith}}}, + MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeEndsWith}}}, Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, Label: "some label", }, { - MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: matchers.MatcherTypeGreaterThanOrEqualToSemver}}}, + MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeGreaterThanOrEqualToSemver}}}, Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, Label: "some label", }, @@ -347,7 +352,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { }, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -356,7 +361,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -386,7 +391,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { assert.Equal(t, 1, len(s.Splits[0].Conditions)) cond := s.Splits[0].Conditions[0] assert.Equal(t, grammar.ConditionTypeWhitelist, cond.ConditionType) - assert.Equal(t, matchers.MatcherTypeAllKeys, cond.MatcherGroup.Matchers[0].MatcherType) + assert.Equal(t, constants.MatcherTypeAllKeys, cond.MatcherGroup.Matchers[0].MatcherType) assert.Equal(t, impressionlabels.UnsupportedMatcherType, cond.Label) assert.Equal(t, []dtos.PartitionDTO{{Treatment: "control", Size: 100}}, cond.Partitions) @@ -408,7 +413,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { Status: "ACTIVE", Conditions: []dtos.ConditionDTO{ { - MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: matchers.MatcherTypeGreaterThanOrEqualToSemver}}}, + MatcherGroup: dtos.MatcherGroupDTO{Matchers: []dtos.MatcherDTO{{MatcherType: constants.MatcherTypeGreaterThanOrEqualToSemver}}}, Partitions: []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, Label: "some label", }, @@ -417,7 +422,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { }, nil). Once() - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -426,7 +431,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { group := router.Group("/api") controller := NewSdkServerController( logger, - &splitFetcher, + splitFetcher, &splitStorage, nil, flagsets.NewMatcher(false, nil), @@ -457,7 +462,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { assert.Equal(t, int64(1), s.Till) cond := s.Splits[0].Conditions[0] - assert.Equal(t, matchers.MatcherTypeGreaterThanOrEqualToSemver, cond.MatcherGroup.Matchers[0].MatcherType) + assert.Equal(t, constants.MatcherTypeGreaterThanOrEqualToSemver, cond.MatcherGroup.Matchers[0].MatcherType) assert.Equal(t, "some label", cond.Label) assert.Equal(t, []dtos.PartitionDTO{{Treatment: "on", Size: 100}}, cond.Partitions) @@ -468,7 +473,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { func TestSegmentChanges(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var segmentStorage psmocks.ProxySegmentStorageMock segmentStorage.On("ChangesSince", "someSegment", int64(-1)). @@ -483,7 +488,7 @@ func TestSegmentChanges(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -512,7 +517,7 @@ func TestSegmentChanges(t *testing.T) { func TestSegmentChangesNotFound(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var segmentStorage psmocks.ProxySegmentStorageMock segmentStorage.On("ChangesSince", "someSegment", int64(-1)). @@ -527,7 +532,7 @@ func TestSegmentChangesNotFound(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -546,7 +551,7 @@ func TestSegmentChangesNotFound(t *testing.T) { func TestMySegments(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var segmentStorage psmocks.ProxySegmentStorageMock segmentStorage.On("SegmentsFor", "someKey"). @@ -561,7 +566,7 @@ func TestMySegments(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -589,7 +594,7 @@ func TestMySegments(t *testing.T) { func TestMySegmentsError(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var segmentStorage psmocks.ProxySegmentStorageMock segmentStorage.On("SegmentsFor", "someKey"). @@ -604,7 +609,7 @@ func TestMySegmentsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -623,7 +628,7 @@ func TestMySegmentsError(t *testing.T) { func TestMemberships(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var segmentStorage psmocks.ProxySegmentStorageMock segmentStorage.On("SegmentsFor", "keyTest"). @@ -641,7 +646,7 @@ func TestMemberships(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) @@ -677,7 +682,7 @@ func TestMemberships(t *testing.T) { func TestMembershipsError(t *testing.T) { gin.SetMode(gin.TestMode) - var splitFetcher splitFetcherMock + splitFetcher := &mocks.MockSplitFetcher{} var splitStorage psmocks.ProxySplitStorageMock var largeSegmentStorageMock largeSegmentStorageMock var segmentStorage psmocks.ProxySegmentStorageMock @@ -691,7 +696,7 @@ func TestMembershipsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, &splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) @@ -707,16 +712,6 @@ func TestMembershipsError(t *testing.T) { segmentStorage.AssertExpectations(t) } -type splitFetcherMock struct { - mock.Mock -} - -// Fetch implements service.SplitFetcher -func (s *splitFetcherMock) Fetch(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - args := s.Called(fetchOptions) - return args.Get(0).(*dtos.SplitChangesDTO), args.Error(1) -} - func ref[T any](t T) *T { return &t } @@ -755,4 +750,3 @@ func (s *largeSegmentStorageMock) TotalKeys(name string) int { // -- var _ cmnStorage.LargeSegmentsStorage = (*largeSegmentStorageMock)(nil) -var _ service.SplitFetcher = (*splitFetcherMock)(nil) diff --git a/splitio/proxy/controllers/telemetry.go b/splitio/proxy/controllers/telemetry.go index 545fcca7..f6f90d4d 100644 --- a/splitio/proxy/controllers/telemetry.go +++ b/splitio/proxy/controllers/telemetry.go @@ -2,15 +2,16 @@ package controllers import ( "encoding/json" - "io/ioutil" + "io" "net/http" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) // TelemetryServerController bundles all request handler for sdk-server apis @@ -63,7 +64,7 @@ func (c *TelemetryServerController) Register(router gin.IRouter, beacon gin.IRou // Config endpoint accepts telemtetry config objects func (c *TelemetryServerController) Config(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -85,7 +86,7 @@ func (c *TelemetryServerController) Config(ctx *gin.Context) { // Usage endpoint accepts telemtetry config objects func (c *TelemetryServerController) Usage(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -110,7 +111,7 @@ func (c *TelemetryServerController) UsageBeacon(ctx *gin.Context) { return } - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -146,7 +147,7 @@ func (c *TelemetryServerController) UsageBeacon(ctx *gin.Context) { func (c *TelemetryServerController) keysClientSide(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error("Error reading request body in keys/cs endpoint: ", err) ctx.JSON(http.StatusInternalServerError, nil) @@ -172,7 +173,7 @@ func (c *TelemetryServerController) keysClientSideBeacon(ctx *gin.Context) { return } - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error(err) ctx.JSON(http.StatusInternalServerError, nil) @@ -208,7 +209,7 @@ func (c *TelemetryServerController) keysClientSideBeacon(ctx *gin.Context) { func (c *TelemetryServerController) keysServerSide(ctx *gin.Context) { metadata := metadataFromHeaders(ctx) - data, err := ioutil.ReadAll(ctx.Request.Body) + data, err := io.ReadAll(ctx.Request.Body) if err != nil { c.logger.Error("Error reading request body in keys/ss endpoint: ", err) ctx.JSON(http.StatusInternalServerError, nil) diff --git a/splitio/proxy/controllers/telemetry_test.go b/splitio/proxy/controllers/telemetry_test.go index 9dcb1722..73b75b76 100644 --- a/splitio/proxy/controllers/telemetry_test.go +++ b/splitio/proxy/controllers/telemetry_test.go @@ -7,12 +7,14 @@ import ( "net/http/httptest" "testing" - "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers/middleware" "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/gin-gonic/gin" ) func TestPostConfig(t *testing.T) { diff --git a/splitio/proxy/controllers/util.go b/splitio/proxy/controllers/util.go index 2dc85f5e..a33f904e 100644 --- a/splitio/proxy/controllers/util.go +++ b/splitio/proxy/controllers/util.go @@ -1,9 +1,10 @@ package controllers import ( + "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/gin-gonic/gin" - "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/dtos" ) func metadataFromHeaders(ctx *gin.Context) dtos.Metadata { diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index b61152b5..248273e7 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -5,19 +5,8 @@ import ( "fmt" "log" "net/url" - "time" - "strings" - - "github.com/splitio/go-split-commons/v6/conf" - "github.com/splitio/go-split-commons/v6/flagsets" - "github.com/splitio/go-split-commons/v6/service/api" - inmemory "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap" - "github.com/splitio/go-split-commons/v6/synchronizer" - "github.com/splitio/go-split-commons/v6/tasks" - "github.com/splitio/go-split-commons/v6/telemetry" - "github.com/splitio/go-toolkit/v5/backoff" - "github.com/splitio/go-toolkit/v5/logging" + "time" "github.com/splitio/split-synchronizer/v5/splitio/admin" adminCommon "github.com/splitio/split-synchronizer/v5/splitio/admin/common" @@ -35,6 +24,16 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" pTasks "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks" "github.com/splitio/split-synchronizer/v5/splitio/util" + + "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/flagsets" + "github.com/splitio/go-split-commons/v8/service/api" + inmemory "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" + "github.com/splitio/go-split-commons/v8/synchronizer" + "github.com/splitio/go-split-commons/v8/tasks" + "github.com/splitio/go-split-commons/v8/telemetry" + "github.com/splitio/go-toolkit/v5/backoff" + "github.com/splitio/go-toolkit/v5/logging" ) // Start initialize in proxy mode diff --git a/splitio/proxy/initialization_test.go b/splitio/proxy/initialization_test.go index cfe0c002..06066f15 100644 --- a/splitio/proxy/initialization_test.go +++ b/splitio/proxy/initialization_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/synchronizer" + "github.com/splitio/go-split-commons/v8/synchronizer" ) type syncManagerMock struct { @@ -25,6 +25,10 @@ func (m *syncManagerMock) Start() { } func (m *syncManagerMock) Stop() { panic("unimplemented") } +func (m *syncManagerMock) StartBGSyng(mstatus chan int, shouldRetry bool, onReady func()) error { + panic("unimplemented") +} + var _ synchronizer.Manager = (*syncManagerMock)(nil) func TestSyncManagerInitializationRetriesWithSnapshot(t *testing.T) { diff --git a/splitio/proxy/internal/dtos.go b/splitio/proxy/internal/dtos.go index 73db37d2..6beda649 100644 --- a/splitio/proxy/internal/dtos.go +++ b/splitio/proxy/internal/dtos.go @@ -1,7 +1,7 @@ package internal import ( - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" ) // RawData represents the raw data submitted by an sdk when posting data with associated metadata diff --git a/splitio/proxy/proxy.go b/splitio/proxy/proxy.go index cf26ec22..3c5c3098 100644 --- a/splitio/proxy/proxy.go +++ b/splitio/proxy/proxy.go @@ -5,10 +5,6 @@ import ( "fmt" "net/http" - "github.com/splitio/go-split-commons/v6/service" - cmnStorage "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers" "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers/middleware" @@ -16,10 +12,14 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks" + "github.com/splitio/gincache" + "github.com/splitio/go-split-commons/v8/service" + cmnStorage "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/logging" + "github.com/gin-contrib/cors" "github.com/gin-contrib/gzip" "github.com/gin-gonic/gin" - "github.com/splitio/gincache" ) // Options struct to set options for Proxy mode. diff --git a/splitio/proxy/proxy_test.go b/splitio/proxy/proxy_test.go index 9296fdaa..0bbb844a 100644 --- a/splitio/proxy/proxy_test.go +++ b/splitio/proxy/proxy_test.go @@ -9,14 +9,16 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/dtos" - serviceMocks "github.com/splitio/go-split-commons/v6/service/mocks" - "github.com/splitio/go-toolkit/v5/logging" ilmock "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener/mocks" "github.com/splitio/split-synchronizer/v5/splitio/proxy/caching" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" pstorageMocks "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/mocks" taskMocks "github.com/splitio/split-synchronizer/v5/splitio/proxy/tasks/mocks" + + "github.com/splitio/go-split-commons/v8/dtos" + serviceMocks "github.com/splitio/go-split-commons/v8/service/mocks" + "github.com/splitio/go-toolkit/v5/logging" + "github.com/stretchr/testify/assert" ) diff --git a/splitio/proxy/storage/mocks/mocks.go b/splitio/proxy/storage/mocks/mocks.go index c802fd89..de6ebf2c 100644 --- a/splitio/proxy/storage/mocks/mocks.go +++ b/splitio/proxy/storage/mocks/mocks.go @@ -1,7 +1,7 @@ package mocks import ( - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/stretchr/testify/mock" ) @@ -47,22 +47,27 @@ func (s *ProxyLargeSegmentStorageMock) SetChangeNumber(name string, till int64) func (s *ProxyLargeSegmentStorageMock) Update(name string, userKeys []string, till int64) { s.Called(name, userKeys, till) } + func (s *ProxyLargeSegmentStorageMock) ChangeNumber(name string) int64 { args := s.Called(name) return args.Get(0).(int64) } + func (s *ProxyLargeSegmentStorageMock) Count() int { args := s.Called() return args.Get(0).(int) } + func (s *ProxyLargeSegmentStorageMock) LargeSegmentsForUser(userKey string) []string { args := s.Called(userKey) return args.Get(0).([]string) } + func (s *ProxyLargeSegmentStorageMock) IsInLargeSegment(name string, key string) (bool, error) { args := s.Called(name, key) return args.Get(0).(bool), args.Error(1) } + func (s *ProxyLargeSegmentStorageMock) TotalKeys(name string) int { return s.Called(name).Get(0).(int) } diff --git a/splitio/proxy/storage/optimized/historic.go b/splitio/proxy/storage/optimized/historic.go index 5a1f1d8e..e4b26d87 100644 --- a/splitio/proxy/storage/optimized/historic.go +++ b/splitio/proxy/storage/optimized/historic.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" ) type HistoricChanges interface { diff --git a/splitio/proxy/storage/optimized/historic_test.go b/splitio/proxy/storage/optimized/historic_test.go index c1d4bb21..f6061749 100644 --- a/splitio/proxy/storage/optimized/historic_test.go +++ b/splitio/proxy/storage/optimized/historic_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/stretchr/testify/assert" ) func TestHistoricSplitStorage(t *testing.T) { - var historic HistoricChangesImpl historic.Update([]dtos.SplitDTO{ {Name: "f1", Sets: []string{"s1", "s2"}, Status: "ACTIVE", ChangeNumber: 1, TrafficTypeName: "tt1"}, diff --git a/splitio/proxy/storage/optimized/mocks/mocks.go b/splitio/proxy/storage/optimized/mocks/mocks.go index c738ab58..6af1a4a5 100644 --- a/splitio/proxy/storage/optimized/mocks/mocks.go +++ b/splitio/proxy/storage/optimized/mocks/mocks.go @@ -1,7 +1,7 @@ package mocks import ( - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" "github.com/stretchr/testify/mock" ) diff --git a/splitio/proxy/storage/persistent/splits.go b/splitio/proxy/storage/persistent/splits.go index 093d5e28..c927f676 100644 --- a/splitio/proxy/storage/persistent/splits.go +++ b/splitio/proxy/storage/persistent/splits.go @@ -6,7 +6,7 @@ import ( "encoding/json" "sync" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/proxy/storage/persistent/splits_test.go b/splitio/proxy/storage/persistent/splits_test.go index b9a29f77..8294476b 100644 --- a/splitio/proxy/storage/persistent/splits_test.go +++ b/splitio/proxy/storage/persistent/splits_test.go @@ -3,7 +3,7 @@ package persistent import ( "testing" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-toolkit/v5/logging" ) diff --git a/splitio/proxy/storage/segments.go b/splitio/proxy/storage/segments.go index a6a01c83..e543494e 100644 --- a/splitio/proxy/storage/segments.go +++ b/splitio/proxy/storage/segments.go @@ -4,14 +4,14 @@ import ( "errors" "fmt" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-toolkit/v5/datastructures/set" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/provisional/observability" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/splitio/go-toolkit/v5/logging" ) // ErrSegmentNotFound is returned when the segment whose changes we're querying isn't cached diff --git a/splitio/proxy/storage/splits.go b/splitio/proxy/storage/splits.go index 2d6fa6e6..b8b79627 100644 --- a/splitio/proxy/storage/splits.go +++ b/splitio/proxy/storage/splits.go @@ -5,16 +5,16 @@ import ( "fmt" "sync" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/flagsets" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/storage/inmemory/mutexmap" - "github.com/splitio/go-toolkit/v5/datastructures/set" - "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/provisional/observability" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/flagsets" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" + "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/splitio/go-toolkit/v5/logging" ) const ( @@ -197,6 +197,14 @@ func (p *ProxySplitStorageImpl) setStartingPoint(cn int64) { p.mtx.Unlock() } +func (p *ProxySplitStorageImpl) ReplaceAll(splits []dtos.SplitDTO, changeNumber int64) error { + panic("not implemented") +} + +func (p *ProxySplitStorageImpl) RuleBasedSegmentNames() *set.ThreadUnsafeSet { + panic("not implemented") +} + func (p *ProxySplitStorageImpl) sinceIsTooOld(since int64) bool { if since == -1 { return false diff --git a/splitio/proxy/storage/splits_test.go b/splitio/proxy/storage/splits_test.go index e2d1fa03..e45c3619 100644 --- a/splitio/proxy/storage/splits_test.go +++ b/splitio/proxy/storage/splits_test.go @@ -7,8 +7,8 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized/mocks" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" - "github.com/splitio/go-split-commons/v6/dtos" - "github.com/splitio/go-split-commons/v6/flagsets" + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/flagsets" "github.com/splitio/go-toolkit/v5/logging" "github.com/stretchr/testify/assert" diff --git a/splitio/proxy/storage/telemetry.go b/splitio/proxy/storage/telemetry.go index 71dc46e9..72325bcc 100644 --- a/splitio/proxy/storage/telemetry.go +++ b/splitio/proxy/storage/telemetry.go @@ -4,9 +4,9 @@ import ( "sync" "time" - "github.com/splitio/go-split-commons/v6/storage" - "github.com/splitio/go-split-commons/v6/storage/inmemory" - "github.com/splitio/go-split-commons/v6/telemetry" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/storage/inmemory" + "github.com/splitio/go-split-commons/v8/telemetry" ) // Local telemetry constants diff --git a/splitio/proxy/storage/telemetryts.go b/splitio/proxy/storage/telemetryts.go index cca69e49..3dbba0d0 100644 --- a/splitio/proxy/storage/telemetryts.go +++ b/splitio/proxy/storage/telemetryts.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/splitio/go-split-commons/v6/storage" + "github.com/splitio/go-split-commons/v8/storage" ) // Granularity selection constants to be used upon component instantiation diff --git a/splitio/proxy/tasks/deferred.go b/splitio/proxy/tasks/deferred.go index da73d5ab..b4d483fe 100644 --- a/splitio/proxy/tasks/deferred.go +++ b/splitio/proxy/tasks/deferred.go @@ -4,7 +4,7 @@ import ( "errors" "sync" - "github.com/splitio/go-split-commons/v6/tasks" + "github.com/splitio/go-split-commons/v8/tasks" "github.com/splitio/go-toolkit/v5/asynctask" "github.com/splitio/go-toolkit/v5/logging" gtSync "github.com/splitio/go-toolkit/v5/sync" diff --git a/splitio/proxy/tasks/events.go b/splitio/proxy/tasks/events.go index 5d1a4d0a..17e2ecb3 100644 --- a/splitio/proxy/tasks/events.go +++ b/splitio/proxy/tasks/events.go @@ -3,12 +3,12 @@ package tasks import ( "fmt" - "github.com/splitio/go-split-commons/v6/service/api" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" + + "github.com/splitio/go-split-commons/v8/service/api" "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/workerpool" - - "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" ) // EventWorker defines a component capable of recording imrpessions in raw form diff --git a/splitio/proxy/tasks/impcount.go b/splitio/proxy/tasks/impcount.go index 87dea4b1..cc063a45 100644 --- a/splitio/proxy/tasks/impcount.go +++ b/splitio/proxy/tasks/impcount.go @@ -3,12 +3,12 @@ package tasks import ( "fmt" - "github.com/splitio/go-split-commons/v6/service/api" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" + + "github.com/splitio/go-split-commons/v8/service/api" "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/workerpool" - - "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" ) // ImpressionCountWorker defines a component capable of recording imrpessions in raw form diff --git a/splitio/proxy/tasks/impressions.go b/splitio/proxy/tasks/impressions.go index cad05061..3d0f00c6 100644 --- a/splitio/proxy/tasks/impressions.go +++ b/splitio/proxy/tasks/impressions.go @@ -3,12 +3,12 @@ package tasks import ( "fmt" - "github.com/splitio/go-split-commons/v6/service/api" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" + + "github.com/splitio/go-split-commons/v8/service/api" "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/workerpool" - - "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" ) // ImpressionWorker defines a component capable of recording imrpessions in raw form diff --git a/splitio/proxy/tasks/telemetry.go b/splitio/proxy/tasks/telemetry.go index b030b7f9..233a47e5 100644 --- a/splitio/proxy/tasks/telemetry.go +++ b/splitio/proxy/tasks/telemetry.go @@ -3,12 +3,12 @@ package tasks import ( "fmt" - "github.com/splitio/go-split-commons/v6/service/api" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" + + "github.com/splitio/go-split-commons/v8/service/api" "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/splitio/go-toolkit/v5/workerpool" - - "github.com/splitio/split-synchronizer/v5/splitio/proxy/internal" ) // CONFIG diff --git a/splitio/util/tls.go b/splitio/util/tls.go index f399c43c..48f3221e 100644 --- a/splitio/util/tls.go +++ b/splitio/util/tls.go @@ -5,7 +5,7 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" + "os" "strings" "github.com/splitio/split-synchronizer/v5/splitio/common/conf" @@ -55,7 +55,7 @@ func TLSConfigForServer(cfg *conf.TLS) (*tls.Config, error) { tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert if cfg.ClientValidationRootCert != "" { - certBytes, err := ioutil.ReadFile(cfg.ClientValidationRootCert) + certBytes, err := os.ReadFile(cfg.ClientValidationRootCert) if err != nil { return nil, fmt.Errorf("error reading root certificate for client validation") } diff --git a/splitio/util/utils.go b/splitio/util/utils.go index b55ab558..1719395c 100644 --- a/splitio/util/utils.go +++ b/splitio/util/utils.go @@ -5,10 +5,11 @@ import ( "fmt" "strings" - "github.com/splitio/go-split-commons/v6/dtos" + "github.com/splitio/split-synchronizer/v5/splitio" + + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-toolkit/v5/hasher" "github.com/splitio/go-toolkit/v5/nethelpers" - "github.com/splitio/split-synchronizer/v5/splitio" ) // HashAPIKey hashes apikey From 7f46c3d885efa2cb2db73be29768ff2bf260032a Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 22 Oct 2025 16:23:03 -0300 Subject: [PATCH 02/36] spec version from config --- splitio/commitversion.go | 2 +- splitio/producer/initialization.go | 3 +-- splitio/proxy/caching/workers.go | 6 +++--- splitio/proxy/controllers/sdk.go | 7 +++++-- splitio/proxy/controllers/sdk_test.go | 25 +++++++++++++++++-------- splitio/proxy/initialization.go | 2 +- splitio/proxy/proxy.go | 3 +++ splitio/proxy/proxy_test.go | 4 ++-- 8 files changed, 33 insertions(+), 19 deletions(-) diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 019c8f13..b182b237 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "28e4d5c" +const CommitVersion = "5e4d9e1" diff --git a/splitio/producer/initialization.go b/splitio/producer/initialization.go index 22fba294..080f3442 100644 --- a/splitio/producer/initialization.go +++ b/splitio/producer/initialization.go @@ -26,7 +26,6 @@ import ( "github.com/splitio/go-split-commons/v8/flagsets" "github.com/splitio/go-split-commons/v8/provisional/strategy" "github.com/splitio/go-split-commons/v8/service/api" - "github.com/splitio/go-split-commons/v8/service/api/specs" "github.com/splitio/go-split-commons/v8/storage/filter" "github.com/splitio/go-split-commons/v8/storage/inmemory" "github.com/splitio/go-split-commons/v8/storage/redis" @@ -128,7 +127,7 @@ func Start(logger logging.LoggerInterface, cfg *conf.Main) error { workers := synchronizer.Workers{ // TODO add ruleBasedSegmentStorage, ruleBuilder, sdkOverrides - SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, nil, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specs.FLAG_V1_3), + SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, nil, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, cfg.FlagSpecVersion), // TODO add ruleBasedSegmentStorage SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, nil, splitAPI.SegmentFetcher, logger, syncTelemetryStorage, appMonitor), diff --git a/splitio/proxy/caching/workers.go b/splitio/proxy/caching/workers.go index 300d1f3c..d73e18ec 100644 --- a/splitio/proxy/caching/workers.go +++ b/splitio/proxy/caching/workers.go @@ -7,7 +7,6 @@ import ( "github.com/splitio/go-split-commons/v8/flagsets" "github.com/splitio/go-split-commons/v8/healthcheck/application" "github.com/splitio/go-split-commons/v8/service" - "github.com/splitio/go-split-commons/v8/service/api/specs" "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-split-commons/v8/synchronizer/worker/largesegment" "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" @@ -31,10 +30,11 @@ func NewCacheAwareSplitSync( cacheFlusher gincache.CacheFlusher, appMonitor application.MonitorProducerInterface, flagSetsFilter flagsets.FlagSetFilter, + specVersion string, ) *CacheAwareSplitSynchronizer { return &CacheAwareSplitSynchronizer{ - // TODO add ruleBasedSegmentStorage, ruleBuilder - wrapped: split.NewSplitUpdater(splitStorage, nil, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specs.FLAG_V1_3), + // TODO add ruleBasedSegmentStorage, ruleBuilder, increase FLAG SPEC when we support RUleBased + wrapped: split.NewSplitUpdater(splitStorage, nil, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specVersion), splitStorage: splitStorage, cacheFlusher: cacheFlusher, } diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index 45fc52db..0e2238af 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -16,6 +16,7 @@ import ( "github.com/splitio/go-split-commons/v8/service" "github.com/splitio/go-split-commons/v8/service/api/specs" cmnStorage "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/gin-gonic/gin" @@ -31,6 +32,7 @@ type SdkServerController struct { fsmatcher flagsets.FlagSetMatcher versionFilter specs.SplitVersionFilter largeSegmentStorage cmnStorage.LargeSegmentsStorage + specVersion string } // NewSdkServerController instantiates a new sdk server controller @@ -41,7 +43,7 @@ func NewSdkServerController( proxySegmentStorage storage.ProxySegmentStorage, fsmatcher flagsets.FlagSetMatcher, largeSegmentStorage cmnStorage.LargeSegmentsStorage, - + specVersion string, ) *SdkServerController { return &SdkServerController{ logger: logger, @@ -51,6 +53,7 @@ func NewSdkServerController( fsmatcher: fsmatcher, versionFilter: specs.NewSplitVersionFilter(), largeSegmentStorage: largeSegmentStorage, + specVersion: specVersion, } } @@ -195,7 +198,7 @@ func (c *SdkServerController) fetchSplitChangesSince(since int64, sets []string) // perform a fetch to the BE using the supplied `since`, have the storage process it's response &, retry // TODO(mredolatti): implement basic collapsing here to avoid flooding the BE with requests - fetchOptions := service.MakeFlagRequestParams().WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted + fetchOptions := service.MakeFlagRequestParams().WithSpecVersion(common.StringRef(c.specVersion)).WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted ruleChanges, err := c.fetcher.Fetch(fetchOptions) if err != nil { return nil, err diff --git a/splitio/proxy/controllers/sdk_test.go b/splitio/proxy/controllers/sdk_test.go index f66cfd2a..6be1ecf9 100644 --- a/splitio/proxy/controllers/sdk_test.go +++ b/splitio/proxy/controllers/sdk_test.go @@ -20,6 +20,7 @@ import ( "github.com/splitio/go-split-commons/v8/service/api/specs" "github.com/splitio/go-split-commons/v8/service/mocks" cmnStorage "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" "github.com/gin-gonic/gin" @@ -49,6 +50,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -99,6 +101,7 @@ func TestSplitChangesRecentSince(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -134,7 +137,7 @@ func TestSplitChangesOlderSince(t *testing.T) { Once() splitFetcher := &mocks.MockSplitFetcher{} - splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))).Return( + splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))).Return( &dtos.FFResponseLegacy{ SplitChanges: dtos.SplitChangesDTO{ Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}, @@ -156,6 +159,7 @@ func TestSplitChangesOlderSince(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -191,7 +195,7 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { Once() splitFetcher := &mocks.MockSplitFetcher{} - splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))). + splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))). Return(nil, errors.New("something")). Once() @@ -210,6 +214,7 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -250,6 +255,7 @@ func TestSplitChangesWithFlagSets(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -299,6 +305,7 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { nil, flagsets.NewMatcher(true, []string{"a", "c"}), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -366,6 +373,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -436,6 +444,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, + specs.FLAG_V1_2, ) controller.Register(group) @@ -488,7 +497,7 @@ func TestSegmentChanges(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -532,7 +541,7 @@ func TestSegmentChangesNotFound(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -566,7 +575,7 @@ func TestMySegments(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -609,7 +618,7 @@ func TestMySegmentsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -646,7 +655,7 @@ func TestMemberships(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) @@ -696,7 +705,7 @@ func TestMembershipsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index 248273e7..ad667c1f 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -119,7 +119,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { // setup feature flags, segments & local telemetry API interactions workers := synchronizer.Workers{ - SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter), + SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter, advanced.FlagsSpecVersion), SegmentUpdater: caching.NewCacheAwareSegmentSync(splitStorage, segmentStorage, splitAPI.SegmentFetcher, logger, localTelemetryStorage, httpCache, appMonitor), TelemetryRecorder: telemetry.NewTelemetrySynchronizer(localTelemetryStorage, telemetryRecorder, splitStorage, segmentStorage, logger, diff --git a/splitio/proxy/proxy.go b/splitio/proxy/proxy.go index 3c5c3098..3f3c9b30 100644 --- a/splitio/proxy/proxy.go +++ b/splitio/proxy/proxy.go @@ -87,6 +87,8 @@ type Options struct { FlagSets []string FlagSetsStrictMatching bool + + SpecVersion string } // API bundles all components required to answer API calls from Split sdks @@ -165,6 +167,7 @@ func setupSdkController(options *Options) *controllers.SdkServerController { options.ProxySegmentStorage, flagsets.NewMatcher(options.FlagSetsStrictMatching, options.FlagSets), options.ProxyLargeSegmentStorage, + options.SpecVersion, ) } diff --git a/splitio/proxy/proxy_test.go b/splitio/proxy/proxy_test.go index 0bbb844a..fe8c8cbb 100644 --- a/splitio/proxy/proxy_test.go +++ b/splitio/proxy/proxy_test.go @@ -3,7 +3,7 @@ package proxy import ( "encoding/json" "fmt" - "io/ioutil" + "io" "math/rand" "net/http" "testing" @@ -312,7 +312,7 @@ func get(path string, port int, headers map[string]string) (status int, body []b } defer resp.Body.Close() - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) if err != nil { panic(err.Error()) } From 786b656bd10b2cb671ec3c4e7a7c9bbc689fc491 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 22 Oct 2025 17:45:39 -0300 Subject: [PATCH 03/36] added rbsproxystorage --- go.mod | 2 +- go.sum | 4 +- splitio/commitversion.go | 2 +- splitio/proxy/caching/workers.go | 7 +- splitio/proxy/controllers/sdk.go | 4 + splitio/proxy/initialization.go | 11 ++- splitio/proxy/initialization_test.go | 6 +- splitio/proxy/storage/rulebasedsegments.go | 105 +++++++++++++++++++++ 8 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 splitio/proxy/storage/rulebasedsegments.go diff --git a/go.mod b/go.mod index 7b6e93da..55a51047 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f + github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 0bc211a1..5f5de935 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f h1:2o8Hu3G4jAoF6Y0Ceptr4Bwp3x9wFDenp494Cu/V5nU= -github.com/splitio/go-split-commons/v8 v8.0.0-20251022154508-1ea26a26874f/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf h1:Gvd5Nv1Jnt943JYAnTXS8XljfLKuAOXNBanr+j9Yyuk= +github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/splitio/commitversion.go b/splitio/commitversion.go index b182b237..72768d1e 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "5e4d9e1" +const CommitVersion = "7f46c3d" diff --git a/splitio/proxy/caching/workers.go b/splitio/proxy/caching/workers.go index d73e18ec..14ba1665 100644 --- a/splitio/proxy/caching/workers.go +++ b/splitio/proxy/caching/workers.go @@ -24,6 +24,7 @@ type CacheAwareSplitSynchronizer struct { // NewCacheAwareSplitSync constructs a split-sync wrapper that evicts cache on updates func NewCacheAwareSplitSync( splitStorage storage.SplitStorage, + ruleBasedStorage storage.RuleBasedSegmentsStorage, splitFetcher service.SplitFetcher, logger logging.LoggerInterface, runtimeTelemetry storage.TelemetryRuntimeProducer, @@ -34,7 +35,7 @@ func NewCacheAwareSplitSync( ) *CacheAwareSplitSynchronizer { return &CacheAwareSplitSynchronizer{ // TODO add ruleBasedSegmentStorage, ruleBuilder, increase FLAG SPEC when we support RUleBased - wrapped: split.NewSplitUpdater(splitStorage, nil, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specVersion), + wrapped: split.NewSplitUpdater(splitStorage, ruleBasedStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specVersion), splitStorage: splitStorage, cacheFlusher: cacheFlusher, } @@ -81,6 +82,7 @@ type CacheAwareSegmentSynchronizer struct { func NewCacheAwareSegmentSync( splitStorage storage.SplitStorage, segmentStorage storage.SegmentStorage, + ruleBasedStorage storage.RuleBasedSegmentsStorage, segmentFetcher service.SegmentFetcher, logger logging.LoggerInterface, runtimeTelemetry storage.TelemetryRuntimeProducer, @@ -88,8 +90,7 @@ func NewCacheAwareSegmentSync( appMonitor application.MonitorProducerInterface, ) *CacheAwareSegmentSynchronizer { return &CacheAwareSegmentSynchronizer{ - // TODO add ruleBasedSegmentStorage - wrapped: segment.NewSegmentUpdater(splitStorage, segmentStorage, nil, segmentFetcher, logger, runtimeTelemetry, appMonitor), + wrapped: segment.NewSegmentUpdater(splitStorage, segmentStorage, ruleBasedStorage, segmentFetcher, logger, runtimeTelemetry, appMonitor), cacheFlusher: cacheFlusher, splitStorage: splitStorage, segmentStorage: segmentStorage, diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index 0e2238af..ad4f963f 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -127,6 +127,10 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { sParam, _ := ctx.GetQuery("s") spec, err := specs.ParseAndValidate(sParam) + if spec == "1.3" { + ctx.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "lala"}) + return + } if err != nil { c.logger.Error(fmt.Sprintf("error parsing spec version: %s.", err)) ctx.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index ad667c1f..912fd1e2 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -83,6 +83,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { // Proxy storages already implement the observable interface, so no need to wrap them splitStorage := storage.NewProxySplitStorage(dbInstance, logger, flagsets.NewFlagSetFilter(cfg.FlagSetsFilter), cfg.Initialization.Snapshot != "") + ruleBasedStorage := storage.NewProxyRuleBasedSegmentsStorage(logger) segmentStorage := storage.NewProxySegmentStorage(dbInstance, logger, cfg.Initialization.Snapshot != "") largeSegmentStorage := inmemory.NewLargeSegmentsStorage() @@ -119,8 +120,8 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { // setup feature flags, segments & local telemetry API interactions workers := synchronizer.Workers{ - SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter, advanced.FlagsSpecVersion), - SegmentUpdater: caching.NewCacheAwareSegmentSync(splitStorage, segmentStorage, splitAPI.SegmentFetcher, logger, localTelemetryStorage, httpCache, + SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, ruleBasedStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter, advanced.FlagsSpecVersion), + SegmentUpdater: caching.NewCacheAwareSegmentSync(splitStorage, segmentStorage, ruleBasedStorage, splitAPI.SegmentFetcher, logger, localTelemetryStorage, httpCache, appMonitor), TelemetryRecorder: telemetry.NewTelemetrySynchronizer(localTelemetryStorage, telemetryRecorder, splitStorage, segmentStorage, logger, metadata, localTelemetryStorage), @@ -165,7 +166,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { // health monitors are only started after successful init (otherwise they'll fail if the app doesn't sync correctly within the /// specified refresh period) before := time.Now() - err = startBGSyng(syncManager, mstatus, cfg.Initialization.Snapshot != "", func() { + err = startBGSync(syncManager, mstatus, cfg.Initialization.Snapshot != "", func() { logger.Info("Synchronizer tasks started") appMonitor.Start() servicesMonitor.Start() @@ -262,6 +263,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { FlagSets: cfg.FlagSetsFilter, FlagSetsStrictMatching: cfg.FlagSetStrictMatching, ProxyLargeSegmentStorage: largeSegmentStorage, + SpecVersion: cfg.FlagSpecVersion, } if ilcfg := cfg.Integrations.ImpressionListener; ilcfg.Endpoint != "" { @@ -286,8 +288,7 @@ var ( errUnrecoverable = errors.New("error and no snapshot available") ) -func startBGSyng(m synchronizer.Manager, mstatus chan int, haveSnapshot bool, onReady func()) error { - +func startBGSync(m synchronizer.Manager, mstatus chan int, haveSnapshot bool, onReady func()) error { attemptInit := func() bool { go m.Start() status := <-mstatus diff --git a/splitio/proxy/initialization_test.go b/splitio/proxy/initialization_test.go index 06066f15..4a6d4d7e 100644 --- a/splitio/proxy/initialization_test.go +++ b/splitio/proxy/initialization_test.go @@ -25,7 +25,7 @@ func (m *syncManagerMock) Start() { } func (m *syncManagerMock) Stop() { panic("unimplemented") } -func (m *syncManagerMock) StartBGSyng(mstatus chan int, shouldRetry bool, onReady func()) error { +func (m *syncManagerMock) StartBGSync(mstatus chan int, shouldRetry bool, onReady func()) error { panic("unimplemented") } @@ -37,7 +37,7 @@ func TestSyncManagerInitializationRetriesWithSnapshot(t *testing.T) { // No snapshot and error complete := make(chan struct{}, 1) - err := startBGSyng(sm, sm.c, false, func() { complete <- struct{}{} }) + err := startBGSync(sm, sm.c, false, func() { complete <- struct{}{} }) if err != errUnrecoverable { t.Error("should be an unrecoverable error. Got: ", err) } @@ -51,7 +51,7 @@ func TestSyncManagerInitializationRetriesWithSnapshot(t *testing.T) { // Snapshot and error atomic.StoreInt64(&sm.execCount, 0) - err = startBGSyng(sm, sm.c, true, func() { complete <- struct{}{} }) + err = startBGSync(sm, sm.c, true, func() { complete <- struct{}{} }) if err != errRetrying { t.Error("should be a retrying error. Got: ", err) } diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go new file mode 100644 index 00000000..6e80bedd --- /dev/null +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -0,0 +1,105 @@ +package storage + +import ( + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage" + "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" + "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/splitio/go-toolkit/v5/logging" +) + +// ProxyRuleBasedSegmentsStorage defines the interface of a storage that can be used for serving payloads +// for different requested `since` parameters +type ProxyRuleBasedSegmentsStorage interface { + ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) +} + +// ProxyRuleBasedSegmentsStorageImpl implements the ProxyRuleBasedSegmentsStorage interface and the SplitProducer interface +type ProxyRuleBasedSegmentsStorageImpl struct { + snapshot mutexmap.RuleBasedSegmentsStorageImpl + logger logging.LoggerInterface + // mtx sync.Mutex +} + +// NewProxyRuleBasedSegmentsStorage instantiates a new proxy storage that wraps an in-memory snapshot of the last known +// flag configuration +func NewProxyRuleBasedSegmentsStorage(logger logging.LoggerInterface) *ProxyRuleBasedSegmentsStorageImpl { + snapshot := mutexmap.NewRuleBasedSegmentsStorage() + + return &ProxyRuleBasedSegmentsStorageImpl{ + snapshot: *snapshot, + logger: logger, + } +} + +// ChangesSince retrieves the rule-based segments changes since the given change number +func (p *ProxyRuleBasedSegmentsStorageImpl) ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) { + cn, _ := p.snapshot.ChangeNumber() + return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: p.snapshot.All()}, nil +} + +// All call is forwarded to the snapshot +func (p *ProxyRuleBasedSegmentsStorageImpl) All() []dtos.RuleBasedSegmentDTO { + return p.snapshot.All() +} + +// ChangeNumber returns the current change number +func (p *ProxyRuleBasedSegmentsStorageImpl) ChangeNumber() (int64, error) { + return p.snapshot.ChangeNumber() +} + +// Contains checks if the given rule-based segments are present in storage +func (p *ProxyRuleBasedSegmentsStorageImpl) Contains(rbs []string) bool { + return p.snapshot.Contains(rbs) +} + +// GetRuleBasedSegmentByName retrieves a rule-based segment by name +func (p *ProxyRuleBasedSegmentsStorageImpl) GetRuleBasedSegmentByName(name string) (*dtos.RuleBasedSegmentDTO, error) { + return p.snapshot.GetRuleBasedSegmentByName(name) +} + +// LargeSegments call is forwarded to the snapshot +func (p *ProxyRuleBasedSegmentsStorageImpl) LargeSegments() *set.ThreadUnsafeSet { + return p.snapshot.LargeSegments() +} + +// ReplaceAll replaces all rule-based segments in storage +func (p *ProxyRuleBasedSegmentsStorageImpl) ReplaceAll(rbs []dtos.RuleBasedSegmentDTO, cn int64) error { + return p.snapshot.ReplaceAll(rbs, cn) +} + +// RuleBasedSegmentNames retrieves the names of all rule-based segments +func (p *ProxyRuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() []string { + return p.snapshot.RuleBasedSegmentNames() +} + +// Segments retrieves the names of all segments used in rule-based segments +func (p *ProxyRuleBasedSegmentsStorageImpl) Segments() *set.ThreadUnsafeSet { + return p.snapshot.Segments() +} + +// SetChangeNumber sets the change number +func (p *ProxyRuleBasedSegmentsStorageImpl) SetChangeNumber(cn int64) error { + return p.snapshot.SetChangeNumber(cn) +} + +// Update +func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, cn int64) { + // TODO Add the other logic + // p.setStartingPoint(changeNumber) // will be executed only the first time this method is called + + // if len(toAdd) == 0 && len(toRemove) == 0 { + // return + // } + + // p.mtx.Lock() + // p.snapshot.Update(toAdd, toRemove, changeNumber) + // p.historic.Update(toAdd, toRemove, changeNumber) + // p.db.Update(toAdd, toRemove, changeNumber) + // p.mtx.Unlock() + + p.snapshot.Update(toAdd, toRemove, cn) +} + +var _ ProxyRuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) +var _ storage.RuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) From 339ac8a9a1823feb2b4e6e1cb1d9eb44dac36ebf Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 24 Oct 2025 12:16:47 -0300 Subject: [PATCH 04/36] Updated commons version --- go.mod | 2 +- go.sum | 4 ++-- splitio/proxy/storage/rulebasedsegments.go | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 55a51047..0fd42fd2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf + github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511 github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 5f5de935..6e6c8f2f 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf h1:Gvd5Nv1Jnt943JYAnTXS8XljfLKuAOXNBanr+j9Yyuk= -github.com/splitio/go-split-commons/v8 v8.0.0-20251022200537-5573c2615caf/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511 h1:JneVSM63LVyRxOC6nXbBFLZ1viWvGTViplMkE4WfDM8= +github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index 6e80bedd..2e9721d9 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -69,7 +69,7 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) ReplaceAll(rbs []dtos.RuleBasedSegme } // RuleBasedSegmentNames retrieves the names of all rule-based segments -func (p *ProxyRuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() []string { +func (p *ProxyRuleBasedSegmentsStorageImpl) RuleBasedSegmentNames() ([]string, error) { return p.snapshot.RuleBasedSegmentNames() } @@ -84,7 +84,7 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) SetChangeNumber(cn int64) error { } // Update -func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, cn int64) { +func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, cn int64) error { // TODO Add the other logic // p.setStartingPoint(changeNumber) // will be executed only the first time this method is called @@ -99,6 +99,7 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegment // p.mtx.Unlock() p.snapshot.Update(toAdd, toRemove, cn) + return nil } var _ ProxyRuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) From 7fac09df99f73b5acae031de50e6f5dc640b906d Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 24 Oct 2025 14:02:07 -0300 Subject: [PATCH 05/36] Updated go commons --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0fd42fd2..957647dc 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511 + github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45 github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 6e6c8f2f..a947a97d 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511 h1:JneVSM63LVyRxOC6nXbBFLZ1viWvGTViplMkE4WfDM8= -github.com/splitio/go-split-commons/v8 v8.0.0-20251024133822-cd9c3455f511/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45 h1:FgK6fsSpZSLnCELfPGU6G5ur+E1lA1T53DgZ6n4uccM= +github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 641a929f1c985f7850027de748766d632e381a6a Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Fri, 24 Oct 2025 14:11:46 -0300 Subject: [PATCH 06/36] added rb storage for producer --- splitio/admin/common/config.go | 30 ++++++++++++++++++++++-------- splitio/commitversion.go | 2 +- splitio/producer/conf/sections.go | 2 +- splitio/producer/initialization.go | 29 +++++++++++++++++++---------- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/splitio/admin/common/config.go b/splitio/admin/common/config.go index b576cc96..63cb4376 100644 --- a/splitio/admin/common/config.go +++ b/splitio/admin/common/config.go @@ -1,14 +1,28 @@ package common -import "github.com/splitio/go-split-commons/v8/storage" +import ( + "github.com/splitio/go-split-commons/v8/engine/grammar/constants" + "github.com/splitio/go-split-commons/v8/storage" +) + +var ProducerFeatureFlagsRules = []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 ProducerRuleBasedSegmentRules = []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} // Storages wraps storages in one struct type Storages struct { - SplitStorage storage.SplitStorage - SegmentStorage storage.SegmentStorage - LocalTelemetryStorage storage.TelemetryRuntimeConsumer - EventStorage storage.EventMultiSdkConsumer - ImpressionStorage storage.ImpressionMultiSdkConsumer - UniqueKeysStorage storage.UniqueKeysMultiSdkConsumer - LargeSegmentStorage storage.LargeSegmentsStorage + SplitStorage storage.SplitStorage + SegmentStorage storage.SegmentStorage + LocalTelemetryStorage storage.TelemetryRuntimeConsumer + EventStorage storage.EventMultiSdkConsumer + ImpressionStorage storage.ImpressionMultiSdkConsumer + UniqueKeysStorage storage.UniqueKeysMultiSdkConsumer + LargeSegmentStorage storage.LargeSegmentsStorage + RuleBasedSegmentsStorage storage.RuleBasedSegmentsStorage } diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 72768d1e..9e867830 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "7f46c3d" +const CommitVersion = "eb30828" diff --git a/splitio/producer/conf/sections.go b/splitio/producer/conf/sections.go index b306e71f..649635f6 100644 --- a/splitio/producer/conf/sections.go +++ b/splitio/producer/conf/sections.go @@ -18,7 +18,7 @@ type Main struct { Integrations conf.Integrations `json:"integrations" s-nested:"true"` Logging conf.Logging `json:"logging" s-nested:"true"` Healthcheck Healthcheck `json:"healthcheck" s-nested:"true"` - FlagSpecVersion string `json:"flagSpecVersion" s-cli:"flag-spec-version" s-def:"1.1" s-desc:"Spec version for flags"` + FlagSpecVersion string `json:"flagSpecVersion" s-cli:"flag-spec-version" s-def:"1.3" s-desc:"Spec version for flags"` } // BuildAdvancedConfig generates a commons-compatible advancedconfig with default + overriden parameters diff --git a/splitio/producer/initialization.go b/splitio/producer/initialization.go index 080f3442..46d7f161 100644 --- a/splitio/producer/initialization.go +++ b/splitio/producer/initialization.go @@ -103,12 +103,13 @@ func Start(logger logging.LoggerInterface, cfg *conf.Main) error { return fmt.Errorf("error instantiating observable segment storage: %w", err) } storages := adminCommon.Storages{ - SplitStorage: splitStorage, - SegmentStorage: segmentStorage, - LocalTelemetryStorage: syncTelemetryStorage, - ImpressionStorage: redis.NewImpressionStorage(redisClient, dtos.Metadata{}, logger), - EventStorage: redis.NewEventsStorage(redisClient, dtos.Metadata{}, logger), - UniqueKeysStorage: redis.NewUniqueKeysMultiSdkConsumer(redisClient, logger), + SplitStorage: splitStorage, + SegmentStorage: segmentStorage, + LocalTelemetryStorage: syncTelemetryStorage, + ImpressionStorage: redis.NewImpressionStorage(redisClient, dtos.Metadata{}, logger), + EventStorage: redis.NewEventsStorage(redisClient, dtos.Metadata{}, logger), + UniqueKeysStorage: redis.NewUniqueKeysMultiSdkConsumer(redisClient, logger), + RuleBasedSegmentsStorage: redis.NewRuleBasedStorage(redisClient, logger), } // Healcheck Monitor @@ -125,11 +126,19 @@ func Start(logger logging.LoggerInterface, cfg *conf.Main) error { // Creating Workers and Tasks eventEvictionMonitor := evcalc.New(1) + ruleBuilder := grammar.NewRuleBuilder( + storages.SegmentStorage, + storages.RuleBasedSegmentsStorage, + storages.LargeSegmentStorage, + adminCommon.ProducerFeatureFlagsRules, + adminCommon.ProducerRuleBasedSegmentRules, + logger, + nil) + workers := synchronizer.Workers{ - // TODO add ruleBasedSegmentStorage, ruleBuilder, sdkOverrides - SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, nil, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, cfg.FlagSpecVersion), - // TODO add ruleBasedSegmentStorage - SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, nil, splitAPI.SegmentFetcher, + // TODO add sdkOverrides + SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, storages.RuleBasedSegmentsStorage, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, ruleBuilder, false, cfg.FlagSpecVersion), + SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, storages.RuleBasedSegmentsStorage, splitAPI.SegmentFetcher, logger, syncTelemetryStorage, appMonitor), ImpressionsCountRecorder: impressionscount.NewRecorderSingle(impressionsCounter, splitAPI.ImpressionRecorder, metadata, logger, syncTelemetryStorage), From 180758997e9a679eb363b302d23a357d6cc0f923 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Mon, 27 Oct 2025 18:28:00 -0300 Subject: [PATCH 07/36] added rulebuilder into proxy --- splitio/proxy/caching/workers.go | 3 ++- splitio/proxy/controllers/sdk.go | 4 ---- splitio/proxy/initialization.go | 12 +++++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/splitio/proxy/caching/workers.go b/splitio/proxy/caching/workers.go index 14ba1665..0f208d3a 100644 --- a/splitio/proxy/caching/workers.go +++ b/splitio/proxy/caching/workers.go @@ -32,10 +32,11 @@ func NewCacheAwareSplitSync( appMonitor application.MonitorProducerInterface, flagSetsFilter flagsets.FlagSetFilter, specVersion string, + ruleBuilder grammar.RuleBuilder, ) *CacheAwareSplitSynchronizer { return &CacheAwareSplitSynchronizer{ // TODO add ruleBasedSegmentStorage, ruleBuilder, increase FLAG SPEC when we support RUleBased - wrapped: split.NewSplitUpdater(splitStorage, ruleBasedStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, grammar.RuleBuilder{}, false, specVersion), + wrapped: split.NewSplitUpdater(splitStorage, ruleBasedStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, ruleBuilder, false, specVersion), splitStorage: splitStorage, cacheFlusher: cacheFlusher, } diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index ad4f963f..0e2238af 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -127,10 +127,6 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { sParam, _ := ctx.GetQuery("s") spec, err := specs.ParseAndValidate(sParam) - if spec == "1.3" { - ctx.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "lala"}) - return - } if err != nil { c.logger.Error(fmt.Sprintf("error parsing spec version: %s.", err)) ctx.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index 912fd1e2..8b5364f7 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -26,6 +26,7 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/util" "github.com/splitio/go-split-commons/v8/conf" + "github.com/splitio/go-split-commons/v8/engine/grammar" "github.com/splitio/go-split-commons/v8/flagsets" "github.com/splitio/go-split-commons/v8/service/api" inmemory "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" @@ -118,9 +119,18 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { eventsRecorder := api.NewHTTPEventsRecorder(cfg.Apikey, *advanced, logger) eventsTask := pTasks.NewEventsFlushTask(eventsRecorder, logger, 1, int(cfg.Sync.Advanced.EventsBuffer), int(cfg.Sync.Advanced.EventsWorkers)) + ruleBuilder := grammar.NewRuleBuilder( + segmentStorage, + ruleBasedStorage, + largeSegmentStorage, + adminCommon.ProducerFeatureFlagsRules, + adminCommon.ProducerRuleBasedSegmentRules, + logger, + nil) + // setup feature flags, segments & local telemetry API interactions workers := synchronizer.Workers{ - SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, ruleBasedStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter, advanced.FlagsSpecVersion), + SplitUpdater: caching.NewCacheAwareSplitSync(splitStorage, ruleBasedStorage, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter, advanced.FlagsSpecVersion, ruleBuilder), SegmentUpdater: caching.NewCacheAwareSegmentSync(splitStorage, segmentStorage, ruleBasedStorage, splitAPI.SegmentFetcher, logger, localTelemetryStorage, httpCache, appMonitor), TelemetryRecorder: telemetry.NewTelemetrySynchronizer(localTelemetryStorage, telemetryRecorder, splitStorage, segmentStorage, logger, From 994902e4c9b9911bdba00d8c0a88daa0da0ab21f Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Wed, 29 Oct 2025 11:05:16 -0300 Subject: [PATCH 08/36] [FME-9988] Updated fetchRulesSince logic to fetch rule-based segments --- go.mod | 2 +- go.sum | 4 +- splitio/commitversion.go | 2 +- splitio/proxy/caching/workers.go | 15 ++- splitio/proxy/conf/sections.go | 2 +- splitio/proxy/controllers/sdk.go | 103 +++++++++++++++------ splitio/proxy/controllers/sdk_test.go | 20 ++-- splitio/proxy/initialization.go | 1 + splitio/proxy/proxy.go | 4 + splitio/proxy/storage/rulebasedsegments.go | 3 + splitio/proxy/storage/splits.go | 4 - 11 files changed, 113 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index 957647dc..3384116d 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45 + github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657 github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index a947a97d..0f54bde1 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45 h1:FgK6fsSpZSLnCELfPGU6G5ur+E1lA1T53DgZ6n4uccM= -github.com/splitio/go-split-commons/v8 v8.0.0-20251024164945-cb8677924d45/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657 h1:FYT0P+uFnXzALLgWOTIAJS6P4J1NpMGNi+rWsv2ZIkU= +github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 9e867830..03f6673b 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "eb30828" +const CommitVersion = "1807589" diff --git a/splitio/proxy/caching/workers.go b/splitio/proxy/caching/workers.go index 0f208d3a..c7614475 100644 --- a/splitio/proxy/caching/workers.go +++ b/splitio/proxy/caching/workers.go @@ -17,6 +17,7 @@ import ( // CacheAwareSplitSynchronizer wraps a SplitSynchronizer and flushes cache when an update happens type CacheAwareSplitSynchronizer struct { splitStorage storage.SplitStorage + rbStorage storage.RuleBasedSegmentsStorage wrapped split.Updater cacheFlusher gincache.CacheFlusher } @@ -35,9 +36,9 @@ func NewCacheAwareSplitSync( ruleBuilder grammar.RuleBuilder, ) *CacheAwareSplitSynchronizer { return &CacheAwareSplitSynchronizer{ - // TODO add ruleBasedSegmentStorage, ruleBuilder, increase FLAG SPEC when we support RUleBased wrapped: split.NewSplitUpdater(splitStorage, ruleBasedStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, ruleBuilder, false, specVersion), splitStorage: splitStorage, + rbStorage: ruleBasedStorage, cacheFlusher: cacheFlusher, } } @@ -45,8 +46,12 @@ func NewCacheAwareSplitSync( // SynchronizeSplits synchronizes feature flags and if something changes, purges the cache appropriately func (c *CacheAwareSplitSynchronizer) SynchronizeSplits(till *int64) (*split.UpdateResult, error) { previous, _ := c.splitStorage.ChangeNumber() + previousRB, _ := c.rbStorage.ChangeNumber() + result, err := c.wrapped.SynchronizeSplits(till) - if current, _ := c.splitStorage.ChangeNumber(); current > previous || (previous != -1 && current == -1) { + current, _ := c.splitStorage.ChangeNumber() + currentRB, _ := c.rbStorage.ChangeNumber() + if current > previous || (previous != -1 && current == -1) || currentRB > previousRB || (previousRB != -1 && currentRB == -1) { // if the changenumber was updated, evict splitChanges responses from cache c.cacheFlusher.EvictBySurrogate(SplitSurrogate) } @@ -63,8 +68,12 @@ func (c *CacheAwareSplitSynchronizer) LocalKill(splitName string, defaultTreatme // SynchronizeFeatureFlags synchronizes feature flags and if something changes, purges the cache appropriately func (c *CacheAwareSplitSynchronizer) SynchronizeFeatureFlags(ffChange *dtos.SplitChangeUpdate) (*split.UpdateResult, error) { previous, _ := c.splitStorage.ChangeNumber() + previousRB, _ := c.rbStorage.ChangeNumber() + result, err := c.wrapped.SynchronizeFeatureFlags(ffChange) - if current, _ := c.splitStorage.ChangeNumber(); current > previous || (previous != -1 && current == -1) { + current, _ := c.splitStorage.ChangeNumber() + currentRB, _ := c.rbStorage.ChangeNumber() + if current > previous || (previous != -1 && current == -1) || currentRB > previousRB || (previousRB != -1 && currentRB == -1) { // if the changenumber was updated, evict splitChanges responses from cache c.cacheFlusher.EvictBySurrogate(SplitSurrogate) } diff --git a/splitio/proxy/conf/sections.go b/splitio/proxy/conf/sections.go index 2e288b09..1e131785 100644 --- a/splitio/proxy/conf/sections.go +++ b/splitio/proxy/conf/sections.go @@ -22,7 +22,7 @@ type Main struct { Logging conf.Logging `json:"logging" s-nested:"true"` Healthcheck Healthcheck `json:"healthcheck" s-nested:"true"` Observability Observability `json:"observability" s-nested:"true"` - FlagSpecVersion string `json:"flagSpecVersion" s-cli:"flag-spec-version" s-def:"1.2" s-desc:"Spec version for flags"` + FlagSpecVersion string `json:"flagSpecVersion" s-cli:"flag-spec-version" s-def:"1.3" s-desc:"Spec version for flags"` } // BuildAdvancedConfig generates a commons-compatible advancedconfig with default + overriden parameters diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index 0e2238af..5e156180 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -25,14 +25,15 @@ import ( // SdkServerController bundles all request handler for sdk-server apis type SdkServerController struct { - logger logging.LoggerInterface - fetcher service.SplitFetcher - proxySplitStorage storage.ProxySplitStorage - proxySegmentStorage storage.ProxySegmentStorage - fsmatcher flagsets.FlagSetMatcher - versionFilter specs.SplitVersionFilter - largeSegmentStorage cmnStorage.LargeSegmentsStorage - specVersion string + logger logging.LoggerInterface + fetcher service.SplitFetcher + proxySplitStorage storage.ProxySplitStorage + proxyRBSegmentStorage storage.ProxyRuleBasedSegmentsStorage + proxySegmentStorage storage.ProxySegmentStorage + fsmatcher flagsets.FlagSetMatcher + versionFilter specs.SplitVersionFilter + largeSegmentStorage cmnStorage.LargeSegmentsStorage + specVersion string } // NewSdkServerController instantiates a new sdk server controller @@ -41,19 +42,21 @@ func NewSdkServerController( fetcher service.SplitFetcher, proxySplitStorage storage.ProxySplitStorage, proxySegmentStorage storage.ProxySegmentStorage, + proxyRBSegmentStorage storage.ProxyRuleBasedSegmentsStorage, fsmatcher flagsets.FlagSetMatcher, largeSegmentStorage cmnStorage.LargeSegmentsStorage, specVersion string, ) *SdkServerController { return &SdkServerController{ - logger: logger, - fetcher: fetcher, - proxySplitStorage: proxySplitStorage, - proxySegmentStorage: proxySegmentStorage, - fsmatcher: fsmatcher, - versionFilter: specs.NewSplitVersionFilter(), - largeSegmentStorage: largeSegmentStorage, - specVersion: specVersion, + logger: logger, + fetcher: fetcher, + proxySplitStorage: proxySplitStorage, + proxySegmentStorage: proxySegmentStorage, + proxyRBSegmentStorage: proxyRBSegmentStorage, + fsmatcher: fsmatcher, + versionFilter: specs.NewSplitVersionFilter(), + largeSegmentStorage: largeSegmentStorage, + specVersion: specVersion, } } @@ -107,6 +110,11 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { since = -1 } + rbsince, err := strconv.ParseInt(ctx.DefaultQuery("rbSince", "-1"), 10, 64) + if err != nil { + rbsince = -1 + } + var rawSets []string if fq, ok := ctx.GetQuery("sets"); ok { rawSets = strings.Split(fq, ",") @@ -116,9 +124,9 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { c.logger.Warning(fmt.Sprintf("SDK [%s] is sending flagsets unordered or with duplicates.", ctx.Request.Header.Get("SplitSDKVersion"))) } - c.logger.Debug(fmt.Sprintf("SDK Fetches Feature Flags Since: %d", since)) + c.logger.Debug(fmt.Sprintf("SDK Fetches Feature Flags Since: %d, RBSince: %d", since, rbsince)) - splits, err := c.fetchSplitChangesSince(since, sets) + rules, err := c.fetchRulesSince(since, rbsince, sets) if err != nil { c.logger.Error("error fetching splitChanges payload from storage: ", err) ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -133,9 +141,23 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { return } - splits.Splits = c.patchUnsupportedMatchers(splits.Splits, spec) + rules.FeatureFlags.Splits = c.patchUnsupportedMatchers(rules.FeatureFlags.Splits, spec) - ctx.JSON(http.StatusOK, splits) + if spec == specs.FLAG_V1_3 { + fmt.Println("1.3 1.3 1.3 1.3 1.3 ") + fmt.Println("cantidad de ff:", len(rules.FeatureFlags.Splits), rules.FeatureFlags.Since, rules.FeatureFlags.Till) + fmt.Println("cantidad de rb:", len(rules.RuleBasedSegments.RuleBasedSegments), rules.RuleBasedSegments.Since, rules.RuleBasedSegments.Till) + ctx.JSON(http.StatusOK, rules) + ctx.Set(caching.SurrogateContextKey, []string{caching.SplitSurrogate}) + ctx.Set(caching.StickyContextKey, true) + return + } + fmt.Println("otra otra otra otra") + ctx.JSON(http.StatusOK, dtos.SplitChangesDTO{ + Splits: rules.FeatureFlags.Splits, + Since: rules.FeatureFlags.Since, + Till: rules.FeatureFlags.Till, + }) ctx.Set(caching.SurrogateContextKey, []string{caching.SplitSurrogate}) ctx.Set(caching.StickyContextKey, true) } @@ -187,26 +209,49 @@ func (c *SdkServerController) MySegments(ctx *gin.Context) { ctx.Set(caching.SurrogateContextKey, caching.MakeSurrogateForMySegments(mySegments)) } -func (c *SdkServerController) fetchSplitChangesSince(since int64, sets []string) (*dtos.SplitChangesDTO, error) { +func (c *SdkServerController) fetchRulesSince(since int64, rbsince int64, sets []string) (*dtos.RuleChangesDTO, error) { + fmt.Println("split change since: ", since) splits, err := c.proxySplitStorage.ChangesSince(since, sets) - if err == nil { - return splits, nil + fmt.Println("split result: ", splits, err) + fmt.Println("rule baseed since: ", rbsince) + rbs, rbsErr := c.proxyRBSegmentStorage.ChangesSince(rbsince) + fmt.Println("rulebased result: ", rbs, rbsErr) + if err == nil && rbsErr == nil { + return &dtos.RuleChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Splits: splits.Splits, + Till: splits.Till, + Since: splits.Since, + }, + RuleBasedSegments: *rbs, + }, err } - if !errors.Is(err, storage.ErrSinceParamTooOld) { + if err != nil && !errors.Is(err, storage.ErrSinceParamTooOld) { return nil, fmt.Errorf("unexpected error fetching feature flag changes from storage: %w", err) } + if rbsErr != nil && !errors.Is(rbsErr, storage.ErrSinceParamTooOld) { + return nil, fmt.Errorf("unexpected error fetching rule-based segments changes from storage: %w", rbsErr) + } + // perform a fetch to the BE using the supplied `since`, have the storage process it's response &, retry // TODO(mredolatti): implement basic collapsing here to avoid flooding the BE with requests - fetchOptions := service.MakeFlagRequestParams().WithSpecVersion(common.StringRef(c.specVersion)).WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted + fetchOptions := service.MakeFlagRequestParams().WithSpecVersion(common.StringRef(c.specVersion)).WithChangeNumber(since).WithChangeNumberRB(rbsince).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted ruleChanges, err := c.fetcher.Fetch(fetchOptions) if err != nil { return nil, err } - return &dtos.SplitChangesDTO{ - Since: ruleChanges.FFSince(), - Till: ruleChanges.FFTill(), - Splits: ruleChanges.FeatureFlags(), + return &dtos.RuleChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Splits: ruleChanges.FeatureFlags(), + Till: ruleChanges.FFTill(), + Since: ruleChanges.FFSince(), + }, + RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ + RuleBasedSegments: ruleChanges.RuleBasedSegments(), + Till: ruleChanges.RBTill(), + Since: ruleChanges.RBSince(), + }, }, nil } diff --git a/splitio/proxy/controllers/sdk_test.go b/splitio/proxy/controllers/sdk_test.go index 6be1ecf9..a8d7fecf 100644 --- a/splitio/proxy/controllers/sdk_test.go +++ b/splitio/proxy/controllers/sdk_test.go @@ -48,6 +48,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -99,6 +100,7 @@ func TestSplitChangesRecentSince(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -157,6 +159,7 @@ func TestSplitChangesOlderSince(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -212,6 +215,7 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -253,6 +257,7 @@ func TestSplitChangesWithFlagSets(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -303,6 +308,7 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(true, []string{"a", "c"}), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -371,6 +377,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -442,6 +449,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { splitFetcher, &splitStorage, nil, + nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -497,7 +505,7 @@ func TestSegmentChanges(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -541,7 +549,7 @@ func TestSegmentChangesNotFound(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -575,7 +583,7 @@ func TestMySegments(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -618,7 +626,7 @@ func TestMySegmentsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -655,7 +663,7 @@ func TestMemberships(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) @@ -705,7 +713,7 @@ func TestMembershipsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index 8b5364f7..60a129a4 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -260,6 +260,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { SplitFetcher: splitAPI.SplitFetcher, ProxySplitStorage: splitStorage, ProxySegmentStorage: segmentStorage, + ProxyRBSegmentStorage: ruleBasedStorage, ImpressionsSink: impressionTask, ImpressionCountSink: impressionCountTask, EventsSink: eventsTask, diff --git a/splitio/proxy/proxy.go b/splitio/proxy/proxy.go index 3f3c9b30..8a76e94c 100644 --- a/splitio/proxy/proxy.go +++ b/splitio/proxy/proxy.go @@ -51,6 +51,9 @@ type Options struct { // used to resolve segmentChanges & mySegments requests ProxySegmentStorage storage.ProxySegmentStorage + // used to resolve splitChanges with rule-based segments requests + ProxyRBSegmentStorage storage.ProxyRuleBasedSegmentsStorage + // ProxyLargeSegmentStorage ProxyLargeSegmentStorage cmnStorage.LargeSegmentsStorage @@ -165,6 +168,7 @@ func setupSdkController(options *Options) *controllers.SdkServerController { options.SplitFetcher, options.ProxySplitStorage, options.ProxySegmentStorage, + options.ProxyRBSegmentStorage, flagsets.NewMatcher(options.FlagSetsStrictMatching, options.FlagSets), options.ProxyLargeSegmentStorage, options.SpecVersion, diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index 2e9721d9..8e9736e3 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -34,6 +34,9 @@ func NewProxyRuleBasedSegmentsStorage(logger logging.LoggerInterface) *ProxyRule // ChangesSince retrieves the rule-based segments changes since the given change number func (p *ProxyRuleBasedSegmentsStorageImpl) ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) { + if since > -1 { + return &dtos.RuleBasedSegmentsDTO{Since: since, Till: since, RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}}, nil + } cn, _ := p.snapshot.ChangeNumber() return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: p.snapshot.All()}, nil } diff --git a/splitio/proxy/storage/splits.go b/splitio/proxy/storage/splits.go index b8b79627..cefc7688 100644 --- a/splitio/proxy/storage/splits.go +++ b/splitio/proxy/storage/splits.go @@ -17,10 +17,6 @@ import ( "github.com/splitio/go-toolkit/v5/logging" ) -const ( - maxRecipes = 1000 -) - // ErrSinceParamTooOld is returned when a summary is not cached for a requested change number var ErrSinceParamTooOld = errors.New("summary for requested change number not cached") From 5680b0d9d904c2262d08ab4d236c9d29c3cfc27b Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 30 Oct 2025 11:24:25 -0300 Subject: [PATCH 09/36] Added historic for rule-based --- go.mod | 2 +- go.sum | 4 +- splitio/commitversion.go | 2 +- .../proxy/storage/optimized/rbshistoric.go | 120 +++++++++++ .../storage/optimized/rbshistoric_test.go | 189 ++++++++++++++++++ splitio/proxy/storage/rulebasedsegments.go | 120 +++++++++-- splitio/proxy/storage/splits.go | 4 +- 7 files changed, 414 insertions(+), 27 deletions(-) create mode 100644 splitio/proxy/storage/optimized/rbshistoric.go create mode 100644 splitio/proxy/storage/optimized/rbshistoric_test.go diff --git a/go.mod b/go.mod index 3384116d..a9287f0f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657 + github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2 github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 0f54bde1..85736fbf 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657 h1:FYT0P+uFnXzALLgWOTIAJS6P4J1NpMGNi+rWsv2ZIkU= -github.com/splitio/go-split-commons/v8 v8.0.0-20251028203151-2b6d18a2f657/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2 h1:M2+G0qWJhi5UC4yfQ8MePtDMmfCPlMXbL7+oJPKjGL8= +github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 03f6673b..01088ee5 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "1807589" +const CommitVersion = "994902e" diff --git a/splitio/proxy/storage/optimized/rbshistoric.go b/splitio/proxy/storage/optimized/rbshistoric.go new file mode 100644 index 00000000..61d2063b --- /dev/null +++ b/splitio/proxy/storage/optimized/rbshistoric.go @@ -0,0 +1,120 @@ +package optimized + +import ( + "sort" + "sync" + + "github.com/splitio/go-split-commons/v8/dtos" +) + +type HistoricChangesRB interface { + GetUpdatedSince(since int64) []RBView + Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, newCN int64) +} + +type HistoricChangesRBImpl struct { + data []RBView + mutex sync.RWMutex +} + +func NewHistoricRBChanges(capacity int) *HistoricChangesRBImpl { + return &HistoricChangesRBImpl{ + data: make([]RBView, 0, capacity), + } +} + +func (h *HistoricChangesRBImpl) GetUpdatedSince(since int64) []RBView { + h.mutex.RLock() + views := h.findNewerThan(since) + toRet := copyAndFilterRB(views, since) + h.mutex.RUnlock() + return toRet +} + +func (h *HistoricChangesRBImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, newCN int64) { + h.mutex.Lock() + h.updateFrom(toAdd) + h.updateFrom(toRemove) + sort.Slice(h.data, func(i, j int) bool { return h.data[i].LastUpdated < h.data[j].LastUpdated }) + h.mutex.Unlock() +} + +// public interface ends here + +func (h *HistoricChangesRBImpl) updateFrom(source []dtos.RuleBasedSegmentDTO) { + for idx := range source { + if current := h.findByName(source[idx].Name); current != nil { + current.updateFrom(&source[idx]) + } else { + var toAdd RBView + toAdd.updateFrom(&source[idx]) + h.data = append(h.data, toAdd) + } + } +} + +func (h *HistoricChangesRBImpl) findByName(name string) *RBView { + // yes, it's linear search because features are sorted by CN, but it's only used + // when processing an update coming from the BE. it's off the critical path of incoming + // requests. + for idx := range h.data { + if h.data[idx].Name == name { + return &h.data[idx] + } + } + return nil +} + +func (h *HistoricChangesRBImpl) findNewerThan(since int64) []RBView { + // precondition: h.data is sorted by CN + start := sort.Search(len(h.data), func(i int) bool { return h.data[i].LastUpdated > since }) + if start == len(h.data) { + return nil + } + return h.data[start:] +} + +type RBView struct { + Name string + Active bool + LastUpdated int64 +} + +func (f *RBView) updateFrom(s *dtos.RuleBasedSegmentDTO) { + f.Name = s.Name + f.Active = s.Status == "ACTIVE" + f.LastUpdated = s.ChangeNumber +} + +func (f *RBView) clone() RBView { + toRet := RBView{ + Name: f.Name, + Active: f.Active, + LastUpdated: f.LastUpdated, + } + return toRet + +} + +func copyAndFilterRB(views []RBView, since int64) []RBView { + toRet := make([]RBView, 0, len(views)) + + // this code computes the intersection in o(views * ) + for idx := range views { + if featureShouldBeReturnedRB(&views[idx], since) { + toRet = append(toRet, views[idx].clone()) + } + } + return toRet +} + +func featureShouldBeReturnedRB(view *RBView, since int64) bool { + // if fetching from sratch & the feature is not active, + // or it hasn't been updated since `since`, it shouldn't even be considered for being returned + if since == -1 && !view.Active || view.LastUpdated < since { + return false + } + return true +} + +var _ HistoricChangesRB = (*HistoricChangesRBImpl)(nil) diff --git a/splitio/proxy/storage/optimized/rbshistoric_test.go b/splitio/proxy/storage/optimized/rbshistoric_test.go new file mode 100644 index 00000000..4297f267 --- /dev/null +++ b/splitio/proxy/storage/optimized/rbshistoric_test.go @@ -0,0 +1,189 @@ +// package optimized + +// import ( +// "testing" + +// "github.com/splitio/go-split-commons/v8/dtos" + +// "github.com/stretchr/testify/assert" +// ) + +// func TestHistoricRuleBasedSegmentStorage(t *testing.T) { +// var historic HistoricChangesRBImpl +// historic.Update([]dtos.RuleBasedSegmentDTO{ +// {Name: "f1", Status: "ACTIVE", ChangeNumber: 1, TrafficTypeName: "tt1"}, +// }, []dtos.RuleBasedSegmentDTO{}, 1) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 1}, +// }, +// historic.GetUpdatedSince(-1)) + +// // process an update with no change in flagsets / rule-based segment status +// // - fetching from -1 && 1 should return the same paylaod as before with only `lastUpdated` bumped to 2 +// // - fetching from 2 should return empty +// historic.Update([]dtos.RuleBasedSegmentDTO{ +// {Name: "f1", Status: "ACTIVE", ChangeNumber: 2, TrafficTypeName: "tt1"}, +// }, []dtos.RuleBasedSegmentDTO{}, 1) + +// // no filter +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// }, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(2)) + +// // ------------------- + +// // process an update with one extra split +// // - fetching from -1, & 1 should return the same payload +// // - fetching from 2 shuold only return f2 +// // - fetching from 3 should return empty +// historic.Update([]dtos.RuleBasedSegmentDTO{ +// {Name: "f2", Status: "ACTIVE", ChangeNumber: 3, TrafficTypeName: "tt1"}, +// }, []dtos.RuleBasedSegmentDTO{}, 1) + +// // assert correct behaviours for CN == 1..3 and no flag sets filter +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(2)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) + +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(2)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) + +// // filtering by s2: +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 2}, +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(2)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) + +// //filtering by s3 +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// }, +// historic.GetUpdatedSince(2)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) + +// // ------------------- + +// // process an update that removes f1 from flagset s1 +// // - fetching without a filter should remain the same +// // - fetching with filter = s1 should not return f1 in CN=-1, should return it without the flagset in greater CNs +// historic.Update([]dtos.RuleBasedSegmentDTO{ +// {Name: "f1", Status: "ACTIVE", ChangeNumber: 4, TrafficTypeName: "tt1"}, +// }, []dtos.RuleBasedSegmentDTO{}, 1) + +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: true, LastUpdated: 3}, +// {Name: "f1", Active: true, LastUpdated: 4}, +// }, +// historic.GetUpdatedSince(-1)) + +// // with filter = s1 (f2 never was associated with s1, f1 is no longer associated) +// assert.Equal(t, +// []RBView{}, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// }, +// historic.GetUpdatedSince(2)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// }, +// historic.GetUpdatedSince(3)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(4)) + +// // process an update that removes f2 (archives the feature) +// // fetching from -1 should not return f2 +// // fetching from any intermediate CN should return f2 as archived +// // fetching from cn=5 should return empty +// historic.Update([]dtos.RuleBasedSegmentDTO{ +// {Name: "f2", Status: "ARCHIVED", ChangeNumber: 5, TrafficTypeName: "tt1"}, +// }, []dtos.RuleBasedSegmentDTO{}, 1) + +// // without filter +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// }, +// historic.GetUpdatedSince(-1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// {Name: "f2", Active: false, LastUpdated: 5}, +// }, +// historic.GetUpdatedSince(1)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// {Name: "f2", Active: false, LastUpdated: 5}, +// }, +// historic.GetUpdatedSince(2)) +// assert.Equal(t, +// []RBView{ +// {Name: "f1", Active: true, LastUpdated: 4}, +// {Name: "f2", Active: false, LastUpdated: 5}, +// }, +// historic.GetUpdatedSince(3)) +// assert.Equal(t, +// []RBView{ +// {Name: "f2", Active: false, LastUpdated: 5}, +// }, +// historic.GetUpdatedSince(4)) +// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(5)) + +// } diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index 8e9736e3..47ddf932 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -1,11 +1,15 @@ package storage import ( + "fmt" + "sync" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" ) // ProxyRuleBasedSegmentsStorage defines the interface of a storage that can be used for serving payloads @@ -14,31 +18,97 @@ type ProxyRuleBasedSegmentsStorage interface { ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) } -// ProxyRuleBasedSegmentsStorageImpl implements the ProxyRuleBasedSegmentsStorage interface and the SplitProducer interface +// ProxyRuleBasedSegmentsStorageImpl implements the ProxyRuleBasedSegmentsStorage interface and the RuleBasedSegmentProducer interface type ProxyRuleBasedSegmentsStorageImpl struct { - snapshot mutexmap.RuleBasedSegmentsStorageImpl - logger logging.LoggerInterface - // mtx sync.Mutex + snapshot mutexmap.RuleBasedSegmentsStorageImpl + logger logging.LoggerInterface + oldestKnownCN int64 + mtx sync.Mutex + historic optimized.HistoricChangesRB } // NewProxyRuleBasedSegmentsStorage instantiates a new proxy storage that wraps an in-memory snapshot of the last known // flag configuration func NewProxyRuleBasedSegmentsStorage(logger logging.LoggerInterface) *ProxyRuleBasedSegmentsStorageImpl { snapshot := mutexmap.NewRuleBasedSegmentsStorage() + historic := optimized.NewHistoricRBChanges(1000) + var initialCN int64 = -1 return &ProxyRuleBasedSegmentsStorageImpl{ - snapshot: *snapshot, - logger: logger, + snapshot: *snapshot, + logger: logger, + oldestKnownCN: initialCN, + historic: historic, + } +} + +func (p *ProxyRuleBasedSegmentsStorageImpl) sinceIsTooOld(since int64) bool { + if since == -1 { + return false + } + + p.mtx.Lock() + defer p.mtx.Unlock() + return since < p.oldestKnownCN +} + +func archivedRBDTOForView(view *optimized.RBView) dtos.RuleBasedSegmentDTO { + return dtos.RuleBasedSegmentDTO{ + ChangeNumber: view.LastUpdated, + Name: view.Name, + Status: "ARCHIVED", + Conditions: make([]dtos.RuleBasedConditionDTO, 0), } } // ChangesSince retrieves the rule-based segments changes since the given change number func (p *ProxyRuleBasedSegmentsStorageImpl) ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) { - if since > -1 { - return &dtos.RuleBasedSegmentsDTO{Since: since, Till: since, RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}}, nil + // No flagsets and fetching from -1, return the current snapshot + if since == -1 { + cn, err := p.snapshot.ChangeNumber() + if err != nil { + return nil, fmt.Errorf("error fetching changeNumber from snapshot: %w", err) + } + all := p.snapshot.All() + return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: all}, nil + } + + if p.sinceIsTooOld(since) { + return nil, ErrSinceParamTooOld + } + + views := p.historic.GetUpdatedSince(since) + namesToFetch := make([]string, 0, len(views)) + all := make([]dtos.RuleBasedSegmentDTO, 0, len(views)) + var till int64 = since + for idx := range views { + if t := views[idx].LastUpdated; t > till { + till = t + } + if views[idx].Active { + namesToFetch = append(namesToFetch, views[idx].Name) + } else { + all = append(all, archivedRBDTOForView(&views[idx])) + } + } + + for name, rbSegments := range p.snapshot.FetchMany(namesToFetch) { + if rbSegments == nil { + p.logger.Warning(fmt.Sprintf( + "possible inconsistency between historic & snapshot storages. Rule-based segment `%s` is missing in the latter", + name, + )) + continue + } + all = append(all, *rbSegments) } - cn, _ := p.snapshot.ChangeNumber() - return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: p.snapshot.All()}, nil + return &dtos.RuleBasedSegmentsDTO{Since: since, Till: till, RuleBasedSegments: all}, nil + + // if since > -1 { + // return &dtos.RuleBasedSegmentsDTO{Since: since, Till: since, RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}}, nil + // } + // cn, _ := p.snapshot.ChangeNumber() + // return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: p.snapshot.All()}, nil } // All call is forwarded to the snapshot @@ -87,23 +157,31 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) SetChangeNumber(cn int64) error { } // Update -func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, cn int64) error { +func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, changeNumber int64) error { // TODO Add the other logic - // p.setStartingPoint(changeNumber) // will be executed only the first time this method is called + p.setStartingPoint(changeNumber) // will be executed only the first time this method is called - // if len(toAdd) == 0 && len(toRemove) == 0 { - // return - // } + if len(toAdd) == 0 && len(toRemove) == 0 { + return nil + } - // p.mtx.Lock() - // p.snapshot.Update(toAdd, toRemove, changeNumber) - // p.historic.Update(toAdd, toRemove, changeNumber) + p.mtx.Lock() + p.snapshot.Update(toAdd, toRemove, changeNumber) + p.historic.Update(toAdd, toRemove, changeNumber) // p.db.Update(toAdd, toRemove, changeNumber) - // p.mtx.Unlock() - - p.snapshot.Update(toAdd, toRemove, cn) + p.mtx.Unlock() return nil } +func (p *ProxyRuleBasedSegmentsStorageImpl) setStartingPoint(cn int64) { + p.mtx.Lock() + // will be executed only the first time this method is called or when + // an older change is registered + if p.oldestKnownCN == -1 || cn < p.oldestKnownCN { + p.oldestKnownCN = cn + } + p.mtx.Unlock() +} + var _ ProxyRuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) var _ storage.RuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) diff --git a/splitio/proxy/storage/splits.go b/splitio/proxy/storage/splits.go index cefc7688..94bc7bcf 100644 --- a/splitio/proxy/storage/splits.go +++ b/splitio/proxy/storage/splits.go @@ -194,11 +194,11 @@ func (p *ProxySplitStorageImpl) setStartingPoint(cn int64) { } func (p *ProxySplitStorageImpl) ReplaceAll(splits []dtos.SplitDTO, changeNumber int64) error { - panic("not implemented") + return p.snapshot.ReplaceAll(splits, changeNumber) } func (p *ProxySplitStorageImpl) RuleBasedSegmentNames() *set.ThreadUnsafeSet { - panic("not implemented") + return p.snapshot.RuleBasedSegmentNames() } func (p *ProxySplitStorageImpl) sinceIsTooOld(since int64) bool { From f8ab00063f3391efe48e3bbb1f639b1d778e0db4 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 30 Oct 2025 13:46:41 -0300 Subject: [PATCH 10/36] Updated tests cases --- splitio/commitversion.go | 2 +- .../storage/optimized/rbshistoric_test.go | 298 +++++++----------- splitio/proxy/storage/rulebasedsegments.go | 4 +- splitio/proxy/storage/splits.go | 5 +- 4 files changed, 115 insertions(+), 194 deletions(-) diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 01088ee5..0474f997 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "994902e" +const CommitVersion = "5680b0d" diff --git a/splitio/proxy/storage/optimized/rbshistoric_test.go b/splitio/proxy/storage/optimized/rbshistoric_test.go index 4297f267..e1cc289a 100644 --- a/splitio/proxy/storage/optimized/rbshistoric_test.go +++ b/splitio/proxy/storage/optimized/rbshistoric_test.go @@ -1,189 +1,109 @@ -// package optimized - -// import ( -// "testing" - -// "github.com/splitio/go-split-commons/v8/dtos" - -// "github.com/stretchr/testify/assert" -// ) - -// func TestHistoricRuleBasedSegmentStorage(t *testing.T) { -// var historic HistoricChangesRBImpl -// historic.Update([]dtos.RuleBasedSegmentDTO{ -// {Name: "f1", Status: "ACTIVE", ChangeNumber: 1, TrafficTypeName: "tt1"}, -// }, []dtos.RuleBasedSegmentDTO{}, 1) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 1}, -// }, -// historic.GetUpdatedSince(-1)) - -// // process an update with no change in flagsets / rule-based segment status -// // - fetching from -1 && 1 should return the same paylaod as before with only `lastUpdated` bumped to 2 -// // - fetching from 2 should return empty -// historic.Update([]dtos.RuleBasedSegmentDTO{ -// {Name: "f1", Status: "ACTIVE", ChangeNumber: 2, TrafficTypeName: "tt1"}, -// }, []dtos.RuleBasedSegmentDTO{}, 1) - -// // no filter -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// }, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(2)) - -// // ------------------- - -// // process an update with one extra split -// // - fetching from -1, & 1 should return the same payload -// // - fetching from 2 shuold only return f2 -// // - fetching from 3 should return empty -// historic.Update([]dtos.RuleBasedSegmentDTO{ -// {Name: "f2", Status: "ACTIVE", ChangeNumber: 3, TrafficTypeName: "tt1"}, -// }, []dtos.RuleBasedSegmentDTO{}, 1) - -// // assert correct behaviours for CN == 1..3 and no flag sets filter -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(2)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) - -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(2)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) - -// // filtering by s2: -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 2}, -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(2)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) - -// //filtering by s3 -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// }, -// historic.GetUpdatedSince(2)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) - -// // ------------------- - -// // process an update that removes f1 from flagset s1 -// // - fetching without a filter should remain the same -// // - fetching with filter = s1 should not return f1 in CN=-1, should return it without the flagset in greater CNs -// historic.Update([]dtos.RuleBasedSegmentDTO{ -// {Name: "f1", Status: "ACTIVE", ChangeNumber: 4, TrafficTypeName: "tt1"}, -// }, []dtos.RuleBasedSegmentDTO{}, 1) - -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: true, LastUpdated: 3}, -// {Name: "f1", Active: true, LastUpdated: 4}, -// }, -// historic.GetUpdatedSince(-1)) - -// // with filter = s1 (f2 never was associated with s1, f1 is no longer associated) -// assert.Equal(t, -// []RBView{}, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// }, -// historic.GetUpdatedSince(2)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// }, -// historic.GetUpdatedSince(3)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(4)) - -// // process an update that removes f2 (archives the feature) -// // fetching from -1 should not return f2 -// // fetching from any intermediate CN should return f2 as archived -// // fetching from cn=5 should return empty -// historic.Update([]dtos.RuleBasedSegmentDTO{ -// {Name: "f2", Status: "ARCHIVED", ChangeNumber: 5, TrafficTypeName: "tt1"}, -// }, []dtos.RuleBasedSegmentDTO{}, 1) - -// // without filter -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// }, -// historic.GetUpdatedSince(-1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// {Name: "f2", Active: false, LastUpdated: 5}, -// }, -// historic.GetUpdatedSince(1)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// {Name: "f2", Active: false, LastUpdated: 5}, -// }, -// historic.GetUpdatedSince(2)) -// assert.Equal(t, -// []RBView{ -// {Name: "f1", Active: true, LastUpdated: 4}, -// {Name: "f2", Active: false, LastUpdated: 5}, -// }, -// historic.GetUpdatedSince(3)) -// assert.Equal(t, -// []RBView{ -// {Name: "f2", Active: false, LastUpdated: 5}, -// }, -// historic.GetUpdatedSince(4)) -// assert.Equal(t, []RBView{}, historic.GetUpdatedSince(5)) - -// } +package optimized + +import ( + "testing" + + "github.com/splitio/go-split-commons/v8/dtos" + + "github.com/stretchr/testify/assert" +) + +func TestHistoricRuleBasedSegmentStorage(t *testing.T) { + var historic HistoricChangesRBImpl + historic.Update([]dtos.RuleBasedSegmentDTO{ + {Name: "rbs1", Status: "ACTIVE", ChangeNumber: 1, TrafficTypeName: "tt1"}, + }, []dtos.RuleBasedSegmentDTO{}, 1) + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 1}, + }, + historic.GetUpdatedSince(-1)) + + // process an update with no change in rule-based segments status + // - fetching from -1 && 1 should return the same paylaod as before with only `lastUpdated` bumped to 2 + // - fetching from 2 should return empty + historic.Update([]dtos.RuleBasedSegmentDTO{ + {Name: "rbs1", Status: "ACTIVE", ChangeNumber: 2, TrafficTypeName: "tt1"}, + }, []dtos.RuleBasedSegmentDTO{}, 1) + + // no filter + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + }, + historic.GetUpdatedSince(-1)) + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + }, + historic.GetUpdatedSince(1)) + assert.Equal(t, []RBView{}, historic.GetUpdatedSince(2)) + + // ------------------- + + // process an update with one extra rule-based + // - fetching from -1, & 1 should return the same payload + // - fetching from 2 shuold only return rbs2 + // - fetching from 3 should return empty + historic.Update([]dtos.RuleBasedSegmentDTO{ + {Name: "rbs2", Status: "ACTIVE", ChangeNumber: 3, TrafficTypeName: "tt1"}, + }, []dtos.RuleBasedSegmentDTO{}, 1) + + // assert correct behaviours for CN == 1..3 and no flag sets filter + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + {Name: "rbs2", Active: true, LastUpdated: 3}, + }, + historic.GetUpdatedSince(-1)) + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + {Name: "rbs2", Active: true, LastUpdated: 3}, + }, + historic.GetUpdatedSince(1)) + assert.Equal(t, + []RBView{ + {Name: "rbs2", Active: true, LastUpdated: 3}, + }, + historic.GetUpdatedSince(2)) + assert.Equal(t, []RBView{}, historic.GetUpdatedSince(3)) + + // process an update that removes rbs2 (archives the rule-based) + // fetching from -1 should not return rbs2 + // fetching from any intermediate CN should return rbs2 as archived + // fetching from cn=5 should return empty + historic.Update([]dtos.RuleBasedSegmentDTO{ + {Name: "rbs2", Status: "ARCHIVED", ChangeNumber: 5, TrafficTypeName: "tt1"}, + }, []dtos.RuleBasedSegmentDTO{}, 1) + + // without filter + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + }, + historic.GetUpdatedSince(-1)) + assert.Equal(t, + []RBView{ + {Name: "rbs1", Active: true, LastUpdated: 2}, + {Name: "rbs2", Active: false, LastUpdated: 5}, + }, + historic.GetUpdatedSince(1)) + assert.Equal(t, + []RBView{ + {Name: "rbs2", Active: false, LastUpdated: 5}, + }, + historic.GetUpdatedSince(2)) + assert.Equal(t, + []RBView{ + {Name: "rbs2", Active: false, LastUpdated: 5}, + }, + historic.GetUpdatedSince(3)) + assert.Equal(t, + []RBView{ + {Name: "rbs2", Active: false, LastUpdated: 5}, + }, + historic.GetUpdatedSince(4)) + assert.Equal(t, []RBView{}, historic.GetUpdatedSince(5)) + +} diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index 47ddf932..73b831b5 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/grammar/constants" "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/datastructures/set" @@ -56,8 +57,7 @@ func archivedRBDTOForView(view *optimized.RBView) dtos.RuleBasedSegmentDTO { return dtos.RuleBasedSegmentDTO{ ChangeNumber: view.LastUpdated, Name: view.Name, - Status: "ARCHIVED", - Conditions: make([]dtos.RuleBasedConditionDTO, 0), + Status: constants.SplitStatusActive, } } diff --git a/splitio/proxy/storage/splits.go b/splitio/proxy/storage/splits.go index 94bc7bcf..1e4960f4 100644 --- a/splitio/proxy/storage/splits.go +++ b/splitio/proxy/storage/splits.go @@ -10,6 +10,7 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/engine/grammar/constants" "github.com/splitio/go-split-commons/v8/flagsets" "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" @@ -194,7 +195,7 @@ func (p *ProxySplitStorageImpl) setStartingPoint(cn int64) { } func (p *ProxySplitStorageImpl) ReplaceAll(splits []dtos.SplitDTO, changeNumber int64) error { - return p.snapshot.ReplaceAll(splits, changeNumber) + panic("not implemented") } func (p *ProxySplitStorageImpl) RuleBasedSegmentNames() *set.ThreadUnsafeSet { @@ -249,7 +250,7 @@ func archivedDTOForView(view *optimized.FeatureView) dtos.SplitDTO { TrafficAllocation: 100, TrafficAllocationSeed: 0, Seed: 0, - Status: "ARCHIVED", + Status: constants.SplitStatusActive, Killed: false, DefaultTreatment: "off", Algo: 1, From 1caa7695bf15831eff742be90d55482927ae2c9c Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 30 Oct 2025 15:49:16 -0300 Subject: [PATCH 11/36] Updated tests cases --- splitio/commitversion.go | 2 +- splitio/proxy/caching/workers_test.go | 12 ++++ splitio/proxy/controllers/sdk_test.go | 68 +++++++++++++++++------ splitio/proxy/proxy_test.go | 11 ++++ splitio/proxy/storage/mocks/rbsstorage.go | 19 +++++++ 5 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 splitio/proxy/storage/mocks/rbsstorage.go diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 0474f997..9e007c48 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "5680b0d" +const CommitVersion = "f8ab000" diff --git a/splitio/proxy/caching/workers_test.go b/splitio/proxy/caching/workers_test.go index 206832e7..e3893e3f 100644 --- a/splitio/proxy/caching/workers_test.go +++ b/splitio/proxy/caching/workers_test.go @@ -6,6 +6,7 @@ import ( "github.com/splitio/split-synchronizer/v5/splitio/proxy/caching/mocks" "github.com/splitio/go-split-commons/v8/dtos" + commons "github.com/splitio/go-split-commons/v8/storage/mocks" "github.com/splitio/go-split-commons/v8/synchronizer/worker/segment" "github.com/splitio/go-split-commons/v8/synchronizer/worker/split" "github.com/splitio/go-toolkit/v5/datastructures/set" @@ -19,9 +20,12 @@ func TestCacheAwareSplitSyncNoChanges(t *testing.T) { var cacheFlusherMock mocks.CacheFlusherMock var storageMock mocks.SplitStorageMock storageMock.On("ChangeNumber").Return(int64(-1), error(nil)) + var rbsStorage commons.MockRuleBasedSegmentStorage + rbsStorage.On("ChangeNumber").Return(int64(-1), error(nil)) css := CacheAwareSplitSynchronizer{ splitStorage: &storageMock, + rbStorage: &rbsStorage, wrapped: &splitSyncMock, cacheFlusher: &cacheFlusherMock, } @@ -39,6 +43,9 @@ func TestCacheAwareSplitSyncChanges(t *testing.T) { var splitSyncMock mocks.SplitUpdaterMock splitSyncMock.On("SynchronizeSplits", (*int64)(nil)).Return((*split.UpdateResult)(nil), error(nil)).Times(2) + var rbsStorage commons.MockRuleBasedSegmentStorage + rbsStorage.On("ChangeNumber").Return(int64(-1), error(nil)) + var cacheFlusherMock mocks.CacheFlusherMock cacheFlusherMock.On("EvictBySurrogate", SplitSurrogate).Times(3) @@ -49,6 +56,7 @@ func TestCacheAwareSplitSyncChanges(t *testing.T) { css := CacheAwareSplitSynchronizer{ splitStorage: &storageMock, wrapped: &splitSyncMock, + rbStorage: &rbsStorage, cacheFlusher: &cacheFlusherMock, } @@ -78,6 +86,9 @@ func TestCacheAwareSplitSyncChangesNewMethod(t *testing.T) { var splitSyncMock mocks.SplitUpdaterMock splitSyncMock.On("SynchronizeFeatureFlags", (*dtos.SplitChangeUpdate)(nil)).Return((*split.UpdateResult)(nil), error(nil)).Times(2) + var rbsStorage commons.MockRuleBasedSegmentStorage + rbsStorage.On("ChangeNumber").Return(int64(-1), error(nil)) + var cacheFlusherMock mocks.CacheFlusherMock cacheFlusherMock.On("EvictBySurrogate", SplitSurrogate).Times(2) @@ -87,6 +98,7 @@ func TestCacheAwareSplitSyncChangesNewMethod(t *testing.T) { css := CacheAwareSplitSynchronizer{ splitStorage: &storageMock, + rbStorage: &rbsStorage, wrapped: &splitSyncMock, cacheFlusher: &cacheFlusherMock, } diff --git a/splitio/proxy/controllers/sdk_test.go b/splitio/proxy/controllers/sdk_test.go index a8d7fecf..78284069 100644 --- a/splitio/proxy/controllers/sdk_test.go +++ b/splitio/proxy/controllers/sdk_test.go @@ -35,6 +35,8 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string(nil)). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE", ImpressionsDisabled: true}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -48,7 +50,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -88,6 +90,9 @@ func TestSplitChangesRecentSince(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -100,7 +105,7 @@ func TestSplitChangesRecentSince(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -137,9 +142,11 @@ func TestSplitChangesOlderSince(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string(nil)). Return((*dtos.SplitChangesDTO)(nil), storage.ErrSinceParamTooOld). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() splitFetcher := &mocks.MockSplitFetcher{} - splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))).Return( + splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithChangeNumberRB(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))).Return( &dtos.FFResponseLegacy{ SplitChanges: dtos.SplitChangesDTO{ Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}, @@ -159,7 +166,7 @@ func TestSplitChangesOlderSince(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -197,8 +204,11 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { Return((*dtos.SplitChangesDTO)(nil), storage.ErrSinceParamTooOld). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + splitFetcher := &mocks.MockSplitFetcher{} - splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))). + splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithChangeNumberRB(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))). Return(nil, errors.New("something")). Once() @@ -215,7 +225,7 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -243,6 +253,9 @@ func TestSplitChangesWithFlagSets(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -257,7 +270,7 @@ func TestSplitChangesWithFlagSets(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -294,6 +307,9 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "s1", Status: "ACTIVE"}, {Name: "s2", Status: "ACTIVE"}}}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -308,7 +324,7 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(true, []string{"a", "c"}), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -365,6 +381,9 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { }, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -377,7 +396,7 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -436,6 +455,8 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { }, }, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() splitFetcher := &mocks.MockSplitFetcher{} var largeSegmentStorageMock largeSegmentStorageMock @@ -449,7 +470,7 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) { splitFetcher, &splitStorage, nil, - nil, + &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2, @@ -497,6 +518,9 @@ func TestSegmentChanges(t *testing.T) { Return(&dtos.SegmentChangesDTO{Name: "someSegment", Added: []string{"k1", "k2"}, Removed: []string{}, Since: -1, Till: 1}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -505,7 +529,7 @@ func TestSegmentChanges(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -540,6 +564,8 @@ func TestSegmentChangesNotFound(t *testing.T) { segmentStorage.On("ChangesSince", "someSegment", int64(-1)). Return((*dtos.SegmentChangesDTO)(nil), storage.ErrSegmentNotFound). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() var largeSegmentStorageMock largeSegmentStorageMock @@ -549,7 +575,7 @@ func TestSegmentChangesNotFound(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/segmentChanges/someSegment?since=-1", nil) @@ -575,6 +601,9 @@ func TestMySegments(t *testing.T) { Return([]string{"segment1", "segment2"}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -583,7 +612,7 @@ func TestMySegments(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -618,6 +647,9 @@ func TestMySegmentsError(t *testing.T) { Return([]string(nil), errors.New("something")). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + var largeSegmentStorageMock largeSegmentStorageMock resp := httptest.NewRecorder() @@ -626,7 +658,7 @@ func TestMySegmentsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil) @@ -651,6 +683,8 @@ func TestMemberships(t *testing.T) { segmentStorage.On("SegmentsFor", "keyTest"). Return([]string{"segment1", "segment2"}, nil). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() var largeSegmentStorageMock largeSegmentStorageMock largeSegmentStorageMock.On("LargeSegmentsForUser", "keyTest"). @@ -663,7 +697,7 @@ func TestMemberships(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) @@ -706,6 +740,8 @@ func TestMembershipsError(t *testing.T) { segmentStorage.On("SegmentsFor", "keyTest"). Return([]string{}, errors.New("error message.")). Once() + var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() resp := httptest.NewRecorder() ctx, router := gin.CreateTestContext(resp) @@ -713,7 +749,7 @@ func TestMembershipsError(t *testing.T) { logger := logging.NewLogger(nil) group := router.Group("/api") - controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, nil, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) + controller := NewSdkServerController(logger, splitFetcher, &splitStorage, &segmentStorage, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2) controller.Register(group) ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil) diff --git a/splitio/proxy/proxy_test.go b/splitio/proxy/proxy_test.go index fe8c8cbb..a2d63f97 100644 --- a/splitio/proxy/proxy_test.go +++ b/splitio/proxy/proxy_test.go @@ -26,6 +26,8 @@ func TestSplitChangesEndpoints(t *testing.T) { opts := makeOpts() var splitStorage pstorageMocks.ProxySplitStorageMock opts.ProxySplitStorage = &splitStorage + var rbsStorage pstorageMocks.MockProxyRuleBasedSegmentStorage + opts.ProxyRBSegmentStorage = &rbsStorage proxy := New(opts) go proxy.Start() time.Sleep(1 * time.Second) // Let the scheduler switch the current thread/gr and start the server @@ -37,6 +39,7 @@ func TestSplitChangesEndpoints(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string(nil)). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "split1", ImpressionsDisabled: true}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() // Make a proper request status, body, headers := get("splitChanges?since=-1", opts.Port, map[string]string{"Authorization": "Bearer someApiKey"}) @@ -65,6 +68,8 @@ func TestSplitChangesEndpoints(t *testing.T) { Return(&dtos.SplitChangesDTO{Since: -1, Till: 2, Splits: []dtos.SplitDTO{{Name: "split2"}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() + opts.Cache.EvictBySurrogate(caching.SplitSurrogate) _, body, headers = get("splitChanges?since=-1", opts.Port, map[string]string{"Authorization": "Bearer someApiKey"}) @@ -81,6 +86,8 @@ func TestSplitChangesWithFlagsetsCaching(t *testing.T) { opts := makeOpts() var splitStorage pstorageMocks.ProxySplitStorageMock opts.ProxySplitStorage = &splitStorage + var rbsStorage pstorageMocks.MockProxyRuleBasedSegmentStorage + opts.ProxyRBSegmentStorage = &rbsStorage proxy := New(opts) go proxy.Start() time.Sleep(1 * time.Second) // Let the scheduler switch the current thread/gr and start the server @@ -88,6 +95,7 @@ func TestSplitChangesWithFlagsetsCaching(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string{"set1", "set2"}). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "split1"}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() // Make a proper request status, body, headers := get("splitChanges?since=-1&sets=set2,set1", opts.Port, map[string]string{"Authorization": "Bearer someApiKey"}) @@ -113,6 +121,7 @@ func TestSplitChangesWithFlagsetsCaching(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string{"set1", "set2", "set3"}). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "split1"}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() status, body, headers = get("splitChanges?since=-1&sets=set2,set1,set3", opts.Port, map[string]string{"Authorization": "Bearer someApiKey"}) assert.Equal(t, 200, status) @@ -129,10 +138,12 @@ func TestSplitChangesWithFlagsetsCaching(t *testing.T) { splitStorage.On("ChangesSince", int64(-1), []string{"set1", "set2"}). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "split1"}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() splitStorage.On("ChangesSince", int64(-1), []string{"set1", "set2", "set3"}). Return(&dtos.SplitChangesDTO{Since: -1, Till: 1, Splits: []dtos.SplitDTO{{Name: "split1"}}}, nil). Once() + rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once() status, body, headers = get("splitChanges?since=-1&sets=set2,set1", opts.Port, map[string]string{"Authorization": "Bearer someApiKey"}) assert.Equal(t, 200, status) diff --git a/splitio/proxy/storage/mocks/rbsstorage.go b/splitio/proxy/storage/mocks/rbsstorage.go new file mode 100644 index 00000000..14c56fb3 --- /dev/null +++ b/splitio/proxy/storage/mocks/rbsstorage.go @@ -0,0 +1,19 @@ +package mocks + +import ( + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" + "github.com/stretchr/testify/mock" +) + +type MockProxyRuleBasedSegmentStorage struct { + mock.Mock +} + +// ChangeNumber mock +func (m *MockProxyRuleBasedSegmentStorage) ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) { + args := m.Called(since) + return args.Get(0).(*dtos.RuleBasedSegmentsDTO), nil +} + +var _ storage.ProxyRuleBasedSegmentsStorage = (*MockProxyRuleBasedSegmentStorage)(nil) From c17efbcdc3c469e3ceef4f229b48c6ed1fd63221 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 30 Oct 2025 17:23:29 -0300 Subject: [PATCH 12/36] Updated storage --- go.mod | 2 +- go.sum | 4 ++-- splitio/proxy/storage/rulebasedsegments.go | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a9287f0f..0fddc192 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2 + github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index 85736fbf..f2852f03 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2 h1:M2+G0qWJhi5UC4yfQ8MePtDMmfCPlMXbL7+oJPKjGL8= -github.com/splitio/go-split-commons/v8 v8.0.0-20251029203719-4fdb9d7a1ff2/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e h1:GUKXzfgM7mpBpWNOlpI63+yBzt4oXY/fEC0CWkD7f78= +github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index 73b831b5..a91bdc53 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -183,5 +183,10 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) setStartingPoint(cn int64) { p.mtx.Unlock() } +// FetchMany fetches rule-based segments in the storage and returns an array of rule-based segments dtos +func (p *ProxyRuleBasedSegmentsStorageImpl) FetchMany(rbsNames []string) map[string]*dtos.RuleBasedSegmentDTO { + return p.snapshot.FetchMany(rbsNames) +} + var _ ProxyRuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) var _ storage.RuleBasedSegmentsStorage = (*ProxyRuleBasedSegmentsStorageImpl)(nil) From 6dbc573829761f95f222d4802f8ef4e18b33faac Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 30 Oct 2025 17:52:56 -0300 Subject: [PATCH 13/36] Clean logs --- splitio/proxy/controllers/sdk.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/splitio/proxy/controllers/sdk.go b/splitio/proxy/controllers/sdk.go index 5e156180..ae3f07dd 100644 --- a/splitio/proxy/controllers/sdk.go +++ b/splitio/proxy/controllers/sdk.go @@ -144,15 +144,11 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) { rules.FeatureFlags.Splits = c.patchUnsupportedMatchers(rules.FeatureFlags.Splits, spec) if spec == specs.FLAG_V1_3 { - fmt.Println("1.3 1.3 1.3 1.3 1.3 ") - fmt.Println("cantidad de ff:", len(rules.FeatureFlags.Splits), rules.FeatureFlags.Since, rules.FeatureFlags.Till) - fmt.Println("cantidad de rb:", len(rules.RuleBasedSegments.RuleBasedSegments), rules.RuleBasedSegments.Since, rules.RuleBasedSegments.Till) ctx.JSON(http.StatusOK, rules) ctx.Set(caching.SurrogateContextKey, []string{caching.SplitSurrogate}) ctx.Set(caching.StickyContextKey, true) return } - fmt.Println("otra otra otra otra") ctx.JSON(http.StatusOK, dtos.SplitChangesDTO{ Splits: rules.FeatureFlags.Splits, Since: rules.FeatureFlags.Since, @@ -210,12 +206,8 @@ func (c *SdkServerController) MySegments(ctx *gin.Context) { } func (c *SdkServerController) fetchRulesSince(since int64, rbsince int64, sets []string) (*dtos.RuleChangesDTO, error) { - fmt.Println("split change since: ", since) splits, err := c.proxySplitStorage.ChangesSince(since, sets) - fmt.Println("split result: ", splits, err) - fmt.Println("rule baseed since: ", rbsince) rbs, rbsErr := c.proxyRBSegmentStorage.ChangesSince(rbsince) - fmt.Println("rulebased result: ", rbs, rbsErr) if err == nil && rbsErr == nil { return &dtos.RuleChangesDTO{ FeatureFlags: dtos.FeatureFlagsDTO{ From ac695fd6a74d721c0cb308bf47944d87b7651386 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 31 Oct 2025 12:37:16 -0300 Subject: [PATCH 14/36] Added sdk overrides --- splitio/producer/initialization.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/splitio/producer/initialization.go b/splitio/producer/initialization.go index 46d7f161..d1803733 100644 --- a/splitio/producer/initialization.go +++ b/splitio/producer/initialization.go @@ -135,9 +135,10 @@ func Start(logger logging.LoggerInterface, cfg *conf.Main) error { logger, nil) + isProxy := splitAPI.SplitFetcher.IsProxy() + workers := synchronizer.Workers{ - // TODO add sdkOverrides - SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, storages.RuleBasedSegmentsStorage, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, ruleBuilder, false, cfg.FlagSpecVersion), + SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, storages.RuleBasedSegmentsStorage, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter, ruleBuilder, isProxy, cfg.FlagSpecVersion), SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, storages.RuleBasedSegmentsStorage, splitAPI.SegmentFetcher, logger, syncTelemetryStorage, appMonitor), ImpressionsCountRecorder: impressionscount.NewRecorderSingle(impressionsCounter, splitAPI.ImpressionRecorder, From 755151dc19646813be4762390d25a691ef9a8896 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Fri, 31 Oct 2025 15:08:18 -0300 Subject: [PATCH 15/36] Updated comments --- splitio/proxy/storage/optimized/rbshistoric.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/splitio/proxy/storage/optimized/rbshistoric.go b/splitio/proxy/storage/optimized/rbshistoric.go index 61d2063b..8ac0650d 100644 --- a/splitio/proxy/storage/optimized/rbshistoric.go +++ b/splitio/proxy/storage/optimized/rbshistoric.go @@ -109,7 +109,7 @@ func copyAndFilterRB(views []RBView, since int64) []RBView { } func featureShouldBeReturnedRB(view *RBView, since int64) bool { - // if fetching from sratch & the feature is not active, + // if fetching from sratch & the rule-based segment is not active, // or it hasn't been updated since `since`, it shouldn't even be considered for being returned if since == -1 && !view.Active || view.LastUpdated < since { return false From 1fbc49881ac7b592cc1804b54b1dc22144a9a3a8 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Mon, 3 Nov 2025 14:53:07 -0300 Subject: [PATCH 16/36] Updated go commons version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0fddc192..5c70f511 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.3.0 github.com/splitio/gincache v1.0.1 - github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e + github.com/splitio/go-split-commons/v8 v8.0.0-20251103170356-22cf36f46c2e github.com/splitio/go-toolkit/v5 v5.4.1 github.com/stretchr/testify v1.11.1 go.etcd.io/bbolt v1.3.6 diff --git a/go.sum b/go.sum index f2852f03..39ad4eef 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/splitio/gincache v1.0.1 h1:dLYdANY/BqH4KcUMCe/LluLyV5WtuE/LEdQWRE06IXU= github.com/splitio/gincache v1.0.1/go.mod h1:CcgJDSM9Af75kyBH0724v55URVwMBuSj5x1eCWIOECY= -github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e h1:GUKXzfgM7mpBpWNOlpI63+yBzt4oXY/fEC0CWkD7f78= -github.com/splitio/go-split-commons/v8 v8.0.0-20251030180738-32b17c825b5e/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= +github.com/splitio/go-split-commons/v8 v8.0.0-20251103170356-22cf36f46c2e h1:ie/GHe/YpceY2O0MWRY8tDrrr2szLI7qkqOrAL2Iy+4= +github.com/splitio/go-split-commons/v8 v8.0.0-20251103170356-22cf36f46c2e/go.mod h1:vgRGPn0s4RC9/zp1nIn4KeeIEj/K3iXE2fxYQbCk/WI= github.com/splitio/go-toolkit/v5 v5.4.1 h1:srTyvDBJZMUcJ/KiiQDMyjCuELVgTBh2TGRVn0sOXEE= github.com/splitio/go-toolkit/v5 v5.4.1/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 514a95c2c15ee79e9b6668df3c327eb47cae35c5 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Tue, 4 Nov 2025 13:50:51 -0300 Subject: [PATCH 17/36] Updated status --- splitio/commitversion.go | 2 +- splitio/proxy/storage/rulebasedsegments.go | 2 +- splitio/proxy/storage/splits.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/splitio/commitversion.go b/splitio/commitversion.go index 9e007c48..e0846185 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "f8ab000" +const CommitVersion = "1fbc498" diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index a91bdc53..e28d3333 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -57,7 +57,7 @@ func archivedRBDTOForView(view *optimized.RBView) dtos.RuleBasedSegmentDTO { return dtos.RuleBasedSegmentDTO{ ChangeNumber: view.LastUpdated, Name: view.Name, - Status: constants.SplitStatusActive, + Status: constants.SplitStatusArchived, } } diff --git a/splitio/proxy/storage/splits.go b/splitio/proxy/storage/splits.go index 1e4960f4..a67d1028 100644 --- a/splitio/proxy/storage/splits.go +++ b/splitio/proxy/storage/splits.go @@ -250,7 +250,7 @@ func archivedDTOForView(view *optimized.FeatureView) dtos.SplitDTO { TrafficAllocation: 100, TrafficAllocationSeed: 0, Seed: 0, - Status: constants.SplitStatusActive, + Status: constants.SplitStatusArchived, Killed: false, DefaultTreatment: "off", Algo: 1, From dfea32366199eb3196e6d15ccd8a2f0d9792628d Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Tue, 4 Nov 2025 18:08:29 -0300 Subject: [PATCH 18/36] Added tests cases --- splitio/commitversion.go | 2 +- .../proxy/storage/rulebasedsegments_test.go | 103 +++++++++++++++ splitio/proxy/storage/splits_test.go | 124 ++++++++++++++++++ 3 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 splitio/proxy/storage/rulebasedsegments_test.go diff --git a/splitio/commitversion.go b/splitio/commitversion.go index e0846185..2b9ff1ac 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "1fbc498" +const CommitVersion = "514a95c" diff --git a/splitio/proxy/storage/rulebasedsegments_test.go b/splitio/proxy/storage/rulebasedsegments_test.go new file mode 100644 index 00000000..9ce4e95d --- /dev/null +++ b/splitio/proxy/storage/rulebasedsegments_test.go @@ -0,0 +1,103 @@ +package storage + +import ( + "testing" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" + "github.com/stretchr/testify/assert" +) + +func TestRBSChangesSince(t *testing.T) { + logger := logging.NewLogger(nil) + + // Initialize storage with some test data + pss := NewProxyRuleBasedSegmentsStorage(logger) + + // Test case 1: since == -1 + { + initialRuleBaseds := []dtos.RuleBasedSegmentDTO{ + {Name: "rbs1", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"}, + {Name: "rbs2", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"}, + } + pss.Update(initialRuleBaseds, nil, 10) + + changes, err := pss.ChangesSince(-1) + assert.Nil(t, err) + assert.Equal(t, int64(-1), changes.Since) + assert.Equal(t, int64(10), changes.Till) + assert.ElementsMatch(t, initialRuleBaseds, changes.RuleBasedSegments) + } + + // Test case 2: Error when since is too old + { + // The storage was initialized with CN 10, so requesting CN 5 should fail + changes, err := pss.ChangesSince(5) + assert.Equal(t, ErrSinceParamTooOld, err) + assert.Nil(t, changes) + } + + // Test case 3: Active and archived rule-based segment + { + // Add a new rule-based segment and archive an existing one + toAdd := []dtos.RuleBasedSegmentDTO{{Name: "rbs3", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"}} + toRemove := []dtos.RuleBasedSegmentDTO{ + { + Name: "rbs2", + ChangeNumber: 15, + Status: "ARCHIVED", + TrafficTypeName: "user", + Conditions: []dtos.RuleBasedConditionDTO{}, + }, + } + + pss.Update(toAdd, toRemove, 15) + + changes, err := pss.ChangesSince(10) + assert.Nil(t, err) + assert.Equal(t, int64(10), changes.Since) + assert.Equal(t, int64(15), changes.Till) + + // Should include both the new active rule-based segment and the archived one + expectedRBSs := []dtos.RuleBasedSegmentDTO{ + { + Name: "rbs2", + ChangeNumber: 15, + Status: "ARCHIVED", + // Note: Archived segments have minimal fields set by archivedRBDTOForView + }, + { + Name: "rbs3", + ChangeNumber: 15, + Status: "ACTIVE", + TrafficTypeName: "user", + Excluded: dtos.ExcludedDTO{ + Keys: nil, + Segments: nil, + }, + Conditions: nil, + }, + } + assert.ElementsMatch(t, expectedRBSs, changes.RuleBasedSegments) + } + + // Test case 4: Proper till calculation with multiple changes + { + // Add changes with different change numbers + changes1 := []dtos.RuleBasedSegmentDTO{{Name: "rbs6", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"}} + changes2 := []dtos.RuleBasedSegmentDTO{{Name: "rbs7", ChangeNumber: 30, Status: "ACTIVE", TrafficTypeName: "user"}} + + pss.Update(changes1, nil, 25) + pss.Update(changes2, nil, 30) + + changes, err := pss.ChangesSince(20) + assert.Nil(t, err) + assert.Equal(t, int64(20), changes.Since) + assert.Equal(t, int64(30), changes.Till) + expectedChanges := []dtos.RuleBasedSegmentDTO{ + {Name: "rbs6", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"}, + {Name: "rbs7", ChangeNumber: 30, Status: "ACTIVE", TrafficTypeName: "user"}, + } + assert.ElementsMatch(t, expectedChanges, changes.RuleBasedSegments) + } +} diff --git a/splitio/proxy/storage/splits_test.go b/splitio/proxy/storage/splits_test.go index e45c3619..59356ab4 100644 --- a/splitio/proxy/storage/splits_test.go +++ b/splitio/proxy/storage/splits_test.go @@ -220,6 +220,130 @@ func TestGetNamesByFlagSets(t *testing.T) { } } +func TestChangesSince(t *testing.T) { + dbw, err := persistent.NewBoltWrapper(persistent.BoltInMemoryMode, nil) + assert.Nil(t, err) + logger := logging.NewLogger(nil) + + // Initialize storage with some test data + pss := NewProxySplitStorage(dbw, logger, flagsets.NewFlagSetFilter(nil), true) + + // Test case 1: since == -1 and no flagSets + { + initialSplits := []dtos.SplitDTO{ + {Name: "split1", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"}, + {Name: "split2", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"}, + } + pss.Update(initialSplits, nil, 10) + + changes, err := pss.ChangesSince(-1, nil) + assert.Nil(t, err) + assert.Equal(t, int64(-1), changes.Since) + assert.Equal(t, int64(10), changes.Till) + assert.ElementsMatch(t, initialSplits, changes.Splits) + } + + // Test case 2: Error when since is too old + { + // The storage was initialized with CN 10, so requesting CN 5 should fail + changes, err := pss.ChangesSince(5, nil) + assert.Equal(t, ErrSinceParamTooOld, err) + assert.Nil(t, changes) + } + + // Test case 3: Active and archived splits + { + // Add a new split and archive an existing one + toAdd := []dtos.SplitDTO{{Name: "split3", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"}} + toRemove := []dtos.SplitDTO{ + { + Name: "split2", + ChangeNumber: 15, + Status: "ARCHIVED", + TrafficTypeName: "user", + TrafficAllocation: 100, + Algo: 1, + DefaultTreatment: "off", + Conditions: []dtos.ConditionDTO{}, + Sets: []string{}, + }, + } + + pss.Update(toAdd, toRemove, 15) + + changes, err := pss.ChangesSince(10, nil) + assert.Nil(t, err) + assert.Equal(t, int64(10), changes.Since) + assert.Equal(t, int64(15), changes.Till) + + // Should include both the new active split and the archived one + expectedSplits := []dtos.SplitDTO{ + { + Name: "split3", + ChangeNumber: 15, + Status: "ACTIVE", + TrafficTypeName: "user", + TrafficAllocation: 0, + Algo: 0, + Conditions: nil, + Sets: nil, + }, + { + Name: "split2", + ChangeNumber: 15, + Status: "ARCHIVED", + TrafficTypeName: "user", + TrafficAllocation: 100, + Algo: 1, + DefaultTreatment: "off", + Conditions: []dtos.ConditionDTO{}, + Sets: []string{}, + }, + } + assert.ElementsMatch(t, expectedSplits, changes.Splits) + } + + // Test case 4: FlagSets filtering + { + // Add splits with flag sets + flagSetSplits := []dtos.SplitDTO{ + {Name: "split4", ChangeNumber: 20, Status: "ACTIVE", Sets: []string{"set1"}, TrafficTypeName: "user"}, + {Name: "split5", ChangeNumber: 20, Status: "ACTIVE", Sets: []string{"set2"}, TrafficTypeName: "user"}, + } + pss.Update(flagSetSplits, nil, 20) + + // Test filtering by set1 + changes, err := pss.ChangesSince(15, []string{"set1"}) + assert.Nil(t, err) + assert.Equal(t, int64(15), changes.Since) + assert.Equal(t, int64(20), changes.Till) + expectedSet1 := []dtos.SplitDTO{ + {Name: "split4", ChangeNumber: 20, Status: "ACTIVE", Sets: []string{"set1"}, TrafficTypeName: "user"}, + } + assert.ElementsMatch(t, expectedSet1, changes.Splits) + } + + // Test case 5: Proper till calculation with multiple changes + { + // Add changes with different change numbers + changes1 := []dtos.SplitDTO{{Name: "split6", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"}} + changes2 := []dtos.SplitDTO{{Name: "split7", ChangeNumber: 30, Status: "ACTIVE", TrafficTypeName: "user"}} + + pss.Update(changes1, nil, 25) + pss.Update(changes2, nil, 30) + + changes, err := pss.ChangesSince(20, nil) + assert.Nil(t, err) + assert.Equal(t, int64(20), changes.Since) + assert.Equal(t, int64(30), changes.Till) + expectedChanges := []dtos.SplitDTO{ + {Name: "split6", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"}, + {Name: "split7", ChangeNumber: 30, Status: "ACTIVE", TrafficTypeName: "user"}, + } + assert.ElementsMatch(t, expectedChanges, changes.Splits) + } +} + func TestGetAllFlagSetNames(t *testing.T) { dbw, err := persistent.NewBoltWrapper(persistent.BoltInMemoryMode, nil) if err != nil { From 5dafda74c93de9e326c35025b2a01cd70998c229 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 5 Nov 2025 12:43:53 -0300 Subject: [PATCH 19/36] moved common logic of changesItem --- splitio/proxy/storage/persistent/common.go | 38 ++++++++++++++++ .../proxy/storage/persistent/common_test.go | 45 +++++++++++++++++++ .../storage/persistent/rulebasedsegments.go | 3 ++ splitio/proxy/storage/persistent/splits.go | 36 +++------------ 4 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 splitio/proxy/storage/persistent/common.go create mode 100644 splitio/proxy/storage/persistent/common_test.go create mode 100644 splitio/proxy/storage/persistent/rulebasedsegments.go diff --git a/splitio/proxy/storage/persistent/common.go b/splitio/proxy/storage/persistent/common.go new file mode 100644 index 00000000..fdcfa0da --- /dev/null +++ b/splitio/proxy/storage/persistent/common.go @@ -0,0 +1,38 @@ +package persistent + +// ChangesItem represents an changesItem service response +type ChangesItem struct { + ChangeNumber int64 `json:"changeNumber"` + Name string `json:"name"` + Status string `json:"status"` + JSON string +} + +// ChangesItem Sortable list +type ChangesItems struct { + items []ChangesItem +} + +func NewChangesItems(size int) ChangesItems { + return ChangesItems{ + items: make([]ChangesItem, 0, size), + } +} + +func (c *ChangesItems) Append(item ChangesItem) { + c.items = append(c.items, item) +} + +func (c *ChangesItems) Len() int { + return len(c.items) +} + +func (c *ChangesItems) Less(i, j int) bool { + return c.items[i].ChangeNumber > c.items[j].ChangeNumber +} + +func (c *ChangesItems) Swap(i, j int) { + c.items[i], c.items[j] = c.items[j], c.items[i] +} + +//---------------------------------------------------- diff --git a/splitio/proxy/storage/persistent/common_test.go b/splitio/proxy/storage/persistent/common_test.go new file mode 100644 index 00000000..a92904a6 --- /dev/null +++ b/splitio/proxy/storage/persistent/common_test.go @@ -0,0 +1,45 @@ +package persistent + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestChangesItem(t *testing.T) { + item := ChangesItem{ + ChangeNumber: 123, + Name: "test_split", + Status: "ACTIVE", + JSON: `{"name":"test_split","status":"ACTIVE"}`, + } + + items := NewChangesItems(2) + items.Append(item) + items.Append(ChangesItem{ + ChangeNumber: 124, + Name: "another_split", + Status: "ARCHIVED", + JSON: `{"name":"another_split","status":"ARCHIVED"}`, + }) + + assert.Equal(t, 2, items.Len(), "Expected length to be 2") + assert.Equal(t, "test_split", items.items[0].Name) + assert.Equal(t, int64(123), items.items[0].ChangeNumber) + assert.Equal(t, "ACTIVE", items.items[0].Status) + assert.Equal(t, `{"name":"test_split","status":"ACTIVE"}`, items.items[0].JSON) + assert.Equal(t, "another_split", items.items[1].Name) + assert.Equal(t, int64(124), items.items[1].ChangeNumber) + assert.Equal(t, "ARCHIVED", items.items[1].Status) + assert.Equal(t, `{"name":"another_split","status":"ARCHIVED"}`, items.items[1].JSON) + assert.True(t, items.Less(1, 0), "Expected item at index 1 to be less than item at index 0") + items.Swap(0, 1) + assert.Equal(t, "another_split", items.items[0].Name) + assert.Equal(t, int64(124), items.items[0].ChangeNumber) + assert.Equal(t, "ARCHIVED", items.items[0].Status) + assert.Equal(t, `{"name":"another_split","status":"ARCHIVED"}`, items.items[0].JSON) + assert.Equal(t, "test_split", items.items[1].Name) + assert.Equal(t, int64(123), items.items[1].ChangeNumber) + assert.Equal(t, "ACTIVE", items.items[1].Status) + assert.Equal(t, `{"name":"test_split","status":"ACTIVE"}`, items.items[1].JSON) +} diff --git a/splitio/proxy/storage/persistent/rulebasedsegments.go b/splitio/proxy/storage/persistent/rulebasedsegments.go new file mode 100644 index 00000000..e21960b4 --- /dev/null +++ b/splitio/proxy/storage/persistent/rulebasedsegments.go @@ -0,0 +1,3 @@ +package persistent + +const ruleBasedSegmentsChangesCollectionName = "RULE_BASED_SEGMENTS_CHANGES_COLLECTION" diff --git a/splitio/proxy/storage/persistent/splits.go b/splitio/proxy/storage/persistent/splits.go index c927f676..73cad538 100644 --- a/splitio/proxy/storage/persistent/splits.go +++ b/splitio/proxy/storage/persistent/splits.go @@ -12,31 +12,6 @@ import ( const splitChangesCollectionName = "SPLIT_CHANGES_COLLECTION" -// SplitChangesItem represents an SplitChanges service response -type SplitChangesItem struct { - ChangeNumber int64 `json:"changeNumber"` - Name string `json:"name"` - Status string `json:"status"` - JSON string -} - -// SplitsChangesItems Sortable list -type SplitsChangesItems []SplitChangesItem - -func (slice SplitsChangesItems) Len() int { - return len(slice) -} - -func (slice SplitsChangesItems) Less(i, j int) bool { - return slice[i].ChangeNumber > slice[j].ChangeNumber -} - -func (slice SplitsChangesItems) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] -} - -//---------------------------------------------------- - // SplitChangesCollection represents a collection of SplitChangesItem type SplitChangesCollection struct { collection CollectionWrapper @@ -54,15 +29,14 @@ func NewSplitChangesCollection(db DBWrapper, logger logging.LoggerInterface) *Sp // Update processes a set of feature flag changes items + a changeNumber bump atomically func (c *SplitChangesCollection) Update(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, cn int64) { - - items := make(SplitsChangesItems, 0, len(toAdd)+len(toRemove)) + items := NewChangesItems(len(toAdd) + len(toRemove)) process := func(split *dtos.SplitDTO) { asJSON, err := json.Marshal(split) if err != nil { // This should not happen unless the DTO class is broken return } - items = append(items, SplitChangesItem{ + items.Append(ChangesItem{ ChangeNumber: split.ChangeNumber, Name: split.Name, Status: split.Status, @@ -80,8 +54,8 @@ func (c *SplitChangesCollection) Update(toAdd []dtos.SplitDTO, toRemove []dtos.S c.mutex.Lock() defer c.mutex.Unlock() - for idx := range items { - err := c.collection.SaveAs([]byte(items[idx].Name), items[idx]) + for idx := range items.items { + err := c.collection.SaveAs([]byte(items.items[idx].Name), items.items[idx]) if err != nil { // TODO(mredolatti): log } @@ -102,7 +76,7 @@ func (c *SplitChangesCollection) FetchAll() ([]dtos.SplitDTO, error) { var decodeBuffer bytes.Buffer for _, v := range items { - var q SplitChangesItem + var q ChangesItem // resets buffer data decodeBuffer.Reset() decodeBuffer.Write(v) From 35fdde31c1dcffffbd774874ba6a044f0be97049 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 5 Nov 2025 15:48:35 -0300 Subject: [PATCH 20/36] added snapshot --- splitio/admin/admin.go | 2 +- splitio/admin/controllers/dashboard.go | 1 + splitio/admin/controllers/helpers.go | 19 ++++ splitio/admin/controllers/helpers_test.go | 24 ++++ splitio/admin/controllers/snapshot.go | 11 +- splitio/admin/controllers/snapshot_test.go | 54 +++------ splitio/admin/views/dashboard/main.go | 9 ++ splitio/commitversion.go | 2 +- splitio/common/snapshot/snapshot.go | 17 +-- splitio/proxy/initialization.go | 11 +- .../storage/persistent/rulebasedsegments.go | 105 ++++++++++++++++++ .../persistent/rulebasedsegments_test.go | 60 ++++++++++ splitio/proxy/storage/rulebasedsegments.go | 48 +++++++- 13 files changed, 300 insertions(+), 63 deletions(-) create mode 100644 splitio/admin/controllers/helpers_test.go create mode 100644 splitio/proxy/storage/persistent/rulebasedsegments_test.go diff --git a/splitio/admin/admin.go b/splitio/admin/admin.go index 3d0da679..47c9f87c 100644 --- a/splitio/admin/admin.go +++ b/splitio/admin/admin.go @@ -96,7 +96,7 @@ func NewServer(options *Options) (*AdminServer, error) { observabilityController.Register(admin) if options.Snapshotter != nil { - snapshotController := controllers.NewSnapshotController(options.Logger, options.Snapshotter) + snapshotController := controllers.NewSnapshotController(options.Logger, options.Snapshotter, options.FlagSpecVersion) snapshotController.Register(admin) } diff --git a/splitio/admin/controllers/dashboard.go b/splitio/admin/controllers/dashboard.go index 49a40bbe..7222b06a 100644 --- a/splitio/admin/controllers/dashboard.go +++ b/splitio/admin/controllers/dashboard.go @@ -166,5 +166,6 @@ func (c *DashboardController) gatherStats() *dashboard.GlobalStats { LoggedMessages: errorMessages, Uptime: int64(c.runtime.Uptime().Seconds()), FlagSets: getFlagSetsInfo(c.storages.SplitStorage), + RuleBasedSegments: bundleRBInfo(c.storages.RuleBasedSegmentsStorage), } } diff --git a/splitio/admin/controllers/helpers.go b/splitio/admin/controllers/helpers.go index 4aa784f3..abc2aa36 100644 --- a/splitio/admin/controllers/helpers.go +++ b/splitio/admin/controllers/helpers.go @@ -316,3 +316,22 @@ func getProxyRequestCount(metrics storage.TelemetryRuntimeConsumer) (ok int64, e return okCount, errorCount } + +func bundleRBInfo(rbStorage storage.RuleBasedSegmentsStorage) []dashboard.RBSummary { + all := rbStorage.All() + summaries := make([]dashboard.RBSummary, 0, len(all)) + for _, segment := range all { + excludedSegments := make([]string, 0, len(segment.Excluded.Segments)) + for _, seg := range segment.Excluded.Segments { + excludedSegments = append(excludedSegments, seg.Name) + } + summaries = append(summaries, dashboard.RBSummary{ + Name: segment.Name, + ChangeNumber: segment.ChangeNumber, + Active: segment.Status == "ACTIVE", + ExcludedKeys: segment.Excluded.Keys, + ExcludedSegments: excludedSegments, + }) + } + return summaries +} diff --git a/splitio/admin/controllers/helpers_test.go b/splitio/admin/controllers/helpers_test.go new file mode 100644 index 00000000..a7d90280 --- /dev/null +++ b/splitio/admin/controllers/helpers_test.go @@ -0,0 +1,24 @@ +package controllers + +import ( + "testing" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-split-commons/v8/storage/mocks" + "github.com/splitio/split-synchronizer/v5/splitio/admin/views/dashboard" + "github.com/stretchr/testify/assert" +) + +func TestBundleRBInfo(t *testing.T) { + rb := &mocks.MockRuleBasedSegmentStorage{} + rb.On("All").Return([]dtos.RuleBasedSegmentDTO{ + {Name: "rb1", ChangeNumber: 1, Status: "ACTIVE", Excluded: dtos.ExcludedDTO{Keys: []string{"one"}}}, + {Name: "rb2", ChangeNumber: 2, Status: "ARCHIVED"}, + }, nil) + result := bundleRBInfo(rb) + assert.Len(t, result, 2) + assert.ElementsMatch(t, result, []dashboard.RBSummary{ + {Name: "rb1", ChangeNumber: 1, Active: true, ExcludedKeys: []string{"one"}, ExcludedSegments: []string{}}, + {Name: "rb2", ChangeNumber: 2, Active: false, ExcludedKeys: nil, ExcludedSegments: []string{}}, + }) +} diff --git a/splitio/admin/controllers/snapshot.go b/splitio/admin/controllers/snapshot.go index 2bc933af..657907b3 100644 --- a/splitio/admin/controllers/snapshot.go +++ b/splitio/admin/controllers/snapshot.go @@ -14,13 +14,14 @@ import ( // SnapshotController bundles endpoints associated to snapshot management type SnapshotController struct { - logger logging.LoggerInterface - db storage.Snapshotter + logger logging.LoggerInterface + db storage.Snapshotter + version string } // NewSnapshotController constructs a new snapshot controller -func NewSnapshotController(logger logging.LoggerInterface, db storage.Snapshotter) *SnapshotController { - return &SnapshotController{logger: logger, db: db} +func NewSnapshotController(logger logging.LoggerInterface, db storage.Snapshotter, version string) *SnapshotController { + return &SnapshotController{logger: logger, db: db, version: version} } // Register mounts the endpoints int he provided router @@ -38,7 +39,7 @@ func (c *SnapshotController) downloadSnapshot(ctx *gin.Context) { return } - s, err := snapshot.New(snapshot.Metadata{Version: 1, Storage: snapshot.StorageBoltDB}, b) + s, err := snapshot.New(snapshot.Metadata{Version: 1, Storage: snapshot.StorageBoltDB, SpecVersion: c.version}, b) if err != nil { c.logger.Error("error building snapshot: ", err) ctx.JSON(http.StatusInternalServerError, gin.H{"error": "error building snapshot"}) diff --git a/splitio/admin/controllers/snapshot_test.go b/splitio/admin/controllers/snapshot_test.go index 28d0e69c..77394a7e 100644 --- a/splitio/admin/controllers/snapshot_test.go +++ b/splitio/admin/controllers/snapshot_test.go @@ -2,7 +2,7 @@ package controllers import ( "bytes" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -13,31 +13,23 @@ import ( "github.com/splitio/go-toolkit/v5/logging" "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" ) func TestDownloadProxySnapshot(t *testing.T) { // Read DB snapshot for test path := "../../../test/snapshot/proxy.snapshot" snap, err := snapshot.DecodeFromFile(path) - if err != nil { - t.Error(err) - return - } + assert.Nil(t, err) tmpDataFile, err := snap.WriteDataToTmpFile() - if err != nil { - t.Error(err) - return - } + assert.Nil(t, err) // loading snapshot from disk dbInstance, err := persistent.NewBoltWrapper(tmpDataFile, nil) - if err != nil { - t.Error(err) - return - } + assert.Nil(t, err) - ctrl := NewSnapshotController(logging.NewLogger(nil), dbInstance) + ctrl := NewSnapshotController(logging.NewLogger(nil), dbInstance, "1.3") resp := httptest.NewRecorder() ctx, router := gin.CreateTestContext(resp) @@ -46,35 +38,19 @@ func TestDownloadProxySnapshot(t *testing.T) { ctx.Request, _ = http.NewRequest(http.MethodGet, "/snapshot", nil) router.ServeHTTP(resp, ctx.Request) - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Error(err) - return - } + responseBody, err := io.ReadAll(resp.Body) + assert.Nil(t, err) snapRes, err := snapshot.Decode(responseBody) - if err != nil { - t.Error(err) - return - } + assert.Nil(t, err) - if snapRes.Meta().Version != 1 { - t.Error("Invalid Metadata version") - } - - if snapRes.Meta().Storage != 1 { - t.Error("Invalid Metadata storage") - } + assert.Equal(t, uint64(1), snapRes.Meta().Version) + assert.Equal(t, uint64(1), snapRes.Meta().Storage) + assert.Equal(t, "1.3", snapRes.Meta().SpecVersion) dat, err := snap.Data() - if err != nil { - t.Error(err) - } + assert.Nil(t, err) resData, err := snapRes.Data() - if err != nil { - t.Error(err) - } - if bytes.Compare(dat, resData) != 0 { - t.Error("loaded snapshot is different to downloaded") - } + assert.Nil(t, err) + assert.Equal(t, 0, bytes.Compare(dat, resData)) } diff --git a/splitio/admin/views/dashboard/main.go b/splitio/admin/views/dashboard/main.go index 876fc873..6149a30c 100644 --- a/splitio/admin/views/dashboard/main.go +++ b/splitio/admin/views/dashboard/main.go @@ -108,6 +108,7 @@ type GlobalStats struct { EventsLambda float64 `json:"eventsLambda"` Uptime int64 `json:"uptime"` FlagSets []FlagSetsSummary `json:"flagSets"` + RuleBasedSegments []RBSummary `json:"ruleBasedSegments"` } // SplitSummary encapsulates a minimalistic view of feature flag properties to be presented in the dashboard @@ -151,6 +152,14 @@ type FlagSetsSummary struct { FeatureFlags string `json:"featureFlags"` } +type RBSummary struct { + Name string `json:"name"` + ChangeNumber int64 `json:"cn"` + Active bool `json:"active"` + ExcludedKeys []string `json:"excludedKeys"` + ExcludedSegments []string `json:"excludedSegments"` +} + // RGBA bundles input to CSS's rgba function type RGBA struct { Red int32 diff --git a/splitio/commitversion.go b/splitio/commitversion.go index e0846185..a80119f6 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "1fbc498" +const CommitVersion = "5dafda7" diff --git a/splitio/common/snapshot/snapshot.go b/splitio/common/snapshot/snapshot.go index 71613ca3..20e4e15a 100644 --- a/splitio/common/snapshot/snapshot.go +++ b/splitio/common/snapshot/snapshot.go @@ -7,6 +7,7 @@ import ( "encoding/gob" "errors" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -38,8 +39,9 @@ var ErrMetadataRead = errors.New("snapshot metadata cannot be decoded") // Metadata represents the Snapshot metadata object type Metadata struct { - Version uint64 - Storage uint64 + Version uint64 + Storage uint64 + SpecVersion string } // Snapshot represents a snapshot struct with metadata and data @@ -71,7 +73,7 @@ func (s *Snapshot) Meta() Metadata { func (s *Snapshot) Data() ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(s.data)) defer gz.Close() - data, err := ioutil.ReadAll(gz) + data, err := io.ReadAll(gz) if err != nil { return nil, fmt.Errorf("error reading gzip data: %w", err) } @@ -80,11 +82,12 @@ func (s *Snapshot) Data() ([]byte, error) { // Encode returns the bytes slice snapshot representation // Snapshot Layout: -// |metadata-size|metadata|data| // -// metadata-size: uint64 (8 bytes) specifies the amount of metadata bytes -// metadata: Gob encoded of Metadata struct -// data: Proxy data, byte slice. The Metadata have information about it, Storage, Gzipped and version. +// |metadata-size|metadata|data| +// +// metadata-size: uint64 (8 bytes) specifies the amount of metadata bytes +// metadata: Gob encoded of Metadata struct +// data: Proxy data, byte slice. The Metadata have information about it, Storage, Gzipped and version. func (s *Snapshot) Encode() ([]byte, error) { metaBytes, err := metaToBytes(s.meta) diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index 60a129a4..82bc2b5e 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -84,7 +84,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { // Proxy storages already implement the observable interface, so no need to wrap them splitStorage := storage.NewProxySplitStorage(dbInstance, logger, flagsets.NewFlagSetFilter(cfg.FlagSetsFilter), cfg.Initialization.Snapshot != "") - ruleBasedStorage := storage.NewProxyRuleBasedSegmentsStorage(logger) + ruleBasedStorage := storage.NewProxyRuleBasedSegmentsStorage(dbInstance, logger, cfg.Initialization.Snapshot != "") segmentStorage := storage.NewProxySegmentStorage(dbInstance, logger, cfg.Initialization.Snapshot != "") largeSegmentStorage := inmemory.NewLargeSegmentsStorage() @@ -208,10 +208,11 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { rtm := common.NewRuntime(false, syncManager, logger, "Split Proxy", nil, nil, appMonitor, servicesMonitor) storages := adminCommon.Storages{ - SplitStorage: splitStorage, - SegmentStorage: segmentStorage, - LocalTelemetryStorage: localTelemetryStorage, - LargeSegmentStorage: largeSegmentStorage, + SplitStorage: splitStorage, + SegmentStorage: segmentStorage, + LocalTelemetryStorage: localTelemetryStorage, + LargeSegmentStorage: largeSegmentStorage, + RuleBasedSegmentsStorage: ruleBasedStorage, } // --------------------------- ADMIN DASHBOARD ------------------------------ diff --git a/splitio/proxy/storage/persistent/rulebasedsegments.go b/splitio/proxy/storage/persistent/rulebasedsegments.go index e21960b4..8361a302 100644 --- a/splitio/proxy/storage/persistent/rulebasedsegments.go +++ b/splitio/proxy/storage/persistent/rulebasedsegments.go @@ -1,3 +1,108 @@ package persistent +import ( + "bytes" + "encoding/gob" + "encoding/json" + "sync" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" +) + const ruleBasedSegmentsChangesCollectionName = "RULE_BASED_SEGMENTS_CHANGES_COLLECTION" + +// RBChangesCollection represents a collection of ChangesItem for rule-based segments +type RBChangesCollection struct { + collection CollectionWrapper + changeNumber int64 + mutex sync.RWMutex +} + +// NewRBChangesCollection returns an instance of RBChangesCollection +func NewRBChangesCollection(db DBWrapper, logger logging.LoggerInterface) *RBChangesCollection { + return &RBChangesCollection{ + collection: &BoltDBCollectionWrapper{db: db, name: ruleBasedSegmentsChangesCollectionName, logger: logger}, + changeNumber: 0, + } +} + +// Update processes a set of rule based changes items + a changeNumber bump atomically +func (c *RBChangesCollection) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, cn int64) { + items := NewChangesItems(len(toAdd) + len(toRemove)) + process := func(rb *dtos.RuleBasedSegmentDTO) { + asJSON, err := json.Marshal(rb) + if err != nil { + // This should not happen unless the DTO class is broken + return + } + items.Append(ChangesItem{ + ChangeNumber: rb.ChangeNumber, + Name: rb.Name, + Status: rb.Status, + JSON: string(asJSON), + }) + } + + for _, rb := range toAdd { + process(&rb) + } + + for _, rb := range toRemove { + process(&rb) + } + + c.mutex.Lock() + defer c.mutex.Unlock() + for idx := range items.items { + err := c.collection.SaveAs([]byte(items.items[idx].Name), items.items[idx]) + if err != nil { + // TODO(mredolatti): log + } + } + c.changeNumber = cn +} + +// FetchAll return a ChangesItem +func (c *RBChangesCollection) FetchAll() ([]dtos.RuleBasedSegmentDTO, error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + items, err := c.collection.FetchAll() + if err != nil { + return nil, err + } + + toReturn := make([]dtos.RuleBasedSegmentDTO, 0) + + var decodeBuffer bytes.Buffer + for _, v := range items { + var q ChangesItem + // resets buffer data + decodeBuffer.Reset() + decodeBuffer.Write(v) + dec := gob.NewDecoder(&decodeBuffer) + + errq := dec.Decode(&q) + if errq != nil { + c.collection.Logger().Error("decode error:", errq, "|", string(v)) + continue + } + + var parsed dtos.RuleBasedSegmentDTO + err := json.Unmarshal([]byte(q.JSON), &parsed) + if err != nil { + c.collection.Logger().Error("error decoding feature flag fetched from db: ", err, "|", q.JSON) + continue + } + toReturn = append(toReturn, parsed) + } + + return toReturn, nil +} + +// ChangeNumber returns changeNumber +func (c *RBChangesCollection) ChangeNumber() int64 { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.changeNumber +} diff --git a/splitio/proxy/storage/persistent/rulebasedsegments_test.go b/splitio/proxy/storage/persistent/rulebasedsegments_test.go new file mode 100644 index 00000000..512eba75 --- /dev/null +++ b/splitio/proxy/storage/persistent/rulebasedsegments_test.go @@ -0,0 +1,60 @@ +package persistent + +import ( + "testing" + + "github.com/splitio/go-split-commons/v8/dtos" + "github.com/splitio/go-toolkit/v5/logging" +) + +func TestRBChangesCollection(t *testing.T) { + dbw, err := NewBoltWrapper(BoltInMemoryMode, nil) + if err != nil { + t.Error("error creating bolt wrapper: ", err) + } + + logger := logging.NewLogger(nil) + rbChangesCollection := NewRBChangesCollection(dbw, logger) + + rbChangesCollection.Update([]dtos.RuleBasedSegmentDTO{ + {Name: "rb1", ChangeNumber: 1, Status: "ACTIVE"}, + {Name: "rb2", ChangeNumber: 1, Status: "ACTIVE"}, + }, nil, 1) + + all, err := rbChangesCollection.FetchAll() + if err != nil { + t.Error("FetchAll should not return an error. Got: ", err) + } + + if len(all) != 2 { + t.Error("invalid number of items fetched.") + return + } + + if all[0].Name != "rb1" || all[1].Name != "rb2" { + t.Error("Invalid payload in fetched changes.") + } + + if rbChangesCollection.ChangeNumber() != 1 { + t.Error("CN should be 1.") + } + + rbChangesCollection.Update([]dtos.RuleBasedSegmentDTO{{Name: "rb1", ChangeNumber: 2, Status: "ARCHIVED"}}, nil, 2) + all, err = rbChangesCollection.FetchAll() + if err != nil { + t.Error("FetchAll should not return an error. Got: ", err) + } + + if len(all) != 2 { + t.Error("invalid number of items fetched.") + return + } + + if all[0].Name != "rb1" || all[0].Status != "ARCHIVED" { + t.Error("rb1 should be archived.") + } + + if rbChangesCollection.ChangeNumber() != 2 { + t.Error("CN should be 2.") + } +} diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index e28d3333..db62f3ee 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -4,13 +4,15 @@ import ( "fmt" "sync" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" + "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent" + "github.com/splitio/go-split-commons/v8/dtos" "github.com/splitio/go-split-commons/v8/engine/grammar/constants" "github.com/splitio/go-split-commons/v8/storage" "github.com/splitio/go-split-commons/v8/storage/inmemory/mutexmap" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" - "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/optimized" ) // ProxyRuleBasedSegmentsStorage defines the interface of a storage that can be used for serving payloads @@ -22,6 +24,7 @@ type ProxyRuleBasedSegmentsStorage interface { // ProxyRuleBasedSegmentsStorageImpl implements the ProxyRuleBasedSegmentsStorage interface and the RuleBasedSegmentProducer interface type ProxyRuleBasedSegmentsStorageImpl struct { snapshot mutexmap.RuleBasedSegmentsStorageImpl + db *persistent.RBChangesCollection logger logging.LoggerInterface oldestKnownCN int64 mtx sync.Mutex @@ -30,16 +33,21 @@ type ProxyRuleBasedSegmentsStorageImpl struct { // NewProxyRuleBasedSegmentsStorage instantiates a new proxy storage that wraps an in-memory snapshot of the last known // flag configuration -func NewProxyRuleBasedSegmentsStorage(logger logging.LoggerInterface) *ProxyRuleBasedSegmentsStorageImpl { +func NewProxyRuleBasedSegmentsStorage(db persistent.DBWrapper, logger logging.LoggerInterface, restoreBackup bool) *ProxyRuleBasedSegmentsStorageImpl { + disk := persistent.NewRBChangesCollection(db, logger) snapshot := mutexmap.NewRuleBasedSegmentsStorage() historic := optimized.NewHistoricRBChanges(1000) - var initialCN int64 = -1 + var initialCN int64 = -1 + if restoreBackup { + initialCN = snapshotFromDiskRB(snapshot, historic, disk, logger) + } return &ProxyRuleBasedSegmentsStorageImpl{ snapshot: *snapshot, + db: disk, + historic: historic, logger: logger, oldestKnownCN: initialCN, - historic: historic, } } @@ -53,6 +61,36 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) sinceIsTooOld(since int64) bool { return since < p.oldestKnownCN } +func snapshotFromDiskRB( + dst *mutexmap.RuleBasedSegmentsStorageImpl, + historic optimized.HistoricChangesRB, + src *persistent.RBChangesCollection, + logger logging.LoggerInterface, +) int64 { + all, err := src.FetchAll() + if err != nil { + logger.Error("error parsing feature flags from snapshot. No data will be available!: ", err) + return -1 + } + + var filtered []dtos.RuleBasedSegmentDTO + var cn = src.ChangeNumber() + for idx := range all { + + // Make sure the CN matches is at least large as the payloads' max. + if thisCN := all[idx].ChangeNumber; thisCN > cn { + cn = thisCN + } + if all[idx].Status == constants.SplitStatusActive { + filtered = append(filtered, all[idx]) + } + } + + dst.Update(filtered, nil, cn) + historic.Update(filtered, nil, cn) + return cn +} + func archivedRBDTOForView(view *optimized.RBView) dtos.RuleBasedSegmentDTO { return dtos.RuleBasedSegmentDTO{ ChangeNumber: view.LastUpdated, @@ -168,7 +206,7 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) Update(toAdd []dtos.RuleBasedSegment p.mtx.Lock() p.snapshot.Update(toAdd, toRemove, changeNumber) p.historic.Update(toAdd, toRemove, changeNumber) - // p.db.Update(toAdd, toRemove, changeNumber) + p.db.Update(toAdd, toRemove, changeNumber) p.mtx.Unlock() return nil } From 4f19e348a5179456515bf7537ba40296a4b1b465 Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Wed, 5 Nov 2025 15:56:43 -0300 Subject: [PATCH 21/36] added flag validation into snapshot --- splitio/commitversion.go | 2 +- splitio/proxy/initialization.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/splitio/commitversion.go b/splitio/commitversion.go index a80119f6..d1705cd1 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "5dafda7" +const CommitVersion = "35fdde3" diff --git a/splitio/proxy/initialization.go b/splitio/proxy/initialization.go index 82bc2b5e..f4628bd2 100644 --- a/splitio/proxy/initialization.go +++ b/splitio/proxy/initialization.go @@ -57,6 +57,10 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error { return fmt.Errorf("error writing temporary snapshot file: %w", err) } + if snap.Meta().SpecVersion != cfg.FlagSpecVersion { + return common.NewInitError(fmt.Errorf("snapshot spec version %s does not match config spec version %s", snap.Meta().SpecVersion, cfg.FlagSpecVersion), common.ExitErrorDB) + } + logger.Debug("Database created from snapshot at", dbpath) } From 8fd26d7495470da60d73f35503540af51f9e4118 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Wed, 5 Nov 2025 16:22:44 -0300 Subject: [PATCH 22/36] Remove comment lines --- splitio/proxy/storage/rulebasedsegments.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/splitio/proxy/storage/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go index e28d3333..cc8ef03f 100644 --- a/splitio/proxy/storage/rulebasedsegments.go +++ b/splitio/proxy/storage/rulebasedsegments.go @@ -103,12 +103,6 @@ func (p *ProxyRuleBasedSegmentsStorageImpl) ChangesSince(since int64) (*dtos.Rul all = append(all, *rbSegments) } return &dtos.RuleBasedSegmentsDTO{Since: since, Till: till, RuleBasedSegments: all}, nil - - // if since > -1 { - // return &dtos.RuleBasedSegmentsDTO{Since: since, Till: since, RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}}, nil - // } - // cn, _ := p.snapshot.ChangeNumber() - // return &dtos.RuleBasedSegmentsDTO{Since: since, Till: cn, RuleBasedSegments: p.snapshot.All()}, nil } // All call is forwarded to the snapshot From adebfaf9683ba5087cca7849dd277080b257c42f Mon Sep 17 00:00:00 2001 From: Matias Melograno Date: Thu, 6 Nov 2025 11:55:42 -0300 Subject: [PATCH 23/36] added spec header and proxy header --- splitio/commitversion.go | 2 +- splitio/proxy/controllers/sdk_test.go | 2 +- splitio/proxy/proxy.go | 6 ++++++ splitio/proxy/proxy_test.go | 10 +++++++++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/splitio/commitversion.go b/splitio/commitversion.go index d1705cd1..0cd35ce1 100644 --- a/splitio/commitversion.go +++ b/splitio/commitversion.go @@ -5,4 +5,4 @@ This file is created automatically, please do not edit */ // CommitVersion is the version of the last commit previous to release -const CommitVersion = "35fdde3" +const CommitVersion = "4f19e34" diff --git a/splitio/proxy/controllers/sdk_test.go b/splitio/proxy/controllers/sdk_test.go index 78284069..1f454602 100644 --- a/splitio/proxy/controllers/sdk_test.go +++ b/splitio/proxy/controllers/sdk_test.go @@ -53,7 +53,7 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) { &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, - specs.FLAG_V1_2, + specs.FLAG_V1_3, ) controller.Register(group) diff --git a/splitio/proxy/proxy.go b/splitio/proxy/proxy.go index 8a76e94c..0bb98012 100644 --- a/splitio/proxy/proxy.go +++ b/splitio/proxy/proxy.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + "github.com/splitio/split-synchronizer/v5/splitio" "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener" "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers" "github.com/splitio/split-synchronizer/v5/splitio/proxy/controllers/middleware" @@ -145,6 +146,11 @@ func New(options *Options) *API { cacheableRouter.Use(options.Cache.Handle) cacheableRouter.Use(gzip.Gzip(gzip.DefaultCompression)) } + cacheableRouter.Use(func(c *gin.Context) { + c.Header("Harness-FME-Proxy-Version", splitio.Version) + c.Header("Harness-FME-Proxy-FlagSpec", options.SpecVersion) + c.Next() + }) authController.Register(cacheableRouter) sdkController.Register(cacheableRouter) eventsController.Register(regular, beacon) diff --git a/splitio/proxy/proxy_test.go b/splitio/proxy/proxy_test.go index a2d63f97..d14aa508 100644 --- a/splitio/proxy/proxy_test.go +++ b/splitio/proxy/proxy_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/splitio/split-synchronizer/v5/splitio" ilmock "github.com/splitio/split-synchronizer/v5/splitio/common/impressionlistener/mocks" "github.com/splitio/split-synchronizer/v5/splitio/proxy/caching" "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage" @@ -79,7 +80,8 @@ func TestSplitChangesEndpoints(t *testing.T) { assert.Equal(t, "split2", changes.Splits[0].Name) assert.False(t, changes.Splits[0].ImpressionsDisabled) assert.Equal(t, "application/json; charset=utf-8", headers.Get("Content-Type")) - + assert.Equal(t, opts.SpecVersion, headers.Get("Harness-FME-Proxy-FlagSpec")) + assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version")) } func TestSplitChangesWithFlagsetsCaching(t *testing.T) { @@ -160,6 +162,8 @@ func TestSplitChangesWithFlagsetsCaching(t *testing.T) { assert.Equal(t, int64(1), changes.Till) assert.Equal(t, "split1", changes.Splits[0].Name) assert.Equal(t, "application/json; charset=utf-8", headers.Get("Content-Type")) + assert.Equal(t, opts.SpecVersion, headers.Get("Harness-FME-Proxy-FlagSpec")) + assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version")) } func TestSegmentChangesAndMySegmentsEndpoints(t *testing.T) { @@ -249,6 +253,8 @@ func TestSegmentChangesAndMySegmentsEndpoints(t *testing.T) { assert.Equal(t, 200, status) assert.Equal(t, []dtos.MySegmentDTO{}, segments) assert.Equal(t, "application/json; charset=utf-8", headers.Get("Content-Type")) + assert.Equal(t, opts.SpecVersion, headers.Get("Harness-FME-Proxy-FlagSpec")) + assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version")) } func TestMembershipEndpoint(t *testing.T) { @@ -284,6 +290,8 @@ func TestMembershipEndpoint(t *testing.T) { assert.Equal(t, 200, status) assert.Equal(t, expected, response) assert.Equal(t, "application/json; charset=utf-8", headers.Get("Content-Type")) + assert.Equal(t, opts.SpecVersion, headers.Get("Harness-FME-Proxy-FlagSpec")) + assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version")) } func makeOpts() *Options { From 88290d5bf4302bcc33d0d2b990e390fc7d3e7c22 Mon Sep 17 00:00:00 2001 From: Nadia Mayor Date: Thu, 6 Nov 2025 13:02:15 -0300 Subject: [PATCH 24/36] Added rule-based segments in the dashboard --- splitio/admin/controllers/dashboard.go | 1 + splitio/admin/controllers/helpers.go | 39 +++++++++++++ .../admin/views/dashboard/datainspector.go | 57 +++++++++++++++++++ splitio/admin/views/dashboard/js.go | 54 ++++++++++++++++++ splitio/admin/views/dashboard/main.go | 53 ++++++++++------- splitio/commitversion.go | 2 +- splitio/proxy/initialization.go | 9 +-- 7 files changed, 191 insertions(+), 24 deletions(-) diff --git a/splitio/admin/controllers/dashboard.go b/splitio/admin/controllers/dashboard.go index 49a40bbe..a51ee444 100644 --- a/splitio/admin/controllers/dashboard.go +++ b/splitio/admin/controllers/dashboard.go @@ -150,6 +150,7 @@ func (c *DashboardController) gatherStats() *dashboard.GlobalStats { FeatureFlags: bundleSplitInfo(c.storages.SplitStorage), Segments: bundleSegmentInfo(c.storages.SplitStorage, c.storages.SegmentStorage), LargeSegments: bundleLargeSegmentInfo(c.storages.SplitStorage, c.storages.LargeSegmentStorage), + RuleBasedSegments: bundleRuleBasedInfo(c.storages.SplitStorage, c.storages.RuleBasedSegmentsStorage), Latencies: bundleProxyLatencies(c.storages.LocalTelemetryStorage), BackendLatencies: bundleLocalSyncLatencies(c.storages.LocalTelemetryStorage), ImpressionsQueueSize: getImpressionSize(c.storages.ImpressionStorage), diff --git a/splitio/admin/controllers/helpers.go b/splitio/admin/controllers/helpers.go index 4aa784f3..b2288b28 100644 --- a/splitio/admin/controllers/helpers.go +++ b/splitio/admin/controllers/helpers.go @@ -108,6 +108,45 @@ func bundleSegmentInfo(splitStorage storage.SplitStorage, segmentStorage storage return summaries } +func bundleRuleBasedInfo(splitStorage storage.SplitStorage, ruleBasedSegmentStorage storage.RuleBasedSegmentStorageConsumer) []dashboard.RuleBasedSegmentSummary { + + names := splitStorage.RuleBasedSegmentNames() + summaries := make([]dashboard.RuleBasedSegmentSummary, 0, names.Size()) + + for _, name := range names.List() { + strName, ok := name.(string) + if !ok { + continue + } + ruleBased, err := ruleBasedSegmentStorage.GetRuleBasedSegmentByName(strName) + + if err != nil { + continue + } + excluededSegments := make([]dashboard.ExcluededSegments, 0, len(ruleBased.Excluded.Segments)) + for _, excludedSegment := range ruleBased.Excluded.Segments { + excluededSegments = append(excluededSegments, dashboard.ExcluededSegments{ + Name: excludedSegment.Name, + Type: excludedSegment.Type, + }) + } + + if ruleBased.Excluded.Keys == nil { + ruleBased.Excluded.Keys = make([]string, 0) + } + + summaries = append(summaries, dashboard.RuleBasedSegmentSummary{ + Name: ruleBased.Name, + Active: ruleBased.Status == "ACTIVE", + ExcludedKeys: ruleBased.Excluded.Keys, + ExcluededSegments: excluededSegments, + LastModified: time.Unix(0, ruleBased.ChangeNumber*int64(time.Millisecond)).UTC().Format(time.UnixDate), + ChangeNumber: ruleBased.ChangeNumber, + }) + } + return summaries +} + func bundleSegmentKeysInfo(name string, segmentStorage storage.SegmentStorageConsumer) []dashboard.SegmentKeySummary { keys := segmentStorage.Keys(name) diff --git a/splitio/admin/views/dashboard/datainspector.go b/splitio/admin/views/dashboard/datainspector.go index f4f63577..3af58c63 100644 --- a/splitio/admin/views/dashboard/datainspector.go +++ b/splitio/admin/views/dashboard/datainspector.go @@ -38,6 +38,19 @@ const dataInspector = ` {{end}} +
  • + + +  Rule-based Segments + +