Update split SDK to 6.0.2 to fix sync bug (#17060)

* Update split SDK to 6.0.2 to fix sync bug

* Vendor and tidy
Этот коммит содержится в:
Joram Wilander
2021-03-04 11:16:08 -05:00
коммит произвёл GitHub
родитель fa2ecad0a9
Коммит aba00a3cfd
150 изменённых файлов: 2500 добавлений и 1960 удалений

13
vendor/github.com/splitio/go-split-commons/v3/synchronizer/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,13 @@
package synchronizer
// Synchronizer interface for syncing data to and from splits servers
type Synchronizer interface {
SyncAll(requestNoCache bool) error
SynchronizeSplits(till *int64, requestNoCache bool) error
LocalKill(splitName string, defaultTreatment string, changeNumber int64)
SynchronizeSegment(segmentName string, till *int64, requestNoCache bool) error
StartPeriodicFetching()
StopPeriodicFetching()
StartPeriodicDataRecording()
StopPeriodicDataRecording()
}

86
vendor/github.com/splitio/go-split-commons/v3/synchronizer/local.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,86 @@
package synchronizer
import (
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
storageMock "github.com/splitio/go-split-commons/v3/storage/mocks"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/split"
"github.com/splitio/go-split-commons/v3/tasks"
"github.com/splitio/go-toolkit/v4/logging"
)
// Local implements Local Synchronizer
type Local struct {
splitTasks SplitTasks
workers Workers
logger logging.LoggerInterface
inMememoryFullQueue chan string
}
// NewLocal creates new Local
func NewLocal(
period int,
splitAPI *service.SplitAPI,
splitStorage storage.SplitStorage,
logger logging.LoggerInterface,
) Synchronizer {
metricStorageMock := storageMock.MockMetricStorage{
IncCounterCall: func(key string) {},
IncLatencyCall: func(metricName string, index int) {},
PopCountersCall: func() []dtos.CounterDTO { return make([]dtos.CounterDTO, 0, 0) },
PopGaugesCall: func() []dtos.GaugeDTO { return make([]dtos.GaugeDTO, 0, 0) },
PopLatenciesCall: func() []dtos.LatenciesDTO { return make([]dtos.LatenciesDTO, 0, 0) },
PutGaugeCall: func(key string, gauge float64) {},
}
metricsWrapper := storage.NewMetricWrapper(metricStorageMock, nil, logger)
workers := Workers{
SplitFetcher: split.NewSplitFetcher(splitStorage, splitAPI.SplitFetcher, metricsWrapper, logger),
}
return &Local{
splitTasks: SplitTasks{
SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitFetcher, period, logger),
},
workers: workers,
logger: logger,
}
}
// SyncAll syncs splits and segments
func (s *Local) SyncAll(requestNoCache bool) error {
_, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
return err
}
// StartPeriodicFetching starts periodic fetchers tasks
func (s *Local) StartPeriodicFetching() {
s.splitTasks.SplitSyncTask.Start()
}
// StopPeriodicFetching stops periodic fetchers tasks
func (s *Local) StopPeriodicFetching() {
s.splitTasks.SplitSyncTask.Stop(false)
}
// StartPeriodicDataRecording starts periodic recorders tasks
func (s *Local) StartPeriodicDataRecording() {
}
// StopPeriodicDataRecording stops periodic recorders tasks
func (s *Local) StopPeriodicDataRecording() {
}
// SynchronizeSplits syncs splits
func (s *Local) SynchronizeSplits(till *int64, requestNoCache bool) error {
_, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
return err
}
// SynchronizeSegment syncs segment
func (s *Local) SynchronizeSegment(name string, till *int64, _ bool) error {
return nil
}
// LocalKill does nothing
func (s *Local) LocalKill(splitName string, defaultTreatment string, changeNumber int64) {
}

