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
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
fa2ecad0a9
Коммит
aba00a3cfd
13
vendor/github.com/splitio/go-split-commons/v3/synchronizer/interface.go
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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)
|
||||
}
|
||||
Ссылка в новой задаче
Block a user