Skip to content

Commit 450c688

Browse files
UI (#206)
* Spike UI * Fix rendering * Delete src/MonitoringDemo/.\Marker.sln * Use storage directory * Multi-instance windows * Small tweaks while looking at the code * ProcessHandle * Recognized commands based on help messages * Minor todo * Cosmetics * Fix recognition of commands * Regex * Forward to all instance when uppercase * Remove obsolete comment * Widgets? Gadgets? * Bring in more chaos! * Outbox! * Remove unused line * Hide instances in the platform window * Use explicit root folder for the transport * Indent properly * Upgrade to alpha because v1 should no longer be used (there might still be rough edges) * Fix keys, I hope * Fix lowercase vs uppercase keys * Simplify process view slightly * Command binding * Hopefully slightly better approach for visibility handling * Simplify process window state * Shitty scale out and in * More robust process handling * Dynamic scale out of all instances except clientui --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com> Co-authored-by: Szymon Pobiega <szymon.pobiega@gmail.com>
1 parent 80d7285 commit 450c688

34 files changed

+1010
-344
lines changed

src/.editorconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[*.{csproj,props,targets,xml}]
2+
indent_style = space
3+
indent_size = 2
4+
xml_space_inside_empty_tag = true

src/Billing/Billing.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<ItemGroup>
1616
<PackageReference Include="NServiceBus" Version="9.*" />
17+
<PackageReference Include="NServiceBus.Persistence.NonDurable" Version="2.*" />
1718
<PackageReference Include="NServiceBus.Heartbeat" Version="5.*" />
1819
<PackageReference Include="NServiceBus.Metrics.ServiceControl" Version="5.*" />
1920
</ItemGroup>
@@ -22,6 +23,9 @@
2223
<Compile Include="..\MonitoringDemo\UserInterface.cs">
2324
<Link>UserInterface.cs</Link>
2425
</Compile>
26+
<Compile Include="..\MonitoringDemo\DeterministicGuid.cs">
27+
<Link>DeterministicGuid.cs</Link>
28+
</Compile>
2529
</ItemGroup>
2630

2731
</Project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using NServiceBus.Pipeline;
2+
using NServiceBus.Transport;
3+
4+
namespace Billing;
5+
6+
public class DispatchingProgressBehavior : Behavior<IBatchDispatchContext>
7+
{
8+
private FailureSimulator failureSimulator = new();
9+
10+
public override async Task Invoke(IBatchDispatchContext context, Func<Task> next)
11+
{
12+
var incomingMessage = context.Extensions.Get<IncomingMessage>();
13+
if (incomingMessage.Headers.ContainsKey("MonitoringDemo.SlowMotion"))
14+
{
15+
Console.WriteLine($"Dispatching outgoing messages {incomingMessage.MessageId}...");
16+
await failureSimulator.RunInteractive(context.CancellationToken);
17+
}
18+
19+
await next().ConfigureAwait(false);
20+
}
21+
22+
public void Failure()
23+
{
24+
failureSimulator.Trigger();
25+
}
26+
}

src/Billing/FailureSimulation.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace Billing;
2+
3+
public class FailureSimulation
4+
{
5+
private RetrievingMessageProgressBehavior retrievingMessageProgressBehavior = new RetrievingMessageProgressBehavior();
6+
private ProcessingMessageProgressBehavior processingMessageProgressBehavior = new ProcessingMessageProgressBehavior();
7+
private DispatchingProgressBehavior dispatchingMessageProgressBehavior = new DispatchingProgressBehavior();
8+
9+
public void Register(EndpointConfiguration endpointConfiguration)
10+
{
11+
endpointConfiguration.Pipeline.Register(retrievingMessageProgressBehavior, "Shows progress of retrieving messages");
12+
13+
endpointConfiguration.Pipeline.Register(processingMessageProgressBehavior, "Shows progress of processing messages");
14+
15+
endpointConfiguration.Pipeline.Register(dispatchingMessageProgressBehavior, "Shows progress of dispatching messages");
16+
}
17+
18+
public void TriggerFailureReceiving()
19+
{
20+
retrievingMessageProgressBehavior.Failure();
21+
}
22+
23+
public void TriggerFailureProcessing()
24+
{
25+
processingMessageProgressBehavior.Failure();
26+
}
27+
28+
public void TriggerFailureDispatching()
29+
{
30+
dispatchingMessageProgressBehavior.Failure();
31+
}
32+
}

src/Billing/FailureSimulator.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Billing;
2+
3+
public class FailureSimulator
4+
{
5+
private bool failureTriggered = false;
6+
7+
#pragma warning disable PS0003
8+
public async Task RunInteractive(CancellationToken cancellationToken)
9+
#pragma warning restore PS0003
10+
{
11+
using var progressBar = new ProgressBar();
12+
13+
for (var i = 0; i <= 100; i++)
14+
{
15+
if (failureTriggered)
16+
{
17+
failureTriggered = false;
18+
throw new Exception("Simulated failure");
19+
}
20+
progressBar.Update(i);
21+
await Task.Delay(25, cancellationToken).ConfigureAwait(false);
22+
}
23+
}
24+
25+
public void Trigger()
26+
{
27+
//TODO: Use Interlocked
28+
failureTriggered = true;
29+
}
30+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using NServiceBus.Pipeline;
2+
3+
namespace Billing;
4+
5+
public class ProcessingMessageProgressBehavior : Behavior<IIncomingLogicalMessageContext>
6+
{
7+
private FailureSimulator failureSimulator = new();
8+
9+
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
10+
{
11+
if (context.Headers.ContainsKey("MonitoringDemo.SlowMotion"))
12+
{
13+
Console.WriteLine($"Processing message {context.MessageId}...");
14+
await failureSimulator.RunInteractive(context.CancellationToken);
15+
}
16+
17+
await next().ConfigureAwait(false);
18+
}
19+
20+
public void Failure()
21+
{
22+
failureSimulator.Trigger();
23+
}
24+
}

src/Billing/Program.cs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
using System.Text.Json;
1+
using System.Reflection;
2+
using System.Text.Json;
23
using Billing;
34
using Messages;
45
using Microsoft.Extensions.DependencyInjection;
56
using Shared;
67

8+
var instancePostfix = args.FirstOrDefault();
9+
10+
var title = string.IsNullOrEmpty(instancePostfix) ? "Failure rate (Billing)" : $"Billing - {instancePostfix}";
11+
var instanceName = string.IsNullOrEmpty(instancePostfix) ? "billing" : $"billing-{instancePostfix}";
12+
13+
var instanceId = DeterministicGuid.Create("Billing", instanceName);
14+
715
var endpointConfiguration = new EndpointConfiguration("Billing");
816
endpointConfiguration.LimitMessageProcessingConcurrencyTo(4);
917

@@ -16,7 +24,12 @@
1624
}
1725
});
1826