207
vendor/github.com/splitio/go-split-commons/v3/synchronizer/manager.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,207 @@
package synchronizer
import (
"errors"
"sync/atomic"
"time"
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v3/push"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-toolkit/v4/backoff"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
)
const (
// Ready represents ready
Ready = iota
// StreamingReady ready
StreamingReady
// Error represents some error in SSE streaming
Error
)
// Operation mode constants
const (
Streaming = iota
Polling
)
// Manager interface
type Manager interface {
Start()
Stop()
IsRunning() bool
}
// ManagerImpl struct
type ManagerImpl struct {
synchronizer Synchronizer
logger logging.LoggerInterface
config conf.AdvancedConfig
pushManager push.Manager
managerStatus chan int
streamingStatus chan int64
operationMode int32
lifecycle lifecycle.Manager
backoff backoff.Interface
}
// NewSynchronizerManager creates new sync manager
func NewSynchronizerManager(
synchronizer Synchronizer,
logger logging.LoggerInterface,
config conf.AdvancedConfig,
authClient service.AuthClient,
splitStorage storage.SplitStorage,
managerStatus chan int,
) (*ManagerImpl, error) {
if managerStatus == nil || cap(managerStatus) < 1 {
return nil, errors.New("Status channel cannot be nil nor having capacity")
}
manager := &ManagerImpl{
backoff: backoff.New(),
synchronizer: synchronizer,
logger: logger,
config: config,
managerStatus: managerStatus,
}
manager.lifecycle.Setup()
if config.StreamingEnabled {
streamingStatus := make(chan int64, 1000)
pushManager, err := push.NewManager(logger, synchronizer, &config, streamingStatus, authClient)
if err != nil {
return nil, err
}
manager.pushManager = pushManager
manager.streamingStatus = streamingStatus
}
return manager, nil
}
// IsRunning returns true if is in Streaming or Polling
func (s *ManagerImpl) IsRunning() bool {
return s.lifecycle.IsRunning()
}
// Start starts synchronization through Split
func (s *ManagerImpl) Start() {
if !s.lifecycle.BeginInitialization() {
s.logger.Info("Manager is already running, skipping start")
return
}
// It's safe to drain the channel here, since it's guaranteed that the manager status is "starting"
// push manager is still stopped
for len(s.managerStatus) > 0 {
<-s.managerStatus
}
err := s.synchronizer.SyncAll(false)
if err != nil {
defer s.lifecycle.ShutdownComplete()
s.managerStatus <- Error
return
}
if !s.lifecycle.InitializationComplete() {
defer s.lifecycle.ShutdownComplete()
return
}
s.logger.Debug("SyncAll Ready")
s.managerStatus <- Ready
s.synchronizer.StartPeriodicDataRecording()
if !s.config.StreamingEnabled {
s.logger.Info("SDK initialized in polling mode")
s.startPolling()
go func() { // create a goroutine that stops everything (the same way the streaming status watcher would)
<-s.lifecycle.ShutdownRequested()
s.stop()
}()
return
}
// Start streaming
s.logger.Info("SDK Initialized in streaming mode")
s.pushManager.Start()
go s.pushStatusWatcher()
}
func (s *ManagerImpl) stop() {
if s.pushManager != nil {
s.pushManager.Stop()
}
s.synchronizer.StopPeriodicFetching()
s.synchronizer.StopPeriodicDataRecording()
s.lifecycle.ShutdownComplete()
}
// Stop stop synchronizaation through Split
func (s *ManagerImpl) Stop() {
if !s.lifecycle.BeginShutdown() {
s.logger.Info("sync manager not yet running, skipping shutdown.")
return
}
s.logger.Info("Stopping all synchronization tasks")
s.lifecycle.AwaitShutdownComplete()
}
func (s *ManagerImpl) pushStatusWatcher() {
defer s.stop()
for {
select {
case <-s.lifecycle.ShutdownRequested():
return
case status := <-s.streamingStatus:
switch status {
case push.StatusUp:
s.stopPolling()
s.logger.Info("streaming up and running")
s.enableStreaming()
s.synchronizer.SyncAll(true)
case push.StatusDown:
s.logger.Info("streaming down, switchin to polling")
s.synchronizer.SyncAll(false)
s.pauseStreaming()
s.startPolling()
case push.StatusRetryableError:
howLong := s.backoff.Next()
s.logger.Error("retryable error in streaming subsystem. Switching to polling and retrying in ", howLong, " seconds")
s.pushManager.Stop()
s.synchronizer.SyncAll(false)
s.startPolling()
time.Sleep(howLong)
s.pushManager.Start()
case push.StatusNonRetryableError:
s.logger.Error("non retryable error in streaming subsystem. Switching to polling until next SDK initialization")
s.pushManager.Stop()
s.synchronizer.SyncAll(false)
s.startPolling()
}
}
}
}
func (s *ManagerImpl) startPolling() {
atomic.StoreInt32(&s.operationMode, Polling)
s.synchronizer.StartPeriodicFetching()
}
func (s *ManagerImpl) stopPolling() {
s.synchronizer.StopPeriodicFetching()
}
func (s *ManagerImpl) pauseStreaming() {
s.pushManager.StartWorkers()
}
func (s *ManagerImpl) enableStreaming() {
s.pushManager.StartWorkers()
atomic.StoreInt32(&s.operationMode, Streaming)
s.backoff.Reset()
}

