Cached Segments
@@ -94,8 +106,8 @@ const cards = `
-
Cached Flag Sets
-
+ Cached Rule-based Segments
+
{{end}}
diff --git a/splitio/commitversion.go b/splitio/commitversion.go
index d8b6bd07..f7990321 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 = "829fcf8"
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/snapshot/snapshot.go b/splitio/common/snapshot/snapshot.go
index 71613ca3..235bfb52 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"
@@ -40,6 +41,7 @@ var ErrMetadataRead = errors.New("snapshot metadata cannot be decoded")
type Metadata struct {
Version uint64
Storage uint64
+ Hash 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/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..649635f6 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
@@ -17,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 77cb05f0..d1803733 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,23 @@ 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/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 (
@@ -102,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
@@ -124,9 +126,20 @@ 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)
+
+ isProxy := splitAPI.SplitFetcher.IsProxy()
+
workers := synchronizer.Workers{
- SplitUpdater: split.NewSplitUpdater(storages.SplitStorage, splitAPI.SplitFetcher, logger, syncTelemetryStorage, appMonitor, flagSetsFilter),
- SegmentUpdater: segment.NewSegmentUpdater(storages.SplitStorage, storages.SegmentStorage, splitAPI.SegmentFetcher,
+ 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,
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..c7614475 100644
--- a/splitio/proxy/caching/workers.go
+++ b/splitio/proxy/caching/workers.go
@@ -1,22 +1,23 @@
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/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
type CacheAwareSplitSynchronizer struct {
splitStorage storage.SplitStorage
+ rbStorage storage.RuleBasedSegmentsStorage
wrapped split.Updater
cacheFlusher gincache.CacheFlusher
}
@@ -24,16 +25,20 @@ 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,
cacheFlusher gincache.CacheFlusher,
appMonitor application.MonitorProducerInterface,
flagSetsFilter flagsets.FlagSetFilter,
+ specVersion string,
+ ruleBuilder grammar.RuleBuilder,
) *CacheAwareSplitSynchronizer {
return &CacheAwareSplitSynchronizer{
- wrapped: split.NewSplitUpdater(splitStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter),
+ wrapped: split.NewSplitUpdater(splitStorage, ruleBasedStorage, splitFetcher, logger, runtimeTelemetry, appMonitor, flagSetsFilter, ruleBuilder, false, specVersion),
splitStorage: splitStorage,
+ rbStorage: ruleBasedStorage,
cacheFlusher: cacheFlusher,
}
}
@@ -41,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)
}
@@ -59,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)
}
@@ -79,6 +92,7 @@ type CacheAwareSegmentSynchronizer struct {
func NewCacheAwareSegmentSync(
splitStorage storage.SplitStorage,
segmentStorage storage.SegmentStorage,
+ ruleBasedStorage storage.RuleBasedSegmentsStorage,
segmentFetcher service.SegmentFetcher,
logger logging.LoggerInterface,
runtimeTelemetry storage.TelemetryRuntimeProducer,
@@ -86,7 +100,7 @@ func NewCacheAwareSegmentSync(
appMonitor application.MonitorProducerInterface,
) *CacheAwareSegmentSynchronizer {
return &CacheAwareSegmentSynchronizer{
- wrapped: segment.NewSegmentUpdater(splitStorage, segmentStorage, 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/caching/workers_test.go b/splitio/proxy/caching/workers_test.go
index 57cda98b..e3893e3f 100644
--- a/splitio/proxy/caching/workers_test.go
+++ b/splitio/proxy/caching/workers_test.go
@@ -3,11 +3,14 @@ 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"
+ 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"
+
"github.com/stretchr/testify/assert"
)
@@ -17,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,
}
@@ -37,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)
@@ -47,6 +56,7 @@ func TestCacheAwareSplitSyncChanges(t *testing.T) {
css := CacheAwareSplitSynchronizer{
splitStorage: &storageMock,
wrapped: &splitSyncMock,
+ rbStorage: &rbsStorage,
cacheFlusher: &cacheFlusherMock,
}
@@ -76,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)
@@ -85,6 +98,7 @@ func TestCacheAwareSplitSyncChangesNewMethod(t *testing.T) {
css := CacheAwareSplitSynchronizer{
splitStorage: &storageMock,
+ rbStorage: &rbsStorage,
wrapped: &splitSyncMock,
cacheFlusher: &cacheFlusherMock,
}
diff --git a/splitio/proxy/conf/sections.go b/splitio/proxy/conf/sections.go
index 5ffb8b8d..1e131785 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
@@ -21,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/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..ae3f07dd 100644
--- a/splitio/proxy/controllers/sdk.go
+++ b/splitio/proxy/controllers/sdk.go
@@ -7,29 +7,33 @@ 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/common"
+ "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
type SdkServerController struct {
- logger logging.LoggerInterface
- fetcher service.SplitFetcher
- proxySplitStorage storage.ProxySplitStorage
- proxySegmentStorage storage.ProxySegmentStorage
- fsmatcher flagsets.FlagSetMatcher
- versionFilter specs.SplitVersionFilter
- largeSegmentStorage cmnStorage.LargeSegmentsStorage
+ 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
@@ -38,18 +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,
+ logger: logger,
+ fetcher: fetcher,
+ proxySplitStorage: proxySplitStorage,
+ proxySegmentStorage: proxySegmentStorage,
+ proxyRBSegmentStorage: proxyRBSegmentStorage,
+ fsmatcher: fsmatcher,
+ versionFilter: specs.NewSplitVersionFilter(),
+ largeSegmentStorage: largeSegmentStorage,
+ specVersion: specVersion,
}
}
@@ -103,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, ",")
@@ -112,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()})
@@ -129,9 +141,19 @@ 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 {
+ ctx.JSON(http.StatusOK, rules)
+ ctx.Set(caching.SurrogateContextKey, []string{caching.SplitSurrogate})
+ ctx.Set(caching.StickyContextKey, true)
+ return
+ }
+ 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)
}
@@ -183,19 +205,46 @@ 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) {
splits, err := c.proxySplitStorage.ChangesSince(since, sets)
- if err == nil {
- return splits, nil
+ rbs, rbsErr := c.proxyRBSegmentStorage.ChangesSince(rbsince)
+ 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().WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted
- return c.fetcher.Fetch(fetchOptions)
+ 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.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
}
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..1f454602 100644
--- a/splitio/proxy/controllers/sdk_test.go
+++ b/splitio/proxy/controllers/sdk_test.go
@@ -8,21 +8,24 @@ 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/common"
+ "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) {
@@ -32,8 +35,10 @@ 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()
- var splitFetcher splitFetcherMock
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -42,11 +47,13 @@ func TestSplitChangesImpressionsDisabled(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_3,
)
controller.Register(group)
@@ -83,7 +90,10 @@ 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
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -92,11 +102,13 @@ func TestSplitChangesRecentSince(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -130,11 +142,16 @@ func TestSplitChangesOlderSince(t *testing.T) {
splitStorage.On("ChangesSince", int64(-1), []string(nil)).
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()
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
+ 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"}},
+ },
+ }, nil).Once()
var largeSegmentStorageMock largeSegmentStorageMock
@@ -146,11 +163,13 @@ func TestSplitChangesOlderSince(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -185,9 +204,12 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) {
Return((*dtos.SplitChangesDTO)(nil), storage.ErrSinceParamTooOld).
Once()
- var splitFetcher splitFetcherMock
- splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1))).
- Return((*dtos.SplitChangesDTO)(nil), errors.New("something")).
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
+ splitFetcher.On("Fetch", ref(*service.MakeFlagRequestParams().WithChangeNumber(-1).WithChangeNumberRB(-1).WithSpecVersion(common.StringRef(specs.FLAG_V1_2)))).
+ Return(nil, errors.New("something")).
Once()
var largeSegmentStorageMock largeSegmentStorageMock
@@ -200,11 +222,13 @@ func TestSplitChangesOlderSinceFetchFails(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -229,7 +253,10 @@ 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
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -240,11 +267,13 @@ func TestSplitChangesWithFlagSets(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -278,7 +307,10 @@ 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
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -289,11 +321,13 @@ func TestSplitChangesWithFlagSetsStrict(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(true, []string{"a", "c"}),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -333,12 +367,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 +381,10 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) {
}, nil).
Once()
- var splitFetcher splitFetcherMock
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
+
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -356,11 +393,13 @@ func TestSplitChangesNewMatcherOldSpec(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -386,7 +425,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 +447,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",
},
@@ -416,8 +455,10 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) {
},
}, nil).
Once()
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
- var splitFetcher splitFetcherMock
+ splitFetcher := &mocks.MockSplitFetcher{}
var largeSegmentStorageMock largeSegmentStorageMock
resp := httptest.NewRecorder()
@@ -426,11 +467,13 @@ func TestSplitChangesNewMatcherNewSpec(t *testing.T) {
group := router.Group("/api")
controller := NewSdkServerController(
logger,
- &splitFetcher,
+ splitFetcher,
&splitStorage,
nil,
+ &rbsStorage,
flagsets.NewMatcher(false, nil),
&largeSegmentStorageMock,
+ specs.FLAG_V1_2,
)
controller.Register(group)
@@ -457,7 +500,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,13 +511,16 @@ 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)).
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()
@@ -483,7 +529,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, &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)
@@ -512,12 +558,14 @@ 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)).
Return((*dtos.SegmentChangesDTO)(nil), storage.ErrSegmentNotFound).
Once()
+ var rbsStorage psmocks.MockProxyRuleBasedSegmentStorage
+ rbsStorage.On("ChangesSince", int64(-1)).Return(&dtos.RuleBasedSegmentsDTO{}).Once()
var largeSegmentStorageMock largeSegmentStorageMock
@@ -527,7 +575,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, &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)
@@ -546,13 +594,16 @@ 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").
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()
@@ -561,7 +612,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, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2)
controller.Register(group)
ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil)
@@ -589,13 +640,16 @@ 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").
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()
@@ -604,7 +658,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, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2)
controller.Register(group)
ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/mySegments/someKey", nil)
@@ -623,12 +677,14 @@ 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").
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").
@@ -641,7 +697,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, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2)
controller.Register(group)
ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil)
@@ -677,13 +733,15 @@ 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
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)
@@ -691,7 +749,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, &rbsStorage, flagsets.NewMatcher(false, nil), &largeSegmentStorageMock, specs.FLAG_V1_2)
controller.Register(group)
ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/memberships/keyTest", nil)
@@ -707,16 +765,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 +803,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..4fbd890e 100644
--- a/splitio/proxy/initialization.go
+++ b/splitio/proxy/initialization.go
@@ -5,19 +5,9 @@ import (
"fmt"
"log"
"net/url"
- "time"
-
+ "strconv"
"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 +25,17 @@ 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/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"
+ "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
@@ -57,6 +58,11 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error {
return fmt.Errorf("error writing temporary snapshot file: %w", err)
}
+ currentHash := util.HashAPIKey(cfg.Apikey + cfg.FlagSpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))
+ if snap.Meta().Hash != strconv.Itoa(int(currentHash)) {
+ return common.NewInitError(errors.New("snapshot cfg (apikey, version, flagsets) does not match the provided one"), common.ExitErrorDB)
+ }
+
logger.Debug("Database created from snapshot at", dbpath)
}
@@ -84,6 +90,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(dbInstance, logger, cfg.Initialization.Snapshot != "")
segmentStorage := storage.NewProxySegmentStorage(dbInstance, logger, cfg.Initialization.Snapshot != "")
largeSegmentStorage := inmemory.NewLargeSegmentsStorage()
@@ -118,10 +125,19 @@ 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, splitAPI.SplitFetcher, logger, localTelemetryStorage, httpCache, appMonitor, flagSetsFilter),
- SegmentUpdater: caching.NewCacheAwareSegmentSync(splitStorage, segmentStorage, splitAPI.SegmentFetcher, logger, localTelemetryStorage, httpCache,
+ 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,
metadata, localTelemetryStorage),
@@ -166,7 +182,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()
@@ -198,14 +214,16 @@ 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 ------------------------------
cfgForAdmin := *cfg
+ hash := util.HashAPIKey(cfgForAdmin.Apikey + cfg.FlagSpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))
cfgForAdmin.Apikey = logging.ObfuscateAPIKey(cfgForAdmin.Apikey)
adminTLSConfig, err := util.TLSConfigForServer(&cfg.Admin.TLS)
@@ -229,6 +247,7 @@ func Start(logger logging.LoggerInterface, cfg *pconf.Main) error {
FullConfig: cfgForAdmin,
TLS: adminTLSConfig,
FlagSpecVersion: cfg.FlagSpecVersion,
+ Hash: strconv.Itoa(int(hash)),
})
if err != nil {
return common.NewInitError(fmt.Errorf("error starting admin server: %w", err), common.ExitAdminError)
@@ -250,6 +269,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,
@@ -263,6 +283,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 != "" {
@@ -287,8 +308,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 cfe0c002..4a6d4d7e 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) StartBGSync(mstatus chan int, shouldRetry bool, onReady func()) error {
+ panic("unimplemented")
+}
+
var _ synchronizer.Manager = (*syncManagerMock)(nil)
func TestSyncManagerInitializationRetriesWithSnapshot(t *testing.T) {
@@ -33,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)
}
@@ -47,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/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..da56dc9a 100644
--- a/splitio/proxy/proxy.go
+++ b/splitio/proxy/proxy.go
@@ -5,10 +5,7 @@ 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"
"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 +13,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.
@@ -51,6 +52,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
@@ -87,6 +91,8 @@ type Options struct {
FlagSets []string
FlagSetsStrictMatching bool
+
+ SpecVersion string
}
// API bundles all components required to answer API calls from Split sdks
@@ -140,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-FlagSpec", options.SpecVersion)
+ c.Next()
+ })
authController.Register(cacheableRouter)
sdkController.Register(cacheableRouter)
eventsController.Register(regular, beacon)
@@ -163,8 +174,10 @@ 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/proxy_test.go b/splitio/proxy/proxy_test.go
index 9296fdaa..bb39a674 100644
--- a/splitio/proxy/proxy_test.go
+++ b/splitio/proxy/proxy_test.go
@@ -3,20 +3,23 @@ package proxy
import (
"encoding/json"
"fmt"
- "io/ioutil"
+ "io"
"math/rand"
"net/http"
"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"
+ "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"
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"
)
@@ -24,6 +27,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
@@ -35,6 +40,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"})
@@ -63,6 +69,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"})
@@ -72,13 +80,16 @@ 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-FlagSpec"))
+ assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version"))
}
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
@@ -86,6 +97,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"})
@@ -111,6 +123,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)
@@ -127,10 +140,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)
@@ -147,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-FlagSpec"))
+ assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version"))
}
func TestSegmentChangesAndMySegmentsEndpoints(t *testing.T) {
@@ -236,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-FlagSpec"))
+ assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version"))
}
func TestMembershipEndpoint(t *testing.T) {
@@ -271,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-FlagSpec"))
+ assert.Equal(t, splitio.Version, headers.Get("Harness-FME-Proxy-Version"))
}
func makeOpts() *Options {
@@ -310,7 +331,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())
}
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/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)
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/optimized/rbshistoric.go b/splitio/proxy/storage/optimized/rbshistoric.go
new file mode 100644
index 00000000..8ac0650d
--- /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 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
+ }
+ 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..e1cc289a
--- /dev/null
+++ b/splitio/proxy/storage/optimized/rbshistoric_test.go
@@ -0,0 +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: "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/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..8361a302
--- /dev/null
+++ b/splitio/proxy/storage/persistent/rulebasedsegments.go
@@ -0,0 +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/persistent/splits.go b/splitio/proxy/storage/persistent/splits.go
index 093d5e28..73cad538 100644
--- a/splitio/proxy/storage/persistent/splits.go
+++ b/splitio/proxy/storage/persistent/splits.go
@@ -6,37 +6,12 @@ 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"
)
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)
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/rulebasedsegments.go b/splitio/proxy/storage/rulebasedsegments.go
new file mode 100644
index 00000000..4f71d6f1
--- /dev/null
+++ b/splitio/proxy/storage/rulebasedsegments.go
@@ -0,0 +1,224 @@
+package storage
+
+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"
+)
+
+// 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 RuleBasedSegmentProducer interface
+type ProxyRuleBasedSegmentsStorageImpl struct {
+ snapshot mutexmap.RuleBasedSegmentsStorageImpl
+ db *persistent.RBChangesCollection
+ 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(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
+ if restoreBackup {
+ initialCN = snapshotFromDiskRB(snapshot, historic, disk, logger)
+ }
+ return &ProxyRuleBasedSegmentsStorageImpl{
+ snapshot: *snapshot,
+ db: disk,
+ historic: historic,
+ logger: logger,
+ oldestKnownCN: initialCN,
+ }
+}
+
+func (p *ProxyRuleBasedSegmentsStorageImpl) sinceIsTooOld(since int64) bool {
+ if since == -1 {
+ return false
+ }
+
+ p.mtx.Lock()
+ defer p.mtx.Unlock()
+ 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,
+ Name: view.Name,
+ Status: constants.SplitStatusArchived,
+ }
+}
+
+// ChangesSince retrieves the rule-based segments changes since the given change number
+func (p *ProxyRuleBasedSegmentsStorageImpl) ChangesSince(since int64) (*dtos.RuleBasedSegmentsDTO, error) {
+ // 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)
+ }
+ return &dtos.RuleBasedSegmentsDTO{Since: since, Till: till, RuleBasedSegments: 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, error) {
+ 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, changeNumber int64) error {
+ // 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 nil
+ }
+
+ p.mtx.Lock()
+ p.snapshot.Update(toAdd, toRemove, changeNumber)
+ p.historic.Update(toAdd, toRemove, changeNumber)
+ p.db.Update(toAdd, toRemove, changeNumber)
+ 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()
+}
+
+// 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)
diff --git a/splitio/proxy/storage/rulebasedsegments_test.go b/splitio/proxy/storage/rulebasedsegments_test.go
new file mode 100644
index 00000000..bccfc534
--- /dev/null
+++ b/splitio/proxy/storage/rulebasedsegments_test.go
@@ -0,0 +1,153 @@
+package storage
+
+import (
+ "testing"
+
+ "github.com/splitio/split-synchronizer/v5/splitio/proxy/storage/persistent"
+
+ "github.com/splitio/go-split-commons/v8/dtos"
+ "github.com/splitio/go-toolkit/v5/datastructures/set"
+ "github.com/splitio/go-toolkit/v5/logging"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRBFromDisk(t *testing.T) {
+ logger := logging.NewLogger(nil)
+
+ dbw, err := persistent.NewBoltWrapper(persistent.BoltInMemoryMode, nil)
+ assert.Nil(t, err)
+
+ rbs := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs1", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ {Name: "rbs2", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+
+ disk := persistent.NewRBChangesCollection(dbw, logger)
+ disk.Update(rbs, nil, 10)
+ rbsStorage := NewProxyRuleBasedSegmentsStorage(dbw, logger, true)
+ assert.ElementsMatch(t, rbs, rbsStorage.All())
+}
+
+func TestRBStorage(t *testing.T) {
+ logger := logging.NewLogger(nil)
+
+ dbw, err := persistent.NewBoltWrapper(persistent.BoltInMemoryMode, nil)
+ assert.Nil(t, err)
+ rbsStorage := NewProxyRuleBasedSegmentsStorage(dbw, logger, true)
+
+ rbs := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs1", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ {Name: "rbs2", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ rbsStorage.Update(rbs, nil, 10)
+
+ assert.ElementsMatch(t, rbs, rbsStorage.All())
+ cn, _ := rbsStorage.ChangeNumber()
+ assert.Equal(t, int64(10), cn)
+ assert.False(t, rbsStorage.Contains([]string{"rbs1", "rbs2", "rbs3"}))
+ assert.True(t, rbsStorage.Contains([]string{"rbs1", "rbs2"}))
+
+ fetchMany := rbsStorage.FetchMany([]string{"rbs1", "rbs2", "rbs3"})
+ assert.Equal(t, 3, len(fetchMany))
+ assert.Equal(t, "rbs1", fetchMany["rbs1"].Name)
+ assert.Equal(t, "rbs2", fetchMany["rbs2"].Name)
+ assert.Nil(t, fetchMany["rbs3"])
+
+ rbs1, _ := rbsStorage.GetRuleBasedSegmentByName("rbs1")
+ assert.Equal(t, rbs[0], *rbs1)
+ rbs2, _ := rbsStorage.GetRuleBasedSegmentByName("rbs2")
+ assert.Equal(t, rbs[1], *rbs2)
+ rbs3, _ := rbsStorage.GetRuleBasedSegmentByName("rbs3")
+ assert.Nil(t, rbs3)
+ assert.Equal(t, set.NewSet(), rbsStorage.LargeSegments())
+
+ newRBS := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs3", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"},
+ {Name: "rbs4", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ assert.Nil(t, rbsStorage.ReplaceAll(newRBS, 15))
+ names, _ := rbsStorage.RuleBasedSegmentNames()
+ assert.ElementsMatch(t, []string{"rbs3", "rbs4"}, names)
+ assert.Equal(t, set.NewSet(), rbsStorage.Segments())
+
+ rbsStorage.SetChangeNumber(20)
+ newCN, _ := rbsStorage.ChangeNumber()
+ assert.Equal(t, int64(20), newCN)
+
+ rbsToAdd := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs5", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ rbsToRemove := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs3", ChangeNumber: 25, Status: "ARCHIVED"},
+ }
+ assert.Nil(t, rbsStorage.Update(rbsToAdd, rbsToRemove, 25))
+ allRBS := rbsStorage.All()
+ expectedRBS := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs4", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"},
+ {Name: "rbs5", ChangeNumber: 25, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ assert.ElementsMatch(t, expectedRBS, allRBS)
+}
+
+func TestRBSChangesSince(t *testing.T) {
+ logger := logging.NewLogger(nil)
+
+ dbw, err := persistent.NewBoltWrapper(persistent.BoltInMemoryMode, nil)
+ assert.Nil(t, err)
+ pss := NewProxyRuleBasedSegmentsStorage(dbw, logger, true)
+
+ // From -1
+ rbs := []dtos.RuleBasedSegmentDTO{
+ {Name: "rbs1", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ {Name: "rbs2", ChangeNumber: 10, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ pss.Update(rbs, 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, rbs, changes.RuleBasedSegments)
+
+ changes, err = pss.ChangesSince(5)
+ assert.Equal(t, ErrSinceParamTooOld, err)
+ assert.Nil(t, changes)
+
+ // 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"},
+ {Name: "rbs3", ChangeNumber: 15, Status: "ACTIVE", TrafficTypeName: "user"},
+ }
+ assert.ElementsMatch(t, expectedRBSs, changes.RuleBasedSegments)
+
+ 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/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..a67d1028 100644
--- a/splitio/proxy/storage/splits.go
+++ b/splitio/proxy/storage/splits.go
@@ -5,20 +5,17 @@ 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"
-)
-const (
- maxRecipes = 1000
+ "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"
+ "github.com/splitio/go-toolkit/v5/datastructures/set"
+ "github.com/splitio/go-toolkit/v5/logging"
)
// ErrSinceParamTooOld is returned when a summary is not cached for a requested change number
@@ -197,6 +194,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 {
+ return p.snapshot.RuleBasedSegmentNames()
+}
+
func (p *ProxySplitStorageImpl) sinceIsTooOld(since int64) bool {
if since == -1 {
return false
@@ -245,7 +250,7 @@ func archivedDTOForView(view *optimized.FeatureView) dtos.SplitDTO {
TrafficAllocation: 100,
TrafficAllocationSeed: 0,
Seed: 0,
- Status: "ARCHIVED",
+ Status: constants.SplitStatusArchived,
Killed: false,
DefaultTreatment: "off",
Algo: 1,
diff --git a/splitio/proxy/storage/splits_test.go b/splitio/proxy/storage/splits_test.go
index e2d1fa03..59356ab4 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"
@@ -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 {
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
diff --git a/splitio/version.go b/splitio/version.go
index 57bf3ddd..3bd5557e 100644
--- a/splitio/version.go
+++ b/splitio/version.go
@@ -2,4 +2,4 @@
package splitio
// Version is the version of this Agent
-const Version = "5.10.4"
+const Version = "5.11.0"