Skip to content

Commit

Permalink
Merge pull request #2 from Particular/add-clientui
Browse files Browse the repository at this point in the history
Add Main UI projects
WilliamBZA authored Aug 7, 2024
2 parents b56008c + 73f3d83 commit 427bb02
Showing 31 changed files with 801 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/binaries/**/*
7 changes: 7 additions & 0 deletions src/Billing/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# Justification: Test project
dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion
20 changes: 20 additions & 0 deletions src/Billing/Billing.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>..\binaries\Billing\</OutputPath>
<ApplicationIcon>failures.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Messages\Messages.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
</Project>
20 changes: 20 additions & 0 deletions src/Billing/OrderPlacedHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Billing;

using System.Threading.Tasks;
using MassTransit;
using Messages;

public class OrderPlacedHandler(SimulationEffects simulationEffects) : IConsumer<OrderPlaced>
{
public async Task Consume(ConsumeContext<OrderPlaced> context)
{
await simulationEffects.SimulatedMessageProcessing(context.CancellationToken);

var orderBilled = new OrderBilled
{
OrderId = context.Message.OrderId
};

await context.Publish(orderBilled);
}
}
86 changes: 86 additions & 0 deletions src/Billing/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma warning disable IDE0010
namespace Billing;

using Microsoft.Extensions.Hosting;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

class Program
{
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});

cfg.ConfigureEndpoints(context);
});

x.AddConfigureEndpointsCallback((name, cfg) =>
{
if (cfg is IRabbitMqReceiveEndpointConfigurator rmq)
{
rmq.SetQuorumQueue();
}
});

x.AddConsumers(Assembly.GetExecutingAssembly());
});

services.AddSingleton<SimulationEffects>();
});

return host;
}

static async Task Main(string[] args)
{
Console.Title = "Failure rate (Billing)";
Console.SetWindowSize(65, 15);

var host = CreateHostBuilder(args).Build();
await host.StartAsync();

var state = host.Services.GetRequiredService<SimulationEffects>();
await RunUserInterfaceLoop(state);
}

static Task RunUserInterfaceLoop(SimulationEffects state)
{
while (true)
{
Console.Clear();
Console.WriteLine("Billing Endpoint");
Console.WriteLine("Press F to increase the simulated failure rate");
Console.WriteLine("Press S to decrease the simulated failure rate");
Console.WriteLine("Press ESC to quit");
Console.WriteLine();

state.WriteState(Console.Out);

var input = Console.ReadKey(true);

switch (input.Key)
{
case ConsoleKey.F:
state.IncreaseFailureRate();
break;
case ConsoleKey.S:
state.DecreaseFailureRate();
break;
case ConsoleKey.Escape:
return Task.CompletedTask;
}
}
}
}
23 changes: 23 additions & 0 deletions src/Billing/SimulationEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Billing;

public class SimulationEffects
{
public void IncreaseFailureRate() => failureRate = Math.Min(1, failureRate + FailureRateIncrement);

public void DecreaseFailureRate() => failureRate = Math.Max(0, failureRate - FailureRateIncrement);

public void WriteState(TextWriter output) => output.WriteLine("Failure rate: {0:P0}", failureRate);

public async Task SimulatedMessageProcessing(CancellationToken cancellationToken = default)
{
await Task.Delay(200, cancellationToken);

if (Random.Shared.NextDouble() < failureRate)
{
throw new Exception("BOOM! A failure occurred");
}
}

double failureRate;
const double FailureRateIncrement = 0.1;
}
Binary file added src/Billing/failures.ico
Binary file not shown.
7 changes: 7 additions & 0 deletions src/ClientUI/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# Justification: Test project
dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion
20 changes: 20 additions & 0 deletions src/ClientUI/ClientUI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>..\binaries\ClientUI\</OutputPath>
<ApplicationIcon>traffic.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Messages\Messages.csproj" />
</ItemGroup>
</Project>
84 changes: 84 additions & 0 deletions src/ClientUI/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#pragma warning disable IDE0010
namespace ClientUI;