179
vendor/github.com/splitio/go-split-commons/v3/synchronizer/synchronizer.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,179 @@
package synchronizer
import (
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/event"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/impression"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/impressionscount"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/metric"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/segment"
"github.com/splitio/go-split-commons/v3/synchronizer/worker/split"
"github.com/splitio/go-split-commons/v3/tasks"
"github.com/splitio/go-toolkit/v4/asynctask"
"github.com/splitio/go-toolkit/v4/logging"
)
// SplitTasks struct for tasks
type SplitTasks struct {
SplitSyncTask *asynctask.AsyncTask
SegmentSyncTask *asynctask.AsyncTask
TelemetrySyncTask *asynctask.AsyncTask
ImpressionSyncTask tasks.Task
EventSyncTask tasks.Task
ImpressionsCountSyncTask *asynctask.AsyncTask
}
// Workers struct for workers
type Workers struct {
SplitFetcher split.Updater
SegmentFetcher segment.Updater
TelemetryRecorder metric.MetricRecorder
ImpressionRecorder impression.ImpressionRecorder
EventRecorder event.EventRecorder
ImpressionsCountRecorder impressionscount.ImpressionsCountRecorder
}
// SynchronizerImpl implements Synchronizer
type SynchronizerImpl struct {
splitTasks SplitTasks
workers Workers
logger logging.LoggerInterface
inMememoryFullQueue chan string
impressionBulkSize int64
eventBulkSize int64
}
// NewSynchronizer creates new SynchronizerImpl
func NewSynchronizer(
confAdvanced conf.AdvancedConfig,
splitTasks SplitTasks,
workers Workers,
logger logging.LoggerInterface,
inMememoryFullQueue chan string,
) Synchronizer {
return &SynchronizerImpl{
impressionBulkSize: confAdvanced.ImpressionsBulkSize,
eventBulkSize: confAdvanced.EventsBulkSize,
splitTasks: splitTasks,
workers: workers,
logger: logger,
inMememoryFullQueue: inMememoryFullQueue,
}
}
func (s *SynchronizerImpl) dataFlusher() {
for true {
msg := <-s.inMememoryFullQueue
switch msg {
case "EVENTS_FULL":
s.logger.Debug("FLUSHING storage queue")
err := s.workers.EventRecorder.SynchronizeEvents(s.eventBulkSize)
if err != nil {
s.logger.Error("Error flushing storage queue", err)
}
break
case "IMPRESSIONS_FULL":
s.logger.Debug("FLUSHING storage queue")
err := s.workers.ImpressionRecorder.SynchronizeImpressions(s.impressionBulkSize)
if err != nil {
s.logger.Error("Error flushing storage queue", err)
}
}
}
}
// SyncAll syncs splits and segments
func (s *SynchronizerImpl) SyncAll(requestNoCache bool) error {
_, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
if err != nil {
return err
}
return s.workers.SegmentFetcher.SynchronizeSegments(requestNoCache)
}
// StartPeriodicFetching starts periodic fetchers tasks
func (s *SynchronizerImpl) StartPeriodicFetching() {
if s.splitTasks.SplitSyncTask != nil {
s.splitTasks.SplitSyncTask.Start()
}
if s.splitTasks.SegmentSyncTask != nil {
s.splitTasks.SegmentSyncTask.Start()
}
}
// StopPeriodicFetching stops periodic fetchers tasks
func (s *SynchronizerImpl) StopPeriodicFetching() {
if s.splitTasks.SplitSyncTask != nil {
s.splitTasks.SplitSyncTask.Stop(false)
}
if s.splitTasks.SegmentSyncTask != nil {
s.splitTasks.SegmentSyncTask.Stop(true)
}
}
// StartPeriodicDataRecording starts periodic recorders tasks
func (s *SynchronizerImpl) StartPeriodicDataRecording() {
if s.inMememoryFullQueue != nil {
go s.dataFlusher()
}
if s.splitTasks.ImpressionSyncTask != nil {
s.splitTasks.ImpressionSyncTask.Start()
}
if s.splitTasks.TelemetrySyncTask != nil {
s.splitTasks.TelemetrySyncTask.Start()
}
if s.splitTasks.EventSyncTask != nil {
s.splitTasks.EventSyncTask.Start()
}
if s.splitTasks.ImpressionsCountSyncTask != nil {
s.splitTasks.ImpressionsCountSyncTask.Start()
}
}
// StopPeriodicDataRecording stops periodic recorders tasks
func (s *SynchronizerImpl) StopPeriodicDataRecording() {
if s.splitTasks.ImpressionSyncTask != nil {
s.splitTasks.ImpressionSyncTask.Stop(true)
}
if s.splitTasks.TelemetrySyncTask != nil {
s.splitTasks.TelemetrySyncTask.Stop(false)
}
if s.splitTasks.EventSyncTask != nil {
s.splitTasks.EventSyncTask.Stop(true)
}
if s.splitTasks.ImpressionsCountSyncTask != nil {
s.splitTasks.ImpressionsCountSyncTask.Stop(true)
}
}
// SynchronizeSplits syncs splits
func (s *SynchronizerImpl) SynchronizeSplits(till *int64, requstNoCache bool) error {
referencedSegments, err := s.workers.SplitFetcher.SynchronizeSplits(till, requstNoCache)
for _, segment := range s.filterCachedSegments(referencedSegments) {
go s.SynchronizeSegment(segment, nil, true) // send segment to workerpool (queue is bypassed)
}
return err
}
func (s *SynchronizerImpl) filterCachedSegments(segmentsReferenced []string) []string {
toRet := make([]string, 0, len(segmentsReferenced))
for _, name := range segmentsReferenced {
if !s.workers.SegmentFetcher.IsSegmentCached(name) {
toRet = append(toRet, name)
}
}
return toRet
}
// LocalKill locally kills a split
func (s *SynchronizerImpl) LocalKill(splitName string, defaultTreatment string, changeNumber int64) {
s.workers.SplitFetcher.LocalKill(splitName, defaultTreatment, changeNumber)
}
// SynchronizeSegment syncs segment
func (s *SynchronizerImpl) SynchronizeSegment(name string, till *int64, requstNoCache bool) error {
return s.workers.SegmentFetcher.SynchronizeSegment(name, till, requstNoCache)
}
var _ Synchronizer = &SynchronizerImpl{}

