diff --git a/Foundatio.sln b/Foundatio.sln
index f4bb98303..505f12cc4 100644
--- a/Foundatio.sln
+++ b/Foundatio.sln
@@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
src\Directory.Build.props = src\Directory.Build.props
samples\Directory.Build.props = samples\Directory.Build.props
NuGet.config = NuGet.config
+ global.json = global.json
+ Dockerfile = Dockerfile
README.md = README.md
EndProjectSection
EndProject
diff --git a/src/Foundatio.AppMetrics/AppMetricsClient.cs b/src/Foundatio.AppMetrics/AppMetricsClient.cs
index 4af0cf4be..114ee7469 100644
--- a/src/Foundatio.AppMetrics/AppMetricsClient.cs
+++ b/src/Foundatio.AppMetrics/AppMetricsClient.cs
@@ -1,5 +1,4 @@
-using System;
-using App.Metrics;
+using App.Metrics;
using App.Metrics.Counter;
using App.Metrics.Gauge;
using App.Metrics.Timer;
diff --git a/src/Foundatio.Extensions.Hosting/Foundatio - Backup.Extensions.Hosting.csproj b/src/Foundatio.Extensions.Hosting/Foundatio - Backup.Extensions.Hosting.csproj
new file mode 100644
index 000000000..ecad27af2
--- /dev/null
+++ b/src/Foundatio.Extensions.Hosting/Foundatio - Backup.Extensions.Hosting.csproj
@@ -0,0 +1,15 @@
+
+
+ true
+ net6.0;net5.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Foundatio.Extensions.Hosting/Foundatio.Extensions.Hosting.csproj b/src/Foundatio.Extensions.Hosting/Foundatio.Extensions.Hosting.csproj
index ecad27af2..94f3dfb4b 100644
--- a/src/Foundatio.Extensions.Hosting/Foundatio.Extensions.Hosting.csproj
+++ b/src/Foundatio.Extensions.Hosting/Foundatio.Extensions.Hosting.csproj
@@ -1,10 +1,10 @@
true
- net6.0;net5.0
-
+
+
diff --git a/src/Foundatio.Extensions.Hosting/Startup/IStartupAction.cs b/src/Foundatio.Extensions.Hosting/Startup/IStartupAction.cs
index 7d38f1570..e811149f7 100644
--- a/src/Foundatio.Extensions.Hosting/Startup/IStartupAction.cs
+++ b/src/Foundatio.Extensions.Hosting/Startup/IStartupAction.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
namespace Foundatio.Extensions.Hosting.Startup {
diff --git a/src/Foundatio.MetricsNET/MetricsNETClient.cs b/src/Foundatio.MetricsNET/MetricsNETClient.cs
index f60048086..6ff08bfca 100644
--- a/src/Foundatio.MetricsNET/MetricsNETClient.cs
+++ b/src/Foundatio.MetricsNET/MetricsNETClient.cs
@@ -1,5 +1,4 @@
-using System;
-using Metrics;
+using Metrics;
namespace Foundatio.Metrics {
public class MetricsNETClient : IMetricsClient {
diff --git a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs
index ec9af67eb..4a9f32212 100644
--- a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs
+++ b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs
@@ -411,15 +411,12 @@ public virtual async Task CanSetMinMaxExpirationAsync() {
using (cache) {
await cache.RemoveAllAsync();
- using (TestSystemClock.Install()) {
- var now = DateTime.UtcNow;
- TestSystemClock.SetFrozenTime(now);
-
- var expires = DateTime.MaxValue - now.AddDays(1);
+ using (var clock = TestSystemClock.Install()) {
+ var expires = DateTime.MaxValue - clock.Now.AddDays(1);
Assert.True(await cache.SetAsync("test1", 1, expires));
Assert.False(await cache.SetAsync("test2", 1, DateTime.MinValue));
Assert.True(await cache.SetAsync("test3", 1, DateTime.MaxValue));
- Assert.True(await cache.SetAsync("test4", 1, DateTime.MaxValue - now.AddDays(-1)));
+ Assert.True(await cache.SetAsync("test4", 1, DateTime.MaxValue - clock.Now.AddDays(-1)));
Assert.Equal(1, (await cache.GetAsync("test1")).Value);
Assert.InRange((await cache.GetExpirationAsync("test1")).Value, expires.Subtract(TimeSpan.FromSeconds(10)), expires);
diff --git a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs
index 8ff028c95..db3f469bf 100644
--- a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs
+++ b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs
@@ -108,7 +108,7 @@ await messageBus.PublishAsync(new DerivedSimpleMessageA {
public virtual async Task CanSendMappedMessageAsync() {
var messageBus = GetMessageBus(b => {
- b.MessageTypeMappings.Add(nameof(SimpleMessageA), typeof(SimpleMessageA));
+ b.TypeNameSerializer = new DefaultTypeNameSerializer(_logger, new Dictionary {{ nameof(SimpleMessageA), typeof(SimpleMessageA) }});
return b;
});
if (messageBus == null)
@@ -136,7 +136,43 @@ await messageBus.PublishAsync(new SimpleMessageA {
}
public virtual async Task CanSendDelayedMessageAsync() {
- const int numConcurrentMessages = 1000;
+ Log.MinimumLevel = LogLevel.Trace;
+ var messageBus = GetMessageBus();
+ if (messageBus == null)
+ return;
+
+ try {
+ var countdown = new AsyncCountdownEvent(1);
+ await messageBus.SubscribeAsync(msg => {
+ var msgDelay = TimeSpan.FromMilliseconds(msg.Count);
+ _logger.LogTrace("Got message delayed by {Delay:g}.", msgDelay);
+
+ Assert.Equal("Hello", msg.Data);
+ countdown.Signal();
+ });
+
+ var sw = Stopwatch.StartNew();
+ var delay = TimeSpan.FromMilliseconds(RandomData.GetInt(250, 1500));
+ await messageBus.PublishAsync(new SimpleMessageA {
+ Data = "Hello",
+ Count = (int)delay.TotalMilliseconds
+ }, delay);
+ _logger.LogTrace("Published message...");
+
+ await countdown.WaitAsync(TimeSpan.FromSeconds(30));
+ sw.Stop();
+
+ _logger.LogTrace("Got message delayed by {Delay:g} in {Duration:g}", delay, sw.Elapsed);
+ Assert.Equal(0, countdown.CurrentCount);
+ Assert.InRange(sw.Elapsed.TotalMilliseconds, 50, 2000);
+ } finally {
+ await CleanupMessageBusAsync(messageBus);
+ }
+ }
+
+ public virtual async Task CanSendParallelDelayedMessagesAsync() {
+ Log.MinimumLevel = LogLevel.Trace;
+ const int numConcurrentMessages = 100;
var messageBus = GetMessageBus();
if (messageBus == null)
return;
@@ -158,17 +194,18 @@ await Run.InParallelAsync(numConcurrentMessages, async i => {
await messageBus.PublishAsync(new SimpleMessageA {
Data = "Hello",
Count = i
- }, new MessageOptions { DeliveryDelay = TimeSpan.FromMilliseconds(RandomData.GetInt(0, 100)) });
+ }, TimeSpan.FromMilliseconds(RandomData.GetInt(100, 500)));
if (i % 500 == 0)
_logger.LogTrace("Published 500 messages...");
});
+ _logger.LogTrace("Done publishing {Count} messages.", numConcurrentMessages);
- await countdown.WaitAsync(TimeSpan.FromSeconds(5));
+ await countdown.WaitAsync(TimeSpan.FromSeconds(30));
sw.Stop();
- if (_logger.IsEnabled(LogLevel.Trace)) _logger.LogTrace("Processed {Processed} in {Duration:g}", numConcurrentMessages - countdown.CurrentCount, sw.Elapsed);
+ _logger.LogTrace("Processed {Processed} in {Duration:g}", numConcurrentMessages - countdown.CurrentCount, sw.Elapsed);
Assert.Equal(0, countdown.CurrentCount);
- Assert.InRange(sw.Elapsed.TotalMilliseconds, 50, 5000);
+ Assert.InRange(sw.Elapsed.TotalMilliseconds, 100, 2000);
} finally {
await CleanupMessageBusAsync(messageBus);
}
diff --git a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs
index 3f98aa427..0c9f1d049 100644
--- a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs
+++ b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Foundatio.Xunit;
using Foundatio.Serializer;
diff --git a/src/Foundatio/DeepCloner/Helpers/DeepClonerSafeTypes.cs b/src/Foundatio/DeepCloner/Helpers/DeepClonerSafeTypes.cs
index 1e29f3a45..de556a4f4 100644
--- a/src/Foundatio/DeepCloner/Helpers/DeepClonerSafeTypes.cs
+++ b/src/Foundatio/DeepCloner/Helpers/DeepClonerSafeTypes.cs
@@ -2,15 +2,13 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
-namespace Foundatio.Force.DeepCloner.Helpers
-{
- ///
- /// Safe types are types, which can be copied without real cloning. e.g. simple structs or strings (it is immutable)
- ///
- internal static class DeepClonerSafeTypes
+namespace Foundatio.Force.DeepCloner.Helpers {
+ ///
+ /// Safe types are types, which can be copied without real cloning. e.g. simple structs or strings (it is immutable)
+ ///
+ internal static class DeepClonerSafeTypes
{
internal static readonly ConcurrentDictionary KnownTypes = new();
diff --git a/src/Foundatio/DeepCloner/Helpers/ShallowObjectCloner.cs b/src/Foundatio/DeepCloner/Helpers/ShallowObjectCloner.cs
index 90e4618f3..6835b0c18 100644
--- a/src/Foundatio/DeepCloner/Helpers/ShallowObjectCloner.cs
+++ b/src/Foundatio/DeepCloner/Helpers/ShallowObjectCloner.cs
@@ -1,8 +1,6 @@
#define NETCORE
using System;
using System.Linq.Expressions;
-using System.Reflection;
-using System.Reflection.Emit;
namespace Foundatio.Force.DeepCloner.Helpers
{
diff --git a/src/Foundatio/Messaging/Envelope.cs b/src/Foundatio/Messaging/Envelope.cs
new file mode 100644
index 000000000..969119492
--- /dev/null
+++ b/src/Foundatio/Messaging/Envelope.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+
+namespace Foundatio.Messaging2 {
+ public interface IEnvelope {
+ // trace parent id used for distributed tracing
+ string TraceParentId { get; }
+ // message type
+ string MessageType { get; }
+ // message body
+ object GetMessage();
+ // number of attempts to deliver the message
+ int Attempts { get; }
+ // when the message was originally sent
+ DateTime SentAtUtc { get; }
+ // when the message should expire
+ DateTime? ExpiresAtUtc { get; }
+ // when the message should be delivered when using delayed delivery
+ DateTime? DeliverAtUtc { get; }
+ // additional message data to store with the message
+ IReadOnlyDictionary Properties { get; }
+ }
+
+ public class Envelope : IEnvelope {
+ private Lazy