using log4net.Core; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace QRBee.Load.Generator { internal class LoadSpike { private readonly IOptions _settings; private readonly ILogger _logger; private TimeSpan _spikeDuration; private TimeSpan _spikeDelay; private double _spikeProbability; private ThreadSafeRandom _rng = new(); private DateTime _spikeEnd = DateTime.MinValue; public LoadSpike( IOptions settings, ILogger logger ) { _settings = settings; _logger = logger; var loadSpike = settings.Value.LoadSpike; _spikeDuration = TimeSpan.Zero; _spikeDelay = TimeSpan.Zero; _spikeProbability = loadSpike?.Probability ?? 0.0; if (loadSpike != null && loadSpike.Probability > 0.0) { if (!loadSpike.Parameters.TryGetValue("Duration", out var duration) || !TimeSpan.TryParse(duration, out _spikeDuration)) { _spikeProbability = 0.0; } else { if (loadSpike.Parameters.TryGetValue("Delay", out duration) && TimeSpan.TryParse(duration, out _spikeDelay)) { _spikeDelay = TimeSpan.FromMilliseconds(10); _logger.LogDebug($"Load spike configured. Probablility={_spikeProbability} Duration=\"{_spikeDuration:g}\" Delay=\"{_spikeDelay:g}\""); } } } } public async Task Delay() { if (DateTime.Now > _spikeEnd) { var dice = _rng.NextDouble(); if (dice < _spikeProbability) { // start load spike _spikeEnd = DateTime.Now + _spikeDuration; _logger.LogWarning($"Anomaly: Load spike until {_spikeEnd} Dice={dice}"); await Task.Delay(_spikeDelay); } else { await Task.Delay(_rng.NextInRange( _settings.Value.DelayBetweenMessagesMSec, _settings.Value.DelayBetweenMessagesMSec + _settings.Value.DelayJitterMSec )); } } else { await Task.Delay(_spikeDelay); } } } }