7
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/event/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,7 @@
package event
// EventRecorder interface
type EventRecorder interface {
SynchronizeEvents(bulkSize int64) error
FlushEvents(bulkSize int64) error
}

77
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/event/single.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,77 @@
package event
import (
"errors"
"strconv"
"time"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v4/logging"
)
// RecorderSingle struct for event sync
type RecorderSingle struct {
eventStorage storage.EventStorageConsumer
eventRecorder service.EventsRecorder
metricsWrapper *storage.MetricWrapper
logger logging.LoggerInterface
metadata dtos.Metadata
}
// NewEventRecorderSingle creates new event synchronizer for posting events
func NewEventRecorderSingle(
eventStorage storage.EventStorageConsumer,
eventRecorder service.EventsRecorder,
metricsWrapper *storage.MetricWrapper,
logger logging.LoggerInterface,
metadata dtos.Metadata,
) EventRecorder {
return &RecorderSingle{
eventStorage: eventStorage,
eventRecorder: eventRecorder,
metricsWrapper: metricsWrapper,
logger: logger,
metadata: metadata,
}
}
// SynchronizeEvents syncs events
func (e *RecorderSingle) SynchronizeEvents(bulkSize int64) error {
queuedEvents, err := e.eventStorage.PopN(bulkSize)
if err != nil {
e.logger.Error("Error reading events queue", err)
return errors.New("Error reading events queue")
}
if len(queuedEvents) == 0 {
e.logger.Debug("No events fetched from queue. Nothing to send")
return nil
}
before := time.Now()
err = e.eventRecorder.Record(queuedEvents, e.metadata)
if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok {
e.metricsWrapper.StoreCounters(storage.PostEventsCounter, strconv.Itoa(httpError.Code))
}
return err
}
bucket := util.Bucket(time.Now().Sub(before).Nanoseconds())
e.metricsWrapper.StoreLatencies(storage.PostEventsLatency, bucket)
e.metricsWrapper.StoreCounters(storage.PostEventsCounter, "ok")
return nil
}
// FlushEvents flushes events
func (e *RecorderSingle) FlushEvents(bulkSize int64) error {
for !e.eventStorage.Empty() {
err := e.SynchronizeEvents(bulkSize)
if err != nil {
return err
}
}
return nil
}

