/// <summary> /// Creates and starts a new partition pump associated with the specified partition. Partition pumps that are overwritten by the creation /// of a new one are properly stopped. /// </summary> /// /// <param name="partitionId">The identifier of the Event Hub partition the partition pump will be associated with. Events will be read only from this partition.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// private async Task AddOrOverwritePartitionPumpAsync(string partitionId) { // Remove and stop the existing partition pump if it exists. We are not specifying any close reason because partition // pumps only are overwritten in case of failure. In these cases, the close reason is delegated to the pump as it may // have more information about what caused the failure. await RemovePartitionPumpIfItExistsAsync(partitionId).ConfigureAwait(false); // Create and start the new partition pump and add it to the dictionary. var partitionContext = new PartitionContext(InnerClient.EventHubName, ConsumerGroup, partitionId); var checkpointManager = new CheckpointManager(partitionContext, Manager, Identifier); try { var partitionProcessor = PartitionProcessorFactory(partitionContext, checkpointManager); var partitionPump = new PartitionPump(InnerClient, ConsumerGroup, partitionId, partitionProcessor, Options); await partitionPump.StartAsync().ConfigureAwait(false); PartitionPumps[partitionId] = partitionPump; } catch (Exception) { // If partition pump creation fails, we'll try again on the next time this method is called. This should happen // on the next load balancing loop as long as this instance still owns the partition. // TODO: delegate the exception handling to an Exception Callback. } }
/// <summary> /// The main loop of an event processor. It loops through every owned <see cref="PartitionPump" />, checking /// its status and creating a new one if necessary. /// </summary> /// /// <param name="cancellationToken">A <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// /// <remarks> /// The actual goal of this method is to perform load balancing between multiple <see cref="EventProcessor" /> /// instances, but this feature is currently out of the scope of the current preview. /// </remarks> /// private async Task RunAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { await Task.WhenAll(PartitionPumps .Where(kvp => !kvp.Value.IsRunning) .Select(async kvp => { try { await kvp.Value.StopAsync(); } catch (Exception) { // We're catching every possible unhandled exception that may have happened during Partition Pump execution. // TODO: delegate the exception handling to an Exception Callback. } var partitionId = kvp.Key; var partitionContext = new PartitionContext(InnerClient.EventHubName, ConsumerGroup, partitionId); var checkpointManager = new CheckpointManager(partitionContext, Manager, Identifier); var partitionProcessor = PartitionProcessorFactory(partitionContext, checkpointManager); var partitionPump = new PartitionPump(InnerClient, ConsumerGroup, partitionId, partitionProcessor, Options); PartitionPumps.TryUpdate(partitionId, partitionPump, partitionPump); await partitionPump.StartAsync(); })).ConfigureAwait(false); try { // Wait 1 second before the next verification. await Task.Delay(1000, cancellationToken).ConfigureAwait(false); } catch (TaskCanceledException) { } } }
/// <summary> /// Starts the event processor. In case it's already running, nothing happens. /// </summary> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// public async Task StartAsync() { if (RunningTask == null) { await RunningTaskSemaphore.WaitAsync().ConfigureAwait(false); try { if (RunningTask == null) { RunningTaskTokenSource?.Cancel(); RunningTaskTokenSource = new CancellationTokenSource(); PartitionPumps.Clear(); var partitionIds = await InnerClient.GetPartitionIdsAsync().ConfigureAwait(false); await Task.WhenAll(partitionIds .Select(partitionId => { var partitionContext = new PartitionContext(InnerClient.EventHubName, ConsumerGroup, partitionId); var checkpointManager = new CheckpointManager(partitionContext, Manager, Identifier); var partitionProcessor = PartitionProcessorFactory(partitionContext, checkpointManager); var partitionPump = new PartitionPump(InnerClient, ConsumerGroup, partitionId, partitionProcessor, Options); PartitionPumps.TryAdd(partitionId, partitionPump); return(partitionPump.StartAsync()); })).ConfigureAwait(false); RunningTask = RunAsync(RunningTaskTokenSource.Token); } } finally { RunningTaskSemaphore.Release(); } } }
/// <summary> /// Creates and starts a new partition pump associated with the specified partition. Partition pumps that are overwritten by the creation /// of a new one are properly stopped. /// </summary> /// /// <param name="partitionId">The identifier of the Event Hub partition the partition pump will be associated with. Events will be read only from this partition.</param> /// <param name="initialSequenceNumber">The sequence number of the event within a partition where the partition pump should begin reading events.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// private async Task AddOrOverwritePartitionPumpAsync(string partitionId, long?initialSequenceNumber) { // Remove and stop the existing partition pump if it exists. We are not specifying any close reason because partition // pumps only are overwritten in case of failure. In these cases, the close reason is delegated to the pump as it may // have more information about what caused the failure. await RemovePartitionPumpIfItExistsAsync(partitionId).ConfigureAwait(false); // Create and start the new partition pump and add it to the dictionary. var partitionContext = new PartitionContext(InnerClient.FullyQualifiedNamespace, InnerClient.EventHubName, ConsumerGroup, partitionId, Identifier, Manager); try { BasePartitionProcessor partitionProcessor = PartitionProcessorFactory(partitionContext); EventProcessorOptions options = Options.Clone(); // Ovewrite the initial event position in case a checkpoint exists. if (initialSequenceNumber.HasValue) { options.InitialEventPosition = EventPosition.FromSequenceNumber(initialSequenceNumber.Value); } var partitionPump = new PartitionPump(InnerClient, ConsumerGroup, partitionContext, partitionProcessor, options); await partitionPump.StartAsync().ConfigureAwait(false); PartitionPumps[partitionId] = partitionPump; } catch (Exception) { // If partition pump creation fails, we'll try again on the next time this method is called. This should happen // on the next load balancing loop as long as this instance still owns the partition. // TODO: delegate the exception handling to an Exception Callback. } }