DEEP-31 Transaction corruption anomaly added. New custom metrics added

This commit is contained in:
Andrey Shabarshov 2023-07-15 12:05:48 +01:00
parent f3979769e2
commit ea1f2abb98
8 changed files with 154 additions and 15 deletions

View File

@ -18,4 +18,6 @@ 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();
}

View File

@ -11,6 +11,7 @@ internal class LoadGenerator : IHostedService
private readonly Client _client;
private readonly ClientPool _clientPool;
private readonly PaymentRequestGenerator _paymentRequestGenerator;
private readonly TransactionDefiler _transactionDefiler;
private readonly ILogger<LoadGenerator> _logger;
private readonly IOptions<GeneratorSettings> _settings;
@ -22,6 +23,7 @@ internal class LoadGenerator : IHostedService
QRBee.Core.Client.Client client,
ClientPool clientPool,
PaymentRequestGenerator paymentRequestGenerator,
TransactionDefiler transactionDefiler,
ILogger<LoadGenerator> logger,
IOptions<GeneratorSettings> settings
)
@ -29,6 +31,7 @@ internal class LoadGenerator : IHostedService
_client = client;
_clientPool = clientPool;
_paymentRequestGenerator = paymentRequestGenerator;
_transactionDefiler = transactionDefiler;
_logger = logger;
_settings = settings;
@ -204,6 +207,8 @@ internal class LoadGenerator : IHostedService
GatewayTransactionId = res.GatewayTransactionId
};
_transactionDefiler.CorruptPaymentConfirmation(paymentConfirmation);
var confirmationTask = _client.ConfirmPayAsync(paymentConfirmation);
_confirmationQueue.Add(confirmationTask);
}
@ -242,6 +247,8 @@ internal class LoadGenerator : IHostedService
_rng.NextInRange(1, _settings.Value.NumberOfMerchants + 1)
);
_transactionDefiler.CorruptPaymentRequest(req);
var resp = _client.PayAsync(req);
_responseQueue.Add(resp);

View File

@ -30,6 +30,7 @@ builder.ConfigureServices((context, services) =>
.Configure<GeneratorSettings>(context.Configuration.GetSection("GeneratorSettings"))
.AddSingleton<ClientPool>()
.AddSingleton<PaymentRequestGenerator>()
.AddSingleton<TransactionDefiler>()
.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>()

View File

@ -0,0 +1,84 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using QRBee.Core.Data;
namespace QRBee.Load.Generator
{
internal class TransactionDefiler
{
private readonly ILogger<TransactionDefiler> _logger;
private ThreadSafeRandom _rng = new ThreadSafeRandom();
private double _corruptionProbability;
private double _coherentCorruptionProbability;
private bool _multipleCorruption = false;
private int _sequenceLengthMin;
private int _sequenceLengthMax;
private int _sequenceLength;
private int _corruptionCounter = 0;
public TransactionDefiler(IOptions<GeneratorSettings> settings, ILogger<TransactionDefiler> logger)
{
_logger = logger;
var transactionCorruption = settings.Value.TransactionCorruption;
_corruptionProbability = transactionCorruption.Probability;
var coherentTransactionCorruption = settings.Value.CoherentTransactionCorruption;
_coherentCorruptionProbability = coherentTransactionCorruption.Probability;
if (_coherentCorruptionProbability > 0)
{
if (coherentTransactionCorruption.Parameters.TryGetValue("SequenceLengthMin", out var s))
_sequenceLengthMin = int.Parse(s);
if (coherentTransactionCorruption.Parameters.TryGetValue("SequenceLengthMax", out s))
_sequenceLengthMax = int.Parse(s);
}
_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)
{
_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;
}
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;
}
}
}
public void CorruptPaymentConfirmation(PaymentConfirmation paymentConfirmation)
{
var dice = _rng.NextDouble();
if (dice < _corruptionProbability)
{
_logger.LogWarning($"Anomaly: Corrupted transaction confirmation Dice={dice}");
paymentConfirmation.GatewayTransactionId = "BadGatewayTransactionId";
}
}
}
}

View File