7
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/impression/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,7 @@
package impression
// ImpressionRecorder interface
type ImpressionRecorder interface {
SynchronizeImpressions(bulkSize int64) error
FlushImpressions(bulkSize int64) error
}

118
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/impression/single.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,118 @@
package impression
import (
"errors"
"strconv"
"time"
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v4/logging"
)
const (
maxImpressionCacheSize = 500000
splitSDKImpressionsMode = "SplitSDKImpressionsMode"
)
// RecorderSingle struct for impression sync
type RecorderSingle struct {
impressionStorage storage.ImpressionStorageConsumer
impressionRecorder service.ImpressionsRecorder
metricsWrapper *storage.MetricWrapper
logger logging.LoggerInterface
metadata dtos.Metadata
mode string
}
// NewRecorderSingle creates new impression synchronizer for posting impressions
func NewRecorderSingle(
impressionStorage storage.ImpressionStorageConsumer,
impressionRecorder service.ImpressionsRecorder,
metricsWrapper *storage.MetricWrapper,
logger logging.LoggerInterface,
metadata dtos.Metadata,
managerConfig conf.ManagerConfig,
) ImpressionRecorder {
mode := conf.ImpressionsModeOptimized
if !util.ShouldBeOptimized(managerConfig) {
mode = conf.ImpressionsModeDebug
}
return &RecorderSingle{
impressionStorage: impressionStorage,
impressionRecorder: impressionRecorder,
metricsWrapper: metricsWrapper,
logger: logger,
metadata: metadata,
mode: mode,
}
}
// SynchronizeImpressions syncs impressions
func (i *RecorderSingle) SynchronizeImpressions(bulkSize int64) error {
queuedImpressions, err := i.impressionStorage.PopN(bulkSize)
if err != nil {
i.logger.Error("Error reading impressions queue", err)
return errors.New("Error reading impressions queue")
}
if len(queuedImpressions) == 0 {
i.logger.Debug("No impressions fetched from queue. Nothing to send")
return nil
}
impressionsToPost := make(map[string][]dtos.ImpressionDTO)
for _, impression := range queuedImpressions {
keyImpression := dtos.ImpressionDTO{
KeyName: impression.KeyName,
Treatment: impression.Treatment,
Time: impression.Time,
ChangeNumber: impression.ChangeNumber,
Label: impression.Label,
BucketingKey: impression.BucketingKey,
Pt: impression.Pt,
}
v, ok := impressionsToPost[impression.FeatureName]
if ok {
v = append(v, keyImpression)
} else {
v = []dtos.ImpressionDTO{keyImpression}
}
impressionsToPost[impression.FeatureName] = v
}
bulkImpressions := make([]dtos.ImpressionsDTO, 0)
for testName, testImpressions := range impressionsToPost {
bulkImpressions = append(bulkImpressions, dtos.ImpressionsDTO{
TestName: testName,
KeyImpressions: testImpressions,
})
}
before := time.Now()
err = i.impressionRecorder.Record(bulkImpressions, i.metadata, map[string]string{splitSDKImpressionsMode: i.mode})
if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok {
i.metricsWrapper.StoreCounters(storage.TestImpressionsCounter, strconv.Itoa(httpError.Code))
}
return err
}
bucket := util.Bucket(time.Now().Sub(before).Nanoseconds())
i.metricsWrapper.StoreLatencies(storage.TestImpressionsLatency, bucket)
i.metricsWrapper.StoreCounters(storage.TestImpressionsCounter, "ok")
return nil
}
// FlushImpressions flushes impressions
func (i *RecorderSingle) FlushImpressions(bulkSize int64) error {
for !i.impressionStorage.Empty() {
err := i.SynchronizeImpressions(bulkSize)
if err != nil {
return err
}
}
return nil
}