using Microsoft.Extensions.Hosting;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

class Program
{
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
{
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});

cfg.ConfigureEndpoints(context);
});

x.AddConfigureEndpointsCallback((name, cfg) =>
{
if (cfg is IRabbitMqReceiveEndpointConfigurator rmq)
{
rmq.SetQuorumQueue();
}
});

x.AddConsumers(Assembly.GetExecutingAssembly());
});

services.AddSingleton<SimulatedCustomers>();
services.AddHostedService(p => p.GetRequiredService<SimulatedCustomers>());
});

return host;
}

static async Task Main(string[] args)
{
Console.Title = "Load (ClientUI)";
Console.SetWindowSize(65, 15);

var host = CreateHostBuilder(args).Build();
await host.StartAsync();

var customers = host.Services.GetRequiredService<SimulatedCustomers>();

await RunUserInterfaceLoop(customers);
}

static Task RunUserInterfaceLoop(SimulatedCustomers simulatedCustomers)
{
while (true)
{
Console.Clear();
Console.WriteLine("Simulating customers placing orders on a website");
Console.WriteLine("Press T to toggle High/Low traffic mode");
Console.WriteLine("Press ESC to quit");
Console.WriteLine();

simulatedCustomers.WriteState(Console.Out);

var input = Console.ReadKey(true);

switch (input.Key)
{
case ConsoleKey.T:
simulatedCustomers.ToggleTrafficMode();
break;
case ConsoleKey.Escape:
return Task.CompletedTask;
}
}
}
}
74 changes: 74 additions & 0 deletions src/ClientUI/SimulatedCustomers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
namespace ClientUI;

using MassTransit;
using Messages;
using Microsoft.Extensions.Hosting;

