From 5b9633ff5914b9172720de43fd8380242ab9d217 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Tue, 20 Apr 2021 08:20:04 +0100 Subject: [PATCH] Added concurrent merchant processing --- .../Program.cs | 144 +++++++++++++----- .../TransactionDataGenerator.csproj | 8 +- .../TransactionDataGenerator.sln | 0 .../TransactionProcessor.DataGenerator.sln | 2 +- 4 files changed, 109 insertions(+), 45 deletions(-) rename TransactionProcessor.DataGenerator/{TransactionDataGenerator => DataGenerator}/Program.cs (66%) rename TransactionProcessor.DataGenerator/{TransactionDataGenerator => DataGenerator}/TransactionDataGenerator.csproj (85%) rename TransactionProcessor.DataGenerator/{TransactionDataGenerator => DataGenerator}/TransactionDataGenerator.sln (100%) diff --git a/TransactionProcessor.DataGenerator/TransactionDataGenerator/Program.cs b/TransactionProcessor.DataGenerator/DataGenerator/Program.cs similarity index 66% rename from TransactionProcessor.DataGenerator/TransactionDataGenerator/Program.cs rename to TransactionProcessor.DataGenerator/DataGenerator/Program.cs index 78a165e..4e8b6d8 100644 --- a/TransactionProcessor.DataGenerator/TransactionDataGenerator/Program.cs +++ b/TransactionProcessor.DataGenerator/DataGenerator/Program.cs @@ -3,11 +3,13 @@ namespace TransactionDataGenerator { using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; + using System.Threading.Tasks.Dataflow; using EstateManagement.Client; using EstateManagement.DataTransferObjects.Requests; using EstateManagement.DataTransferObjects.Responses; @@ -17,6 +19,17 @@ namespace TransactionDataGenerator using TransactionProcessor.Client; using TransactionProcessor.DataTransferObjects; + public class Gimp + { + public class V1 + { + public class GimpAddedEvent + { + + } + } + } + /// /// /// @@ -81,27 +94,28 @@ static async Task Main(string[] args) Program.TransactionProcessorClient = new TransactionProcessorClient(baseAddressFunc, httpClient); // Set an estate - Guid estateId = Guid.Parse("3bf2dab2-86d6-44e3-bcf8-51bec65cf8bc"); + Guid estateId = Guid.Parse("5b07ec2d-3856-4d05-ab31-e2dedb95ea27"); // Get a token await Program.GetToken(CancellationToken.None); // Get the the merchant list for the estate List merchants = await Program.EstateClient.GetMerchants(Program.TokenResponse.AccessToken, estateId, CancellationToken.None); - - //merchants = merchants.Where(m => m.MerchantName == "S7 Merchant").ToList(); - + // Set the date range - DateTime startDate = new DateTime(2020,12,01); - DateTime endDate = new DateTime(2020, 12, 11); + DateTime startDate = new DateTime(2021,4,1); + DateTime endDate = new DateTime(2021, 4, 19); // This is the date of te last generated transaction List dateRange = Program.GenerateDateRange(startDate, endDate); // Only use merchants that have a device merchants = merchants.Where(m => m.Devices != null && m.Devices.Any()).ToList(); - - await Program.GenerateTransactions(merchants, dateRange); - Console.WriteLine($"Process Complete - {Program.TransactionCount} transactions generated"); + foreach (DateTime dateTime in dateRange) + { + await Program.GenerateTransactions(merchants, dateTime, CancellationToken.None); + } + + Console.WriteLine($"Process Complete"); } /// @@ -133,36 +147,86 @@ private static async Task GetToken(CancellationToken cancellationToken) /// The merchants. /// The date range. private static async Task GenerateTransactions(List merchants, - List dateRange) + DateTime dateTime, + CancellationToken cancellationToken) { - foreach (DateTime dateTime in dateRange) + Int32 maxDegreeOfParallelism = 5; + Int32 boundedCapacityForActionBlock = merchants.Count; + + ActionBlock<(MerchantResponse merchant, CancellationToken cancellationToken)> workerBlock = + new ActionBlock<(MerchantResponse merchant, CancellationToken cancellationToken)>(async (message) => + { + try + { + Int32 transactionCount = 0; + + // Do a logon transaction for each merchant + await Program.DoLogonTransaction(message.merchant, dateTime); + Console + .WriteLine($"Logon sent for Merchant [{message.merchant.MerchantName}]"); + + // Now generate some sales + List saleRequests = + await Program.CreateSaleRequests(message.merchant, + dateTime); + + // Work out how much of a deposit the merchant needs (minus 1 sale) + IEnumerable> metadata = + saleRequests.Select(s => s.AdditionalTransactionMetadata); + List amounts = metadata.Select(m => m["Amount"]) + .ToList(); + + Decimal depositAmount = amounts.TakeLast(amounts.Count - 1) + .Sum(a => Decimal.Parse(a)); + + await Program.MakeMerchantDeposit(message.merchant, + depositAmount, + dateTime); + + // Now send the sales + saleRequests = saleRequests.OrderBy(s => s.TransactionDateTime) + .ToList(); + foreach (SaleTransactionRequest saleTransactionRequest in + saleRequests) + { + await Program.DoSaleTransaction(saleTransactionRequest); + Console + .WriteLine($"Sale sent for Merchant [{message.merchant.MerchantName}]"); + transactionCount++; + } + + Console.ForegroundColor = ConsoleColor.Green; + Console + .WriteLine($"{transactionCount} transactions generated for {message.merchant.MerchantName} on date {dateTime.ToLongDateString()}"); + } + catch(Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed"); + Console.WriteLine(ex); + } + }, + new ExecutionDataflowBlockOptions + { + MaxDegreeOfParallelism = maxDegreeOfParallelism, + BoundedCapacity = boundedCapacityForActionBlock + }); + + + try { - foreach (MerchantResponse merchant in merchants) + foreach (var merchant in merchants) { - // Do a logon transaction for each merchant - await Program.DoLogonTransaction(merchant, dateTime); - Console.WriteLine($"Logon sent for Merchant [{merchant.MerchantName}]"); - - // Now generate some sales - List saleRequests = await Program.CreateSaleRequests(merchant, dateTime); - - // Work out how much of a deposit the merchant needs (minus 1 sale) - IEnumerable> metadata = saleRequests.Select(s => s.AdditionalTransactionMetadata); - List amounts = metadata.Select(m => m["Amount"]).ToList(); - - Decimal depositAmount = amounts.TakeLast(amounts.Count - 1).Sum(a => Decimal.Parse(a)); - - await Program.MakeMerchantDeposit(merchant, depositAmount, dateTime); - - // Now send the sales - saleRequests = saleRequests.OrderBy(s => s.TransactionDateTime).ToList(); - foreach (SaleTransactionRequest saleTransactionRequest in saleRequests) - { - await Program.DoSaleTransaction(saleTransactionRequest); - Console.WriteLine($"Sale sent for Merchant [{merchant.MerchantName}]"); - TransactionCount++; - } + await workerBlock.SendAsync((merchant, cancellationToken), cancellationToken); } + + workerBlock.Complete(); + + await workerBlock.Completion; + } + catch (Exception e) + { + Console.WriteLine(e); } } @@ -184,7 +248,7 @@ await Program.EstateClient.MakeMerchantDeposit(Program.TokenResponse.AccessToken new MakeMerchantDepositRequest { Amount = depositAmount, - DepositDateTime = dateTime, + DepositDateTime = dateTime.AddSeconds(55), Reference = "Test Data Gen Deposit", Source = MerchantDepositSource.Manual }, @@ -203,8 +267,8 @@ private static async Task DoSaleTransaction(SaleTransactionRequest saleTransacti await Program.GetToken(CancellationToken.None); SerialisedMessage requestSerialisedMessage = new SerialisedMessage(); - requestSerialisedMessage.Metadata.Add("EstateId", saleTransactionRequest.EstateId.ToString()); - requestSerialisedMessage.Metadata.Add("MerchantId", saleTransactionRequest.MerchantId.ToString()); + requestSerialisedMessage.Metadata.Add("estate_id", saleTransactionRequest.EstateId.ToString()); + requestSerialisedMessage.Metadata.Add("merchant_id", saleTransactionRequest.MerchantId.ToString()); requestSerialisedMessage.SerialisedData = JsonConvert.SerializeObject(saleTransactionRequest, new JsonSerializerSettings { @@ -327,8 +391,8 @@ private static async Task DoLogonTransaction(MerchantResponse merchant, }; SerialisedMessage requestSerialisedMessage = new SerialisedMessage(); - requestSerialisedMessage.Metadata.Add("EstateId", merchant.EstateId.ToString()); - requestSerialisedMessage.Metadata.Add("MerchantId", merchant.MerchantId.ToString()); + requestSerialisedMessage.Metadata.Add("estate_id", merchant.EstateId.ToString()); + requestSerialisedMessage.Metadata.Add("merchant_id", merchant.MerchantId.ToString()); requestSerialisedMessage.SerialisedData = JsonConvert.SerializeObject(logonTransactionRequest, new JsonSerializerSettings { diff --git a/TransactionProcessor.DataGenerator/TransactionDataGenerator/TransactionDataGenerator.csproj b/TransactionProcessor.DataGenerator/DataGenerator/TransactionDataGenerator.csproj similarity index 85% rename from TransactionProcessor.DataGenerator/TransactionDataGenerator/TransactionDataGenerator.csproj rename to TransactionProcessor.DataGenerator/DataGenerator/TransactionDataGenerator.csproj index cc7dd97..ee2b6d5 100644 --- a/TransactionProcessor.DataGenerator/TransactionDataGenerator/TransactionDataGenerator.csproj +++ b/TransactionProcessor.DataGenerator/DataGenerator/TransactionDataGenerator.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,10 +6,10 @@ - + - - + + diff --git a/TransactionProcessor.DataGenerator/TransactionDataGenerator/TransactionDataGenerator.sln b/TransactionProcessor.DataGenerator/DataGenerator/TransactionDataGenerator.sln similarity index 100% rename from TransactionProcessor.DataGenerator/TransactionDataGenerator/TransactionDataGenerator.sln rename to TransactionProcessor.DataGenerator/DataGenerator/TransactionDataGenerator.sln diff --git a/TransactionProcessor.DataGenerator/TransactionProcessor.DataGenerator.sln b/TransactionProcessor.DataGenerator/TransactionProcessor.DataGenerator.sln index c47e40a..87c260b 100644 --- a/TransactionProcessor.DataGenerator/TransactionProcessor.DataGenerator.sln +++ b/TransactionProcessor.DataGenerator/TransactionProcessor.DataGenerator.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.30523.141 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransactionGeneratorWorker", "TransactionGeneratorWorker\TransactionGeneratorWorker.csproj", "{DF5050CF-91D8-4248-BEF6-25FAF1A05677}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransactionDataGenerator", "TransactionDataGenerator\TransactionDataGenerator.csproj", "{BC85594F-6F0B-425D-868F-560B9A3AC104}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransactionDataGenerator", "DataGenerator\TransactionDataGenerator.csproj", "{BC85594F-6F0B-425D-868F-560B9A3AC104}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution