mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
DEEP-33, DEEP-34, DEEP-35 Anomaly generators separated, Connections set to 500, Custom metrics added
This commit is contained in:
parent
ea1f2abb98
commit
aeb8154241
@ -19,5 +19,5 @@ internal class GeneratorSettings
|
||||
public Anomaly LoadSpike { get; set; } = new();
|
||||
public Anomaly LargeAmount { get; set; } = new();
|
||||
public Anomaly TransactionCorruption { get; set;} = new();
|
||||
public Anomaly CoherentTransactionCorruption { get; set; } = new();
|
||||
public Anomaly UnconfirmedTransaction { get; set; } = new();
|
||||
}
|
||||
|
||||
@ -12,18 +12,18 @@ internal class LoadGenerator : IHostedService
|
||||
private readonly ClientPool _clientPool;
|
||||
private readonly PaymentRequestGenerator _paymentRequestGenerator;
|
||||
private readonly TransactionDefiler _transactionDefiler;
|
||||
private readonly UnconfirmedTransactions _unconfirmedTransactions;
|
||||
private readonly LoadSpike _loadSpike;
|
||||
private readonly ILogger<LoadGenerator> _logger;
|
||||
private readonly IOptions<GeneratorSettings> _settings;
|
||||
|
||||
private TimeSpan _spikeDuration;
|
||||
private TimeSpan _spikeDelay;
|
||||
private double _spikeProbability;
|
||||
|
||||
public LoadGenerator(
|
||||
QRBee.Core.Client.Client client,
|
||||
ClientPool clientPool,
|
||||
PaymentRequestGenerator paymentRequestGenerator,
|
||||
TransactionDefiler transactionDefiler,
|
||||
UnconfirmedTransactions unconfirmedTransactions,
|
||||
LoadSpike loadSpike,
|
||||
ILogger<LoadGenerator> logger,
|
||||
IOptions<GeneratorSettings> settings
|
||||
)
|
||||
@ -32,30 +32,10 @@ internal class LoadGenerator : IHostedService
|
||||
_clientPool = clientPool;
|
||||
_paymentRequestGenerator = paymentRequestGenerator;
|
||||
_transactionDefiler = transactionDefiler;
|
||||
_unconfirmedTransactions = unconfirmedTransactions;
|
||||
_loadSpike = loadSpike;
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@ -199,6 +179,8 @@ internal class LoadGenerator : IHostedService
|
||||
Interlocked.Increment(ref _paymentsProcessed);
|
||||
|
||||
if (res?.Success ?? false)
|
||||
{
|
||||
if (_unconfirmedTransactions.ShouldConfirm())
|
||||
{
|
||||
var paymentConfirmation = new PaymentConfirmation
|
||||
{
|
||||
@ -212,6 +194,7 @@ internal class LoadGenerator : IHostedService
|
||||
var confirmationTask = _client.ConfirmPayAsync(paymentConfirmation);
|
||||
_confirmationQueue.Add(confirmationTask);
|
||||
}
|
||||
}
|
||||
else
|
||||
Interlocked.Increment(ref _paymentsFailed);
|
||||
}
|
||||
@ -236,8 +219,6 @@ internal class LoadGenerator : IHostedService
|
||||
// initial delay
|
||||
await Task.Delay(500 + _rng.Next() % 124);
|
||||
|
||||
var spikeEnd = DateTime.MinValue;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@ -260,28 +241,7 @@ internal class LoadGenerator : IHostedService
|
||||
_logger.LogError(ex, "Generation thread");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
await _loadSpike.Delay();
|
||||
}
|
||||
}
|
||||
}
|
||||
81
QRBee.Load.Generator/LoadSpike.cs
Normal file
81
QRBee.Load.Generator/LoadSpike.cs
Normal file
@ -0,0 +1,81 @@
|
||||
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<GeneratorSettings> _settings;
|
||||
private readonly ILogger<LoadSpike> _logger;
|
||||
|
||||
private TimeSpan _spikeDuration;
|
||||
private TimeSpan _spikeDelay;
|
||||
private double _spikeProbability;
|
||||
|
||||
private ThreadSafeRandom _rng = new();
|
||||
private DateTime _spikeEnd = DateTime.MinValue;
|
||||
|
||||
public LoadSpike( IOptions<GeneratorSettings> settings, ILogger<LoadSpike> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ using QRBee.Api.Services;
|
||||
using QRBee.Droid.Services;
|
||||
using QRBee.Load.Generator;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Net;
|
||||
|
||||
Console.WriteLine("=== QRBee artificaial load generator ===");
|
||||
|
||||
@ -31,13 +32,15 @@ builder.ConfigureServices((context, services) =>
|
||||
.AddSingleton<ClientPool>()
|
||||
.AddSingleton<PaymentRequestGenerator>()
|
||||
.AddSingleton<TransactionDefiler>()
|
||||
.AddSingleton<UnconfirmedTransactions>()
|
||||
.AddSingleton<LoadSpike>()
|
||||
.AddSingleton<PrivateKeyHandlerFactory>(x => no => new PrivateKeyHandler(x.GetRequiredService<ILogger<ServerPrivateKeyHandler>>(), x.GetRequiredService<IConfiguration>(), no))
|
||||
.AddSingleton<SecurityServiceFactory>(x => no => new AndroidSecurityService(x.GetRequiredService<PrivateKeyHandlerFactory>()(no)))
|
||||
.AddHostedService<LoadGenerator>()
|
||||
;
|
||||
});
|
||||
|
||||
|
||||
ServicePointManager.DefaultConnectionLimit = 500;
|
||||
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
|
||||
@ -7,14 +7,13 @@ namespace QRBee.Load.Generator
|
||||
internal class TransactionDefiler
|
||||
{
|
||||
private readonly ILogger<TransactionDefiler> _logger;
|
||||
private ThreadSafeRandom _rng = new ThreadSafeRandom();
|
||||
|
||||
private ThreadSafeRandom _rng = new();
|
||||
|
||||
private double _corruptionProbability;
|
||||
private double _coherentCorruptionProbability;
|
||||
private bool _multipleCorruption = false;
|
||||
private int _sequenceLengthMin;
|
||||
private int _sequenceLengthMax;
|
||||
private int _sequenceLength;
|
||||
private int _corruptionCounter = 0;
|
||||
private int _corruptionCounter;
|
||||
|
||||
public TransactionDefiler(IOptions<GeneratorSettings> settings, ILogger<TransactionDefiler> logger)
|
||||
{
|
||||
@ -22,63 +21,40 @@ namespace QRBee.Load.Generator
|
||||
|
||||
var transactionCorruption = settings.Value.TransactionCorruption;
|
||||
_corruptionProbability = transactionCorruption.Probability;
|
||||
|
||||
var coherentTransactionCorruption = settings.Value.CoherentTransactionCorruption;
|
||||
_coherentCorruptionProbability = coherentTransactionCorruption.Probability;
|
||||
if (_coherentCorruptionProbability > 0)
|
||||
if (_corruptionProbability > 0)
|
||||
{
|
||||
if (coherentTransactionCorruption.Parameters.TryGetValue("SequenceLengthMin", out var s))
|
||||
if (transactionCorruption.Parameters.TryGetValue("SequenceLengthMin", out var s))
|
||||
_sequenceLengthMin = int.Parse(s);
|
||||
if (coherentTransactionCorruption.Parameters.TryGetValue("SequenceLengthMax", out s))
|
||||
if (transactionCorruption.Parameters.TryGetValue("SequenceLengthMax", out s))
|
||||
_sequenceLengthMax = int.Parse(s);
|
||||
_logger.LogDebug($"Transaction corruption configured: Probability={_corruptionProbability}, SequenceLengthMin={_sequenceLengthMin}, SequenceLengthMax={_sequenceLengthMax}");
|
||||
}
|
||||
|
||||
|
||||
_logger.LogDebug($"Transaction corruption configured: Probability={_corruptionProbability}");
|
||||
_logger.LogDebug($"Coherent transaction corruption configured: Probability={_coherentCorruptionProbability}, SequenceLengthMin={_sequenceLengthMin}, SequenceLengthMax={_sequenceLengthMax}");
|
||||
}
|
||||
|
||||
public void CorruptPaymentRequest(PaymentRequest paymentRequest)
|
||||
{
|
||||
var dice = _rng.NextDouble();
|
||||
if(_multipleCorruption)
|
||||
if(Interlocked.Decrement(ref _corruptionCounter) > 0 )
|
||||
{
|
||||
_corruptionCounter++;
|
||||
_logger.LogWarning($"Anomaly: Coherent corrupted transaction Dice={dice}, Corruption counter = {_corruptionCounter}, Sequence length = {_sequenceLength}");
|
||||
paymentRequest.ClientResponse.MerchantRequest.Amount += 0.01M;
|
||||
|
||||
if (_corruptionCounter >= _sequenceLength)
|
||||
{
|
||||
_corruptionCounter = 0;
|
||||
_multipleCorruption = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var dice = _rng.NextDouble();
|
||||
if (dice < _corruptionProbability)
|
||||
{
|
||||
_logger.LogWarning($"Anomaly: Corrupted transaction Dice={dice}");
|
||||
paymentRequest.ClientResponse.MerchantRequest.Amount += 10M;
|
||||
|
||||
if(dice < _coherentCorruptionProbability && _multipleCorruption==false)
|
||||
{
|
||||
_sequenceLength = _rng.NextInRange(_sequenceLengthMin,_sequenceLengthMax);
|
||||
_multipleCorruption = true;
|
||||
var cc = _rng.NextInRange(_sequenceLengthMin, _sequenceLengthMax);
|
||||
_corruptionCounter = cc;
|
||||
_logger.LogWarning($"Anomaly: Corrupted transaction. Dice={dice} SequenceLength={cc}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void CorruptPaymentConfirmation(PaymentConfirmation paymentConfirmation)
|
||||
{
|
||||
var dice = _rng.NextDouble();
|
||||
if (dice < _corruptionProbability)
|
||||
if (Interlocked.Decrement(ref _corruptionCounter) > 0)
|
||||
{
|
||||
_logger.LogWarning($"Anomaly: Corrupted transaction confirmation Dice={dice}");
|
||||
paymentConfirmation.GatewayTransactionId = "BadGatewayTransactionId";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
QRBee.Load.Generator/UnconfirmedTransactions.cs
Normal file
52
QRBee.Load.Generator/UnconfirmedTransactions.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace QRBee.Load.Generator;
|
||||
|
||||
internal class UnconfirmedTransactions
|
||||
{
|
||||
private readonly ILogger<UnconfirmedTransactions> _logger;
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -14,40 +14,43 @@
|
||||
"GeneratorSettings": {
|
||||
"NumberOfClients": 100,
|
||||
"NumberOfMerchants": 10,
|
||||
"NumberOfThreads": 5,
|
||||
"NumberOfThreads": 10,
|
||||
"DelayBetweenMessagesMSec": 500,
|
||||
"DelayJitterMSec": 50,
|
||||
"MinAmount": 10,
|
||||
"MaxAmount": 100,
|
||||
|
||||
//0.004
|
||||
"LoadSpike": {
|
||||
"Probability": 0.004,
|
||||
"Probability": 0.005,
|
||||
"Parameters": {
|
||||
"Duration": "00:00:15",
|
||||
"Delay": "00:00:00.0100000"
|
||||
}
|
||||
},
|
||||
|
||||
//0.002
|
||||
"TransactionCorruption": {
|
||||
"Probability": 0.004,
|
||||
"Parameters": {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
"CoherentTransactionCorruption": {
|
||||
"Probability": 0.002,
|
||||
"Probability": 0,
|
||||
"Parameters": {
|
||||
"SequenceLengthMin": 2,
|
||||
"SequenceLengthMax": 10
|
||||
}
|
||||
},
|
||||
|
||||
//0.003
|
||||
"LargeAmount": {
|
||||
"Probability": 0.003,
|
||||
"Probability": 0,
|
||||
"Parameters": {
|
||||
"Value": "1000"
|
||||
}
|
||||
},
|
||||
|
||||
//0.003
|
||||
"UnconfirmedTransaction": {
|
||||
"Probability": 0,
|
||||
"Parameters": {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ namespace QRBee.Api.Services
|
||||
private Counter<long> TotalCreditCardCheckTime { get; }
|
||||
private Counter<long> TotalPaymentTime { get; }
|
||||
|
||||
private UpDownCounter<int> ConcurrentPayments { get; }
|
||||
private UpDownCounter<int> ConcurrentConfirmations { get; }
|
||||
|
||||
public string MetricName { get; }
|
||||
|
||||
@ -33,6 +35,10 @@ namespace QRBee.Api.Services
|
||||
|
||||
TotalCreditCardCheckTime = meter.CreateCounter<long>("total-credit-card-check-time","msec");
|
||||
TotalPaymentTime = meter.CreateCounter<long>("total-payment-time","msec");
|
||||
|
||||
ConcurrentPayments = meter.CreateUpDownCounter<int>("concurrent-payments");
|
||||
ConcurrentConfirmations = meter.CreateUpDownCounter<int>("concurrent-confirmations");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -46,5 +52,10 @@ namespace QRBee.Api.Services
|
||||
public void AddTotalCreditCardCheckTime(long milliseconds) => TotalCreditCardCheckTime.Add(milliseconds);
|
||||
public void AddTotalPaymentTime(long milliseconds) => TotalPaymentTime.Add(milliseconds);
|
||||
|
||||
public void IncreaseConcurrentPayments() => ConcurrentPayments.Add(1);
|
||||
public void DecreaseConcurrentPayments() => ConcurrentPayments.Add(-1);
|
||||
public void IncreaseConcurrentConfirmations() => ConcurrentConfirmations.Add(1);
|
||||
public void DecreaseConcurrentConfirmation() => ConcurrentConfirmations.Add(-1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,15 @@ namespace QRBee.Api.Services
|
||||
private const int MaxNameLength = 512;
|
||||
private const int MaxEmailLength = 512;
|
||||
|
||||
public QRBeeAPIService(IStorage storage, ISecurityService securityService, IPrivateKeyHandler privateKeyHandler, IPaymentGateway paymentGateway, ILogger<QRBeeAPIService> logger, TransactionMonitoring transactionMonitoring, CustomMetrics metrics)
|
||||
public QRBeeAPIService(
|
||||
IStorage storage,
|
||||
ISecurityService securityService,
|
||||
IPrivateKeyHandler privateKeyHandler,
|
||||
IPaymentGateway paymentGateway,
|
||||
ILogger<QRBeeAPIService> logger,
|
||||
TransactionMonitoring transactionMonitoring,
|
||||
CustomMetrics metrics
|
||||
)
|
||||
{
|
||||
_storage = storage;
|
||||
_securityService = securityService;
|
||||
@ -132,8 +140,19 @@ namespace QRBee.Api.Services
|
||||
throw new ApplicationException($"Digital signature is not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PaymentResponse> Pay(PaymentRequest value)
|
||||
{
|
||||
_customMetrics.IncreaseConcurrentPayments();
|
||||
try
|
||||
{
|
||||
return await PayInternal(value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_customMetrics.DecreaseConcurrentPayments();
|
||||
}
|
||||
}
|
||||
public async Task<PaymentResponse> PayInternal(PaymentRequest value)
|
||||
{
|
||||
|
||||
// --------------------------------- RECEIVE PAYMENT REQUEST --------------------------------------
|
||||
@ -233,6 +252,7 @@ namespace QRBee.Api.Services
|
||||
//10. Make response for merchant
|
||||
var response = MakePaymentResponse(value, info.TransactionId ?? "", gatewayResponse.GatewayTransactionId ?? "", info.Status==TransactionInfo.TransactionStatus.Succeeded, info.RejectReason);
|
||||
|
||||
|
||||
_customMetrics.AddMerchantResponse();
|
||||
return response;
|
||||
}
|
||||
@ -404,6 +424,18 @@ namespace QRBee.Api.Services
|
||||
}
|
||||
|
||||
public async Task ConfirmPay(PaymentConfirmation value)
|
||||
{
|
||||
_customMetrics.IncreaseConcurrentConfirmations();
|
||||
try
|
||||
{
|
||||
await ConfirmPayInternal(value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_customMetrics.DecreaseConcurrentConfirmation();
|
||||
}
|
||||
}
|
||||
public async Task ConfirmPayInternal(PaymentConfirmation value)
|
||||
{
|
||||
var id = $"{value.MerchantId}-{value.MerchantTransactionId}";
|
||||
var trans = await _storage.GetTransactionInfoByTransactionId(id);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user