class SimulatedCustomers(IBus _bus) : BackgroundService
{
public void WriteState(TextWriter output)
{
var trafficMode = highTrafficMode ? "High" : "Low";
output.WriteLine($"{trafficMode} traffic mode - sending {rate} orders / second");
}

public void ToggleTrafficMode()
{
highTrafficMode = !highTrafficMode;
rate = highTrafficMode ? HightTrafficRate : LowTrafficRate;
}

Task PlaceSingleOrder(CancellationToken cancellationToken)
{
var placeOrderCommand = new PlaceOrder
{
OrderId = Guid.NewGuid().ToString()
};

return _bus.Publish(placeOrderCommand, cancellationToken);
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
nextReset = DateTime.UtcNow.AddSeconds(1);
currentIntervalCount = 0;

while (!cancellationToken.IsCancellationRequested)
{
var now = DateTime.UtcNow;
if (now > nextReset)
{
currentIntervalCount = 0;
nextReset = now.AddSeconds(1);
}

await PlaceSingleOrder(cancellationToken);
currentIntervalCount++;

try
{
if (currentIntervalCount >= rate)
{
var delay = nextReset - DateTime.UtcNow;
if (delay > TimeSpan.Zero)
{
await Task.Delay(delay, cancellationToken);
}
}
}
catch (TaskCanceledException)
{
break;
}
}
}

bool highTrafficMode;

DateTime nextReset;
int currentIntervalCount;
int rate = LowTrafficRate;

const int HightTrafficRate = 8;
const int LowTrafficRate = 1;
}
Binary file added src/ClientUI/traffic.ico
Binary file not shown.
53 changes: 53 additions & 0 deletions src/MassTransitShowcaseDemo.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34902.65
MinimumVisualStudioVersion = 15.0.26730.12
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7D29C905-CE3A-4D93-8271-7BA09CEE1631}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClientUI", "ClientUI\ClientUI.csproj", "{918001C1-B9F6-4E81-894B-128271E8D910}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Messages", "Messages\Messages.csproj", "{CFF586B0-0FA1-4F3C-B860-44BE86B0F341}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sales", "Sales\Sales.csproj", "{6AD27F13-8B6B-4851-BBB8-A93D7FE463D9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Billing", "Billing\Billing.csproj", "{709E5DF7-B76F-4FA6-BCB3-EF0C43C51FC6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shipping", "Shipping\Shipping.csproj", "{457FCA71-C1D9-43FF-838A-825A47E9E112}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{918001C1-B9F6-4E81-894B-128271E8D910}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{918001C1-B9F6-4E81-894B-128271E8D910}.Debug|Any CPU.Build.0 = Debug|Any CPU
{918001C1-B9F6-4E81-894B-128271E8D910}.Release|Any CPU.ActiveCfg = Release|Any CPU
{918001C1-B9F6-4E81-894B-128271E8D910}.Release|Any CPU.Build.0 = Release|Any CPU
{CFF586B0-0FA1-4F3C-B860-44BE86B0F341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CFF586B0-0FA1-4F3C-B860-44BE86B0F341}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CFF586B0-0FA1-4F3C-B860-44BE86B0F341}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CFF586B0-0FA1-4F3C-B860-44BE86B0F341}.Release|Any CPU.Build.0 = Release|Any CPU
{6AD27F13-8B6B-4851-BBB8-A93D7FE463D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AD27F13-8B6B-4851-BBB8-A93D7FE463D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD27F13-8B6B-4851-BBB8-A93D7FE463D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD27F13-8B6B-4851-BBB8-A93D7FE463D9}.Release|Any CPU.Build.0 = Release|Any CPU
{709E5DF7-B76F-4FA6-BCB3-EF0C43C51FC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{709E5DF7-B76F-4FA6-BCB3-EF0C43C51FC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{709E5DF7-B76F-4FA6-BCB3-EF0C43C51FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{709E5DF7-B76F-4FA6-BCB3-EF0C43C51FC6}.Release|Any CPU.Build.0 = Release|Any CPU
{457FCA71-C1D9-43FF-838A-825A47E9E112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{457FCA71-C1D9-43FF-838A-825A47E9E112}.Debug|Any CPU.Build.0 = Debug|Any CPU
{457FCA71-C1D9-43FF-838A-825A47E9E112}.Release|Any CPU.ActiveCfg = Release|Any CPU
{457FCA71-C1D9-43FF-838A-825A47E9E112}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {081FC59E-04F4-4FB2-88A6-64A7C18BAA10}
EndGlobalSection
EndGlobal
7 changes: 7 additions & 0 deletions src/Messages/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# Justification: Test project
dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion
9 changes: 9 additions & 0 deletions src/Messages/Messages.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
6 changes: 6 additions & 0 deletions src/Messages/OrderBilled.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Messages;

public class OrderBilled
{
public string? OrderId { get; set; }
}
6 changes: 6 additions & 0 deletions src/Messages/OrderPlaced.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Messages;

public class OrderPlaced
{
public string? OrderId { get; set; }
}
6 changes: 6 additions & 0 deletions src/Messages/PlaceOrder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Messages;

public class PlaceOrder
{
public string? OrderId { get; set; }
}
7 changes: 7 additions & 0 deletions src/Sales/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# Justification: Test project
dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion
21 changes: 21 additions & 0 deletions src/Sales/PlaceOrderHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Sales;

using Messages;
using MassTransit;
using System.Threading.Tasks;

public class PlaceOrderHandler(SimulationEffects simulationEffects) : IConsumer<PlaceOrder>
{
public async Task Consume(ConsumeContext<PlaceOrder> context)
{
// Simulate the time taken to process a message
await simulationEffects.SimulateMessageProcessing(context.CancellationToken);

var orderPlaced = new OrderPlaced
{
OrderId = context.Message.OrderId
};

await context.Publish(orderPlaced);
}
}
89 changes: 89 additions & 0 deletions src/Sales/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma warning disable IDE0010
namespace Sales;

using Microsoft.Extensions.Hosting;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;

class Program
{
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});

cfg.ConfigureEndpoints(context);
});

x.AddConsumers(Assembly.GetExecutingAssembly());

x.AddConfigureEndpointsCallback((name, cfg) =>
{
if (cfg is IRabbitMqReceiveEndpointConfigurator rmq)
{
rmq.SetQuorumQueue();
}
});
});