6
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/impressionscount/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,6 @@
package impressionscount
// ImpressionsCountRecorder interface
type ImpressionsCountRecorder interface {
SynchronizeImpressionsCount() error
}

51
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/impressionscount/single.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,51 @@
package impressionscount
import (
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/provisional"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-toolkit/v4/logging"
)
// RecorderSingle struct for impressionsCount sync
type RecorderSingle struct {
impressionsCounter *provisional.ImpressionsCounter
impressionRecorder service.ImpressionsRecorder
metadata dtos.Metadata
logger logging.LoggerInterface
}
// NewRecorderSingle creates new impressionsCount synchronizer for posting impressionsCount
func NewRecorderSingle(
impressionsCounter *provisional.ImpressionsCounter,
impressionRecorder service.ImpressionsRecorder,
metadata dtos.Metadata,
logger logging.LoggerInterface,
) ImpressionsCountRecorder {
return &RecorderSingle{
impressionsCounter: impressionsCounter,
impressionRecorder: impressionRecorder,
metadata: metadata,
logger: logger,
}
}
// SynchronizeImpressionsCount syncs imp counts
func (m *RecorderSingle) SynchronizeImpressionsCount() error {
impressionsCount := m.impressionsCounter.PopAll()
impressionsInTimeFrame := make([]dtos.ImpressionsInTimeFrameDTO, 0)
for key, count := range impressionsCount {
impressionInTimeFrame := dtos.ImpressionsInTimeFrameDTO{
FeatureName: key.FeatureName,
RawCount: count,
TimeFrame: key.TimeFrame,
}
impressionsInTimeFrame = append(impressionsInTimeFrame, impressionInTimeFrame)
}
pf := dtos.ImpressionsCountDTO{
PerFeature: impressionsInTimeFrame,
}
return m.impressionRecorder.RecordImpressionsCount(pf, m.metadata)
}

6
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/metric/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,6 @@
package metric
// MetricRecorder interface
type MetricRecorder interface {
SynchronizeTelemetry() error
}

74
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/metric/single.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,74 @@
package metric
import (
"errors"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
)
// RecorderSingle struct for metric sync
type RecorderSingle struct {
metricStorage storage.MetricsStorageConsumer
metricRecorder service.MetricsRecorder
metadata dtos.Metadata
}
// NewRecorderSingle creates new metric synchronizer for posting metrics
func NewRecorderSingle(
metricStorage storage.MetricsStorageConsumer,
metricRecorder service.MetricsRecorder,
metadata dtos.Metadata,
) MetricRecorder {
return &RecorderSingle{
metricStorage: metricStorage,
metricRecorder: metricRecorder,
metadata: metadata,
}
}
func (m *RecorderSingle) synchronizeLatencies() error {
latencies := m.metricStorage.PopLatencies()
if len(latencies) > 0 {
err := m.metricRecorder.RecordLatencies(latencies, m.metadata)
return err
}
return nil
}
func (m *RecorderSingle) synchronizeGauges() error {
var errs []error
for _, gauge := range m.metricStorage.PopGauges() {
err := m.metricRecorder.RecordGauge(gauge, m.metadata)
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errors.New("Some gauges could not be posted")
}
return nil
}
func (m *RecorderSingle) synchronizeCounters() error {
counters := m.metricStorage.PopCounters()
if len(counters) > 0 {
err := m.metricRecorder.RecordCounters(counters, m.metadata)
return err
}
return nil
}
// SynchronizeTelemetry syncs telemetry
func (m *RecorderSingle) SynchronizeTelemetry() error {
err := m.synchronizeGauges()
if err != nil {
return err
}
err = m.synchronizeLatencies()
if err != nil {
return err
}
return m.synchronizeCounters()
}

9
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/segment/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,9 @@
package segment
// Updater interface
type Updater interface {
SynchronizeSegment(name string, till *int64, requestNoCache bool) error
SynchronizeSegments(requestNoCache bool) error
SegmentNames() []interface{}
IsSegmentCached(segmentName string) bool
}

