async Task InvokeAfterReceiveTimeoutTrue() { const int ReceiveTimeoutInSeconds = 15; TestUtility.Log("Testing EventProcessorHost with InvokeProcessorAfterReceiveTimeout=true"); var emptyBatchReceiveEvents = new ConcurrentDictionary <string, AsyncAutoResetEvent>(); foreach (var partitionId in PartitionIds) { emptyBatchReceiveEvents[partitionId] = new AsyncAutoResetEvent(false); } var eventProcessorHost = new EventProcessorHost( string.Empty, PartitionReceiver.DefaultConsumerGroupName, TestUtility.EventHubsConnectionString, TestUtility.StorageConnectionString, this.LeaseContainerName); var processorOptions = new EventProcessorOptions { ReceiveTimeout = TimeSpan.FromSeconds(ReceiveTimeoutInSeconds), InvokeProcessorAfterReceiveTimeout = true, MaxBatchSize = 100 }; var processorFactory = new TestEventProcessorFactory(); processorFactory.OnCreateProcessor += (f, createArgs) => { var processor = createArgs.Item2; string partitionId = createArgs.Item1.PartitionId; processor.OnOpen += (_, partitionContext) => TestUtility.Log($"Partition {partitionId} TestEventProcessor opened"); processor.OnProcessEvents += (_, eventsArgs) => { int eventCount = eventsArgs.Item2.events != null?eventsArgs.Item2.events.Count() : 0; TestUtility.Log($"Partition {partitionId} TestEventProcessor processing {eventCount} event(s)"); if (eventCount == 0) { var emptyBatchReceiveEvent = emptyBatchReceiveEvents[partitionId]; emptyBatchReceiveEvent.Set(); } }; }; await eventProcessorHost.RegisterEventProcessorFactoryAsync(processorFactory, processorOptions); try { TestUtility.Log("Waiting for each partition to receive an empty batch of events..."); foreach (var partitionId in PartitionIds) { var emptyBatchReceiveEvent = emptyBatchReceiveEvents[partitionId]; bool emptyBatchReceived = await emptyBatchReceiveEvent.WaitAsync(TimeSpan.FromSeconds(ReceiveTimeoutInSeconds * 2)); Assert.True(emptyBatchReceived, $"Partition {partitionId} didn't receive an empty batch!"); } } finally { TestUtility.Log("Calling UnregisterEventProcessorAsync"); await eventProcessorHost.UnregisterEventProcessorAsync(); } }
async Task MultipleConsumerGroups() { var customConsumerGroupName = "cgroup1"; var ehClient = EventHubClient.CreateFromConnectionString(TestUtility.EventHubsConnectionString); // Generate a new lease container name that will be used through out the test. string leaseContainerName = Guid.NewGuid().ToString(); var consumerGroupNames = new[] { PartitionReceiver.DefaultConsumerGroupName, customConsumerGroupName }; var processorOptions = new EventProcessorOptions { ReceiveTimeout = TimeSpan.FromSeconds(15), MaxBatchSize = 100 }; var processorFactory = new TestEventProcessorFactory(); var partitionReceiveEvents = new ConcurrentDictionary <string, AsyncAutoResetEvent>(); var hosts = new List <EventProcessorHost>(); // Confirm that custom consumer group exists before starting hosts. try { // Create a receiver on the consumer group and try to receive. // Receive call will fail if consumer group is missing. var receiver = ehClient.CreateReceiver(customConsumerGroupName, this.PartitionIds.First(), PartitionReceiver.StartOfStream); await receiver.ReceiveAsync(1, TimeSpan.FromSeconds(5)); } catch (MessagingEntityNotFoundException) { throw new Exception($"Cunsumer group {customConsumerGroupName} cannot be found. MultipleConsumerGroups unit test requires consumer group '{customConsumerGroupName}' to be created before running the test."); } processorFactory.OnCreateProcessor += (f, createArgs) => { var processor = createArgs.Item2; string partitionId = createArgs.Item1.PartitionId; string hostName = createArgs.Item1.Owner; string consumerGroupName = createArgs.Item1.ConsumerGroupName; processor.OnOpen += (_, partitionContext) => TestUtility.Log($"{hostName} > {consumerGroupName} > Partition {partitionId} TestEventProcessor opened"); processor.OnClose += (_, closeArgs) => TestUtility.Log($"{hostName} > {consumerGroupName} > Partition {partitionId} TestEventProcessor closing: {closeArgs.Item2}"); processor.OnProcessError += (_, errorArgs) => TestUtility.Log($"{hostName} > {consumerGroupName} > Partition {partitionId} TestEventProcessor process error {errorArgs.Item2.Message}"); processor.OnProcessEvents += (_, eventsArgs) => { int eventCount = eventsArgs.Item2.events != null?eventsArgs.Item2.events.Count() : 0; TestUtility.Log($"{hostName} > {consumerGroupName} > Partition {partitionId} TestEventProcessor processing {eventCount} event(s)"); if (eventCount > 0) { var receivedEvent = partitionReceiveEvents[consumerGroupName + "-" + partitionId]; receivedEvent.Set(); } }; }; try { // Register a new host for each consumer group. foreach (var consumerGroupName in consumerGroupNames) { var eventProcessorHost = new EventProcessorHost( string.Empty, consumerGroupName, TestUtility.EventHubsConnectionString, TestUtility.StorageConnectionString, leaseContainerName); TestUtility.Log($"Calling RegisterEventProcessorAsync on consumer group {consumerGroupName}"); foreach (var partitionId in PartitionIds) { partitionReceiveEvents[consumerGroupName + "-" + partitionId] = new AsyncAutoResetEvent(false); } await eventProcessorHost.RegisterEventProcessorFactoryAsync(processorFactory, processorOptions); hosts.Add(eventProcessorHost); } TestUtility.Log("Sending an event to each partition"); var sendTasks = new List <Task>(); foreach (var partitionId in PartitionIds) { sendTasks.Add(TestUtility.SendToPartitionAsync(ehClient, partitionId, $"{partitionId} event.")); } await Task.WhenAll(sendTasks); TestUtility.Log("Verifying an event was received by each partition for each consumer group"); foreach (var consumerGroupName in consumerGroupNames) { foreach (var partitionId in PartitionIds) { var receivedEvent = partitionReceiveEvents[consumerGroupName + "-" + partitionId]; bool partitionReceivedMessage = await receivedEvent.WaitAsync(TimeSpan.FromSeconds(30)); Assert.True(partitionReceivedMessage, $"ConsumerGroup {consumerGroupName} > Partition {partitionId} didn't receive any message!"); } } TestUtility.Log("Success"); } finally { TestUtility.Log("Calling UnregisterEventProcessorAsync on both hosts."); foreach (var eph in hosts) { await eph.UnregisterEventProcessorAsync(); } } }
async Task MultipleProcessorHosts() { int hostCount = 3; TestUtility.Log($"Testing with {hostCount} EventProcessorHost instances"); // Prepare partition trackers. var partitionReceiveEvents = new ConcurrentDictionary <string, AsyncAutoResetEvent>(); foreach (var partitionId in PartitionIds) { partitionReceiveEvents[partitionId] = new AsyncAutoResetEvent(false); } // Prepare host trackers. var hostReceiveEvents = new ConcurrentDictionary <string, AsyncAutoResetEvent>(); var hosts = new List <EventProcessorHost>(); try { for (int hostId = 0; hostId < hostCount; hostId++) { var thisHostName = $"host-{hostId}"; hostReceiveEvents[thisHostName] = new AsyncAutoResetEvent(false); TestUtility.Log("Creating EventProcessorHost"); var eventProcessorHost = new EventProcessorHost( thisHostName, string.Empty, // Passing empty as entity path here rsince path is already in EH connection string. PartitionReceiver.DefaultConsumerGroupName, TestUtility.EventHubsConnectionString, TestUtility.StorageConnectionString, this.LeaseContainerName); hosts.Add(eventProcessorHost); TestUtility.Log($"Calling RegisterEventProcessorAsync"); var processorOptions = new EventProcessorOptions { ReceiveTimeout = TimeSpan.FromSeconds(10), InvokeProcessorAfterReceiveTimeout = true, MaxBatchSize = 100 }; var processorFactory = new TestEventProcessorFactory(); processorFactory.OnCreateProcessor += (f, createArgs) => { var processor = createArgs.Item2; string partitionId = createArgs.Item1.PartitionId; string hostName = createArgs.Item1.Owner; processor.OnOpen += (_, partitionContext) => TestUtility.Log($"{hostName} > Partition {partitionId} TestEventProcessor opened"); processor.OnClose += (_, closeArgs) => TestUtility.Log($"{hostName} > Partition {partitionId} TestEventProcessor closing: {closeArgs.Item2}"); processor.OnProcessError += (_, errorArgs) => TestUtility.Log($"{hostName} > Partition {partitionId} TestEventProcessor process error {errorArgs.Item2.Message}"); processor.OnProcessEvents += (_, eventsArgs) => { int eventCount = eventsArgs.Item2.events != null?eventsArgs.Item2.events.Count() : 0; if (eventCount > 0) { TestUtility.Log($"{hostName} > Partition {partitionId} TestEventProcessor processing {eventCount} event(s)"); partitionReceiveEvents[partitionId].Set(); hostReceiveEvents[hostName].Set(); } }; }; await eventProcessorHost.RegisterEventProcessorFactoryAsync(processorFactory, processorOptions); } // Allow some time for each host to own at least 1 partition. // Partition stealing logic balances partition ownership one at a time. TestUtility.Log("Waiting for partition ownership to settle..."); await Task.Delay(TimeSpan.FromSeconds(60)); TestUtility.Log("Sending an event to each partition"); var ehClient = EventHubClient.CreateFromConnectionString(TestUtility.EventHubsConnectionString); var sendTasks = new List <Task>(); foreach (var partitionId in PartitionIds) { sendTasks.Add(TestUtility.SendToPartitionAsync(ehClient, partitionId, $"{partitionId} event.")); } await Task.WhenAll(sendTasks); TestUtility.Log("Verifying an event was received by each partition"); foreach (var e in partitionReceiveEvents) { bool ret = await e.Value.WaitAsync(TimeSpan.FromSeconds(30)); Assert.True(ret, $"Partition {e.Key} didn't receive any message!"); } TestUtility.Log("Verifying at least an event was received by each host"); foreach (var e in hostReceiveEvents) { bool ret = await e.Value.WaitAsync(TimeSpan.FromSeconds(30)); Assert.True(ret, $"Host {e.Key} didn't receive any message!"); } } finally { var shutdownTasks = new List <Task>(); foreach (var host in hosts) { TestUtility.Log($"Host {host} Calling UnregisterEventProcessorAsync."); shutdownTasks.Add(host.UnregisterEventProcessorAsync()); } await Task.WhenAll(shutdownTasks); } }