19-
endpointConfiguration.UseTransport<LearningTransport>();
27+
var transport = new LearningTransport
28+
{
29+
StorageDirectory = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location)!.Parent!.FullName, ".learningtransport"),
30+
TransportTransactionMode = TransportTransactionMode.ReceiveOnly
31+
};
32+
endpointConfiguration.UseTransport(transport);
2033

2134
endpointConfiguration.Recoverability()
2235
.Delayed(delayed => delayed.NumberOfRetries(0));
@@ -25,27 +38,30 @@
2538
endpointConfiguration.SendHeartbeatTo("Particular.ServiceControl");
2639

2740
endpointConfiguration.UniquelyIdentifyRunningInstance()
28-
.UsingCustomIdentifier(new Guid("1C62248E-2681-45A4-B44D-5CF93584BAD6"))
29-
.UsingCustomDisplayName("original-instance");
41+
.UsingCustomIdentifier(instanceId)
42+
.UsingCustomDisplayName(instanceName);
3043

3144
var metrics = endpointConfiguration.EnableMetrics();
3245
metrics.SendMetricDataToServiceControl(
3346
"Particular.Monitoring",
3447
TimeSpan.FromMilliseconds(500)
3548
);
3649

50+
endpointConfiguration.UsePersistence<NonDurablePersistence>();
51+
endpointConfiguration.EnableOutbox();
52+
53+
var failureSimulation = new FailureSimulation();
54+
failureSimulation.Register(endpointConfiguration);
55+
3756
var simulationEffects = new SimulationEffects();
3857
endpointConfiguration.RegisterComponents(cc => cc.AddSingleton(simulationEffects));
3958

