public void UpdatePosition(EventPosition position) { if (position.PreparePosition <= _lastEventPosition.PreparePosition) // handle prepare only throw new InvalidOperationException( string.Format("Event at position {0} has been already processed", position)); _lastEventPosition = position; }
public void UpdateToZero() { var zero = new EventPosition(0, -1); if (_lastEventPosition != zero || _lastTag != null) throw new InvalidOperationException("Posistion tagger has be already updated"); _lastTag = _positionTagger.MakeZeroCheckpointTag(); }
public TransactionFileReaderEventDistributionPoint( IPublisher publisher, Guid distibutionPointCorrelationId, EventPosition from) : base(publisher, distibutionPointCorrelationId) { if (publisher == null) throw new ArgumentNullException("publisher"); _from = @from; }
public CommittedEventDistributed( Guid correlationId, EventPosition position, string eventStreamId, int eventSequenceNumber, bool resolvedLinkTo, Event data) : this(correlationId, position, eventStreamId, eventSequenceNumber, eventStreamId, eventSequenceNumber, resolvedLinkTo, data, position.PreparePosition, 11.1f) { }
public TransactionFileEventReader(IPublisher publisher, Guid distibutionPointCorrelationId, EventPosition @from, ITimeProvider timeProvider, bool stopOnEof = false, bool deliverEndOfTFPosition = true) : base(publisher, distibutionPointCorrelationId, stopOnEof) { if (publisher == null) throw new ArgumentNullException("publisher"); _from = @from; _deliverEndOfTfPosition = deliverEndOfTFPosition; _timeProvider = timeProvider; }
public TransactionFileReaderEventDistributionPoint( IPublisher publisher, Guid distibutionPointCorrelationId, EventPosition from, bool deliverEndOfTFPosition = true) : base(publisher, distibutionPointCorrelationId) { if (publisher == null) throw new ArgumentNullException("publisher"); _from = @from; _deliverEndOfTfPosition = deliverEndOfTFPosition; }
public bool ProcessEvent( EventPosition position, CheckpointTag eventPosition, string streamId, string eventType, string category1, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { emittedEvents = null; newState = null; if (sequenceNumber != 0) return false; // not our event emittedEvents = new[] { new EmittedEvent(SystemStreams.StreamsStream, Guid.NewGuid(), SystemEventTypes.LinkTo, sequenceNumber + "@" + streamId, eventPosition, expectedTag: null) }; return true; }
public bool ProcessEvent( EventPosition position, string streamId, string eventType, string category1, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { emittedEvents = null; newState = null; if (sequenceNumber != 0) return false; // not our event emittedEvents = new[] {new EmittedEvent("$streams", Guid.NewGuid(), "$>", sequenceNumber + "@" + streamId)}; return true; }
//NOTE: committed event with null event _data means - end of the source reached. // Current last available TF commit position is in _position.CommitPosition // TODO: separate message? public CommittedEventDistributed( Guid correlationId, EventPosition position, string positionStreamId, int positionSequenceNumber, string eventStreamId, int eventSequenceNumber, bool resolvedLinkTo, Event data, long? safeTransactionFileReaderJoinPosition, float progress) { _correlationId = correlationId; _data = data; _safeTransactionFileReaderJoinPosition = safeTransactionFileReaderJoinPosition; _progress = progress; _position = position; _positionStreamId = positionStreamId; _positionSequenceNumber = positionSequenceNumber; _eventStreamId = eventStreamId; _eventSequenceNumber = eventSequenceNumber; _resolvedLinkTo = resolvedLinkTo; }
public bool ProcessEvent( EventPosition position, string streamId, string eventType, string category1, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { emittedEvents = null; newState = null; if (sequenceNumber != 0) return false; // not our event var lastSlashPos = streamId.LastIndexOf(_separator); if (lastSlashPos < 0) return true; // handled but not interesting to us var category = streamId.Substring(0, lastSlashPos); emittedEvents = new[] {new EmittedEvent("$category" + _separator + category, Guid.NewGuid(), "StreamCreated", streamId)}; return true; }
public bool ProcessEvent( EventPosition position, CheckpointTag eventPosition, string streamId, string eventType, string category1, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { emittedEvents = null; newState = null; if (streamId.StartsWith(_indexStreamPrefix)) return false; if (eventType == "$>") return false; emittedEvents = new[] { new EmittedEvent( _indexStreamPrefix + eventType, Guid.NewGuid(), "$>", sequenceNumber + "@" + streamId, eventPosition, expectedTag: null) }; return true; }
public ProjectionSubscription( Guid projectionCorrelationId, CheckpointTag from, IHandle<ProjectionMessage.Projections.CommittedEventReceived> eventHandler, IHandle<ProjectionMessage.Projections.CheckpointSuggested> checkpointHandler, CheckpointStrategy checkpointStrategy, long? checkpointUnhandledBytesThreshold) { if (eventHandler == null) throw new ArgumentNullException("eventHandler"); if (checkpointHandler == null) throw new ArgumentNullException("checkpointHandler"); if (checkpointStrategy == null) throw new ArgumentNullException("checkpointStrategy"); _eventHandler = eventHandler; _checkpointHandler = checkpointHandler; _checkpointStrategy = checkpointStrategy; _checkpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; _projectionCorrelationId = projectionCorrelationId; _lastPassedOrCheckpointedEventPosition = from.Position; _eventFilter = checkpointStrategy.EventFilter; _positionTagger = checkpointStrategy.PositionTagger; _positionTracker = new PositionTracker(_positionTagger); _positionTracker.UpdateByCheckpointTagInitial(from); }
/// <summary> /// Receives a batch of <see cref="EventData" /> from the Event Hub partition. /// </summary> /// /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param> /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the per-try timeout specified by the retry policy will be used.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The batch of <see cref="EventData" /> from the Event Hub partition this consumer is associated with. If no events are present, an empty set is returned.</returns> /// public override async Task <IReadOnlyList <EventData> > ReceiveAsync(int maximumMessageCount, TimeSpan?maximumWaitTime, CancellationToken cancellationToken) { Argument.AssertNotClosed(_closed, nameof(AmqpConsumer)); Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount)); var receivedEventCount = 0; var failedAttemptCount = 0; var tryTimeout = RetryPolicy.CalculateTryTimeout(0); var waitTime = (maximumWaitTime ?? tryTimeout); var link = default(ReceivingAmqpLink); var retryDelay = default(TimeSpan?); var amqpMessages = default(IEnumerable <AmqpMessage>); var receivedEvents = default(List <EventData>); var lastReceivedEvent = default(EventData); var stopWatch = ValueStopwatch.StartNew(); try { while ((!cancellationToken.IsCancellationRequested) && (!_closed)) { try { // Creation of the link happens without explicit knowledge of the cancellation token // used for this operation; validate the token state before attempting link creation and // again after the operation completes to provide best efforts in respecting it. EventHubsEventSource.Log.EventReceiveStart(EventHubName, ConsumerGroup, PartitionId); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var messagesReceived = await Task.Factory.FromAsync ( (callback, state) => link.BeginReceiveMessages(maximumMessageCount, waitTime, callback, state), (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages), TaskCreationOptions.RunContinuationsAsynchronously ).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // If event messages were received, then package them for consumption and // return them. if ((messagesReceived) && (amqpMessages != null)) { receivedEvents ??= new List <EventData>(); foreach (AmqpMessage message in amqpMessages) { link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome); receivedEvents.Add(MessageConverter.CreateEventFromMessage(message)); message.Dispose(); } receivedEventCount = receivedEvents.Count; if (receivedEventCount > 0) { lastReceivedEvent = receivedEvents[receivedEventCount - 1]; if (lastReceivedEvent.Offset > long.MinValue) { CurrentEventPosition = EventPosition.FromOffset(lastReceivedEvent.Offset, false); } if (TrackLastEnqueuedEventProperties) { LastReceivedEvent = lastReceivedEvent; } } return(receivedEvents); } // No events were available. return(EmptyEventSet); } catch (EventHubsException ex) when(ex.Reason == EventHubsException.FailureReason.ServiceTimeout) { // Because the timeout specified with the request is intended to be the maximum // amount of time to wait for events, a timeout isn't considered an error condition, // rather a sign that no events were available in the requested period. return(EmptyEventSet); } catch (Exception ex) { Exception activeEx = ex.TranslateServiceException(EventHubName); // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop. // Otherwise, bubble the exception. ++failedAttemptCount; retryDelay = RetryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount); if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested)) { EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, activeEx.Message); await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.GetElapsedTime())), cancellationToken).ConfigureAwait(false); tryTimeout = RetryPolicy.CalculateTryTimeout(failedAttemptCount); } else if (ex is AmqpException) { ExceptionDispatchInfo.Capture(activeEx).Throw(); } else { throw; } } } // If no value has been returned nor exception thrown by this point, // then cancellation has been requested. throw new TaskCanceledException(); } catch (TaskCanceledException) { throw; } catch (Exception ex) { EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, ex.Message); throw; } finally { EventHubsEventSource.Log.EventReceiveComplete(EventHubName, ConsumerGroup, PartitionId, receivedEventCount); } }
public async Task ReceiverIdentifier() { await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); var receivers = new List <PartitionReceiver>(); try { for (int i = 0; i < 5; i++) { TestUtility.Log($"Creating receiver {i}"); var newReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart(), new ReceiverOptions() { Identifier = $"receiver{i}" }); // Issue a receive call so link will become active. await newReceiver.ReceiveAsync(10); receivers.Add(newReceiver); } try { // Attempt to create 6th receiver. This should fail. var failReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart()); await failReceiver.ReceiveAsync(10); throw new InvalidOperationException("6th receiver should have encountered QuotaExceededException."); } catch (QuotaExceededException ex) { TestUtility.Log($"Received expected exception {ex.GetType()}: {ex.Message}"); foreach (var receiver in receivers) { Assert.True(ex.Message.Contains(receiver.Identifier), $"QuotaExceededException message is missing receiver identifier '{receiver.Identifier}'"); } } } finally { // Close all receivers. foreach (var receiver in receivers) { await Task.WhenAll( receiver.CloseAsync(), ehClient.CloseAsync()); } } } }
public bool CanJoinAt(EventPosition firstAvailableTransactionFileEvent, CheckpointTag eventCheckpointTag) { //NOTE: here the committed event MUST pass the projection subscription source filter as it was sent to us by specialized event // distribution point. MakeCheckpointTag fails otherwise. var result = _checkpointStrategy.IsCheckpointTagAfterEventPosition( eventCheckpointTag, firstAvailableTransactionFileEvent); if (result) _logger.Trace( "Projection subscription '{0}' can join distribution at '{1}' when the first available event is '{2}'", _projectionCorrelationId, eventCheckpointTag, firstAvailableTransactionFileEvent); return result; }
public IPartitionReceiver CreateEpochReceiver(string consumerGroupName, string partitionId, EventPosition eventPosition, string offset, long epoch, ReceiverOptions receiverOptions) { return(new PartitionReceiverWrapper(this.inner.CreateEpochReceiver(consumerGroupName, partitionId, eventPosition, epoch, receiverOptions))); }
async Task PartitionKeyValidation() { int NumberOfMessagesToSend = 100; var partitionOffsets = new Dictionary <string, string>(); // Discover the end of stream on each partition. TestUtility.Log("Discovering end of stream on each partition."); foreach (var partitionId in this.PartitionIds) { var lastEvent = await this.EventHubClient.GetPartitionRuntimeInformationAsync(partitionId); partitionOffsets.Add(partitionId, lastEvent.LastEnqueuedOffset); TestUtility.Log($"Partition {partitionId} has last message with offset {lastEvent.LastEnqueuedOffset}"); } // Now send a set of messages with different partition keys. TestUtility.Log($"Sending {NumberOfMessagesToSend} messages."); Random rnd = new Random(); for (int i = 0; i < NumberOfMessagesToSend; i++) { var partitionKey = rnd.Next(10); await this.EventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")), partitionKey.ToString()); } // It is time to receive all messages that we just sent. // Prepare partition key to partition map while receiving. // Validation: All messages of a partition key should be received from a single partition. TestUtility.Log("Starting to receive all messages from each partition."); var receiveTasks = new Dictionary <string, Task <List <EventData> > >(); var receivers = new List <PartitionReceiver>(); foreach (var partitionId in this.PartitionIds) { var receiver = this.EventHubClient.CreateReceiver( PartitionReceiver.DefaultConsumerGroupName, partitionId, EventPosition.FromOffset(partitionOffsets[partitionId])); receivers.Add(receiver); receiveTasks.Add(partitionId, ReceiveAllMessages(receiver)); } int totalReceived = 0; var partitionMap = new Dictionary <string, string>(); try { foreach (var receiveTask in receiveTasks) { var partitionId = receiveTask.Key; var messagesFromPartition = await receiveTask.Value; totalReceived += messagesFromPartition.Count; TestUtility.Log($"Received {messagesFromPartition.Count} messages from partition {partitionId}."); foreach (var ed in messagesFromPartition) { var pk = ed.SystemProperties.PartitionKey; if (partitionMap.ContainsKey(pk) && partitionMap[pk] != partitionId) { throw new Exception($"Received a message from partition {partitionId} with partition key {pk}, whereas the same key was observed on partition {partitionMap[pk]} before."); } partitionMap[pk] = partitionId; } } } finally { foreach (var receiver in receivers) { await receiver.CloseAsync(); } } Assert.True(totalReceived == NumberOfMessagesToSend, $"Didn't receive the same number of messages that we sent. Sent: {NumberOfMessagesToSend}, Received: {totalReceived}"); }
public async Task SendAndReceive() { string targetPartitionId = "0"; TestUtility.Log("Creating Event Hub client"); await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(GetWebSocketConnectionString(connectionString)); PartitionSender sender = null; try { // Send single message TestUtility.Log("Sending single event"); sender = ehClient.CreatePartitionSender(targetPartitionId); var eventData = new EventData(Encoding.UTF8.GetBytes("This event will be transported via web-sockets")); await sender.SendAsync(eventData); } finally { await sender?.CloseAsync(); } PartitionReceiver receiver = null; try { // Receive single message. TestUtility.Log("Receiving single event"); receiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, targetPartitionId, EventPosition.FromStart()); var msg = await receiver.ReceiveAsync(1); Assert.True(msg != null, $"Failed to receive single event from partition {targetPartitionId}"); } finally { await receiver?.CloseAsync(); } } }
public RequiresEventPosition(EventPosition eventPosition) { _eventPosition = eventPosition; _eventService = ObjectFactory.GetInstance<IEventService>(); }
public async Task SendBatchWithPartitionKey() { string targetPartitionKey = "this is the partition key"; var ehClient = EventHubClient.CreateFromConnectionString(TestUtility.EventHubsConnectionString); var receiver = default(PartitionReceiver); try { // Mark end of each partition so that we can start reading from there. var partitionIds = await this.GetPartitionsAsync(ehClient); var partitions = await TestUtility.DiscoverEndOfStreamForPartitionsAsync(ehClient, partitionIds); // Send a batch of 2 messages. using (var eventData1 = new EventData(Guid.NewGuid().ToByteArray())) using (var eventData2 = new EventData(Guid.NewGuid().ToByteArray())) { await ehClient.SendAsync(new[] { eventData1, eventData2 }, targetPartitionKey); } // Now find out the partition where our messages landed. var targetPartition = ""; foreach (var pId in partitionIds) { var pInfo = await ehClient.GetPartitionRuntimeInformationAsync(pId); if (pInfo.LastEnqueuedOffset != partitions[pId]) { targetPartition = pId; TestUtility.Log($"Batch landed on partition {targetPartition}"); } } // Confirm that we identified the partition with our messages. Assert.True(targetPartition != "", "None of the partition offsets moved."); // Receive all messages from target partition. receiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, targetPartition, EventPosition.FromOffset(partitions[targetPartition])); var messages = await ReceiveAllMessagesAsync(receiver); // Validate 2 messages received. Assert.True(messages.Count == 2, $"Received {messages.Count} messages instead of 2."); // Validate both messages carry correct partition id. Assert.True(messages[0].SystemProperties.PartitionKey == targetPartitionKey, $"First message returned partition key value '{messages[0].SystemProperties.PartitionKey}'"); Assert.True(messages[1].SystemProperties.PartitionKey == targetPartitionKey, $"Second message returned partition key value '{messages[1].SystemProperties.PartitionKey}'"); } finally { await Task.WhenAll( receiver?.CloseAsync(), ehClient.CloseAsync()); } }
public override EventHubWrappers.IPartitionReceiver CreateEpochReceiver(string consumerGroupName, string partitionId, EventPosition eventPosition, long epoch, ReceiverOptions receiverOptions) { long startSeq = CalculateStartSeq(eventPosition); return(new TimeoutPartitionReceiverMock(partitionId, startSeq, this.token, this.csb.OperationTimeout)); }
public bool ProcessEvent( EventPosition position, CheckpointTag eventPosition, string streamId, string eventType, string category1, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { emittedEvents = null; newState = null; if (streamId.StartsWith(_categoryStreamPrefix)) return false; var lastSlashPos = streamId.LastIndexOf(_separator); if (lastSlashPos < 0) return true; // handled but not interesting to us var category = streamId.Substring(0, lastSlashPos); emittedEvents = new[] { new EmittedEvent( _categoryStreamPrefix + category, Guid.NewGuid(), "$>", sequenceNumber + "@" + streamId, eventPosition, expectedTag: null) }; return true; }
public void UpdateByCheckpointTag(CheckpointTag checkpointTag) { var zero = new EventPosition(0, -1); if (_lastEventPosition != zero || _lastTag != null) throw new InvalidOperationException("Posistion tagger has be already updated"); _lastEventPosition = checkpointTag.Position; _lastTag = checkpointTag; }
public void Handle(ProjectionMessage.Projections.CommittedEventDistributed message) { if (message.Data == null) throw new NotSupportedException(); // NOTE: we may receive here messages from heading event distribution point // and they may not pass out source filter. Discard them first if (!_eventFilter.PassesSource(message.ResolvedLinkTo, message.PositionStreamId)) return; var eventCheckpointTag = _positionTagger.MakeCheckpointTag(_positionTracker.LastTag, message); //TODO: when joining heading distribution point replayed events may cause invalid operation exception on comparison if (eventCheckpointTag <= _positionTracker.LastTag) { _logger.Trace( "Skipping replayed event {0}@{1} at position {2}. the last processed event checkpoint tag is: {3}", message.PositionSequenceNumber, message.PositionStreamId, message.Position, _positionTracker.LastTag); return; } _positionTracker.UpdateByCheckpointTagForward(eventCheckpointTag); if (_eventFilter.Passes(message.ResolvedLinkTo, message.PositionStreamId, message.Data.EventType)) { _lastPassedOrCheckpointedEventPosition = message.Position; var convertedMessage = ProjectionMessage.Projections.CommittedEventReceived.FromCommittedEventDistributed(message, eventCheckpointTag); _eventHandler.Handle(convertedMessage); } else { if (_checkpointUnhandledBytesThreshold != null && message.Position.CommitPosition - _lastPassedOrCheckpointedEventPosition.CommitPosition > _checkpointUnhandledBytesThreshold) { _lastPassedOrCheckpointedEventPosition = message.Position; _checkpointHandler.Handle( new ProjectionMessage.Projections.CheckpointSuggested( _projectionCorrelationId, _positionTracker.LastTag)); } } }
public override void Handle(ClientMessage.ReadAllEventsForwardCompleted message) { if (_disposed) return; if (!_eventsRequested) throw new InvalidOperationException("Read events has not been requested"); if (_paused) throw new InvalidOperationException("Paused"); _eventsRequested = false; if (message.Result.Events.Length == 0) { // the end if (_deliverEndOfTfPosition) DeliverLastCommitPosition(_from); // allow joining heading distribution SendIdle(); SendEof(); } else { for (int index = 0; index < message.Result.Events.Length; index++) { var @event = message.Result.Events[index]; DeliverEvent(@event, message.Result.TfEofPosition); } _from = message.Result.NextPos; } if (_disposed) return; if (_pauseRequested) _paused = true; else if (message.Result.Events.Length == 0) RequestEvents(delay: true); else _publisher.Publish(CreateTickMessage()); }
public bool ProcessEvent( EventPosition position, CheckpointTag eventPosition, string streamId, string eventType, string category, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { if (_failOnProcessEvent) { throw new Exception("PROCESS_EVENT_FAILED"); } _lastProcessedStreamId = streamId; _lastProcessedEventType = eventType; _lastProcessedEventId = eventId; _lastProcessedSequencenumber = sequenceNumber; _lastProcessedMetadata = metadata; _lastProcessedData = data; _eventsProcessed++; switch (eventType) { case "skip_this_type": _loadedState = newState = null; emittedEvents = null; return(false); case "handle_this_type": _loadedState = newState = data; emittedEvents = null; return(true); case "append": _loadedState = newState = _loadedState + data; emittedEvents = null; return(true); case "no_state_emit1_type": _loadedState = newState = ""; emittedEvents = new[] { new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data, eventPosition, null), }; return(true); case "emit1_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data, eventPosition, null), }; return(true); case "emit22_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit1Data, eventPosition, null), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit2Data, eventPosition, null), }; return(true); case "emit212_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit1Data, eventPosition, null), new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit2Data, eventPosition, null), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit3Data, eventPosition, null), }; return(true); case "emit12_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data, eventPosition, null), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit2Data, eventPosition, null), }; return(true); default: throw new NotSupportedException(); } }
public void ConstructorRequiresTheConnectionScope() { Assert.That(() => new AmqpEventHubConsumer("theMostAwesomeHubEvar", "$DEFAULT", "0", EventPosition.FromSequenceNumber(123), new EventHubConsumerOptions(), null, Mock.Of <AmqpMessageConverter>(), Mock.Of <EventHubRetryPolicy>(), null), Throws.ArgumentNullException); }
private static async Task MainAsync() { Console.WriteLine("Connecting to event hub..."); var eventHubClient = EventHubClient.CreateFromConnectionString(connectionString); var runtimeInformation = await eventHubClient.GetRuntimeInformationAsync(); var partitionReceivers = runtimeInformation.PartitionIds.Select(partitionId => eventHubClient.CreateReceiver("wired_brain_coffee_console_direct", partitionId, EventPosition.FromStart())).ToList(); Console.WriteLine("Waiting for incoming events..."); foreach (var partitionReceiver in partitionReceivers) { partitionReceiver.SetReceiveHandler( new WiredBrainCoffeePartitionReceiveHandler(partitionReceiver.PartitionId)); } Console.WriteLine($"Press any key to shut down"); Console.ReadLine(); await eventHubClient.CloseAsync(); }
/// <summary> /// Retrieves a list of all the checkpoints in a data store for a given namespace, Event Hub and consumer group. /// </summary> /// /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace the ownership are associated with. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="eventHubName">The name of the specific Event Hub the ownership are associated with, relative to the Event Hubs namespace that contains it.</param> /// <param name="consumerGroup">The name of the consumer group the ownership are associated with.</param> /// <param name="cancellationToken">A <see cref="CancellationToken" /> instance to signal the request to cancel the operation.</param> /// /// <returns>An enumerable containing all the existing checkpoints for the associated Event Hub and consumer group.</returns> /// public override async Task <IEnumerable <EventProcessorCheckpoint> > ListCheckpointsAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); ListCheckpointsStart(fullyQualifiedNamespace, eventHubName, consumerGroup); async Task <List <EventProcessorCheckpoint> > listCheckpointsAsync(CancellationToken listCheckpointsToken) { var prefix = string.Format(CultureInfo.InvariantCulture, CheckpointPrefix, fullyQualifiedNamespace.ToLowerInvariant(), eventHubName.ToLowerInvariant(), consumerGroup.ToLowerInvariant()); var checkpoints = new List <EventProcessorCheckpoint>(); await foreach (BlobItem blob in ContainerClient.GetBlobsAsync(traits: BlobTraits.Metadata, prefix: prefix, cancellationToken: listCheckpointsToken).ConfigureAwait(false)) { var partitionId = blob.Name.Substring(prefix.Length); var startingPosition = default(EventPosition?); var offset = default(long?); var sequenceNumber = default(long?); if (blob.Metadata.TryGetValue(BlobMetadataKey.Offset, out var str) && long.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { offset = result; startingPosition = EventPosition.FromOffset(result, false); } else if (blob.Metadata.TryGetValue(BlobMetadataKey.SequenceNumber, out str) && long.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { sequenceNumber = result; startingPosition = EventPosition.FromSequenceNumber(result, false); } // If either the offset or the sequence number was not populated, // this is not a valid checkpoint. if (startingPosition.HasValue) { checkpoints.Add(new BlobStorageCheckpoint { FullyQualifiedNamespace = fullyQualifiedNamespace, EventHubName = eventHubName, ConsumerGroup = consumerGroup, PartitionId = partitionId, StartingPosition = startingPosition.Value, Offset = offset, SequenceNumber = sequenceNumber }); } else { InvalidCheckpointFound(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup); } } return(checkpoints); }; async Task <List <EventProcessorCheckpoint> > listLegacyCheckpointsAsync(List <EventProcessorCheckpoint> existingCheckpoints, CancellationToken listCheckpointsToken) { // Legacy checkpoints are not normalized to lowercase var legacyPrefix = string.Format(CultureInfo.InvariantCulture, LegacyCheckpointPrefix, fullyQualifiedNamespace, eventHubName, consumerGroup); var checkpoints = new List <EventProcessorCheckpoint>(); await foreach (BlobItem blob in ContainerClient.GetBlobsAsync(prefix: legacyPrefix, cancellationToken: listCheckpointsToken).ConfigureAwait(false)) { // Skip new checkpoints and empty blobs if (blob.Properties.ContentLength == 0) { continue; } var partitionId = blob.Name.Substring(legacyPrefix.Length); // Check whether there is already a checkpoint for this partition id if (existingCheckpoints.Any(existingCheckpoint => string.Equals(existingCheckpoint.PartitionId, partitionId, StringComparison.Ordinal))) { continue; } var startingPosition = default(EventPosition?); BlobBaseClient blobClient = ContainerClient.GetBlobClient(blob.Name); using var memoryStream = new MemoryStream(); await blobClient.DownloadToAsync(memoryStream, listCheckpointsToken).ConfigureAwait(false); TryReadLegacyCheckpoint( memoryStream.GetBuffer().AsSpan(0, (int)memoryStream.Length), out long?offset, out long?sequenceNumber); if (offset.HasValue) { startingPosition = EventPosition.FromOffset(offset.Value, false); } else if (sequenceNumber.HasValue) { startingPosition = EventPosition.FromSequenceNumber(sequenceNumber.Value, false); } if (startingPosition.HasValue) { checkpoints.Add(new BlobStorageCheckpoint { FullyQualifiedNamespace = fullyQualifiedNamespace, EventHubName = eventHubName, ConsumerGroup = consumerGroup, PartitionId = partitionId, StartingPosition = startingPosition.Value, Offset = offset, SequenceNumber = sequenceNumber }); } else { InvalidCheckpointFound(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup); } } return(checkpoints); }; List <EventProcessorCheckpoint> checkpoints = null; try { checkpoints = await ApplyRetryPolicy(listCheckpointsAsync, cancellationToken).ConfigureAwait(false); if (InitializeWithLegacyCheckpoints) { checkpoints.AddRange(await ApplyRetryPolicy(ct => listLegacyCheckpointsAsync(checkpoints, ct), cancellationToken).ConfigureAwait(false)); } return(checkpoints); } catch (RequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.ContainerNotFound) { ListCheckpointsError(fullyQualifiedNamespace, eventHubName, consumerGroup, ex); throw new RequestFailedException(BlobsResourceDoesNotExist); } catch (Exception ex) { ListCheckpointsError(fullyQualifiedNamespace, eventHubName, consumerGroup, ex); throw; } finally { ListCheckpointsComplete(fullyQualifiedNamespace, eventHubName, consumerGroup, checkpoints?.Count ?? 0); } }
public async Task InvalidProxy() { // Send call should fail. await Assert.ThrowsAsync <WebSocketException>(async() => { await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(GetWebSocketConnectionString(connectionString)); ehClient.WebProxy = new WebProxy("http://1.2.3.4:9999"); var edToFail = new EventData(Encoding.UTF8.GetBytes("This is a sample event.")); await ehClient.SendAsync(edToFail); } }); // Receive call should fail. await Assert.ThrowsAsync <WebSocketException>(async() => { await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(GetWebSocketConnectionString(connectionString)); ehClient.WebProxy = new WebProxy("http://1.2.3.4:9999"); await ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", EventPosition.FromStart()).ReceiveAsync(1); } }); // Management link call should fail. await Assert.ThrowsAsync <WebSocketException>(async() => { await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(GetWebSocketConnectionString(connectionString)); ehClient.WebProxy = new WebProxy("http://1.2.3.4:9999"); await ehClient.GetRuntimeInformationAsync(); } }); // Send/receive should work fine w/o proxy. await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehNoProxyClient = EventHubClient.CreateFromConnectionString(GetWebSocketConnectionString(connectionString)); var eventData = new EventData(Encoding.UTF8.GetBytes("This is a sample event.")); } }
private void DeliverEvent(ResolvedEventRecord @event, long lastCommitPosition) { EventRecord positionEvent = (@event.Link ?? @event.Event); var receivedPosition = new EventPosition(@event.CommitPosition, positionEvent.LogPosition); if (_from > receivedPosition) throw new Exception( string.Format( "ReadFromTF returned events in incorrect order. Last known position is: {0}. Received position is: {1}", _from, receivedPosition)); _publisher.Publish( new ProjectionCoreServiceMessage.CommittedEventDistributed( _distibutionPointCorrelationId, receivedPosition, positionEvent.EventStreamId, positionEvent.EventNumber, @event.Event.EventStreamId, @event.Event.EventNumber, @event.Link != null, new Event( @event.Event.EventId, @event.Event.EventType, (@event.Event.Flags & PrepareFlags.IsJson) != 0, @event.Event.Data, @event.Event.Metadata), receivedPosition.PreparePosition, 100.0f * positionEvent.LogPosition / lastCommitPosition)); }
/// <summary> /// Creates an PartitionReceiver from the given connection sting and partition key. /// The Reliable Dictionaries are used to create a receiver from wherever the service last left off, /// or from the current date/time if it's the first time the service is coming up. /// </summary> /// <param name="client"></param> /// <param name="consumerGroup"></param> /// <param name="servicePartitionKey"></param> /// <param name="epochDictionary"></param> /// <param name="offsetDictionary"></param> /// <returns>PartitionReceiver</returns> private async Task <PartitionReceiver> ConnectToIoTHubAsync( EventHubClient client, string consumerGroup, long servicePartitionKey, IReliableDictionary <string, long> epochDictionary, IReliableDictionary <string, string> offsetDictionary) { // This gives each partition its own dedicated TCP connection to IoT Hub. var eventHubRuntimeInfo = await client.GetRuntimeInformationAsync(); PartitionReceiver partitionReceiver; // Get an IoT Hub partition ID that corresponds to this partition's low key. // This assumes that this service has a partition count 'n' that is equal to the IoT Hub partition count and a partition range of 0..n-1. // For example, given an IoT Hub with 32 partitions, this service should be created with: // partition count = 32 // partition range = 0..31 string eventHubPartitionId = eventHubRuntimeInfo.PartitionIds[servicePartitionKey]; using (var tx = this.StateManager.CreateTransaction()) { ConditionalValue <string> offsetResult = await offsetDictionary.TryGetValueAsync(tx, "offset", LockMode.Default); ConditionalValue <long> epochResult = await epochDictionary.TryGetValueAsync(tx, "epoch", LockMode.Update); long newEpoch = epochResult.HasValue ? epochResult.Value + 1 : 0; if (offsetResult.HasValue) { // continue where the service left off before the last failover or restart. ServiceEventSource.Current.ServiceMessage( this.Context, "Creating EventHub listener on partition {0} with offset {1}", eventHubPartitionId, offsetResult.Value); partitionReceiver = client.CreateEpochReceiver(consumerGroup, eventHubPartitionId, EventPosition.FromOffset(offsetResult.Value), newEpoch); } else { // first time this service is running so there is no offset value yet. // start with the current time. ServiceEventSource.Current.ServiceMessage( this.Context, "Creating EventHub listener on partition {0} with offset {1}", eventHubPartitionId, DateTime.UtcNow); partitionReceiver = client.CreateEpochReceiver(consumerGroup, eventHubPartitionId, EventPosition.FromEnqueuedTime(DateTime.UtcNow), newEpoch); } // epoch is recorded each time the service fails over or restarts. await epochDictionary.SetAsync(tx, "epoch", newEpoch); await tx.CommitAsync(); } return(partitionReceiver); }
public async Task PartitionReceiverEpochReceive() { TestUtility.Log("Testing EpochReceiver semantics"); await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); var epochReceiver1 = ehClient.CreateEpochReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart(), 1); var epochReceiver2 = ehClient.CreateEpochReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart(), 2); try { // Read the events from Epoch 1 Receiver until we're at the end of the stream TestUtility.Log("Starting epoch 1 receiver"); IEnumerable <EventData> events; do { events = await epochReceiver1.ReceiveAsync(10); var count = events?.Count() ?? 0; }while (events != null); TestUtility.Log("Starting epoch 2 receiver"); var epoch2ReceiveTask = epochReceiver2.ReceiveAsync(10); DateTime stopTime = DateTime.UtcNow.AddSeconds(30); do { events = await epochReceiver1.ReceiveAsync(10); var count = events?.Count() ?? 0; TestUtility.Log($"Epoch 1 receiver got {count} event(s)"); }while (DateTime.UtcNow < stopTime); throw new InvalidOperationException("Epoch 1 receiver should have encountered an exception by now!"); } catch (ReceiverDisconnectedException disconnectedException) { TestUtility.Log($"Received expected exception {disconnectedException.GetType()}: {disconnectedException.Message}"); try { await epochReceiver1.ReceiveAsync(10); throw new InvalidOperationException("Epoch 1 receiver should throw ReceiverDisconnectedException here too!"); } catch (ReceiverDisconnectedException e) { TestUtility.Log($"Received expected exception {e.GetType()}"); } } finally { await Task.WhenAll( epochReceiver1.CloseAsync(), epochReceiver2.CloseAsync(), ehClient.CloseAsync()); } } }
/// <summary> /// Creates a new Checkpoint for a particular partition ID. /// </summary> /// <param name="partitionId">The partition ID for the checkpoint</param> public Checkpoint(string partitionId) : this(partitionId, EventPosition.FromStart().Offset, 0) { }
public async Task CreateEpochReceiverAfterNonEpochReceiver() { await using (var scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); var nonEpochReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart()); var epochReceiver = ehClient.CreateEpochReceiver(PartitionReceiver.DefaultConsumerGroupName, "1", EventPosition.FromStart(), 1); try { TestUtility.Log("Starting nonepoch receiver"); await nonEpochReceiver.ReceiveAsync(10); await Task.Delay(TimeSpan.FromSeconds(10)); TestUtility.Log("Starting epoch receiver"); await epochReceiver.ReceiveAsync(10); await Task.Delay(TimeSpan.FromSeconds(10)); try { TestUtility.Log("Restarting nonepoch receiver, this should fail"); await nonEpochReceiver.ReceiveAsync(10); throw new InvalidOperationException("Non-Epoch receiver should have encountered an exception by now!"); } catch (ReceiverDisconnectedException ex) when(ex.Message.Contains("non-epoch receiver is not allowed")) { TestUtility.Log($"Received expected exception {ex.GetType()}: {ex.Message}"); } } finally { await Task.WhenAll( epochReceiver.CloseAsync(), nonEpochReceiver.CloseAsync(), ehClient.CloseAsync()); } } }
public async Task CreateReceiverWithInclusiveSequenceNumber() { var receiver = default(PartitionReceiver); await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); try { // Randomly pick one of the available partitons. var partitions = await this.GetPartitionsAsync(ehClient); var partitionId = partitions[new Random().Next(partitions.Length)]; TestUtility.Log($"Randomly picked partition {partitionId}"); await TestUtility.SendToPartitionAsync(ehClient, partitionId, $"{partitionId} event."); // Find out where to start reading on the partition. var pInfo = await ehClient.GetPartitionRuntimeInformationAsync(partitionId); // Send a message which is expected to go to the end of stream. // We are expecting to receive this message as well. await TestUtility.SendToPartitionAsync(ehClient, partitionId, $"{partitionId} event."); // Create a new receiver which will start reading from the last message on the stream. TestUtility.Log($"Creating a new receiver with sequence number {pInfo.LastEnqueuedSequenceNumber}"); receiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, partitionId, EventPosition.FromSequenceNumber(pInfo.LastEnqueuedSequenceNumber, true)); var receivedMessages = await receiver.ReceiveAsync(100); // We should have received only 1 message from this call. Assert.True(receivedMessages.Count() == 2, $"Didn't receive 2 messages. Received {receivedMessages.Count()} messages(s)."); // Next receive on this partition shouldn't return any more messages. receivedMessages = await receiver.ReceiveAsync(100, TimeSpan.FromSeconds(15)); Assert.True(receivedMessages == null, $"Received messages at the end."); } finally { await Task.WhenAll( receiver.CloseAsync(), ehClient.CloseAsync()); } } }
private void DeliverLastCommitPosition(EventPosition lastPosition) { if (_stopOnEof) return; _publisher.Publish( new ProjectionCoreServiceMessage.CommittedEventDistributed( _distibutionPointCorrelationId, default(EventPosition), null, int.MinValue, null, int.MinValue, false, null, lastPosition.PreparePosition, 100.0f)); //TODO: check was is passed here }
public async Task CloseSenderClient() { var ehClient = EventHubClient.CreateFromConnectionString(TestUtility.EventHubsConnectionString); var pSender = ehClient.CreatePartitionSender("0"); var pReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", EventPosition.FromStart()); try { TestUtility.Log("Sending single event to partition 0"); using (var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!"))) { await pSender.SendAsync(eventData); TestUtility.Log("Closing partition sender"); await pSender.CloseAsync(); } await Assert.ThrowsAsync <ObjectDisposedException>(async() => { TestUtility.Log("Sending another event to partition 0 on the closed sender, this should fail"); using (var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!"))) { await pSender.SendAsync(eventData); } }); } finally { await Task.WhenAll( pReceiver.CloseAsync(), ehClient.CloseAsync()); } }
private void ValidateEventOrder(ProjectionCoreServiceMessage.CommittedEventDistributed message) { if (_lastEventPosition >= message.Position) throw new InvalidOperationException( string.Format( "Invalid committed event order. Last: '{0}' Received: '{1}'", _lastEventPosition, message.Position)); _lastEventPosition = message.Position; }
public bool ProcessEvent( EventPosition position, string streamId, string eventType, string category, Guid eventId, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { if (_failOnProcessEvent) throw new Exception("PROCESS_EVENT_FAILED"); _lastProcessedStreamId = streamId; _lastProcessedEventType = eventType; _lastProcessedEventId = eventId; _lastProcessedSequencenumber = sequenceNumber; _lastProcessedMetadata = metadata; _lastProcessedData = data; _eventsProcessed++; switch (eventType) { case "skip_this_type": _loadedState = newState = null; emittedEvents = null; return false; case "handle_this_type": _loadedState = newState = data; emittedEvents = null; return true; case "append": _loadedState = newState = _loadedState + data; emittedEvents = null; return true; case "no_state_emit1_type": _loadedState = newState = ""; emittedEvents = new[] {new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data),}; return true; case "emit1_type": _loadedState = newState = data; emittedEvents = new[] {new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data),}; return true; case "emit22_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit1Data), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit2Data), }; return true; case "emit212_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit1Data), new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit2Data), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit3Data), }; return true; case "emit12_type": _loadedState = newState = data; emittedEvents = new[] { new EmittedEvent(_emit1StreamId, Guid.NewGuid(), _emit1EventType, _emit1Data), new EmittedEvent(_emit2StreamId, Guid.NewGuid(), _emit2EventType, _emit2Data), }; return true; default: throw new NotSupportedException(); } }
private void CacheRecentMessage(ProjectionMessage.Projections.CommittedEventReceived message) { _lastMessages.Enqueue(message); if (_lastMessages.Count > _eventCacheSize) { _lastMessages.Dequeue(); } var lastAvailableCommittedevent = _lastMessages.Peek(); _subscribeFromPosition = lastAvailableCommittedevent.Position; }
public void ConstructorRequiresTheOptions() { Assert.That(() => new AmqpEventHubConsumer("theMostAwesomeHubEvar", "$DEFAULT", "0", EventPosition.FromOffset(1), null, Mock.Of <AmqpConnectionScope>(), Mock.Of <AmqpMessageConverter>(), Mock.Of <EventHubRetryPolicy>(), new LastEnqueuedEventProperties("hub", "0")), Throws.ArgumentNullException); }
private CommittedEventReceived( Guid correlationId, Guid subscriptionId, EventPosition position, CheckpointTag checkpointTag, string positionStreamId, int positionSequenceNumber, string eventStreamId, int eventSequenceNumber, string eventCategory, bool resolvedLinkTo, ResolvedEvent data, float progress, long subscriptionMessageSequenceNumber) : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber) { if (data == null) throw new ArgumentNullException("data"); _data = data; _position = position; _positionStreamId = positionStreamId; _positionSequenceNumber = positionSequenceNumber; _eventStreamId = eventStreamId; _eventSequenceNumber = eventSequenceNumber; _eventCategory = eventCategory; _resolvedLinkTo = resolvedLinkTo; }
public async Task CreateReceiverWithSequenceNumber() { var receiver = default(PartitionReceiver); await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); try { // Randomly pick one of the available partitons. var partitions = await this.GetPartitionsAsync(ehClient); var partitionId = partitions[new Random().Next(partitions.Length)]; TestUtility.Log($"Randomly picked partition {partitionId}"); // Send and receive a message to identify the end of stream. var pInfo = await ehClient.GetPartitionRuntimeInformationAsync(partitionId); // Send a new message which is expected to go to the end of stream. // We are expecting to receive only this message. var eventSent = new EventData(new byte[1]); eventSent.Properties["stamp"] = Guid.NewGuid().ToString(); await ehClient.CreatePartitionSender(partitionId).SendAsync(eventSent); // Create a new receiver which will start reading from the last message on the stream. TestUtility.Log($"Creating a new receiver with sequence number {pInfo.LastEnqueuedSequenceNumber}"); receiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, partitionId, EventPosition.FromSequenceNumber(pInfo.LastEnqueuedSequenceNumber)); var receivedMessages = await receiver.ReceiveAsync(100); // We should have received only 1 message from this call. Assert.True(receivedMessages.Count() == 1, $"Didn't receive 1 message. Received {receivedMessages.Count()} messages(s)."); // Check stamp. Assert.True(receivedMessages.Single().Properties["stamp"].ToString() == eventSent.Properties["stamp"].ToString() , "Stamps didn't match on the message sent and received!"); TestUtility.Log("Received correct message as expected."); // Next receive on this partition shouldn't return any more messages. receivedMessages = await receiver.ReceiveAsync(100, TimeSpan.FromSeconds(15)); Assert.True(receivedMessages == null, $"Received messages at the end."); } finally { await Task.WhenAll( receiver.CloseAsync(), ehClient.CloseAsync()); } } }
private CommittedEventReceived( Guid correlationId, Guid subscriptionId, EventPosition position, string eventStreamId, int eventSequenceNumber, string eventCategory, bool resolvedLinkTo, ResolvedEvent data, float progress, long subscriptionMessageSequenceNumber) : this( correlationId, subscriptionId, position, CheckpointTag.FromPosition(position.CommitPosition, position.PreparePosition), eventStreamId, eventSequenceNumber, eventStreamId, eventSequenceNumber, eventCategory, resolvedLinkTo, data, progress, subscriptionMessageSequenceNumber) { }
public async Task PartitionReceiverReceiveBatch() { const int MaxBatchSize = 5; TestUtility.Log("Receiving Events via PartitionReceiver.ReceiveAsync(BatchSize)"); const string partitionId = "0"; await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); var partitionSender = ehClient.CreatePartitionSender(partitionId); var partitionReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, partitionId, EventPosition.FromEnqueuedTime(DateTime.UtcNow.AddMinutes(-10))); try { int eventCount = 20; TestUtility.Log($"Sending {eventCount} events to Partition {partitionId}"); var sendEvents = new List <EventData>(eventCount); for (int i = 0; i < eventCount; i++) { sendEvents.Add(new EventData(Encoding.UTF8.GetBytes($"Hello EventHub! Message {i}"))); } await partitionSender.SendAsync(sendEvents); int maxReceivedBatchSize = 0; while (true) { IEnumerable <EventData> partition1Events = await partitionReceiver.ReceiveAsync(MaxBatchSize); int receivedEventCount = partition1Events != null?partition1Events.Count() : 0; TestUtility.Log($"Received {receivedEventCount} event(s)"); if (partition1Events == null) { break; } maxReceivedBatchSize = Math.Max(maxReceivedBatchSize, receivedEventCount); } Assert.True(maxReceivedBatchSize == MaxBatchSize, $"A max batch size of {MaxBatchSize} events was not honored! Actual {maxReceivedBatchSize}."); } finally { await Task.WhenAll( partitionReceiver.CloseAsync(), partitionSender.CloseAsync(), ehClient.CloseAsync()); } } }
public static CommittedEventReceived Sample( Guid correlationId, Guid subscriptionId, EventPosition position, string eventStreamId, int eventSequenceNumber, bool resolvedLinkTo, ResolvedEvent data, long subscriptionMessageSequenceNumber) { return new CommittedEventReceived( correlationId, subscriptionId, position, eventStreamId, eventSequenceNumber, null, resolvedLinkTo, data, 77.7f, subscriptionMessageSequenceNumber); }
public async Task CreateReceiverWithEndOfStream() { var receiver = default(PartitionReceiver); var partitionSender = default(PartitionSender); await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); try { // Randomly pick one of the available partitons. var partitions = await this.GetPartitionsAsync(ehClient); var partitionId = partitions[new Random().Next(partitions.Length)]; TestUtility.Log($"Randomly picked partition {partitionId}"); partitionSender = ehClient.CreatePartitionSender(partitionId); // Send couple of messages before creating an EndOfStream receiver. // We are not expecting to receive these messages would be sent before receiver creation. for (int i = 0; i < 10; i++) { var ed = new EventData(new byte[1]); await partitionSender.SendAsync(ed); } // Create a new receiver which will start reading from the end of the stream. TestUtility.Log($"Creating a new receiver with offset EndOFStream"); receiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, partitionId, EventPosition.FromEnd()); // Attemp to receive the message. This should return only 1 message. var receiveTask = receiver.ReceiveAsync(100); // Send a new message which is expected to go to the end of stream. // We are expecting to receive only this message. // Wait 5 seconds before sending to avoid race. await Task.Delay(5000); var eventToReceive = new EventData(new byte[1]); eventToReceive.Properties["stamp"] = Guid.NewGuid().ToString(); await partitionSender.SendAsync(eventToReceive); // Complete asyncy receive task. var receivedMessages = await receiveTask; // We should have received only 1 message from this call. Assert.True(receivedMessages.Count() == 1, $"Didn't receive 1 message. Received {receivedMessages.Count()} messages(s)."); // Check stamp. Assert.True(receivedMessages.Single().Properties["stamp"].ToString() == eventToReceive.Properties["stamp"].ToString() , "Stamps didn't match on the message sent and received!"); TestUtility.Log("Received correct message as expected."); // Next receive on this partition shouldn't return any more messages. receivedMessages = await receiver.ReceiveAsync(100, TimeSpan.FromSeconds(15)); Assert.True(receivedMessages == null, $"Received messages at the end."); } finally { await Task.WhenAll( partitionSender.CloseAsync(), receiver.CloseAsync(), ehClient.CloseAsync()); } } }
public bool IsCheckpointTagAfterEventPosition(CheckpointTag checkpointTag, EventPosition position) { if (_streams == null || _streams.Count == 0) { return checkpointTag.Position >= position; } else if (_streams != null && _streams.Count == 1) { return checkpointTag.PreparePosition >= position.CommitPosition; } else if (_categories != null && _categories.Count == 1) { return checkpointTag.PreparePosition >= position.CommitPosition; } else throw new NotSupportedException(); }
public async Task CloseReceiverClient() { await using (var scope = await EventHubScope.CreateAsync(1)) { var connectionString = TestUtility.BuildEventHubsConnectionString(scope.EventHubName); var ehClient = EventHubClient.CreateFromConnectionString(connectionString); var pSender = ehClient.CreatePartitionSender("0"); var pReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", EventPosition.FromStart()); try { TestUtility.Log("Sending single event to partition 0"); var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")); await pSender.SendAsync(eventData); TestUtility.Log("Receiving the event."); var events = await pReceiver.ReceiveAsync(1); Assert.True(events != null && events.Count() == 1, "Failed to receive 1 event"); } finally { TestUtility.Log("Closing partition receiver"); await Task.WhenAll( pReceiver.CloseAsync(), ehClient.CloseAsync()); } await Assert.ThrowsAsync <ObjectDisposedException>(async() => { TestUtility.Log("Receiving another event from partition 0 on the closed receiver, this should fail"); await pReceiver.ReceiveAsync(1); }); } }
/// <summary> /// Starts running a task responsible for receiving and processing events in the context of a specified partition. /// </summary> /// /// <param name="partitionId">The identifier of the Event Hub partition the task is associated with. Events will be read only from this partition.</param> /// <param name="startingPosition">The position within the partition where the task should begin reading events.</param> /// <param name="maximumReceiveWaitTime">The maximum amount of time to wait to for an event to be available before emitting an empty item; if <c>null</c>, empty items will not be published.</param> /// <param name="retryOptions">The set of options to use for determining whether a failed operation should be retried and, if so, the amount of time to wait between retry attempts.</param> /// <param name="trackLastEnqueuedEventInformation">Indicates whether or not the task should request information on the last enqueued event on the partition associated with a given event, and track that information as events are received.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The running task that is currently receiving and processing events in the context of the specified partition.</returns> /// protected virtual Task RunPartitionProcessingAsync(string partitionId, EventPosition startingPosition, TimeSpan?maximumReceiveWaitTime, RetryOptions retryOptions, bool trackLastEnqueuedEventInformation, CancellationToken cancellationToken = default) { // TODO: should the retry options used here be the same for the abstract RetryPolicy property? Argument.AssertNotNullOrEmpty(partitionId, nameof(partitionId)); Argument.AssertNotNull(retryOptions, nameof(retryOptions)); return(Task.Run(async() => { // TODO: should we double check if a previous run already exists and close it? We have a race condition. Maybe we should throw in case another task exists. var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var taskCancellationToken = cancellationSource.Token; ActivePartitionProcessorTokenSources[partitionId] = cancellationSource; // Context is set to default if operation fails. This shouldn't fail unless the user tries processing // a partition they don't own. PartitionContexts.TryGetValue(partitionId, out var context); var options = new EventHubConsumerClientOptions { RetryOptions = retryOptions, TrackLastEnqueuedEventInformation = trackLastEnqueuedEventInformation }; await using var connection = CreateConnection(); await using (var consumer = new EventHubConsumerClient(ConsumerGroup, connection, options)) { await foreach (var partitionEvent in consumer.ReadEventsFromPartitionAsync(partitionId, startingPosition, maximumReceiveWaitTime, taskCancellationToken)) { using DiagnosticScope diagnosticScope = EventDataInstrumentation.ClientDiagnostics.CreateScope(DiagnosticProperty.EventProcessorProcessingActivityName); diagnosticScope.AddAttribute("kind", "server"); if (diagnosticScope.IsEnabled && partitionEvent.Data != null && EventDataInstrumentation.TryExtractDiagnosticId(partitionEvent.Data, out string diagnosticId)) { diagnosticScope.AddLink(diagnosticId); } diagnosticScope.Start(); try { await ProcessEventAsync(partitionEvent, context).ConfigureAwait(false); } catch (Exception eventProcessingException) { diagnosticScope.Failed(eventProcessingException); throw; } } } })); }
public void InitializeHandlerArgs() { #region Snippet:EventHubs_Processor_Sample03_InitializeHandlerArgs #if SNIPPET var storageConnectionString = "<< CONNECTION STRING FOR THE STORAGE ACCOUNT >>"; var blobContainerName = "<< NAME OF THE BLOB CONTAINER >>"; var eventHubsConnectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>"; var eventHubName = "<< NAME OF THE EVENT HUB >>"; var consumerGroup = "<< NAME OF THE EVENT HUB CONSUMER GROUP >>"; #else var storageConnectionString = StorageTestEnvironment.Instance.StorageConnectionString; var blobContainerName = "not-real"; var eventHubsConnectionString = EventHubsTestEnvironment.Instance.EventHubsConnectionString; var eventHubName = "fakeHub"; var consumerGroup = "fakeConsumer"; #endif var storageClient = new BlobContainerClient( storageConnectionString, blobContainerName); var processor = new EventProcessorClient( storageClient, consumerGroup, eventHubsConnectionString, eventHubName); Task initializeEventHandler(PartitionInitializingEventArgs args) { try { if (args.CancellationToken.IsCancellationRequested) { return(Task.CompletedTask); } Debug.WriteLine($"Initialize partition: { args.PartitionId }"); // If no checkpoint was found, start processing // events enqueued now or in the future. EventPosition startPositionWhenNoCheckpoint = EventPosition.FromEnqueuedTime(DateTimeOffset.UtcNow); args.DefaultStartingPosition = startPositionWhenNoCheckpoint; } catch { // Take action to handle the exception. // It is important that all exceptions are // handled and none are permitted to bubble up. } return(Task.CompletedTask); } try { processor.PartitionInitializingAsync += initializeEventHandler; // Starting and stopping the processor are not // shown in this example. } finally { processor.PartitionInitializingAsync -= initializeEventHandler; } #endregion }
private Task <PartitionReceiver> SetupFromEndOfStreamAsync(string consumerGroup, string partitionId) { return(Task.FromResult(_client.CreateReceiver(consumerGroup, partitionId, EventPosition.FromEnd()))); }
/// <summary> /// Initializes a new instance of the type /// </summary> /// <param name="containerName">The name of the container to use in logging operations</param> /// <param name="diagnosticsConfiguration">A diagnostics instance to use when instantiating the container</param> /// <param name="probeConfiguration">The availability and liveness probe configuration</param> /// <param name="startPosition">The event position to start processing if no checkpoint is found for the partition</param> protected LegacyProcessorBase(string containerName, DiagnosticsConfiguration diagnosticsConfiguration, IProbeConfiguration probeConfiguration, EventPosition startPosition) : base(containerName, probeConfiguration, diagnosticsConfiguration) { _loggerFactory = diagnosticsConfiguration.LoggerFactory; _startPosition = startPosition; _iterationCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_iteration_counter", "Counts the number of iterations the demo container has performed.", false, new string[0]); _scaleEventCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_scale_event_counter", "Counts the number of times a scale event has occurred.", false, new string[0]); _healthProbeCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_health_counter", "Counts the number of times the liveness probe endpoint has been accessed since the start of the container.", false, new string[0]); _healthProbeFailureCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_health_failure_counter", "Counts the number of times the liveness probe endpoint has been accessed and failed since the start of the container.", false, new string[0]); _availabilityProbeCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_availability_counter", "Counts the number of times the readiness probe endpoint has been accessed since the start of the container.", false, new string[0]); _availabilityProbeFailureCounter = diagnosticsConfiguration.MetricFactory.CreateCounter("pro_availability_failure_counter", "Counts the number of times the readiness probe endpoint has been accessed and failed since the start of the container.", false, new string[0]); }
public async Task ReadPartitionWaitTime() { await using var scope = await EventHubScope.CreateAsync(1); #region Snippet:EventHubs_Sample05_ReadPartitionWaitTime var connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>"; var eventHubName = "<< NAME OF THE EVENT HUB >>"; var consumerGroup = EventHubConsumerClient.DefaultConsumerGroupName; /*@@*/ /*@@*/ connectionString = EventHubsTestEnvironment.Instance.EventHubsConnectionString; /*@@*/ eventHubName = scope.EventHubName; var consumer = new EventHubConsumerClient( consumerGroup, connectionString, eventHubName); try { using CancellationTokenSource cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(TimeSpan.FromSeconds(30)); string firstPartition = (await consumer.GetPartitionIdsAsync(cancellationSource.Token)).First(); EventPosition startingPosition = EventPosition.Earliest; int loopTicks = 0; int maximumTicks = 10; var options = new ReadEventOptions { MaximumWaitTime = TimeSpan.FromSeconds(1) }; await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync( firstPartition, startingPosition, options)) { if (partitionEvent.Data != null) { string readFromPartition = partitionEvent.Partition.PartitionId; byte[] eventBodyBytes = partitionEvent.Data.EventBody.ToArray(); Debug.WriteLine($"Read event of length { eventBodyBytes.Length } from { readFromPartition }"); } else { Debug.WriteLine("Wait time elapsed; no event was available."); } loopTicks++; if (loopTicks >= maximumTicks) { break; } } } catch (TaskCanceledException) { // This is expected if the cancellation token is // signaled. } finally { await consumer.CloseAsync(); } #endregion }
/// <summary> /// Creates an AMQP link for use with receiving operations. /// </summary> /// /// <param name="connection">The active and opened AMQP connection to use for this link.</param> /// <param name="endpoint">The fully qualified endpoint to open the link for.</param> /// <param name="eventPosition">The position of the event in the partition where the link should be filtered to.</param> /// <param name="consumerOptions">The set of active options for the consumer that will make use of the link.</param> /// <param name="timeout">The timeout to apply when creating the link.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>A link for use for operations related to receiving events.</returns> /// protected virtual async Task <ReceivingAmqpLink> CreateReceivingLinkAsync(AmqpConnection connection, Uri endpoint, EventPosition eventPosition, EventHubConsumerOptions consumerOptions, TimeSpan timeout, CancellationToken cancellationToken) { Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var session = default(AmqpSession); var stopWatch = Stopwatch.StartNew(); try { // Perform the initial authorization for the link. var authClaims = new[] { EventHubsClaim.Listen }; var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, TokenProvider, endpoint, endpoint.AbsoluteUri, endpoint.AbsoluteUri, authClaims, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the AMQP session associated with the link. var sessionSettings = new AmqpSessionSettings { Properties = new Fields() }; session = connection.CreateSession(sessionSettings); await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // Create and open the link. var filters = new FilterSet(); filters.Add(AmqpFilter.ConsumerFilterName, AmqpFilter.CreateConsumerFilter(AmqpFilter.BuildFilterExpression(eventPosition))); var linkSettings = new AmqpLinkSettings { Role = true, TotalLinkCredit = (uint)consumerOptions.PrefetchCount, AutoSendFlow = consumerOptions.PrefetchCount > 0, SettleType = SettleMode.SettleOnSend, Source = new Source { Address = endpoint.AbsolutePath, FilterSet = filters }, Target = new Target { Address = Guid.NewGuid().ToString() } }; linkSettings.AddProperty(AmqpProperty.EntityType, (int)AmqpProperty.Entity.ConsumerGroup); if (!string.IsNullOrEmpty(consumerOptions.Identifier)) { linkSettings.AddProperty(AmqpProperty.ConsumerIdentifier, consumerOptions.Identifier); } if (consumerOptions.OwnerLevel.HasValue) { linkSettings.AddProperty(AmqpProperty.OwnerLevel, consumerOptions.OwnerLevel.Value); } if (consumerOptions.TrackLastEnqueuedEventInformation) { linkSettings.DesiredCapabilities = new Multiple <AmqpSymbol>(new List <AmqpSymbol> { AmqpProperty.TrackLastEnqueuedEventInformation }); } var link = new ReceivingAmqpLink(linkSettings); linkSettings.LinkName = $"{ Id };{ connection.Identifier };{ session.Identifier };{ link.Identifier }"; link.AttachTo(session); stopWatch.Stop(); // Configure refresh for authorization of the link. var refreshTimer = default(Timer); var refreshHandler = CreateAuthorizationRefreshHandler ( connection, link, TokenProvider, endpoint, endpoint.AbsoluteUri, endpoint.AbsoluteUri, authClaims, AuthorizationRefreshTimeout, () => refreshTimer ); refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); // Track the link before returning it, so that it can be managed with the scope. BeginTrackingLinkAsActive(link, refreshTimer); return(link); } catch { // Aborting the session will perform any necessary cleanup of // the associated link as well. session?.Abort(); throw; } }
public async Task EventProcessorCanReceiveFromSpecifiedInitialEventPosition() { await using (EventHubScope scope = await EventHubScope.CreateAsync(2)) { var connectionString = TestEnvironment.BuildConnectionStringForEventHub(scope.EventHubName); await using (var client = new EventHubClient(connectionString)) { int receivedEventsCount = 0; // Send some events. var expectedEventsCount = 20; var dummyEvent = new EventData(Encoding.UTF8.GetBytes("I'm dummy.")); DateTimeOffset enqueuedTime; await using (EventHubProducer producer = client.CreateProducer()) { // Send a few dummy events. We are not expecting to receive these. for (int i = 0; i < 30; i++) { await producer.SendAsync(dummyEvent); } // Wait a reasonable amount of time so the events are able to reach the service. await Task.Delay(1000); // Send the events we expect to receive. enqueuedTime = DateTimeOffset.UtcNow; for (int i = 0; i < expectedEventsCount; i++) { await producer.SendAsync(dummyEvent); } } // Create the event processor manager to manage our event processors. var eventProcessorManager = new EventProcessorManager ( EventHubConsumer.DefaultConsumerGroupName, client, options: new EventProcessorOptions { InitialEventPosition = EventPosition.FromEnqueuedTime(enqueuedTime) }, onProcessEvents: (partitionContext, events, cancellationToken) => { // Make it a list so we can safely enumerate it. var eventsList = new List <EventData>(events ?? Enumerable.Empty <EventData>()); if (eventsList.Count > 0) { Interlocked.Add(ref receivedEventsCount, eventsList.Count); } } ); eventProcessorManager.AddEventProcessors(1); // Start the event processors. await eventProcessorManager.StartAllAsync(); // Make sure the event processors have enough time to stabilize and receive events. await eventProcessorManager.WaitStabilization(); // Stop the event processors. await eventProcessorManager.StopAllAsync(); // Validate results. Assert.That(receivedEventsCount, Is.EqualTo(expectedEventsCount)); } } }
public bool ProcessEvent( EventPosition position, CheckpointTag eventPosition, string streamId, string eventType, string category, Guid eventid, int sequenceNumber, string metadata, string data, out string newState, out EmittedEvent[] emittedEvents) { CheckDisposed(); if (eventType == null) throw new ArgumentNullException("eventType"); if (streamId == null) throw new ArgumentNullException("streamId"); _eventPosition = eventPosition; _emittedEvents = null; _query.Push( data.Trim(), // trimming data passed to a JS new string[] {streamId, eventType, category ?? "", sequenceNumber.ToString(CultureInfo.InvariantCulture), metadata ?? "", position.PreparePosition.ToString()}); newState = _query.GetState(); emittedEvents = _emittedEvents == null ? null : _emittedEvents.ToArray(); return true; }
public bool CanJoinAt(EventPosition firstAvailableTransactionFileEvent, CheckpointTag eventCheckpointTag) { return eventCheckpointTag.PreparePosition >= firstAvailableTransactionFileEvent.CommitPosition; }