public LinkerService(ILinkerConnectionBuilder originBuilder, ILinkerConnectionBuilder destinationBuilder,
                             IPositionRepository positionRepository, IFilterService filterService, Settings settings, ILinkerLogger logger)
        {
            Ensure.NotNull(originBuilder, nameof(originBuilder));
            Ensure.NotNull(destinationBuilder, nameof(destinationBuilder));
            Ensure.NotNull(positionRepository, nameof(positionRepository));

            _logger = logger;
            Name    = $"Replica From-{originBuilder.ConnectionName}-To-{destinationBuilder.ConnectionName}";
            _connectionBuilderForOrigin      = originBuilder;
            _connectionBuilderForDestination = destinationBuilder;
            _positionRepository = positionRepository;
            _filterService      = filterService;
            _handleConflicts    = settings.HandleConflicts;
            _resolveLinkTos     = settings.ResolveLinkTos;

            _timerForStats          = new Timer(settings.StatsInterval);
            _timerForStats.Elapsed += _timerForStats_Elapsed;

            _processor          = new Timer(settings.SynchronisationInterval);
            _processor.Elapsed += Processor_Elapsed;
            _perfTunedSettings  =
                new PerfTuneSettings(settings.MaxBufferSize, settings.MaxLiveQueue, settings.ReadBatchSize);
            _replicaHelper = new LinkerHelper();
        }
        public PerfTuneSettings OptimizeSettings(long lastExecutionTime, PerfTuneSettings currentPerfTuneSettings, int maxBufferSizeLimit = 1500, double differentialLimit = 1.10, int geoReplicaClock = 1000)
        {
            if (differentialLimit > 1.50)
            {
                differentialLimit = 1.50;
            }
            if (differentialLimit < 1)
            {
                differentialLimit = 1.01;
            }

            if (maxBufferSizeLimit > 5000)
            {
                maxBufferSizeLimit = 5000;
            }
            if (maxBufferSizeLimit < 1)
            {
                maxBufferSizeLimit = 1;
            }

            var optimizedMaxBufferSize = currentPerfTuneSettings.MaxBufferSize;
            var optimizedMaxLiveQueue  = currentPerfTuneSettings.MaxLiveQueue;
            var optimizedReadBatchSize = currentPerfTuneSettings.ReadBatchSize;

            // Should we increase performances?
            if (currentPerfTuneSettings.MaxBufferSize <= maxBufferSizeLimit && lastExecutionTime < geoReplicaClock)
            {
                optimizedMaxBufferSize = Convert.ToInt32(Math.Round(currentPerfTuneSettings.MaxBufferSize * differentialLimit,
                                                                    MidpointRounding.AwayFromZero));
            }
            if (currentPerfTuneSettings.MaxLiveQueue <= CatchUpSubscriptionSettings.Default.MaxLiveQueueSize && lastExecutionTime < geoReplicaClock)
            {
                optimizedMaxLiveQueue = Convert.ToInt32(Math.Round(currentPerfTuneSettings.MaxLiveQueue * differentialLimit,
                                                                   MidpointRounding.AwayFromZero));
            }
            if (currentPerfTuneSettings.ReadBatchSize <= CatchUpSubscriptionSettings.Default.ReadBatchSize && lastExecutionTime < geoReplicaClock)
            {
                optimizedReadBatchSize = Convert.ToInt32(Math.Round(currentPerfTuneSettings.ReadBatchSize * differentialLimit,
                                                                    MidpointRounding.AwayFromZero));
            }

            // Should we decrease performances?
            if (currentPerfTuneSettings.MaxBufferSize >= maxBufferSizeLimit && lastExecutionTime > geoReplicaClock)
            {
                optimizedMaxBufferSize = Convert.ToInt32(Math.Round(currentPerfTuneSettings.MaxBufferSize / differentialLimit,
                                                                    MidpointRounding.AwayFromZero));
            }
            if (currentPerfTuneSettings.MaxLiveQueue >= CatchUpSubscriptionSettings.Default.MaxLiveQueueSize && lastExecutionTime > geoReplicaClock)
            {
                optimizedMaxLiveQueue = Convert.ToInt32(Math.Round(currentPerfTuneSettings.MaxLiveQueue / differentialLimit,
                                                                   MidpointRounding.AwayFromZero));
            }
            if (currentPerfTuneSettings.ReadBatchSize >= CatchUpSubscriptionSettings.Default.ReadBatchSize && lastExecutionTime > geoReplicaClock)
            {
                optimizedReadBatchSize = Convert.ToInt32(Math.Round(currentPerfTuneSettings.ReadBatchSize / differentialLimit,
                                                                    MidpointRounding.AwayFromZero));
            }

            return(new PerfTuneSettings(optimizedMaxBufferSize, optimizedMaxLiveQueue, optimizedReadBatchSize));
        }
 private void Processor_Elapsed(object sender, ElapsedEventArgs e)
 {
     if (_internalBuffer.IsEmpty)
     {
         return;
     }
     try
     {
         _processor.Stop();
         _allCatchUpSubscription.Stop();
         var watch           = System.Diagnostics.Stopwatch.StartNew();
         var eventsToProcess = _internalBuffer.Count;
         var oldPerfSettings = _perfTunedSettings.Clone() as PerfTuneSettings;
         ProcessQueueAndWaitAll();
         watch.Stop();
         var elapsedMs = watch.ElapsedMilliseconds;
         _logger.Debug($"{Name} Replicated '{eventsToProcess}' events in {elapsedMs}ms");
         _perfTunedSettings = _replicaHelper.OptimizeSettings(elapsedMs, _perfTunedSettings);
         if (!_perfTunedSettings.Equals(oldPerfSettings))
         {
             _logger.Debug($"{Name} Old PerfSettings: {oldPerfSettings}");
             _logger.Debug($"{Name} New PerfSettings: {_perfTunedSettings}");
         }
         Subscribe(_lastPosition);
         _processor.Start();
     }
     catch (Exception exception)
     {
         _logger.Error($"Error while Processor_Elapsed: {exception.GetBaseException().Message}");
         Stop();
         Start();
     }
 }
 protected bool Equals(PerfTuneSettings other)
 {
     return(MaxBufferSize == other.MaxBufferSize && MaxLiveQueue == other.MaxLiveQueue &&
            ReadBatchSize == other.ReadBatchSize);
 }