4059
var endpointInstance = await Endpoint.Start(endpointConfiguration);
4160

42-
var nonInteractive = args.Length > 1 && bool.TryParse(args[1], out var isInteractive) && !isInteractive;
43-
var interactive = !nonInteractive;
44-
45-
UserInterface.RunLoop("Failure rate (Billing)", new Dictionary<char, (string, Action)>
61+
UserInterface.RunLoop(title, new Dictionary<char, (string, Action)>
4662
{
4763
['w'] = ("increase the simulated failure rate", () => simulationEffects.IncreaseFailureRate()),
4864
['s'] = ("decrease the simulated failure rate", () => simulationEffects.DecreaseFailureRate())
49-
}, writer => simulationEffects.WriteState(writer), interactive);
65+
}, writer => simulationEffects.WriteState(writer));
5066

5167
await endpointInstance.Stop();

src/Billing/ProgressBar.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Billing;
2+
3+
public class ProgressBar : IDisposable
4+
{
5+
private readonly string widgetId = Guid.NewGuid().ToString("N");
6+
7+
public ProgressBar()
8+
{
9+
Console.WriteLine($"!BeginWidget Progress {widgetId}");
10+
}
11+
12+
public void Update(int percent)
13+
{
14+
Console.WriteLine($"!Widget {widgetId} {percent}");
15+
}
16+
17+
public void Dispose()
18+
{
19+
Console.WriteLine($"!EndWidget {widgetId}");
20+
}
21+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using NServiceBus.Pipeline;
2+
using Shared;
3+
4+
namespace Billing;
5+
6+
public class RetrievingMessageProgressBehavior : Behavior<ITransportReceiveContext>
7+
{
8+
private FailureSimulator failureSimulator = new();
9+
10+
public override async Task Invoke(ITransportReceiveContext context, Func<Task> next)
11+
{
12+
if (context.Message.Headers.ContainsKey("MonitoringDemo.SlowMotion"))
13+
{
14+
Console.WriteLine($"Retrieving message {context.Message.MessageId}...");
15+
await failureSimulator.RunInteractive(context.CancellationToken);
16+
}
17+
18+
await next().ConfigureAwait(false);
19+
}
20+
21+
public void Failure()
22+
{
23+
failureSimulator.Trigger();
24+
}
25+
}

src/ClientUI/Program.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.Json;
1+
using System.Reflection;
2+
using System.Text.Json;
23
using ClientUI;
34
using Messages;
45
using Shared;
@@ -14,7 +15,11 @@
1415
}
1516
});
1617

17-
var transport = endpointConfiguration.UseTransport<LearningTransport>();
18+
var transport = new LearningTransport
19+
{
20+
StorageDirectory = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location)!.Parent!.FullName, ".learningtransport")
21+
};
22+
var routing = endpointConfiguration.UseTransport(transport);
1823

1924
endpointConfiguration.AuditProcessedMessagesTo("audit");
2025
endpointConfiguration.SendHeartbeatTo("Particular.ServiceControl");
@@ -29,7 +34,6 @@
2934
TimeSpan.FromMilliseconds(500)
3035
);
3136

32-
var routing = transport.Routing();
3337
routing.RouteToEndpoint(typeof(PlaceOrder), "Sales");
3438

3539
var endpointInstance = await Endpoint.Start(endpointConfiguration);
@@ -38,13 +42,13 @@
3842
var cancellation = new CancellationTokenSource();
3943
var simulatedWork = simulatedCustomers.Run(cancellation.Token);
4044

41-
var nonInteractive = args.Length > 1 && bool.TryParse(args[1], out var isInteractive) && !isInteractive;
42-
var interactive = !nonInteractive;
4345

4446
UserInterface.RunLoop("Load (ClientUI)", new Dictionary<char, (string, Action)>
4547
{
4648
['c'] = ("toggle High/Low traffic mode", () => simulatedCustomers.ToggleTrafficMode()),
47-
}, writer => simulatedCustomers.WriteState(writer), interactive);
49+
['v'] = ("toggle manual mode", () => simulatedCustomers.ToggleManualMode()),
50+
['b'] = ("send message manually", () => simulatedCustomers.SendManually()),
51+
}, writer => simulatedCustomers.WriteState(writer));
4852

4953
cancellation.Cancel();
5054

0 commit comments

Comments
 (0)