146
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/segment/segment.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,146 @@
package segment
import (
"fmt"
"strconv"
"sync"
"time"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v4/datastructures/set"
"github.com/splitio/go-toolkit/v4/logging"
)
// UpdaterImpl struct for segment sync
type UpdaterImpl struct {
splitStorage storage.SplitStorageConsumer
segmentStorage storage.SegmentStorage
segmentFetcher service.SegmentFetcher
metricsWrapper *storage.MetricWrapper
logger logging.LoggerInterface
}
// NewSegmentFetcher creates new segment synchronizer for processing segment updates
func NewSegmentFetcher(
splitStorage storage.SplitStorage,
segmentStorage storage.SegmentStorage,
segmentFetcher service.SegmentFetcher,
metricsWrapper *storage.MetricWrapper,
logger logging.LoggerInterface,
) Updater {
return &UpdaterImpl{
splitStorage: splitStorage,
segmentStorage: segmentStorage,
segmentFetcher: segmentFetcher,
metricsWrapper: metricsWrapper,
logger: logger,
}
}
func (s *UpdaterImpl) processUpdate(segmentChanges *dtos.SegmentChangesDTO) {
name := segmentChanges.Name
oldSegment := s.segmentStorage.Keys(name)
if oldSegment == nil {
keys := set.NewSet()
for _, key := range segmentChanges.Added {
keys.Add(key)
}
s.logger.Debug(fmt.Sprintf("Segment [%s] doesn't exist now, it will add (%d) keys", name, keys.Size()))
s.segmentStorage.Update(name, keys, set.NewSet(), segmentChanges.Till)
} else {
toAdd := set.NewSet()
toRemove := set.NewSet()
// Segment exists, must add new members and remove old ones
for _, key := range segmentChanges.Added {
toAdd.Add(key)
}
for _, key := range segmentChanges.Removed {
toRemove.Add(key)
}
if toAdd.Size() > 0 || toRemove.Size() > 0 {
s.logger.Debug(fmt.Sprintf("Segment [%s] exists, it will be updated. %d keys added, %d keys removed", name, toAdd.Size(), toRemove.Size()))
s.segmentStorage.Update(name, toAdd, toRemove, segmentChanges.Till)
}
}
}
// SynchronizeSegment syncs segment
func (s *UpdaterImpl) SynchronizeSegment(name string, till *int64, requestNoCache bool) error {
for {
s.logger.Debug(fmt.Sprintf("Synchronizing segment %s", name))
changeNumber, _ := s.segmentStorage.ChangeNumber(name)
if changeNumber == 0 {
changeNumber = -1
}
if till != nil && *till < changeNumber {
return nil
}
before := time.Now()
segmentChanges, err := s.segmentFetcher.Fetch(name, changeNumber, requestNoCache)
if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok {
s.metricsWrapper.StoreCounters(storage.SegmentChangesCounter, strconv.Itoa(httpError.Code))
}
return err
}
s.processUpdate(segmentChanges)
bucket := util.Bucket(time.Now().Sub(before).Nanoseconds())
s.metricsWrapper.StoreLatencies(storage.SegmentChangesLatency, bucket)
s.metricsWrapper.StoreCounters(storage.SegmentChangesCounter, "ok")
if segmentChanges.Till == segmentChanges.Since || (till != nil && segmentChanges.Till >= *till) {
return nil
}
}
}
// SynchronizeSegments syncs segments at once
func (s *UpdaterImpl) SynchronizeSegments(requestNoCache bool) error {
// @TODO: add delays
segmentNames := s.splitStorage.SegmentNames().List()
s.logger.Debug("Segment Sync", segmentNames)
wg := sync.WaitGroup{}
wg.Add(len(segmentNames))
failedSegments := set.NewThreadSafeSet()
for _, name := range segmentNames {
conv, ok := name.(string)
if !ok {
s.logger.Warning("Skipping non-string segment present in storage at initialization-time!")
continue
}
go func(segmentName string) {
defer wg.Done() // Make sure the "finished" signal is always sent
ready := false
var err error
for !ready {
err = s.SynchronizeSegment(segmentName, nil, requestNoCache)
if err != nil {
failedSegments.Add(segmentName)
}
return
}
}(conv)
}
wg.Wait()
if failedSegments.Size() > 0 {
return fmt.Errorf("The following segments failed to be fetched %v", failedSegments.List())
}
return nil
}
// SegmentNames returns all segments
func (s *UpdaterImpl) SegmentNames() []interface{} {
return s.splitStorage.SegmentNames().List()
}
// IsSegmentCached returns true if a segment exists
func (s *UpdaterImpl) IsSegmentCached(segmentName string) bool {
cn, _ := s.segmentStorage.ChangeNumber(segmentName)
return cn != -1
}

