mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
DEEP-37 Base class for anomalies created
This commit is contained in:
parent
58b7565597
commit
c5895adaa5
71
QRBee.Load.Generator/AnomalyBase.cs
Normal file
71
QRBee.Load.Generator/AnomalyBase.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace QRBee.Load.Generator;
|
||||||
|
|
||||||
|
internal class AnomalyBase
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IAnomalyReporter _anomalyReporter;
|
||||||
|
protected ThreadSafeRandom _rng = new();
|
||||||
|
private bool _anomalyActive;
|
||||||
|
private DateTime _anomalyStart = DateTime.MinValue;
|
||||||
|
private DateTime _anomalyEnd = DateTime.MinValue;
|
||||||
|
private double _anomalyProbability;
|
||||||
|
private TimeSpan _anomalyDuration;
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public AnomalyBase(string name, Anomaly settings, ILogger logger, IAnomalyReporter anomalyReporter)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
_logger = logger;
|
||||||
|
_anomalyReporter = anomalyReporter;
|
||||||
|
|
||||||
|
_anomalyProbability = settings.Probability;
|
||||||
|
_anomalyDuration = settings.Duration;
|
||||||
|
if (IsEnabled)
|
||||||
|
_logger.LogDebug($"{Name} configured: Probability={_anomalyProbability}, Duration={_anomalyDuration}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled => _anomalyProbability > 0.0;
|
||||||
|
|
||||||
|
protected bool IsActive()
|
||||||
|
{
|
||||||
|
if (DateTime.UtcNow < _anomalyEnd)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (_anomalyActive)
|
||||||
|
{
|
||||||
|
// double locking
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_anomalyActive)
|
||||||
|
{
|
||||||
|
_anomalyActive = false;
|
||||||
|
_anomalyReporter.Report(_anomalyStart, _anomalyEnd, "Unconfirmed transaction");
|
||||||
|
_logger.LogWarning($"Anomaly:{Name} ended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dice = _rng.NextDouble();
|
||||||
|
if (dice < _anomalyProbability)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!_anomalyActive)
|
||||||
|
{
|
||||||
|
_anomalyStart = DateTime.Now;
|
||||||
|
_anomalyEnd = _anomalyStart + _anomalyDuration;
|
||||||
|
_anomalyActive = true;
|
||||||
|
_logger.LogWarning($"Anomaly: {Name}. Dice={dice} Ends=\"{_anomalyEnd.ToLocalTime():s}\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
QRBee.Load.Generator/AnomalyReporter.cs
Normal file
64
QRBee.Load.Generator/AnomalyReporter.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QRBee.Load.Generator
|
||||||
|
{
|
||||||
|
internal class AnomalyReporter : IDisposable, IAnomalyReporter
|
||||||
|
{
|
||||||
|
private readonly StreamWriter _writer;
|
||||||
|
private bool disposedValue;
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
public AnomalyReporter()
|
||||||
|
{
|
||||||
|
const string fileName = "anomalies.csv";
|
||||||
|
var writeHeader = !File.Exists(fileName);
|
||||||
|
|
||||||
|
var file = new FileStream(fileName, writeHeader ? FileMode.Create : FileMode.Append, FileAccess.Write);
|
||||||
|
_writer = new StreamWriter(file);
|
||||||
|
|
||||||
|
if (writeHeader)
|
||||||
|
{
|
||||||
|
_writer.WriteLine("Start,End,Label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Report(DateTime start, DateTime end, string description)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_writer.WriteLine($"\"{start:O}\",\"{end:O}\",\"{description}\"");
|
||||||
|
_writer.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~AnomalyReporter()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
internal class Anomaly
|
internal class Anomaly
|
||||||
{
|
{
|
||||||
public double Probability { get; set; }
|
public double Probability { get; set; }
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
public Dictionary<string,string> Parameters { get; set; } = new();
|
public Dictionary<string,string> Parameters { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
QRBee.Load.Generator/IAnomalyReporter.cs
Normal file
7
QRBee.Load.Generator/IAnomalyReporter.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace QRBee.Load.Generator
|
||||||
|
{
|
||||||
|
internal interface IAnomalyReporter
|
||||||
|
{
|
||||||
|
void Report(DateTime start, DateTime end, string description);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
QRBee.Load.Generator/LargeAmount.cs
Normal file
27
QRBee.Load.Generator/LargeAmount.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace QRBee.Load.Generator;
|
||||||
|
|
||||||
|
internal class LargeAmount : AnomalyBase
|
||||||
|
{
|
||||||
|
private double _minAmount = 1;
|
||||||
|
private double _maxAmount = 100;
|
||||||
|
private decimal _largeAmountValue = 1000;
|
||||||
|
|
||||||
|
public LargeAmount(IOptions<GeneratorSettings> settings, ILogger<UnconfirmedTransactions> logger, IAnomalyReporter anomalyReporter)
|
||||||
|
: base("Large amount", settings.Value.LargeAmount, logger, anomalyReporter)
|
||||||
|
{
|
||||||
|
_minAmount = settings.Value.MinAmount;
|
||||||
|
_maxAmount = settings.Value.MaxAmount;
|
||||||
|
if (settings.Value.LargeAmount.Parameters.TryGetValue("Value", out var s))
|
||||||
|
_largeAmountValue = decimal.Parse(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public decimal GetAmount()
|
||||||
|
{
|
||||||
|
if (IsActive())
|
||||||
|
return _largeAmountValue;
|
||||||
|
return Convert.ToDecimal(_rng.NextDoubleInRange(_minAmount, _maxAmount));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,91 +1,42 @@
|
|||||||
using log4net.Core;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QRBee.Load.Generator
|
namespace QRBee.Load.Generator;
|
||||||
|
|
||||||
|
internal class LoadSpike : AnomalyBase
|
||||||
{
|
{
|
||||||
internal class LoadSpike
|
private TimeSpan _spikeDelay;
|
||||||
|
private int _delayBetweenMessagesMSec;
|
||||||
|
private int _delayJitterMSec;
|
||||||
|
|
||||||
|
public LoadSpike(IOptions<GeneratorSettings> settings, ILogger<LoadSpike> logger, IAnomalyReporter anomalyReporter)
|
||||||
|
: base("Load spike", settings.Value.LoadSpike, logger, anomalyReporter)
|
||||||
{
|
{
|
||||||
private readonly IOptions<GeneratorSettings> _settings;
|
var loadSpike = settings.Value.LoadSpike;
|
||||||
private readonly ILogger<LoadSpike> _logger;
|
_spikeDelay = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
private TimeSpan _spikeDuration;
|
_delayBetweenMessagesMSec = settings.Value.DelayBetweenMessagesMSec;
|
||||||
private TimeSpan _spikeDelay;
|
_delayJitterMSec = settings.Value.DelayJitterMSec;
|
||||||
private double _spikeProbability;
|
|
||||||
private bool _spikeActive;
|
|
||||||
|
|
||||||
private ThreadSafeRandom _rng = new();
|
if (IsEnabled)
|
||||||
private DateTime _spikeEnd = DateTime.MinValue;
|
|
||||||
|
|
||||||
public LoadSpike( IOptions<GeneratorSettings> settings, ILogger<LoadSpike> logger )
|
|
||||||
{
|
{
|
||||||
_settings = settings;
|
if (loadSpike.Parameters.TryGetValue("Delay", out var duration)
|
||||||
_logger = logger;
|
&& TimeSpan.TryParse(duration, out _spikeDelay))
|
||||||
|
|
||||||
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)
|
_spikeDelay = TimeSpan.FromMilliseconds(10);
|
||||||
|| !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)
|
|
||||||
{
|
|
||||||
if (_spikeActive)
|
|
||||||
{
|
|
||||||
_spikeActive = false;
|
|
||||||
_logger.LogWarning($"Anomaly: Load spike ended");
|
|
||||||
}
|
|
||||||
|
|
||||||
var dice = _rng.NextDouble();
|
|
||||||
if (dice < _spikeProbability)
|
|
||||||
{
|
|
||||||
// start load spike
|
|
||||||
_spikeEnd = DateTime.Now + _spikeDuration;
|
|
||||||
_spikeActive = true;
|
|
||||||
|
|
||||||
_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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task Delay()
|
||||||
|
{
|
||||||
|
if (IsActive())
|
||||||
|
await Task.Delay(_spikeDelay);
|
||||||
|
else
|
||||||
|
await Task.Delay(_rng.NextInRange(
|
||||||
|
_delayBetweenMessagesMSec,
|
||||||
|
_delayBetweenMessagesMSec + _delayJitterMSec
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,31 +9,13 @@ internal class PaymentRequestGenerator
|
|||||||
{
|
{
|
||||||
private readonly ClientPool _clientPool;
|
private readonly ClientPool _clientPool;
|
||||||
private readonly ILogger<PaymentRequestGenerator> _logger;
|
private readonly ILogger<PaymentRequestGenerator> _logger;
|
||||||
private ThreadSafeRandom _rng = new ThreadSafeRandom();
|
private readonly LargeAmount _largeAmount;
|
||||||
private double _minAmount = 1;
|
|
||||||
private double _maxAmount = 100;
|
|
||||||
private double _largeAmountProbability;
|
|
||||||
private double _largeAmountValue;
|
|
||||||
|
|
||||||
public PaymentRequestGenerator(ClientPool clientPool, IOptions<GeneratorSettings> settings, ILogger<PaymentRequestGenerator> logger)
|
public PaymentRequestGenerator(ClientPool clientPool, IOptions<GeneratorSettings> settings, ILogger<PaymentRequestGenerator> logger, LargeAmount largeAmount)
|
||||||
{
|
{
|
||||||
_clientPool = clientPool;
|
_clientPool = clientPool;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_minAmount = settings.Value.MinAmount;
|
_largeAmount = largeAmount;
|
||||||
_maxAmount = settings.Value.MaxAmount;
|
|
||||||
|
|
||||||
var largeAmount = settings.Value.LargeAmount;
|
|
||||||
_largeAmountProbability = largeAmount.Probability;
|
|
||||||
if (_largeAmountProbability > 0)
|
|
||||||
{
|
|
||||||
if ( largeAmount.Parameters.TryGetValue("Value", out var s))
|
|
||||||
_largeAmountValue = Double.Parse(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_largeAmountValue <= 0.0)
|
|
||||||
_largeAmountProbability = 0.0;
|
|
||||||
else
|
|
||||||
_logger.LogDebug($"Large amount spike configured: Probability={_largeAmountProbability} Value=\"{_largeAmountValue}\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PaymentRequest> GeneratePaymentRequest(int clientId, int merchantId)
|
public async Task<PaymentRequest> GeneratePaymentRequest(int clientId, int merchantId)
|
||||||
@ -79,16 +61,7 @@ internal class PaymentRequestGenerator
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private decimal GetAmount()
|
private decimal GetAmount() => _largeAmount.GetAmount();
|
||||||
{
|
|
||||||
var dice = _rng.NextDouble();
|
|
||||||
if (dice < _largeAmountProbability)
|
|
||||||
{
|
|
||||||
_logger.LogWarning($"Anomaly: Large amount Dice={dice}");
|
|
||||||
return Convert.ToDecimal(_rng.NextDoubleInRange(_largeAmountValue, _largeAmountValue* 1.10));
|
|
||||||
}
|
|
||||||
return Convert.ToDecimal(_rng.NextDoubleInRange(_minAmount, _maxAmount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task<ClientSettings> GetMerchant(int id) => _clientPool.GetMerchant(id);
|
private Task<ClientSettings> GetMerchant(int id) => _clientPool.GetMerchant(id);
|
||||||
|
|
||||||
|
|||||||
@ -39,15 +39,18 @@ builder.ConfigureServices((context, services) =>
|
|||||||
.AddSingleton<TransactionDefiler>()
|
.AddSingleton<TransactionDefiler>()
|
||||||
.AddSingleton<UnconfirmedTransactions>()
|
.AddSingleton<UnconfirmedTransactions>()
|
||||||
.AddSingleton<LoadSpike>()
|
.AddSingleton<LoadSpike>()
|
||||||
|
.AddSingleton<LargeAmount>()
|
||||||
|
.AddSingleton<IAnomalyReporter, AnomalyReporter>()
|
||||||
.AddSingleton<PrivateKeyHandlerFactory>(x => no => new PrivateKeyHandler(x.GetRequiredService<ILogger<ServerPrivateKeyHandler>>(), x.GetRequiredService<IConfiguration>(), no))
|
.AddSingleton<PrivateKeyHandlerFactory>(x => no => new PrivateKeyHandler(x.GetRequiredService<ILogger<ServerPrivateKeyHandler>>(), x.GetRequiredService<IConfiguration>(), no))
|
||||||
.AddSingleton<SecurityServiceFactory>(x => no => new AndroidSecurityService(x.GetRequiredService<PrivateKeyHandlerFactory>()(no)))
|
.AddSingleton<SecurityServiceFactory>(x => no => new AndroidSecurityService(x.GetRequiredService<PrivateKeyHandlerFactory>()(no)))
|
||||||
.AddHostedService<LoadGenerator>()
|
.AddHostedService<LoadGenerator>()
|
||||||
;
|
;
|
||||||
});
|
});
|
||||||
|
|
||||||
ServicePointManager.DefaultConnectionLimit = 10;
|
// ServicePointManager.DefaultConnectionLimit = 500;
|
||||||
ServicePointManager.ReusePort = true;
|
ServicePointManager.ReusePort = true;
|
||||||
ServicePointManager.CheckCertificateRevocationList = false;
|
ServicePointManager.CheckCertificateRevocationList = false;
|
||||||
|
|
||||||
|
|
||||||
var host = builder.Build();
|
var host = builder.Build();
|
||||||
host.Run();
|
host.Run();
|
||||||
|
|||||||
@ -4,57 +4,23 @@ using QRBee.Core.Data;
|
|||||||
|
|
||||||
namespace QRBee.Load.Generator
|
namespace QRBee.Load.Generator
|
||||||
{
|
{
|
||||||
internal class TransactionDefiler
|
internal class TransactionDefiler : AnomalyBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<TransactionDefiler> _logger;
|
public TransactionDefiler(IOptions<GeneratorSettings> settings, ILogger<TransactionDefiler> logger, IAnomalyReporter anomalyReporter)
|
||||||
|
: base("Corrupted transaction", settings.Value.TransactionCorruption, logger, anomalyReporter)
|
||||||
private ThreadSafeRandom _rng = new();
|
|
||||||
|
|
||||||
private double _corruptionProbability;
|
|
||||||
private int _sequenceLengthMin;
|
|
||||||
private int _sequenceLengthMax;
|
|
||||||
private int _corruptionCounter;
|
|
||||||
|
|
||||||
public TransactionDefiler(IOptions<GeneratorSettings> settings, ILogger<TransactionDefiler> logger)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
var transactionCorruption = settings.Value.TransactionCorruption;
|
|
||||||
_corruptionProbability = transactionCorruption.Probability;
|
|
||||||
if (_corruptionProbability > 0)
|
|
||||||
{
|
|
||||||
if (transactionCorruption.Parameters.TryGetValue("SequenceLengthMin", out var s))
|
|
||||||
_sequenceLengthMin = int.Parse(s);
|
|
||||||
if (transactionCorruption.Parameters.TryGetValue("SequenceLengthMax", out s))
|
|
||||||
_sequenceLengthMax = int.Parse(s);
|
|
||||||
_logger.LogDebug($"Transaction corruption configured: Probability={_corruptionProbability}, SequenceLengthMin={_sequenceLengthMin}, SequenceLengthMax={_sequenceLengthMax}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CorruptPaymentRequest(PaymentRequest paymentRequest)
|
public void CorruptPaymentRequest(PaymentRequest paymentRequest)
|
||||||
{
|
{
|
||||||
if(Interlocked.Decrement(ref _corruptionCounter) > 0 )
|
if ( IsActive() )
|
||||||
{
|
|
||||||
paymentRequest.ClientResponse.MerchantRequest.Amount += 0.01M;
|
paymentRequest.ClientResponse.MerchantRequest.Amount += 0.01M;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dice = _rng.NextDouble();
|
|
||||||
if (dice < _corruptionProbability)
|
|
||||||
{
|
|
||||||
paymentRequest.ClientResponse.MerchantRequest.Amount += 10M;
|
|
||||||
var cc = _rng.NextInRange(_sequenceLengthMin, _sequenceLengthMax);
|
|
||||||
_corruptionCounter = cc;
|
|
||||||
_logger.LogWarning($"Anomaly: Corrupted transaction. Dice={dice} SequenceLength={cc}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CorruptPaymentConfirmation(PaymentConfirmation paymentConfirmation)
|
public void CorruptPaymentConfirmation(PaymentConfirmation paymentConfirmation)
|
||||||
{
|
{
|
||||||
if (Interlocked.Decrement(ref _corruptionCounter) > 0)
|
if (IsActive())
|
||||||
{
|
|
||||||
paymentConfirmation.GatewayTransactionId = "BadGatewayTransactionId";
|
paymentConfirmation.GatewayTransactionId = "BadGatewayTransactionId";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,50 +3,12 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
namespace QRBee.Load.Generator;
|
namespace QRBee.Load.Generator;
|
||||||
|
|
||||||
internal class UnconfirmedTransactions
|
internal class UnconfirmedTransactions : AnomalyBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<UnconfirmedTransactions> _logger;
|
public UnconfirmedTransactions(IOptions<GeneratorSettings> settings, ILogger<UnconfirmedTransactions> logger, IAnomalyReporter anomalyReporter)
|
||||||
|
: base("Unconfirmed transaction", settings.Value.UnconfirmedTransaction, logger, anomalyReporter)
|
||||||
private double _unconfirmedProbability;
|
|
||||||
private TimeSpan _unconfirmedDuration;
|
|
||||||
private DateTime _anomalyEnd = DateTime.MinValue;
|
|
||||||
private ThreadSafeRandom _rng = new();
|
|
||||||
|
|
||||||
public UnconfirmedTransactions(IOptions<GeneratorSettings> settings, ILogger<UnconfirmedTransactions> logger)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_unconfirmedProbability = settings.Value.UnconfirmedTransaction.Probability;
|
|
||||||
_unconfirmedDuration = TimeSpan.Zero;
|
|
||||||
|
|
||||||
if (_unconfirmedProbability > 0.0)
|
|
||||||
{
|
|
||||||
if (settings.Value.UnconfirmedTransaction.Parameters.TryGetValue("Duration", out var duration)
|
|
||||||
|| TimeSpan.TryParse(duration, out _unconfirmedDuration))
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"Unconfirmed transactions configured. Probablility={_unconfirmedProbability}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_unconfirmedProbability = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldConfirm()
|
public bool ShouldConfirm() => IsActive();
|
||||||
{
|
|
||||||
if ( DateTime.UtcNow < _anomalyEnd )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dice = _rng.NextDouble();
|
|
||||||
if (dice < _unconfirmedProbability)
|
|
||||||
{
|
|
||||||
_anomalyEnd = DateTime.UtcNow + _unconfirmedDuration;
|
|
||||||
_logger.LogWarning($"Anomaly: Unconfirmed transaction. Dice={dice} Ends=\"{_anomalyEnd.ToLocalTime():s}\"");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
"QRBeeURL": "http://localhost:5000/",
|
"QRBeeURL": "http://localhost:5000/",
|
||||||
"NumberOfClients": 100,
|
"NumberOfClients": 100,
|
||||||
"NumberOfMerchants": 10,
|
"NumberOfMerchants": 10,
|
||||||
"NumberOfThreads": 10,
|
"NumberOfThreads": 6,
|
||||||
"DelayBetweenMessagesMSec": 500,
|
"DelayBetweenMessagesMSec": 500,
|
||||||
"DelayJitterMSec": 50,
|
"DelayJitterMSec": 50,
|
||||||
"MinAmount": 10,
|
"MinAmount": 10,
|
||||||
@ -24,8 +24,8 @@
|
|||||||
//0.004
|
//0.004
|
||||||
"LoadSpike": {
|
"LoadSpike": {
|
||||||
"Probability": 0.002,
|
"Probability": 0.002,
|
||||||
|
"Duration": "00:00:15",
|
||||||
"Parameters": {
|
"Parameters": {
|
||||||
"Duration": "00:00:15",
|
|
||||||
"Delay": "00:00:00.0100000"
|
"Delay": "00:00:00.0100000"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -33,15 +33,15 @@
|
|||||||
//0.002
|
//0.002
|
||||||
"TransactionCorruption": {
|
"TransactionCorruption": {
|
||||||
"Probability": 0,
|
"Probability": 0,
|
||||||
|
"Duration": "00:00:15",
|
||||||
"Parameters": {
|
"Parameters": {
|
||||||
"SequenceLengthMin": 2,
|
|
||||||
"SequenceLengthMax": 10
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//0.003
|
//0.003
|
||||||
"LargeAmount": {
|
"LargeAmount": {
|
||||||
"Probability": 0,
|
"Probability": 0,
|
||||||
|
"Duration": "00:00:15",
|
||||||
"Parameters": {
|
"Parameters": {
|
||||||
"Value": "1000"
|
"Value": "1000"
|
||||||
}
|
}
|
||||||
@ -50,6 +50,7 @@
|
|||||||
//0.003
|
//0.003
|
||||||
"UnconfirmedTransaction": {
|
"UnconfirmedTransaction": {
|
||||||
"Probability": 0,
|
"Probability": 0,
|
||||||
|
"Duration": "00:00:15",
|
||||||
"Parameters": {
|
"Parameters": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user