public void Can_handle_requests_concurrently_in_4_threads(int noOfThreads, int msgs) { var timesCalled = 0; using var mqHost = new ServiceBusMqServer(ConnectionString); var queueNames = QueueNames <Wait> .AllQueueNames.Select(SafeQueueName).ToList(); #if NETCOREAPP2_1 queueNames.ForEach(q => mqHost.ManagementClient.DeleteQueueAsync(q).GetAwaiter().GetResult()); #else queueNames.ForEach(q => mqHost.NamespaceManager.DeleteQueue(q)); #endif mqHost.RegisterHandler <Wait>(m => { Interlocked.Increment(ref timesCalled); Thread.Sleep(m.GetBody().ForMs); return(null); }, noOfThreads); mqHost.Start(); var dto = new Wait { ForMs = 100 }; using var mqClient = mqHost.CreateMessageQueueClient(); msgs.Times(i => mqClient.Publish(dto)); ExecUtils.RetryUntilTrue(() => timesCalled == msgs, TimeSpan.FromSeconds(5)); }
public void Can_receive_and_process_same_reply_responses() { var called = 0; var mqHost = CreateMqServer(); using (var conn = mqHost.ConnectionFactory.CreateConnection()) using (var channel = conn.CreateModel()) { channel.PurgeQueue <Incr>(); } mqHost.RegisterHandler <Incr>(m => { Debug.WriteLine("In Incr #" + m.GetBody().Value); Interlocked.Increment(ref called); return(m.GetBody().Value > 0 ? new Incr { Value = m.GetBody().Value - 1 } : null); }); mqHost.Start(); var mqClient = mqHost.CreateMessageQueueClient(); var incr = new Incr { Value = 5 }; mqClient.Publish(incr); ExecUtils.RetryOnException(() => { Assert.That(called, Is.EqualTo(1 + incr.Value)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); }
public void Can_consume_messages_from_RabbitMQ_with_BasicConsume() { RabbitMqConfig.UsingChannel(channel => { var consumer = new RabbitMqBasicConsumer(channel); channel.BasicConsume(MqQueueNames <HelloRabbit> .Direct, false, consumer); string recvMsg = null; ExecUtils.ExecMultiThreading(1, () => PublishHelloRabbit(channel)); while (true) { try { var e = consumer.Queue.Dequeue(); Console.WriteLine("Dequeued"); recvMsg = e.Body.FromUtf8Bytes(); // ... process the message Console.WriteLine(recvMsg); channel.BasicAck(e.DeliveryTag, false); break; } catch (OperationInterruptedException) { // The consumer was removed, either through // channel or connection closure, or through the // action of IModel.BasicCancel(). Console.WriteLine("End of the road..."); break; } } Assert.That(recvMsg, Is.Not.Null); }); }
private static void RunHandlerOnMultipleThreads(int noOfThreads, int msgs) { using var mqHost = CreateMqServer(); var timesCalled = 0; mqHost.RegisterHandler <Wait>(m => { Interlocked.Increment(ref timesCalled); Thread.Sleep(m.GetBody().ForMs); return(null); }, noOfThreads); mqHost.Start(); using var mqClient = mqHost.CreateMessageQueueClient(); var dto = new Wait { ForMs = 100 }; msgs.Times(i => mqClient.Publish(dto)); ExecUtils.RetryOnException(() => { Thread.Sleep(300); Assert.That(timesCalled, Is.EqualTo(msgs)); }, TimeSpan.FromSeconds(5)); }
public void Only_allows_1_BgThread_to_run_at_a_time() { using (var mqServer = CreateMqServer()) { mqServer.RegisterHandler <Reverse>(x => { return(new ReverseResponse { Value = string.Join(",", x.GetBody().Value.Reverse()) }); }); mqServer.RegisterHandler <NothingHere>(x => { return(new NothingHereResponse { Value = x.GetBody().Value }); }); ExecUtils.ExecMultiThreading(5, () => mqServer.Start()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Started")); Assert.That(mqServer.BgThreadCount, Is.EqualTo(1)); ExecUtils.ExecMultiThreading(10, () => mqServer.Stop()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Stopped")); ExecUtils.ExecMultiThreading(1, () => mqServer.Start()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Started")); Assert.That(mqServer.BgThreadCount, Is.EqualTo(2)); Console.WriteLine(mqServer.GetStats()); } }
public void Does_process_messages_sent_before_it_was_started() { var reverseCalled = 0; using (var mqServer = RabbitMqServerTests.CreateMqServer()) { using (var conn = mqServer.ConnectionFactory.CreateConnection()) using (var channel = conn.CreateModel()) { channel.PurgeQueue <Reverse>(); } mqServer.RegisterHandler <Reverse>(x => { Interlocked.Increment(ref reverseCalled); return(x.GetBody().Value.Reverse()); }); using (var mqClient = mqServer.CreateMessageQueueClient()) { RabbitMqServerTests.Publish_4_messages(mqClient); mqServer.Start(); ExecUtils.RetryOnException(() => { Assert.That(mqServer.GetStats().TotalMessagesProcessed, Is.EqualTo(4)); Assert.That(reverseCalled, Is.EqualTo(4)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); } } }
public virtual RedisClient CreateRedisClient(RedisEndpoint config, bool master) { var client = ClientFactory(config); if (master && RedisConfig.VerifyMasterConnections) { var firstAttempt = DateTime.UtcNow; Exception firstEx = null; var retryTimeSpan = TimeSpan.FromMilliseconds(config.RetryTimeout); var i = 0; while (DateTime.UtcNow - firstAttempt < retryTimeSpan) { try { client = GetValidMaster(client, config); return(client); } catch (Exception ex) { if (!RedisConfig.RetryReconnectOnFailedMasters) { throw; } firstEx ??= ex; ExecUtils.SleepBackOffMultiplier(++i); } } throw new TimeoutException($"Could not resolve master instance within {config.RetryTimeout}ms RetryTimeout", firstEx); } return(client); }
public void Only_allows_1_BgThread_to_run_at_a_time() { using (var mqHost = CreateMqServer()) { mqHost.RegisterHandler <Reverse>(x => x.GetBody().Value.Reverse()); mqHost.RegisterHandler <Rot13>(x => x.GetBody().Value.ToRot13()); 5.Times(x => ThreadPool.QueueUserWorkItem(y => mqHost.Start())); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Started")); Assert.That(mqHost.BgThreadCount, Is.EqualTo(1)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); 10.Times(x => ThreadPool.QueueUserWorkItem(y => mqHost.Stop())); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Stopped")); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); ThreadPool.QueueUserWorkItem(y => mqHost.Start()); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Started")); Assert.That(mqHost.BgThreadCount, Is.EqualTo(2)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); //Debug.WriteLine(mqHost.GetStats()); } }
public RedisLock(IRedisClient redisClient, string key, TimeSpan?timeout) { this._redisClient = redisClient; this._key = key; bool LockAction() { // This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx // Calculate a unix time for when the lock should expire var realSpan = timeout ?? new TimeSpan(365, 0, 0, 0); // if nothing is passed in the timeout hold for a year var expireTime = DateTime.UtcNow.Add(realSpan); var lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); // Try to set the lock, if it does not exist this will succeed and the lock is obtained var nx = redisClient.SetValueIfNotExists(key, lockString); if (nx) { return(true); } // If we've gotten here then a key for the lock is present. This could be because the lock is // correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly). // Therefore we need to get the value of the lock to see when it should expire redisClient.Watch(key); var lockExpireString = redisClient.Get <string>(key); if (!long.TryParse(lockExpireString, out var lockExpireTime)) { redisClient.UnWatch(); // since the client is scoped externally return(false); } // If the expire time is greater than the current time then we can't let the lock go yet if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) { redisClient.UnWatch(); // since the client is scoped externally return(false); } // If the expire time is less than the current time then it wasn't released properly and we can attempt to // acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes // before we call Commit() below, the Commit will fail and return false, which means that another thread // was able to acquire the lock before we finished processing. using (var trans = redisClient.CreateTransaction()) { // we started the "Watch" above; this tx will succeed if the value has not moved trans.QueueCommand(r => r.Set(key, lockString)); return(trans.Commit()); // returns false if Transaction failed } } ExecUtils.RetryUntilTrue(LockAction, timeout); }
// [Test] public void TestProtoTypes() => TestsConfig.BaseUri.CombineWith("/types/proto").GetStringFromUrl().Print(); // [Test] // needs: listenOptions.Protocols = HttpProtocols.Http1AndHttp2; public void UpdateProto() { Directory.GetCurrentDirectory().Print(); var protoc = TestsConfig.BaseUri.CombineWith("/types/proto").GetStringFromUrl(); protoc = protoc.Replace("ServiceStack.Extensions.Tests", "ServiceStack.Extensions.Tests.Protoc"); Directory.SetCurrentDirectory("../../../Protoc"); File.WriteAllText("services.proto", protoc); ExecUtils.ShellExec("x proto-csharp services.proto"); }
// [Test] // needs: listenOptions.Protocols = HttpProtocols.Http1AndHttp2; public void UpdateProto() { Directory.GetCurrentDirectory().Print(); var protoc = "http://localhost:20000/types/proto".GetStringFromUrl(); protoc = protoc.Replace("ServiceStack.Extensions.Tests", "ServiceStack.Extensions.Tests.Protoc"); Directory.SetCurrentDirectory("../../../Protoc"); File.WriteAllText("services.proto", protoc); ExecUtils.ShellExec("web proto-csharp services.proto"); }
public void Can_use_RefreshTokenCookie_to_authenticate_and_get_new_AccessToken() { string initialAccessToken = null; var client = new JsonServiceClient(Config.ListeningOn) { ResponseFilter = res => { if (initialAccessToken == null) { var accessToken = res.Cookies[Keywords.TokenCookie]; Assert.That(accessToken.Value, Is.Not.Null); initialAccessToken = accessToken.Value; var refreshToken = res.Cookies[Keywords.RefreshTokenCookie]; Assert.That(refreshToken.Value, Is.Not.Null); } } }; var authResponse = client.Post(new Authenticate { provider = "credentials", UserName = Username, Password = Password }); var request = new Secured { Name = "test" }; var response = client.Send(request); Assert.That(response.Result, Is.EqualTo(request.Name)); var reqLogger = HostContext.TryResolve <IRequestLogger>(); var lastEntrySession = reqLogger.GetLatestLogs(1)[0]?.Session as AuthUserSession; Assert.That(lastEntrySession, Is.Not.Null); Assert.That(lastEntrySession.AuthProvider, Is.EqualTo("jwt")); Assert.That(lastEntrySession.UserName, Is.EqualTo(Username)); string lastAccessToken = null; client.ResponseFilter = res => { var accessToken = res.Cookies[Keywords.TokenCookie]; lastAccessToken = accessToken.Value; }; var i = 0; do { var accessTokenResponse = client.Post(new GetAccessToken()); ExecUtils.SleepBackOffMultiplier(++i); //need to wait for iat to tick +1s so JWT's are different }while (lastAccessToken == initialAccessToken); }
internal async Task <long> AtomicIncDecAsync(string key, long amount, CancellationToken token = default) { long count = 0; bool updated = false; await ExecUtils.RetryUntilTrueAsync(async() => { var entry = await GetEntryAsync(key, token); if (entry == null) { count = amount; entry = CreateTableEntry(key, Serialize(count)); try { updated = (await table.ExecuteAsync(TableOperation.Insert(entry), token)).HttpStatusCode == (int)HttpStatusCode.NoContent; } catch (StorageException ex) { if (!ex.HasStatus(HttpStatusCode.Conflict)) { throw; } } } else { count = Deserialize <long>(entry.Data) + amount; entry.Data = Serialize(count); var op = TableOperation.Replace(entry); try { var result = (await table.ExecuteAsync(op, null, null, token)).HttpStatusCode; updated = result == (int)HttpStatusCode.OK || result == (int)HttpStatusCode.NoContent; } catch (StorageException ex) { if (!ex.HasStatus(HttpStatusCode.PreconditionFailed)) { throw; } } } return(updated); }, TimeSpan.FromSeconds(30)); return(count); }
internal long AtomicIncDec(string key, long amount) { long count = 0; bool updated = false; ExecUtils.RetryUntilTrue(() => { var entry = GetEntry(key); if (entry == null) { count = amount; entry = CreateTableEntry(key, Serialize(count)); try { updated = table.Execute(TableOperation.Insert(entry)).HttpStatusCode == (int)HttpStatusCode.NoContent; } catch (StorageException ex) { if (!ex.InnerException.HasStatus(HttpStatusCode.Conflict)) { throw; } } } else { count = Deserialize <long>(entry.Data) + amount; entry.Data = Serialize <long>(count); var op = TableOperation.Replace(entry); try { var result = table.Execute(op).HttpStatusCode; updated = result == (int)HttpStatusCode.OK || result == (int)HttpStatusCode.NoContent; } catch (StorageException ex) { if (!ex.InnerException.HasStatus(HttpStatusCode.PreconditionFailed)) { throw; } } } return(updated); }, TimeSpan.FromSeconds(30)); return(count); }
public static string AssertDirectory(string dirPath, int timeoutMs = 1000) { if (string.IsNullOrEmpty(dirPath)) { return(null); } ExecUtils.RetryOnException(() => { if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } }, TimeSpan.FromMilliseconds(timeoutMs)); return(dirPath); }
// async version of ExecUtils.RetryUntilTrue private static async ValueTask RetryUntilTrue(Func <CancellationToken, ValueTask <bool> > action, TimeSpan?timeOut = null, CancellationToken token = default) { var i = 0; var firstAttempt = DateTime.UtcNow; while (timeOut == null || DateTime.UtcNow - firstAttempt < timeOut.Value) { token.ThrowIfCancellationRequested(); i++; if (await action(token).ConfigureAwait(false)) { return; } await Task.Delay(ExecUtils.CalculateFullJitterBackOffDelay(i)).ConfigureAwait(false); } throw new TimeoutException($"Exceeded timeout of {timeOut.Value}"); }
public void RedisLock(IRedisClient redisClient, string key, TimeSpan?timeOut) { //this.redisClient = redisClient; //this.key = key; ExecUtils.RetryUntilTrue(delegate { TimeSpan value = timeOut ?? new TimeSpan(365, 0, 0, 0); DateTime dateTime = DateTime.UtcNow.Add(value); string lockString = (dateTime.ToUnixTimeMs() + 1).ToString(); if (redisClient.SetValueIfNotExists(key, lockString)) { return(true); } redisClient.Watch(key); long result = -1L; string s = redisClient.Get <string>(key); if (!long.TryParse(s, out result)) { redisClient.UnWatch(); return(false); } if (result <= DateTime.UtcNow.ToUnixTimeMs()) { using (IRedisTransaction redisTransaction = redisClient.CreateTransaction()) { redisTransaction.QueueCommand((IRedisClient r) => r.Set(key, lockString)); System.Threading.Thread.Sleep(5 * 1000); bool ok = redisTransaction.Commit(); var lockString2 = redisClient.Get <string>(key); if (lockString2 != lockString) { } return(ok); } } redisClient.UnWatch(); return(false); }, timeOut); }
public IDictionary <string, T> GetAll <T>(IEnumerable <string> keys) { foreach (var client in cacheClients) { try { var result = client.GetAll <T>(keys); if (result != null) { return(result); } } catch (Exception ex) { ExecUtils.LogError(client.GetType(), "Get", ex); } } return(new Dictionary <string, T>()); }
public void Can_consume_messages_with_BasicConsumer() { RabbitMqConfig.UsingChannel(channel => { OperationInterruptedException lastEx = null; channel.Close(); void CallBack() { try { PublishHelloRabbit(channel); } catch (Exception ex) { lastEx = ex as OperationInterruptedException; Console.WriteLine("Caught {0}: {1}", ex.GetType().Name, ex); } } ExecUtils.ExecMultiThreading(1, CallBack); Assert.That(lastEx, Is.Not.Null); }); }
public void Can_receive_and_process_standard_request_reply_combo() { using (var mqHost = CreateMqServer()) { using (var conn = mqHost.ConnectionFactory.CreateConnection()) using (var channel = conn.CreateModel()) { channel.PurgeQueue <Hello>(); channel.PurgeQueue <HelloResponse>(); } string messageReceived = null; mqHost.RegisterHandler <Hello>(m => new HelloResponse { Result = "Hello, " + m.GetBody().Name }); mqHost.RegisterHandler <HelloResponse>(m => { messageReceived = m.GetBody().Result; return(null); }); mqHost.Start(); using (var mqClient = mqHost.CreateMessageQueueClient()) { var dto = new Hello { Name = "ServiceStack" }; mqClient.Publish(dto); ExecUtils.RetryOnException(() => { Thread.Sleep(300); Assert.That(messageReceived, Is.EqualTo("Hello, ServiceStack")); }, TimeSpan.FromSeconds(5)); } } }
/// <summary> /// Sleep using AWS's recommended Exponential BackOff with Full Jitter from: /// https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ /// </summary> /// <param name="retriesAttempted"></param> internal static void SleepBackOffMultiplier(this int retriesAttempted) => Thread.Sleep(ExecUtils.CalculateFullJitterBackOffDelay(retriesAttempted));
public virtual void WaitForWorkersToStop(TimeSpan?timeout = null) { ExecUtils.RetryUntilTrue( () => Interlocked.CompareExchange(ref status, 0, 0) == WorkerStatus.Stopped, timeout); }
public void Does_retry_messages_with_errors_by_RetryCount() { var retryCount = 1; var totalRetries = 1 + retryCount; //in total, inc. first try using (var mqHost = RabbitMqServerTests.CreateMqServer(retryCount)) { using (var conn = mqHost.ConnectionFactory.CreateConnection()) using (var channel = conn.CreateModel()) { channel.PurgeQueue <Reverse>(); channel.PurgeQueue <Rot13>(); channel.PurgeQueue <AlwaysThrows>(); } var reverseCalled = 0; var rot13Called = 0; mqHost.RegisterHandler <Reverse>(x => { Interlocked.Increment(ref reverseCalled); return(x.GetBody().Value.Reverse()); }); mqHost.RegisterHandler <Rot13>(x => { Interlocked.Increment(ref rot13Called); return(x.GetBody().Value.ToRot13()); }); mqHost.RegisterHandler <AlwaysThrows>(x => { throw new Exception("Always Throwing! " + x.GetBody().Value); }); mqHost.Start(); using (var mqClient = mqHost.CreateMessageQueueClient()) { mqClient.Publish(new AlwaysThrows { Value = "1st" }); mqClient.Publish(new Reverse { Value = "Hello" }); mqClient.Publish(new Reverse { Value = "World" }); mqClient.Publish(new Rot13 { Value = "ServiceStack" }); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStats().TotalMessagesFailed, Is.EqualTo(1 * totalRetries)); Assert.That(mqHost.GetStats().TotalMessagesProcessed, Is.EqualTo(2 + 1)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); 5.Times(x => mqClient.Publish(new AlwaysThrows { Value = "#" + x })); mqClient.Publish(new Reverse { Value = "Hello" }); mqClient.Publish(new Reverse { Value = "World" }); mqClient.Publish(new Rot13 { Value = "ServiceStack" }); } //Debug.WriteLine(mqHost.GetStatsDescription()); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStats().TotalMessagesFailed, Is.EqualTo((1 + 5) * totalRetries)); Assert.That(mqHost.GetStats().TotalMessagesProcessed, Is.EqualTo(6)); Assert.That(reverseCalled, Is.EqualTo(2 + 2)); Assert.That(rot13Called, Is.EqualTo(1 + 1)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); } }
public void Does_process_all_messages_and_Starts_Stops_correctly_with_multiple_threads_racing() { using (var mqHost = RabbitMqServerTests.CreateMqServer()) { using (var conn = mqHost.ConnectionFactory.CreateConnection()) using (var channel = conn.CreateModel()) { channel.PurgeQueue <Reverse>(); channel.PurgeQueue <Rot13>(); } var reverseCalled = 0; var rot13Called = 0; mqHost.RegisterHandler <Reverse>(x => { "Processing Reverse {0}...".Print(Interlocked.Increment(ref reverseCalled)); return(x.GetBody().Value.Reverse()); }); mqHost.RegisterHandler <Rot13>(x => { "Processing Rot13 {0}...".Print(Interlocked.Increment(ref rot13Called)); return(x.GetBody().Value.ToRot13()); }); using (var mqClient = mqHost.CreateMessageQueueClient()) { mqClient.Publish(new Reverse { Value = "Hello" }); mqClient.Publish(new Reverse { Value = "World" }); mqClient.Publish(new Rot13 { Value = "ServiceStack" }); mqHost.Start(); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Started")); Assert.That(mqHost.GetStats().TotalMessagesProcessed, Is.EqualTo(3)); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); mqClient.Publish(new Reverse { Value = "Foo" }); mqClient.Publish(new Rot13 { Value = "Bar" }); 10.Times(x => ThreadPool.QueueUserWorkItem(y => mqHost.Start())); Assert.That(mqHost.GetStatus(), Is.EqualTo("Started")); 5.Times(x => ThreadPool.QueueUserWorkItem(y => mqHost.Stop())); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Stopped")); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); 10.Times(x => ThreadPool.QueueUserWorkItem(y => mqHost.Start())); ExecUtils.RetryOnException(() => { Assert.That(mqHost.GetStatus(), Is.EqualTo("Started")); Thread.Sleep(100); }, TimeSpan.FromSeconds(5)); Debug.WriteLine("\n" + mqHost.GetStats()); Assert.That(mqHost.GetStats().TotalMessagesProcessed, Is.GreaterThanOrEqualTo(5)); Assert.That(reverseCalled, Is.EqualTo(3)); Assert.That(rot13Called, Is.EqualTo(2)); } } }
public void Does_process_all_messages_and_Starts_Stops_correctly_with_multiple_threads_racing() { using (var mqServer = RabbitMqServerTests.CreateMqServer()) { void Action0(IModel channel) { channel.PurgeQueue <Reverse>(); channel.PurgeQueue <NothingHere>(); } RabbitMqConfig.UsingChannel(mqServer.ConnectionFactory, Action0); var reverseCalled = 0; var nothingHereCalled = 0; mqServer.RegisterHandler <Reverse>(x => { Console.WriteLine("Processing Reverse {0}...", Interlocked.Increment(ref reverseCalled)); return(new ReverseResponse { Value = string.Join(",", x.GetBody().Value.Reverse()) }); }); mqServer.RegisterHandler <NothingHere>(x => { Console.WriteLine("Processing NothingHere {0}...", Interlocked.Increment(ref nothingHereCalled)); return(new NothingHereResponse { Value = x.GetBody().Value }); }); using (var mqClient = mqServer.CreateMessageQueueClient()) { mqClient.Publish(new Reverse { Value = "Hello" }); mqClient.Publish(new Reverse { Value = "World" }); mqClient.Publish(new NothingHere { Value = "HelloWorld" }); mqServer.Start(); Thread.Sleep(1500); Assert.That(mqServer.GetStatus(), Is.EqualTo("Started")); Assert.That(mqServer.GetStats().TotalMessagesProcessed, Is.EqualTo(3)); mqClient.Publish(new Reverse { Value = "Foo" }); mqClient.Publish(new NothingHere { Value = "Bar" }); ExecUtils.ExecMultiThreading(10, () => mqServer.Start()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Started")); ExecUtils.ExecMultiThreading(5, () => mqServer.Stop()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Stopped")); ExecUtils.ExecMultiThreading(10, () => mqServer.Start()); Assert.That(mqServer.GetStatus(), Is.EqualTo("Started")); Console.WriteLine("\n" + mqServer.GetStats()); Assert.That(mqServer.GetStats().TotalMessagesProcessed, Is.GreaterThanOrEqualTo(5)); Assert.That(reverseCalled, Is.EqualTo(3)); Assert.That(nothingHereCalled, Is.EqualTo(2)); } } }
internal static async Task SleepBackOffMultiplierAsync(this int retriesAttempted, CancellationToken token = default) => await Task.Delay(ExecUtils.CalculateFullJitterBackOffDelay(retriesAttempted), token);