services.AddSingleton<SimulationEffects>();
});

return host;
}

static async Task Main(string[] args)
{
Console.SetWindowSize(65, 15);
Console.Title = "Processing (Sales)";

var host = CreateHostBuilder(args).Build();
await host.StartAsync();

var state = host.Services.GetRequiredService<SimulationEffects>();
await RunUserInterfaceLoop(state);
}

static Task RunUserInterfaceLoop(SimulationEffects state)
{
while (true)
{
Console.Clear();
Console.WriteLine($"Sales Endpoint");
Console.WriteLine("Press F to process messages faster");
Console.WriteLine("Press S to process messages slower");

Console.WriteLine("Press ESC to quit");
Console.WriteLine();

state.WriteState(Console.Out);

var input = Console.ReadKey(true);

switch (input.Key)
{
case ConsoleKey.F:
state.ProcessMessagesFaster();
break;
case ConsoleKey.S:
state.ProcessMessagesSlower();
break;
case ConsoleKey.Escape:
return Task.CompletedTask;
}
}
}
}
20 changes: 20 additions & 0 deletions src/Sales/Sales.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>..\binaries\Sales\</OutputPath>
<ApplicationIcon>processing-time-alternate.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Messages\Messages.csproj" />
</ItemGroup>
</Project>
30 changes: 30 additions & 0 deletions src/Sales/SimulationEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Sales;

public class SimulationEffects
{
public void WriteState(TextWriter output)
{
output.WriteLine("Base time to handle each order: {0} seconds", baseProcessingTime.TotalSeconds);
}

public Task SimulateMessageProcessing(CancellationToken cancellationToken = default)
{
return Task.Delay(baseProcessingTime, cancellationToken);
}

public void ProcessMessagesFaster()
{
if (baseProcessingTime > TimeSpan.Zero)
{
baseProcessingTime -= increment;
}
}

public void ProcessMessagesSlower()
{
baseProcessingTime += increment;
}

TimeSpan baseProcessingTime = TimeSpan.FromMilliseconds(1300);
TimeSpan increment = TimeSpan.FromMilliseconds(100);
}
Binary file added src/Sales/processing-time-alternate.ico
Binary file not shown.
7 changes: 7 additions & 0 deletions src/Shipping/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# Justification: Test project
dotnet_diagnostic.CA2007.severity = none

# Justification: Tests don't support cancellation and don't need to forward IMessageHandlerContext.CancellationToken
dotnet_diagnostic.NSB0002.severity = suggestion
13 changes: 13 additions & 0 deletions src/Shipping/OrderBilledHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Shipping;

using System.Threading.Tasks;
using MassTransit;
using Messages;

public class OrderBilledHandler(SimulationEffects simulationEffects) : IConsumer<OrderBilled>
{
public Task Consume(ConsumeContext<OrderBilled> context)
{
return simulationEffects.SimulateOrderBilledMessageProcessing(context.CancellationToken);
}
}
13 changes: 13 additions & 0 deletions src/Shipping/OrderPlacedHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Shipping;

using System.Threading.Tasks;
using MassTransit;
using Messages;

public class OrderPlacedHandler(SimulationEffects simulationEffects) : IConsumer<OrderPlaced>
{
public Task Consume(ConsumeContext<OrderPlaced> context)
{
return simulationEffects.SimulateOrderPlacedMessageProcessing(context.CancellationToken);
}
}
93 changes: 93 additions & 0 deletions src/Shipping/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#pragma warning disable IDE0010
namespace Shipping;

