public async Task CorrelationOverflowGuardWorks() { var correlationId = -1; var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: TimeSpan.FromMilliseconds(5)), TestConfig.Log)) { server.OnReceivedAsync = data => { var context = KafkaDecoder.DecodeHeader(data.Skip(Request.IntegerByteSize)); correlationId = context.CorrelationId; return(Task.FromResult(0)); }; try { Connection.OverflowGuard = 10; await AssertAsync.Throws <TimeoutException>(() => conn.SendAsync(new MetadataRequest(), CancellationToken.None)); var initialCorrelation = correlationId; await AssertAsync.Throws <TimeoutException>(() => Task.WhenAll(Enumerable.Range(initialCorrelation, Connection.OverflowGuard - 1).Select(i => conn.SendAsync(new MetadataRequest(), CancellationToken.None)))); await AssertAsync.ThatEventually(() => correlationId > 1, () => $"correlation {correlationId}"); var currentCorrelation = correlationId; await AssertAsync.Throws <TimeoutException>(() => Task.WhenAll(Enumerable.Range(0, Connection.OverflowGuard / 2).Select(i => conn.SendAsync(new MetadataRequest(), CancellationToken.None)))); await AssertAsync.ThatEventually(() => correlationId < currentCorrelation, () => $"correlation {correlationId}"); } finally { Connection.OverflowGuard = int.MaxValue >> 1; } } }
public async Task ShouldDisposeEvenWhileAwaitingReadAndThrowException() { int readSize = 0; var config = new ConnectionConfiguration(onReading: (e, size) => readSize = size); var endpoint = TestConfig.ServerEndpoint(); using (CreateServer(endpoint.Ip.Port, TestConfig.Log)) { var transport = CreateTransport(endpoint, config, TestConfig.Log); try { await transport.ConnectAsync(CancellationToken.None); var buffer = new byte[4]; var taskResult = transport.ReadBytesAsync(new ArraySegment <byte>(buffer), CancellationToken.None); await AssertAsync.ThatEventually(() => readSize > 0, () => $"readSize {readSize}"); using (transport) { } transport = null; await taskResult.CancelAfter(); Assert.Fail("Expected ObjectDisposedException to be thrown"); } catch (ObjectDisposedException) { // expected } finally { transport?.Dispose(); } } }
public async Task ReadShouldIgnoreMessageWithUnknownCorrelationId() { const int correlationId = 99; var onRead = 0; var log = new MemoryLog(); var config = new ConnectionConfiguration(onRead: (e, buffer, elapsed) => Interlocked.Increment(ref onRead)); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, config, log: log)) { //send correlation message await server.SendDataAsync(new ArraySegment <byte>(CreateCorrelationMessage(correlationId))); //wait for connection await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); await AssertAsync.ThatEventually(() => onRead > 0, () => $"read attempts {onRead}"); // shortly after receivedData, but still after await AssertAsync.ThatEventually(() => log.LogEvents.Any(e => e.Item1 == LogLevel.Warn && e.Item2.Message == $"Unexpected response (id {correlationId}, 4? bytes) from {endpoint}"), log.ToString); } }
public async Task MessagesStillLogWhenSendTimesOut() { var logger = new MemoryLog(); var received = 0; var timeout = TimeSpan.FromMilliseconds(100); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: timeout, onRead: (e, read, elapsed) => Interlocked.Increment(ref received)), logger)) { await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); server.OnReceivedAsync = async data => { var context = KafkaDecoder.DecodeHeader(data.Skip(Request.IntegerByteSize)); await Task.Delay(timeout); await server.SendDataAsync(KafkaDecoder.EncodeResponseBytes(context, new MetadataResponse())); }; await AssertAsync.Throws <TimeoutException>(() => conn.SendAsync(new MetadataRequest(), CancellationToken.None)); await AssertAsync.ThatEventually(() => received > 0, () => $"received {received}"); await AssertAsync.ThatEventually(() => logger.LogEvents.Any(e => e.Item1 == LogLevel.Debug && e.Item2.Message.StartsWith("Timed out -----> (timed out or otherwise errored in client)")), () => logger.ToString(LogLevel.Debug)); } }
public async Task ReadShouldThrowServerDisconnectedExceptionWhenDisconnected() { var disconnectedCount = 0; var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) { server.OnDisconnected = () => Interlocked.Increment(ref disconnectedCount); using (var transport = CreateTransport(endpoint, TestConfig.Options.ConnectionConfiguration, TestConfig.Log)) { await transport.ConnectAsync(CancellationToken.None); var buffer = new byte[48]; var taskResult = transport.ReadBytesAsync(new ArraySegment <byte>(buffer, 0, 4), CancellationToken.None); //wait till connected await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); server.DropConnection(); await AssertAsync.ThatEventually(() => disconnectedCount > 0, () => $"disconnected {disconnectedCount}"); await Task.WhenAny(taskResult, Task.Delay(1000)).ConfigureAwait(false); Assert.That(taskResult.IsFaulted, Is.True); Assert.That(taskResult.Exception.InnerException, Is.TypeOf <ConnectionException>()); } } }
public async Task WriteAsyncShouldSendData() { var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) using (var transport = CreateTransport(endpoint, TestConfig.Options.ConnectionConfiguration, TestConfig.Log)) { const int testData = 99; var bytes = new byte[4]; int read = 0; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously server.OnReceivedAsync = async data => { var offset = Interlocked.Add(ref read, data.Count) - data.Count; for (var i = 0; i < data.Count; i++) { bytes[offset + i] = data.Array[data.Offset + i]; } }; #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously await transport.ConnectAsync(CancellationToken.None); await transport.WriteBytesAsync(new ArraySegment <byte>(testData.ToBytes()), CancellationToken.None); await AssertAsync.ThatEventually(() => read == 4 && bytes.ToInt32() == testData, () => $"read {read}"); } }
//someTime failed public async Task ProducerShouldBlockWhenFullBufferReached() { int count = 0; //with max buffer set below the batch size, this should cause the producer to block until batch delay time. var scenario = new RoutingScenario(); scenario.Connection1.Add(ApiKey.Produce, async _ => { await Task.Delay(200); return(new ProduceResponse()); }); using (var producer = new Producer(scenario.CreateRouter(), new ProducerConfiguration(batchSize: 10, batchMaxDelay: TimeSpan.FromMilliseconds(500)))) { var senderTask = Task.Factory.StartNew(async() => { for (int i = 0; i < 3; i++) { await producer.SendAsync(new Message(i.ToString()), RoutingScenario.TestTopic, CancellationToken.None); TestConfig.Log.Info(() => LogEvent.Create($"Buffered {producer.BufferedMessageCount}, In Flight: {producer.InFlightMessageCount}")); Interlocked.Increment(ref count); } }); await AssertAsync.ThatEventually(() => count > 0 && producer.BufferedMessageCount == 1, () => $"buffered {producer.BufferedMessageCount}, count {count}"); TestConfig.Log.Info(() => LogEvent.Create("Waiting for the rest...")); await Task.WhenAny(senderTask, Task.Delay(5000)); Assert.That(senderTask.IsCompleted); Assert.That(producer.BufferedMessageCount, Is.EqualTo(1), "One message should be left in the buffer."); } }
public async Task ProducerShouldReportCorrectNumberOfAsyncRequests() { var semaphore = new SemaphoreSlim(0); var scenario = new RoutingScenario(); //block the second call returning from send message async scenario.Connection1.Add(ApiKey.Produce, async _ => { await semaphore.WaitAsync(); return(new ProduceResponse()); }); var router = scenario.CreateRouter(); using (var producer = new Producer(router, new ProducerConfiguration(requestParallelization: 1, batchSize: 1))) { var messages = new[] { new Message("1") }; Assert.That(producer.ActiveSenders, Is.EqualTo(0)); var sendTask = producer.SendAsync(messages, RoutingScenario.TestTopic, CancellationToken.None); await AssertAsync.ThatEventually(() => producer.ActiveSenders >= 1, () => $"senders {producer.ActiveSenders}"); semaphore.Release(); await Task.WhenAny(sendTask, Task.Delay(2500)); Assert.That(sendTask.IsCompleted, Is.True, "Send task should be marked as completed."); Assert.That(producer.ActiveSenders, Is.EqualTo(0), "Async should now show zero count."); } }
public async Task WriteShouldCancelWhileSendingData() { var clientWriteAttempts = 0; var config = new ConnectionConfiguration(onWritingBytes: (e, payload) => Interlocked.Increment(ref clientWriteAttempts)); var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) { using (var transport = CreateTransport(endpoint, config, TestConfig.Log)) { using (var token = new CancellationTokenSource()) { await transport.ConnectAsync(token.Token); var write = transport.WriteBytesAsync(new ArraySegment <byte>(1.ToBytes()), token.Token); await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); await AssertAsync.ThatEventually(() => clientWriteAttempts == 1, () => $"attempts {clientWriteAttempts}"); //create a buffer write that will take a long time var data = Enumerable.Range(0, 1000000).Select(b => (byte)b).ToArray(); token.Cancel(); var taskResult = transport.WriteBytesAsync(new ArraySegment <byte>(data), token.Token, 6); await Task.WhenAny(taskResult, Task.Delay(TimeSpan.FromSeconds(2))).ConfigureAwait(false); Assert.That(taskResult.IsCanceled || !taskResult.IsFaulted, Is.True, "Task should have cancelled."); } } } }
public async Task ShouldStartReadPollingOnConstruction() { var log = new MemoryLog(); var endpoint = TestConfig.ServerEndpoint(); using (new Connection(endpoint, log: log)) { await AssertAsync.ThatEventually(() => log.LogEvents.Any(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith("Connecting to")), log.ToString); } }
public async Task AsynchronousWriteAndReadShouldBeConsistent() { const int requests = 10; var expected = requests.Repeat(i => i).ToList(); var readOnClient = new ConcurrentBag <int>(); var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) { using (var transport = CreateTransport(endpoint, TestConfig.Options.ConnectionConfiguration, TestConfig.Log)) { var bytes = new byte[4 * requests]; int bytesReadOnServer = 0; server.OnReceivedAsync = data => { var offset = Interlocked.Add(ref bytesReadOnServer, data.Count) - data.Count; for (var i = 0; i < data.Count; i++) { bytes[offset + i] = data.Array[data.Offset + i]; } return(Task.FromResult(0)); }; await transport.ConnectAsync(CancellationToken.None); var clientWriteTasks = expected.Select(i => transport.WriteBytesAsync(new ArraySegment <byte>(i.ToBytes()), CancellationToken.None)); var clientReadTasks = expected.Select( i => { var b = new byte[4]; return(transport.ReadBytesAsync(new ArraySegment <byte>(b), CancellationToken.None) .ContinueWith(t => readOnClient.Add(b.ToInt32()))); }); var serverWriteTasks = expected.Select(i => server.SendDataAsync(new ArraySegment <byte>(i.ToBytes()))); await Task.WhenAll(clientWriteTasks.Union(clientReadTasks).Union(serverWriteTasks)); await AssertAsync.ThatEventually(() => bytesReadOnServer == bytes.Length, () => $"length {bytes.Length}, read {bytesReadOnServer}"); var readOnServer = new List <int>(); foreach (var value in bytes.Batch(4).Select(x => x.ToArray().ToInt32())) { readOnServer.Add(value); } Assert.That(readOnServer.Count, Is.EqualTo(requests), "not all writes propagated to the server in time"); await AssertAsync.ThatEventually(() => readOnClient.Count == requests, () => $"read {readOnClient.Count}, requests {requests}"); var w = readOnServer.OrderBy(x => x); var r = readOnClient.OrderBy(x => x); for (var i = 0; i < requests; i++) { Assert.That(w.ElementAt(i), Is.EqualTo(expected[i])); } for (var i = 0; i < requests; i++) { Assert.That(r.ElementAt(i), Is.EqualTo(expected[i])); } } } }
public async Task ShouldAttemptMultipleTimesWhenConnectionFails() { var count = 0; var config = new ConnectionConfiguration(onConnecting: (e, a, _) => Interlocked.Increment(ref count)); using (var transport = CreateTransport(TestConfig.ServerEndpoint(), config, TestConfig.Log)) { var task = transport.ConnectAsync(CancellationToken.None); await AssertAsync.ThatEventually(() => count > 1, TimeSpan.FromSeconds(10), () => $"count {count}"); } }
public async Task ShouldStartDedicatedThreadOnConstruction() { var count = 0; var config = new ConnectionConfiguration(onConnecting: (e, a, _) => Interlocked.Increment(ref count)); var endpoint = TestConfig.ServerEndpoint(); using (new Connection(endpoint, config, log: TestConfig.Log)) { await AssertAsync.ThatEventually(() => count > 0, () => $"count {count}"); } }
public async Task ShouldAttemptMultipleTimesWhenConnectionFails() { var count = 0; var config = new ConnectionConfiguration(onConnecting: (e, a, _) => Interlocked.Increment(ref count)); using (var conn = new Connection(TestConfig.ServerEndpoint(), config, TestConfig.Log)) { var task = conn.SendAsync(new FetchRequest(), CancellationToken.None); //will force a connection await AssertAsync.ThatEventually(() => count > 1, TimeSpan.FromSeconds(10), () => $"count {count}"); } }
public async Task ShouldFinishPartiallyReadMessage() { var log = new MemoryLog(); var bytesRead = 0; var firstLength = 99; var config = new ConnectionConfiguration(onReadBytes: (e, attempted, actual, elapsed) => { if (Interlocked.Add(ref bytesRead, actual) == firstLength) { throw new OutOfMemoryException(); } }); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (new Connection(endpoint, config, log)) { // send size var size = 200; await server.SendDataAsync(new ArraySegment <byte>(size.ToBytes())); var bytes = new byte[size]; new Random(42).NextBytes(bytes); var offset = 0; var correlationId = 200; foreach (var b in correlationId.ToBytes()) { bytes[offset++] = b; } // send half of payload await server.SendDataAsync(new ArraySegment <byte>(bytes, 0, firstLength)); await AssertAsync.ThatEventually(() => bytesRead >= firstLength, () => $"read {bytesRead}, length {bytes.Length}"); Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Warn && e.Item2.Message.StartsWith($"Unexpected response (id {correlationId}, {size}? bytes) from")), Is.EqualTo(1), log.ToString()); Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Debug && e.Item2.Message.StartsWith($"Received {size} bytes (id {correlationId})")), Is.EqualTo(0)); // send half of payload should be skipped while (!await server.SendDataAsync(new ArraySegment <byte>(bytes, firstLength, size - firstLength))) { // repeat until the connection is all up and working ... } await AssertAsync.ThatEventually(() => bytesRead >= size, () => $"read {bytesRead}, size {size}"); await AssertAsync.ThatEventually(() => log.LogEvents.Count(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith($"Received {size} bytes (id {correlationId})")) == 1, log.ToString); } }
public async Task TimedOutQueueIsClearedWhenTooBig() { var logger = new MemoryLog(); var timeout = TimeSpan.FromMilliseconds(1); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: timeout), logger)) { await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); await AssertAsync.Throws <TimeoutException>(() => Task.WhenAll(Enumerable.Range(0, 201).Select(i => conn.SendAsync(new MetadataRequest(), CancellationToken.None)))); await AssertAsync.ThatEventually(() => logger.LogEvents.Any(e => e.Item1 == LogLevel.Debug && e.Item2.Message.StartsWith("Clearing timed out requests to avoid overflow")), () => logger.ToString(LogLevel.Debug)); } }
public async Task SendAsyncShouldUseStatictVersionInfo() { IRequestContext context = null; var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: TimeSpan.FromSeconds(1000), versionSupport: VersionSupport.Kafka10), log: TestConfig.Log)) { server.OnReceivedAsync = async data => { context = KafkaDecoder.DecodeHeader(data.Skip(Request.IntegerByteSize)); await server.SendDataAsync(KafkaDecoder.EncodeResponseBytes(context, new FetchResponse())); }; await conn.SendAsync(new FetchRequest(new FetchRequest.Topic("Foo", 0, 0)), CancellationToken.None); await AssertAsync.ThatEventually(() => context != null && context.ApiVersion.GetValueOrDefault() == 2, () => $"version {context?.ApiVersion}"); } }
public async Task ShouldDisposeEvenWhilePollingToReconnect() { var connectionAttempt = -1; var config = new ConnectionConfiguration(Retry.Until(TimeSpan.FromSeconds(10)), onConnecting: (e, a, _) => connectionAttempt = a); var endpoint = TestConfig.ServerEndpoint(); using (var transport = CreateTransport(endpoint, config, TestConfig.Log)) { var taskResult = transport.ConnectAsync(CancellationToken.None); await AssertAsync.ThatEventually(() => connectionAttempt >= 0, () => $"attempt {connectionAttempt}"); transport.Dispose(); using (var cancellation = new TimedCancellation(CancellationToken.None, TimeSpan.FromSeconds(3))) { await AssertAsync.Throws <ObjectDisposedException>(() => taskResult.ThrowIfCancellationRequested(cancellation.Token)); } } }
public async Task ReadShouldReconnectEvenAfterCancelledRead() { using (var token = new CancellationTokenSource()) { var connectionAttempt = 0; var config = new ConnectionConfiguration( onConnecting: (e, attempt, elapsed) => { if (Interlocked.Increment(ref connectionAttempt) > 1) { token.Cancel(); } }); var endpoint = TestConfig.ServerEndpoint(); using (var conn = new Connection(endpoint, config, TestConfig.Log)) { var taskResult = conn.SendAsync(new FetchRequest(), token.Token); await AssertAsync.ThatEventually(() => connectionAttempt > 1, () => $"attempt {connectionAttempt}"); } } }
public async Task SendAsyncWithDynamicVersionInfoMakesVersionCallFirst() { var firstCorrelation = -1; var correlationId = 0; var sentVersion = (short)-1; var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: TimeSpan.FromSeconds(3), versionSupport: VersionSupport.Kafka8.Dynamic()), log: TestConfig.Log)) { var apiVersion = (short)3; server.OnReceivedAsync = async data => { var context = KafkaDecoder.DecodeHeader(data.Skip(Request.IntegerByteSize)); if (firstCorrelation < 0) { firstCorrelation = context.CorrelationId; } correlationId = context.CorrelationId; switch (correlationId - firstCorrelation) { case 0: await server.SendDataAsync(KafkaDecoder.EncodeResponseBytes(context, new ApiVersionsResponse(ErrorCode.NONE, new[] { new ApiVersionsResponse.VersionSupport(ApiKey.Fetch, apiVersion, apiVersion) }))); break; case 1: sentVersion = context.ApiVersion.GetValueOrDefault(); await server.SendDataAsync(KafkaDecoder.EncodeResponseBytes(context, new FetchResponse())); break; default: return; } }; await conn.SendAsync(new FetchRequest(new FetchRequest.Topic("Foo", 0, 0)), CancellationToken.None); await AssertAsync.ThatEventually(() => correlationId - firstCorrelation >= 1, () => $"first {firstCorrelation}, current {correlationId}"); Assert.That(sentVersion, Is.EqualTo(apiVersion)); } }
public async Task ShouldLogDisconnectAndRecover() { var log = new MemoryLog(); var clientDisconnected = 0; var clientConnected = 0; var serverConnected = 0; var config = new ConnectionConfiguration( onDisconnected: (e, exception) => { Interlocked.Increment(ref clientDisconnected); }, onConnected: (e, attempt, elapsed) => { Interlocked.Increment(ref clientConnected); }); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log) { OnConnected = () => Interlocked.Increment(ref serverConnected) }) using (new Connection(endpoint, config, log: log)) { for (var connectionAttempt = 1; connectionAttempt <= 4; connectionAttempt++) { var currentAttempt = connectionAttempt; await AssertAsync.ThatEventually(() => serverConnected == currentAttempt, () => $"server {serverConnected}, attempt {currentAttempt}"); await server.SendDataAsync(new ArraySegment <byte>(CreateCorrelationMessage(connectionAttempt))); TestConfig.Log.Write(LogLevel.Info, () => LogEvent.Create($"Sent CONNECTION attempt {currentAttempt}")); await AssertAsync.ThatEventually(() => clientConnected == currentAttempt, TimeSpan.FromMilliseconds(200), () => $"client {clientConnected}, attempt {currentAttempt}"); await AssertAsync.ThatEventually(() => log.LogEvents.Count(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith("Received 4 bytes (id ")) == currentAttempt, () => $"attempt {currentAttempt}\n" + log.ToString(LogLevel.Info)); TestConfig.Log.Write(LogLevel.Info, () => LogEvent.Create($"Dropping CONNECTION attempt {currentAttempt}")); server.DropConnection(); await AssertAsync.ThatEventually(() => clientDisconnected == currentAttempt, () => $"client {clientDisconnected}, attempt {currentAttempt}"); Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Info && e.Item2.Message.StartsWith("Disposing transport to")), Is.AtLeast(currentAttempt)); } } }
public async Task ReadShouldBlockUntilAllBytesRequestedAreReceived() { var readCompleted = 0; var bytesReceived = 0; var config = new ConnectionConfiguration( onReadBytes: (e, attempted, actual, elapsed) => Interlocked.Add(ref bytesReceived, actual), onRead: (e, read, elapsed) => Interlocked.Increment(ref readCompleted)); var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) { using (var transport = CreateTransport(endpoint, config, TestConfig.Log)) { await transport.ConnectAsync(CancellationToken.None); var buffer = new byte[4]; var readTask = transport.ReadBytesAsync(new ArraySegment <byte>(buffer), CancellationToken.None); TestConfig.Log.Info(() => LogEvent.Create("Sending the first 3 bytes")); await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); await server.SendDataAsync(new ArraySegment <byte>(new byte[] { 0, 0, 0 })); // Three bytes should have been received and we are waiting on the last byte. await AssertAsync.ThatEventually(() => bytesReceived >= 3, () => $"bytesReceived {bytesReceived}"); Assert.That(readTask.IsCompleted, Is.False, "Task should still be running, blocking."); Assert.That(readCompleted, Is.EqualTo(0), "Should still block even though bytes have been received."); TestConfig.Log.Info(() => LogEvent.Create("Sending the last bytes")); var sendLastByte = await server.SendDataAsync(new ArraySegment <byte>(new byte[] { 0 })); Assert.That(sendLastByte, Is.True, "Last byte should have sent."); // Ensuring task unblocks... await AssertAsync.ThatEventually(() => readTask.IsCompleted); Assert.That(bytesReceived, Is.EqualTo(4), ""); Assert.That(readCompleted, Is.EqualTo(1), "Task ContinueWith should have executed."); } } }
public async Task ProducerShouldAllowFullBatchSizeOfMessagesToQueue() { var scenario = new RoutingScenario(); var producer = new Producer(scenario.CreateRouter(), new ProducerConfiguration(batchSize: 1002, batchMaxDelay: TimeSpan.FromSeconds(10000))); using (producer) { var count = 1000; var senderTask = Task.Run(() => { for (var i = 0; i < count; i++) { producer.SendAsync(new Message(i.ToString()), RoutingScenario.TestTopic, CancellationToken.None); } }); await senderTask; TestConfig.Log.Info(() => LogEvent.Create("Finished test send task")); Assert.That(senderTask.IsCompleted); await AssertAsync.ThatEventually(() => producer.InFlightMessageCount + producer.BufferedMessageCount == count, () => $"in flight {producer.InFlightMessageCount}, buffered {producer.BufferedMessageCount}, total {count}"); } }
public async Task ShouldReconnectAfterLosingConnectionAndBeAbleToStartNewRead() { var log = TestConfig.Log; var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) { var serverDisconnects = 0; var serverConnects = 0; var clientDisconnects = 0; var clientReads = 0; var clientBytesRead = 0; server.OnConnected = () => Interlocked.Increment(ref serverConnects); server.OnDisconnected = () => Interlocked.Increment(ref serverDisconnects); var config = new ConnectionConfiguration( onDisconnected: (e, exception) => Interlocked.Increment(ref clientDisconnects), onReading: (e, available) => Interlocked.Increment(ref clientReads), onRead: (e, read, elapsed) => Interlocked.Add(ref clientBytesRead, read)); using (var conn = new Connection(endpoint, config, log)) { await AssertAsync.ThatEventually(() => serverConnects > 0, () => $"connects {serverConnects}"); await AssertAsync.ThatEventually(() => clientReads > 0, TimeSpan.FromSeconds(1), () => $"reads {clientReads}"); server.DropConnection(); await AssertAsync.ThatEventually(() => clientDisconnects > 0, TimeSpan.FromSeconds(10), () => $"disconnects {clientDisconnects}"); Assert.That(clientBytesRead, Is.EqualTo(0), "client should not have received any bytes."); await AssertAsync.ThatEventually(() => serverConnects == 2, TimeSpan.FromSeconds(6), () => $"connects {serverConnects}"); await server.SendDataAsync(new ArraySegment <byte>(8.ToBytes())); await server.SendDataAsync(new ArraySegment <byte>(99.ToBytes())); await AssertAsync.ThatEventually(() => clientBytesRead == 8, TimeSpan.FromSeconds(1), () => $"bytes read {clientBytesRead}"); } } }
public async Task SendAsyncShouldBlockWhenMaximumAsyncQueueReached() { TestConfig.Log.Info(() => LogEvent.Create("Start SendAsyncShouldBlockWhenMaximumAsyncQueueReached")); int count = 0; var semaphore = new SemaphoreSlim(0); var scenario = new RoutingScenario(); //block the second call returning from send message async scenario.Connection1.Add(ApiKey.Produce, async _ => { await semaphore.WaitAsync(); return(new ProduceResponse()); }); var router = scenario.CreateRouter(); using (var producer = new Producer(router, new ProducerConfiguration(requestParallelization: 1, batchSize: 1))) { var messages = new[] { new Message("1") }; var task = Task.Run(async() => { var t = producer.SendAsync(messages, RoutingScenario.TestTopic, CancellationToken.None); Interlocked.Increment(ref count); await t; t = producer.SendAsync(messages, RoutingScenario.TestTopic, CancellationToken.None); Interlocked.Increment(ref count); await t; }); await AssertAsync.ThatEventually(() => producer.ActiveSenders == 1 && count > 0, () => $"senders {producer.ActiveSenders}, count {count}"); semaphore.Release(); // The second SendMessagesAsync should continue after semaphore is released. await AssertAsync.ThatEventually(() => count > 1, () => $"count {count}"); } }
public async Task WriteShouldHandleLargeVolumeSendAsynchronously([Values(1000, 5000)] int requests) { var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) { using (var transport = CreateTransport(endpoint, TestConfig.Options.ConnectionConfiguration, TestConfig.Log)) { var bytes = new byte[4 * requests]; int bytesReadOnServer = 0; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously server.OnReceivedAsync = async data => { var offset = Interlocked.Add(ref bytesReadOnServer, data.Count) - data.Count; for (var i = 0; i < data.Count; i++) { bytes[offset + i] = data.Array[data.Offset + i]; } }; #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously await transport.ConnectAsync(CancellationToken.None); var clientWriteTasks = Enumerable.Range(1, requests).Select(i => transport.WriteBytesAsync(new ArraySegment <byte>(i.ToBytes()), CancellationToken.None)); await Task.WhenAll(clientWriteTasks); await AssertAsync.ThatEventually(() => bytesReadOnServer == bytes.Length, () => $"bytes read {bytesReadOnServer}, total {bytes.Length}"); var readOnServer = new List <int>(); foreach (var value in bytes.Batch(4).Select(x => x.ToArray().ToInt32())) { readOnServer.Add(value); } Assert.That(readOnServer.Count, Is.EqualTo(requests), "not all writes propagated to the server in time"); Assert.That(readOnServer.OrderBy(x => x), Is.EqualTo(Enumerable.Range(1, requests))); } } }
public async Task ShouldNotFinishPartiallyReadMessage() { var log = new MemoryLog(); var bytesRead = 0; var config = new ConnectionConfiguration(onReadBytes: (e, attempted, actual, elapsed) => Interlocked.Add(ref bytesRead, actual)); var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (new Connection(endpoint, config, log)) { // send size var size = 200; await server.SendDataAsync(new ArraySegment <byte>(size.ToBytes())); var bytes = new byte[99]; new Random(42).NextBytes(bytes); var offset = 0; var correlationId = 200; foreach (var b in correlationId.ToBytes()) { bytes[offset++] = b; } // send half of payload await server.SendDataAsync(new ArraySegment <byte>(bytes, 0, 99)); await AssertAsync.ThatEventually(() => bytesRead >= bytes.Length, () => $"read {bytesRead}, length {bytes.Length}"); Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Warn && e.Item2.Message.StartsWith($"Unexpected response (id {correlationId}, {size}? bytes) from")), Is.EqualTo(1), log.ToString()); Assert.That(log.LogEvents.Count(e => e.Item1 == LogLevel.Debug && e.Item2.Message.StartsWith($"Received {size} bytes (id {correlationId})")), Is.EqualTo(0)); server.DropConnection(); await AssertAsync.ThatEventually(() => log.LogEvents.Count(e => e.Item1 == LogLevel.Warn && e.Item2.Message.StartsWith("Socket has been re-established, so recovery of the remaining of the current message is uncertain at best")) == 1, log.ToString); } }
public async Task WriteAsyncShouldAllowMoreThanOneWrite() { var endpoint = TestConfig.ServerEndpoint(); using (var server = CreateServer(endpoint.Ip.Port, TestConfig.Log)) using (var transport = CreateTransport(endpoint, TestConfig.Options.ConnectionConfiguration, TestConfig.Log)) { const int testData = 99; var results = new List <byte>(); #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously server.OnReceivedAsync = async data => results.AddRange(data.Array.Skip(data.Offset).Take(data.Count)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously await transport.ConnectAsync(CancellationToken.None); await Task.WhenAll( transport.WriteBytesAsync(new ArraySegment <byte>(testData.ToBytes()), CancellationToken.None), transport.WriteBytesAsync(new ArraySegment <byte>(testData.ToBytes()), CancellationToken.None)); await AssertAsync.ThatEventually(() => results.Count == 8, () => $"count {results.Count}"); } }
public async Task SendAsyncShouldTimeoutMultipleMessagesAtATime() { var endpoint = TestConfig.ServerEndpoint(); using (var server = new TcpServer(endpoint.Ip.Port, TestConfig.Log)) using (var conn = new Connection(endpoint, new ConnectionConfiguration(requestTimeout: TimeSpan.FromMilliseconds(100)), log: TestConfig.Log)) { await Task.WhenAny(server.ClientConnected, Task.Delay(TimeSpan.FromSeconds(3))); var tasks = new[] { conn.SendAsync(new MetadataRequest(), CancellationToken.None), conn.SendAsync(new MetadataRequest(), CancellationToken.None), conn.SendAsync(new MetadataRequest(), CancellationToken.None) }; await AssertAsync.ThatEventually(() => tasks.All(t => t.IsFaulted)); foreach (var task in tasks) { Assert.That(task.IsFaulted, Is.True, "Task should have faulted."); Assert.That(task.Exception.InnerException, Is.TypeOf <TimeoutException>(), "Task fault has wrong type."); } } }