@ -14,22 +14,37 @@
"GeneratorSettings": {
"NumberOfClients": 100,
"NumberOfMerchants": 10,
"NumberOfThreads": 10,
"DelayBetweenMessagesMSec": 300,
"NumberOfThreads": 5,
"DelayBetweenMessagesMSec": 500,
"DelayJitterMSec": 50,
"MinAmount": 10,
"MaxAmount": 100,
"LoadSpike": {
"Probability": 0.001,
"Probability": 0.004,
"Parameters": {
"Duration": "00:00:15",
"Delay": "00:00:00.0100000"
}
},
"TransactionCorruption": {
"Probability": 0.004,
"Parameters": {
}
},
"CoherentTransactionCorruption": {
"Probability": 0.002,
"Parameters": {
"SequenceLengthMin": 2,
"SequenceLengthMax": 10
}
},
"LargeAmount": {
"Probability": 0.03,
"Probability": 0.003,
"Parameters": {
"Value": "1000"
}

View File

@ -37,11 +37,19 @@
</layout>
</appender>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="log-file.txt" />
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender" >
<bufferSize value="1"/>
<appender-ref ref="DebugAppender" />
<appender-ref ref="Console" />
<!-- <appender-ref ref="RollingFile" /> -->
<appender-ref ref="RollingFile" />
</appender>
<logger name="System.Net.Http">

View File

@ -8,6 +8,9 @@ namespace QRBee.Api.Services
private Counter<int> MerchantResponseCounter { get; }
private Counter<int> SucceededTransactionsCounter { get; }
private Counter<int> FailedTransactionsCounter { get; }
private Counter<int> CorruptTransactionsCounter { get; }
private Counter<int> SucceededPaymentConfirmationsCounter { get; }
private Counter<int> FailedPaymentConfirmationsCounter { get; }
private Counter<long> TotalCreditCardCheckTime { get; }
private Counter<long> TotalPaymentTime { get; }
@ -20,11 +23,16 @@ namespace QRBee.Api.Services
MerchantRequestCounter = meter.CreateCounter<int>("merchant-requests", description: "Merchant has sent a request");
MerchantResponseCounter = meter.CreateCounter<int>("merchant-responses");
SucceededTransactionsCounter = meter.CreateCounter<int>("transaction-succeeded", description: "Transaction succeeded");
FailedTransactionsCounter = meter.CreateCounter<int>("transaction-failed", description: "Transaction failed");
CorruptTransactionsCounter = meter.CreateCounter<int>("transaction-corrupt", description: "Transaction was corrupted");
TotalCreditCardCheckTime = meter.CreateCounter<long>("Total-credit-card-check-time","msec");
TotalPaymentTime = meter.CreateCounter<long>("Total-payment-time","msec");
SucceededPaymentConfirmationsCounter = meter.CreateCounter<int>("payment-confirmation-succeeded", description: "Payment confirmation succeeded");
FailedPaymentConfirmationsCounter = meter.CreateCounter<int>("payment-confirmation-failed", description: "Payment confirmation failed");
TotalCreditCardCheckTime = meter.CreateCounter<long>("total-credit-card-check-time","msec");
TotalPaymentTime = meter.CreateCounter<long>("total-payment-time","msec");
}
@ -32,6 +40,9 @@ namespace QRBee.Api.Services
public void AddMerchantResponse() => MerchantResponseCounter.Add(1);
public void AddSucceededTransaction() => SucceededTransactionsCounter.Add(1);
public void AddFailedTransaction() => FailedTransactionsCounter.Add(1);
public void AddCorruptTransaction() => CorruptTransactionsCounter.Add(1);
public void AddSucceededPaymentConfirmation() => SucceededPaymentConfirmationsCounter.Add(1);
public void AddFailedPaymentConfirmation() => FailedPaymentConfirmationsCounter.Add(1);
public void AddTotalCreditCardCheckTime(long milliseconds) => TotalCreditCardCheckTime.Add(milliseconds);
public void AddTotalPaymentTime(long milliseconds) => TotalPaymentTime.Add(milliseconds);

View File

@ -172,8 +172,17 @@ namespace QRBee.Api.Services
var t4 = CheckTransaction(tid);
//Parallel task execution
try
{
await Task.WhenAll(t2, t3, t4);
_logger.LogInformation($"Transaction=\"{tid}\" Fully validated");
}
catch(Exception)
{
_customMetrics.AddCorruptTransaction();
throw;
}
//5. Decrypt client card data
var creditCardCheckTime = Stopwatch.StartNew();
@ -206,17 +215,17 @@ namespace QRBee.Api.Services
//9. Record transaction with result
if (gatewayResponse.Success)
{
_customMetrics.AddSucceededTransaction();
info.Status=TransactionInfo.TransactionStatus.Succeeded;
info.GatewayTransactionId=gatewayResponse.GatewayTransactionId;
_customMetrics.AddSucceededTransaction();
}
else
{
_customMetrics.AddFailedTransaction();
info.Status = TransactionInfo.TransactionStatus.Rejected;
info.RejectReason = gatewayResponse.ErrorMessage;
_customMetrics.AddFailedTransaction();
}
await _storage.UpdateTransaction(info);
_logger.LogInformation($"Transaction=\"{tid}\" complete Status=\"{info.Status}\"");
@ -403,9 +412,11 @@ namespace QRBee.Api.Services
trans.Status = TransactionInfo.TransactionStatus.Confirmed;
await _storage.UpdateTransaction(trans);
_logger.LogInformation($"Transaction with MerchantTransactionId: {trans.MerchantTransactionId} confirmed");
_customMetrics.AddSucceededPaymentConfirmation();
}
else
{
_customMetrics.AddFailedPaymentConfirmation();
throw new ApplicationException($"Transaction with gatewayTransactionId:{value.GatewayTransactionId} failed.");
}
}