using Microsoft.Extensions.Hosting;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;

class Program
{
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
{
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});

cfg.ConfigureEndpoints(context);
});

x.AddConsumers(Assembly.GetExecutingAssembly());

x.AddConfigureEndpointsCallback((name, cfg) =>
{
if (cfg is IRabbitMqReceiveEndpointConfigurator rmq)
{
rmq.SetQuorumQueue();
}
});
});

services.AddSingleton<SimulationEffects>();
});

return host;
}

static async Task Main(string[] args)
{
Console.SetWindowSize(65, 15);

Console.Title = "Processing (Shipping)";

var host = CreateHostBuilder(args).Build();
await host.StartAsync();

var state = host.Services.GetRequiredService<SimulationEffects>();
await RunUserInterfaceLoop(state);
}

static Task RunUserInterfaceLoop(SimulationEffects state)
{
while (true)
{
Console.Clear();
Console.WriteLine("Shipping Endpoint");
Console.WriteLine("Press D to toggle resource degradation simulation");
Console.WriteLine("Press F to process OrderBilled events faster");
Console.WriteLine("Press S to process OrderBilled events slower");
Console.WriteLine("Press ESC to quit");
Console.WriteLine();

state.WriteState(Console.Out);

var input = Console.ReadKey(true);

switch (input.Key)
{
case ConsoleKey.D:
state.ToggleDegradationSimulation();
break;
case ConsoleKey.F:
state.ProcessMessagesFaster();
break;
case ConsoleKey.S:
state.ProcessMessagesSlower();
break;
case ConsoleKey.Escape:
return Task.CompletedTask;
}
}
}
}
21 changes: 21 additions & 0 deletions src/Shipping/Shipping.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>..\binaries\Shipping\</OutputPath>
<ApplicationIcon>processing-time.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Messages\Messages.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

</Project>
58 changes: 58 additions & 0 deletions src/Shipping/SimulationEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Shipping;

public class SimulationEffects
{
public void WriteState(TextWriter output)
{
output.WriteLine("Base time to handle each OrderBilled event: {0} seconds", baseProcessingTime.TotalSeconds);

output.Write("Simulated degrading resource: ");
output.WriteLine(degradingResourceSimulationStarted.HasValue ? "ON" : "OFF");
}

public Task SimulateOrderBilledMessageProcessing(CancellationToken cancellationToken = default)
{
return Task.Delay(baseProcessingTime, cancellationToken);
}

public void ProcessMessagesFaster()
{
if (baseProcessingTime > TimeSpan.Zero)
{
baseProcessingTime -= increment;
}
}

public void ProcessMessagesSlower()
{
baseProcessingTime += increment;
}

public Task SimulateOrderPlacedMessageProcessing(CancellationToken cancellationToken = default)
{
var delay = TimeSpan.FromMilliseconds(200) + Degradation();
return Task.Delay(delay, cancellationToken);
}

public void ToggleDegradationSimulation()
{
degradingResourceSimulationStarted = degradingResourceSimulationStarted.HasValue ? default(DateTime?) : DateTime.UtcNow;
}

TimeSpan Degradation()
{
var timeSinceDegradationStarted = DateTime.UtcNow - (degradingResourceSimulationStarted ?? DateTime.MaxValue);
if (timeSinceDegradationStarted < TimeSpan.Zero)
{
return TimeSpan.Zero;
}

return new TimeSpan(timeSinceDegradationStarted.Ticks / degradationRate);
}

TimeSpan baseProcessingTime = TimeSpan.FromMilliseconds(700);
TimeSpan increment = TimeSpan.FromMilliseconds(100);

DateTime? degradingResourceSimulationStarted;
const int degradationRate = 5;
}
Binary file added src/Shipping/processing-time.ico
Binary file not shown.

0 comments on commit 427bb02

Please sign in to comment.