From aeb815424195db5da6b32a744ab05712d47a5879 Mon Sep 17 00:00:00 2001 From: Andrey Shabarshov Date: Tue, 18 Jul 2023 17:34:14 +0100 Subject: [PATCH] DEEP-33, DEEP-34, DEEP-35 Anomaly generators separated, Connections set to 500, Custom metrics added --- QRBee.Load.Generator/GeneratorSettings.cs | 2 +- QRBee.Load.Generator/LoadGenerator.cs | 86 +++++-------------- QRBee.Load.Generator/LoadSpike.cs | 81 +++++++++++++++++ QRBee.Load.Generator/Program.cs | 5 +- QRBee.Load.Generator/TransactionDefiler.cs | 58 ++++--------- .../UnconfirmedTransactions.cs | 52 +++++++++++ QRBee.Load.Generator/appsettings.json | 25 +++--- QRBeeApi/Services/CustomMetrics.cs | 65 ++++++++------ QRBeeApi/Services/QRBeeAPIService.cs | 50 +++++++++-- 9 files changed, 271 insertions(+), 153 deletions(-) create mode 100644 QRBee.Load.Generator/LoadSpike.cs create mode 100644 QRBee.Load.Generator/UnconfirmedTransactions.cs diff --git a/QRBee.Load.Generator/GeneratorSettings.cs b/QRBee.Load.Generator/GeneratorSettings.cs index 4a22931..edd495d 100644 --- a/QRBee.Load.Generator/GeneratorSettings.cs +++ b/QRBee.Load.Generator/GeneratorSettings.cs @@ -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(); } diff --git a/QRBee.Load.Generator/LoadGenerator.cs b/QRBee.Load.Generator/LoadGenerator.cs index 408670b..dfbfa8a 100644 --- a/QRBee.Load.Generator/LoadGenerator.cs +++ b/QRBee.Load.Generator/LoadGenerator.cs @@ -8,22 +8,22 @@ using QRBee.Load.Generator; internal class LoadGenerator : IHostedService { - private readonly Client _client; - private readonly ClientPool _clientPool; - private readonly PaymentRequestGenerator _paymentRequestGenerator; - private readonly TransactionDefiler _transactionDefiler; - private readonly ILogger _logger; + private readonly Client _client; + private readonly ClientPool _clientPool; + private readonly PaymentRequestGenerator _paymentRequestGenerator; + private readonly TransactionDefiler _transactionDefiler; + private readonly UnconfirmedTransactions _unconfirmedTransactions; + private readonly LoadSpike _loadSpike; + private readonly ILogger _logger; private readonly IOptions _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 logger, IOptions 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) { @@ -200,17 +180,20 @@ internal class LoadGenerator : IHostedService if (res?.Success ?? false) { - var paymentConfirmation = new PaymentConfirmation + if (_unconfirmedTransactions.ShouldConfirm()) { - MerchantId = res.PaymentRequest.ClientResponse.MerchantRequest.MerchantId, - MerchantTransactionId = res.PaymentRequest.ClientResponse.MerchantRequest.MerchantTransactionId, - GatewayTransactionId = res.GatewayTransactionId - }; + var paymentConfirmation = new PaymentConfirmation + { + MerchantId = res.PaymentRequest.ClientResponse.MerchantRequest.MerchantId, + MerchantTransactionId = res.PaymentRequest.ClientResponse.MerchantRequest.MerchantTransactionId, + GatewayTransactionId = res.GatewayTransactionId + }; - _transactionDefiler.CorruptPaymentConfirmation(paymentConfirmation); + _transactionDefiler.CorruptPaymentConfirmation(paymentConfirmation); - var confirmationTask = _client.ConfirmPayAsync(paymentConfirmation); - _confirmationQueue.Add(confirmationTask); + 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(); } } } \ No newline at end of file diff --git a/QRBee.Load.Generator/LoadSpike.cs b/QRBee.Load.Generator/LoadSpike.cs new file mode 100644 index 0000000..28d3598 --- /dev/null +++ b/QRBee.Load.Generator/LoadSpike.cs @@ -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 _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); + } + } + + } +} diff --git a/QRBee.Load.Generator/Program.cs b/QRBee.Load.Generator/Program.cs index 868bde8..2699d16 100644 --- a/QRBee.Load.Generator/Program.cs +++ b/QRBee.Load.Generator/Program.cs @@ -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() .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton(x => no => new PrivateKeyHandler(x.GetRequiredService>(), x.GetRequiredService(), no)) .AddSingleton(x => no => new AndroidSecurityService(x.GetRequiredService()(no))) .AddHostedService() ; }); - +ServicePointManager.DefaultConnectionLimit = 500; var host = builder.Build(); host.Run(); diff --git a/QRBee.Load.Generator/TransactionDefiler.cs b/QRBee.Load.Generator/TransactionDefiler.cs index 37c20d2..00adb8b 100644 --- a/QRBee.Load.Generator/TransactionDefiler.cs +++ b/QRBee.Load.Generator/TransactionDefiler.cs @@ -7,14 +7,13 @@ namespace QRBee.Load.Generator internal class TransactionDefiler { private readonly ILogger _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 _sequenceLengthMin; + private int _sequenceLengthMax; + private int _corruptionCounter; public TransactionDefiler(IOptions settings, ILogger 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"; } - } } } diff --git a/QRBee.Load.Generator/UnconfirmedTransactions.cs b/QRBee.Load.Generator/UnconfirmedTransactions.cs new file mode 100644 index 0000000..56c84a9 --- /dev/null +++ b/QRBee.Load.Generator/UnconfirmedTransactions.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace QRBee.Load.Generator; + +internal class UnconfirmedTransactions +{ + private readonly ILogger _logger; + + private double _unconfirmedProbability; + private TimeSpan _unconfirmedDuration; + private DateTime _anomalyEnd = DateTime.MinValue; + private ThreadSafeRandom _rng = new(); + + public UnconfirmedTransactions(IOptions settings, ILogger 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; + } +} diff --git a/QRBee.Load.Generator/appsettings.json b/QRBee.Load.Generator/appsettings.json index 193d165..03e7ed1 100644 --- a/QRBee.Load.Generator/appsettings.json +++ b/QRBee.Load.Generator/appsettings.json @@ -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": { + } } } } diff --git a/QRBeeApi/Services/CustomMetrics.cs b/QRBeeApi/Services/CustomMetrics.cs index 774702b..2ba58e0 100644 --- a/QRBeeApi/Services/CustomMetrics.cs +++ b/QRBeeApi/Services/CustomMetrics.cs @@ -4,47 +4,58 @@ namespace QRBee.Api.Services { public class CustomMetrics { - private Counter MerchantRequestCounter { get; } - private Counter MerchantResponseCounter { get; } - private Counter SucceededTransactionsCounter { get; } - private Counter FailedTransactionsCounter { get; } - private Counter CorruptTransactionsCounter { get; } - private Counter SucceededPaymentConfirmationsCounter { get; } - private Counter FailedPaymentConfirmationsCounter { get; } - private Counter TotalCreditCardCheckTime { get; } - private Counter TotalPaymentTime { get; } - + private Counter MerchantRequestCounter { get; } + private Counter MerchantResponseCounter { get; } + private Counter SucceededTransactionsCounter { get; } + private Counter FailedTransactionsCounter { get; } + private Counter CorruptTransactionsCounter { get; } + private Counter SucceededPaymentConfirmationsCounter { get; } + private Counter FailedPaymentConfirmationsCounter { get; } + private Counter TotalCreditCardCheckTime { get; } + private Counter TotalPaymentTime { get; } + + private UpDownCounter ConcurrentPayments { get; } + private UpDownCounter ConcurrentConfirmations { get; } public string MetricName { get; } public CustomMetrics(string meterName = "QRBeeMetrics") { - var meter = new Meter(meterName); + var meter = new Meter(meterName); MetricName = meterName; - MerchantRequestCounter = meter.CreateCounter("merchant-requests", description: "Merchant has sent a request"); - MerchantResponseCounter = meter.CreateCounter("merchant-responses"); + MerchantRequestCounter = meter.CreateCounter("merchant-requests", description: "Merchant has sent a request"); + MerchantResponseCounter = meter.CreateCounter("merchant-responses"); - SucceededTransactionsCounter = meter.CreateCounter("transaction-succeeded", description: "Transaction succeeded"); - FailedTransactionsCounter = meter.CreateCounter("transaction-failed", description: "Transaction failed"); - CorruptTransactionsCounter = meter.CreateCounter("transaction-corrupt", description: "Transaction was corrupted"); + SucceededTransactionsCounter = meter.CreateCounter("transaction-succeeded", description: "Transaction succeeded"); + FailedTransactionsCounter = meter.CreateCounter("transaction-failed", description: "Transaction failed"); + CorruptTransactionsCounter = meter.CreateCounter("transaction-corrupt", description: "Transaction was corrupted"); SucceededPaymentConfirmationsCounter = meter.CreateCounter("payment-confirmation-succeeded", description: "Payment confirmation succeeded"); - FailedPaymentConfirmationsCounter = meter.CreateCounter("payment-confirmation-failed", description: "Payment confirmation failed"); + FailedPaymentConfirmationsCounter = meter.CreateCounter("payment-confirmation-failed", description: "Payment confirmation failed"); + + TotalCreditCardCheckTime = meter.CreateCounter("total-credit-card-check-time","msec"); + TotalPaymentTime = meter.CreateCounter("total-payment-time","msec"); + + ConcurrentPayments = meter.CreateUpDownCounter("concurrent-payments"); + ConcurrentConfirmations = meter.CreateUpDownCounter("concurrent-confirmations"); - TotalCreditCardCheckTime = meter.CreateCounter("total-credit-card-check-time","msec"); - TotalPaymentTime = meter.CreateCounter("total-payment-time","msec"); } - public void AddMerchantRequest() => MerchantRequestCounter.Add(1); - 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 AddMerchantRequest() => MerchantRequestCounter.Add(1); + 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); + 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); } } diff --git a/QRBeeApi/Services/QRBeeAPIService.cs b/QRBeeApi/Services/QRBeeAPIService.cs index e39b9f7..103cc1d 100644 --- a/QRBeeApi/Services/QRBeeAPIService.cs +++ b/QRBeeApi/Services/QRBeeAPIService.cs @@ -15,20 +15,28 @@ namespace QRBee.Api.Services /// public class QRBeeAPIService: IQRBeeAPI { - private readonly IStorage _storage; - private readonly ISecurityService _securityService; - private readonly IPrivateKeyHandler _privateKeyHandler; - private readonly IPaymentGateway _paymentGateway; + private readonly IStorage _storage; + private readonly ISecurityService _securityService; + private readonly IPrivateKeyHandler _privateKeyHandler; + private readonly IPaymentGateway _paymentGateway; private readonly ILogger _logger; - private readonly TransactionMonitoring _transactionMonitoring; - private static readonly object _lock = new (); + private readonly TransactionMonitoring _transactionMonitoring; + private static readonly object _lock = new (); - private readonly CustomMetrics _customMetrics; + private readonly CustomMetrics _customMetrics; private const int MaxNameLength = 512; private const int MaxEmailLength = 512; - public QRBeeAPIService(IStorage storage, ISecurityService securityService, IPrivateKeyHandler privateKeyHandler, IPaymentGateway paymentGateway, ILogger logger, TransactionMonitoring transactionMonitoring, CustomMetrics metrics) + public QRBeeAPIService( + IStorage storage, + ISecurityService securityService, + IPrivateKeyHandler privateKeyHandler, + IPaymentGateway paymentGateway, + ILogger 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 Pay(PaymentRequest value) + { + _customMetrics.IncreaseConcurrentPayments(); + try + { + return await PayInternal(value); + } + finally + { + _customMetrics.DecreaseConcurrentPayments(); + } + } + public async Task PayInternal(PaymentRequest value) { // --------------------------------- RECEIVE PAYMENT REQUEST -------------------------------------- @@ -232,6 +251,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);