7
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/split/interface.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,7 @@
package split
// Updater interface
type Updater interface {
SynchronizeSplits(till *int64, requestNoCache bool) ([]string, error)
LocalKill(splitName string, defaultTreatment string, changeNumber int64)
}

116
vendor/github.com/splitio/go-split-commons/v3/synchronizer/worker/split/split.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,116 @@
package split
import (
"strconv"
"time"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v4/logging"
)
const (
matcherTypeInSegment = "IN_SEGMENT"
)
// UpdaterImpl struct for split sync
type UpdaterImpl struct {
splitStorage storage.SplitStorage
splitFetcher service.SplitFetcher
metricsWrapper *storage.MetricWrapper
logger logging.LoggerInterface
}
// NewSplitFetcher creates new split synchronizer for processing split updates
func NewSplitFetcher(
splitStorage storage.SplitStorage,
splitFetcher service.SplitFetcher,
metricsWrapper *storage.MetricWrapper,
logger logging.LoggerInterface,
) *UpdaterImpl {
return &UpdaterImpl{
splitStorage: splitStorage,
splitFetcher: splitFetcher,
metricsWrapper: metricsWrapper,
logger: logger,
}
}
func (s *UpdaterImpl) processUpdate(splits *dtos.SplitChangesDTO) {
inactiveSplits := make([]dtos.SplitDTO, 0)
activeSplits := make([]dtos.SplitDTO, 0)
for _, split := range splits.Splits {
if split.Status == "ACTIVE" {
activeSplits = append(activeSplits, split)
} else {
inactiveSplits = append(inactiveSplits, split)
}
}
// Add/Update active splits
s.splitStorage.PutMany(activeSplits, splits.Till)
// Remove inactive splits
for _, split := range inactiveSplits {
s.splitStorage.Remove(split.Name)
}
}
// SynchronizeSplits syncs splits
func (s *UpdaterImpl) SynchronizeSplits(till *int64, requestNoCache bool) ([]string, error) {
// @TODO: add delays
segments := make([]string, 0)
for {
changeNumber, _ := s.splitStorage.ChangeNumber()
if changeNumber == 0 {
changeNumber = -1
}
if till != nil && *till < changeNumber {
return segments, nil
}
before := time.Now()
splits, err := s.splitFetcher.Fetch(changeNumber, requestNoCache)
if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok {
s.metricsWrapper.StoreCounters(storage.SplitChangesCounter, strconv.Itoa(httpError.Code))
}
return segments, err
}
s.processUpdate(splits)
segments = append(segments, extractSegments(splits)...)
bucket := util.Bucket(time.Now().Sub(before).Nanoseconds())
s.metricsWrapper.StoreCounters(storage.SplitChangesCounter, "ok")
s.metricsWrapper.StoreLatencies(storage.SplitChangesLatency, bucket)
if splits.Till == splits.Since || (till != nil && splits.Till >= *till) {
return segments, nil
}
}
}
func extractSegments(splits *dtos.SplitChangesDTO) []string {
names := make(map[string]struct{})
for _, split := range splits.Splits {
for _, cond := range split.Conditions {
for _, matcher := range cond.MatcherGroup.Matchers {
if matcher.MatcherType == matcherTypeInSegment && matcher.UserDefinedSegment != nil {
names[matcher.UserDefinedSegment.SegmentName] = struct{}{}
}
}
}
}
toRet := make([]string, 0, len(names))
for name := range names {
toRet = append(toRet, name)
}
return toRet
}
// LocalKill marks a spit as killed in local storage
func (s *UpdaterImpl) LocalKill(splitName string, defaultTreatment string, changeNumber int64) {
s.splitStorage.KillLocally(splitName, defaultTreatment, changeNumber)
}