public async Task CheckpointUpdateDoesNotInterfereWithOtherNamespaces() { var storageManager = new InMemoryStorageManager(); var mockEvent = new MockEventData( eventBody: Array.Empty <byte>(), offset: 10, sequenceNumber: 20); await storageManager.UpdateCheckpointAsync(new EventProcessorCheckpoint { FullyQualifiedNamespace = "namespace1", EventHubName = "eventHubName", ConsumerGroup = "consumerGroup", PartitionId = "partitionId" }, mockEvent); await storageManager.UpdateCheckpointAsync(new EventProcessorCheckpoint { FullyQualifiedNamespace = "namespace2", EventHubName = "eventHubName", ConsumerGroup = "consumerGroup", PartitionId = "partitionId" }, mockEvent); IEnumerable <EventProcessorCheckpoint> storedCheckpointsList1 = await storageManager.ListCheckpointsAsync("namespace1", "eventHubName", "consumerGroup"); IEnumerable <EventProcessorCheckpoint> storedCheckpointsList2 = await storageManager.ListCheckpointsAsync("namespace2", "eventHubName", "consumerGroup"); Assert.That(storedCheckpointsList1, Is.Not.Null); Assert.That(storedCheckpointsList1.Count, Is.EqualTo(1)); Assert.That(storedCheckpointsList2, Is.Not.Null); Assert.That(storedCheckpointsList2.Count, Is.EqualTo(1)); }
public async Task ProcessorClientBeginsWithTheNextEventAfterCheckpointing() { // Setup the environment. await using EventHubScope scope = await EventHubScope.CreateAsync(1); var connectionString = EventHubsTestEnvironment.Instance.BuildConnectionStringForEventHub(scope.EventHubName); using var cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); // Send a set of events. var segmentEventCount = 25; var beforeCheckpointEvents = EventGenerator.CreateEvents(segmentEventCount).ToList(); var afterCheckpointEvents = EventGenerator.CreateEvents(segmentEventCount).ToList(); var sourceEvents = Enumerable.Concat(beforeCheckpointEvents, afterCheckpointEvents).ToList(); var checkpointEvent = beforeCheckpointEvents.Last(); var sentCount = await SendEvents(connectionString, sourceEvents, cancellationSource.Token); Assert.That(sentCount, Is.EqualTo(sourceEvents.Count), "Not all of the source events were sent."); // Attempt to read back the first half of the events and checkpoint. Func <ProcessEventArgs, Task> processedEventCallback = async args => { if (args.Data.IsEquivalentTo(checkpointEvent)) { await args.UpdateCheckpointAsync(cancellationSource.Token); } }; var processedEvents = new ConcurrentDictionary <string, EventData>(); var completionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var beforeCheckpointProcessHandler = CreateEventTrackingHandler(segmentEventCount, processedEvents, completionSource, cancellationSource.Token, processedEventCallback); var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMilliseconds(250) }; var storageManager = new InMemoryStorageManager(_ => { }); var processor = CreateProcessor(scope.ConsumerGroups.First(), connectionString, storageManager, options); processor.ProcessErrorAsync += CreateAssertingErrorHandler(); processor.ProcessEventAsync += beforeCheckpointProcessHandler; await processor.StartProcessingAsync(cancellationSource.Token); await Task.WhenAny(completionSource.Task, Task.Delay(Timeout.Infinite, cancellationSource.Token)); Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled."); await processor.StopProcessingAsync(cancellationSource.Token); // Validate a checkpoint was created and that events were processed. var checkpoints = (await storageManager.ListCheckpointsAsync(processor.FullyQualifiedNamespace, processor.EventHubName, processor.ConsumerGroup, cancellationSource.Token))?.ToList(); Assert.That(checkpoints, Is.Not.Null, "A checkpoint should have been created."); Assert.That(checkpoints.Count, Is.EqualTo(1), "A single checkpoint should exist."); Assert.That(processedEvents.Count, Is.AtLeast(beforeCheckpointEvents.Count), "All events before the checkpoint should have been processed."); // Reset state and start the processor again; it should resume from the event following the checkpoint. processedEvents.Clear(); completionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); processor.ProcessEventAsync -= beforeCheckpointProcessHandler; processor.ProcessEventAsync += CreateEventTrackingHandler(segmentEventCount, processedEvents, completionSource, cancellationSource.Token); await processor.StartProcessingAsync(cancellationSource.Token); await Task.WhenAny(completionSource.Task, Task.Delay(Timeout.Infinite, cancellationSource.Token)); Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled."); await processor.StopProcessingAsync(cancellationSource.Token); cancellationSource.Cancel(); foreach (var sourceEvent in afterCheckpointEvents) { var sourceId = sourceEvent.Properties[EventGenerator.IdPropertyName].ToString(); Assert.That(processedEvents.TryGetValue(sourceId, out var processedEvent), Is.True, $"The event with custom identifier [{ sourceId }] was not processed."); Assert.That(sourceEvent.IsEquivalentTo(processedEvent), $"The event with custom identifier [{ sourceId }] did not match the corresponding processed event."); } }