From d467bb609355c035eb6010a0d81684569592be73 Mon Sep 17 00:00:00 2001 From: Madalyn Redding <66138537+m-redding@users.noreply.github.com> Date: Sat, 11 May 2024 21:40:33 -0400 Subject: [PATCH 1/4] [Event Hubs] GeoDR - OffsetString The focus of these changes is to introduce members for the string-based offset format and mark the numeric-based offset members as obsolete. This is part of the Event Hubs GeoDR feature design for the service, where replication-based offsets will be composite strings and no longer parsable to numeric values. Not included in these changes is the error handling for the new error for an invalid reader position after failover. The error details are still being finalized by the service and will be included in a future change set. --- eng/ApiListing.exclude-attributes.txt | 1 + ...ging.EventHubs.Processor.netstandard2.0.cs | 4 + .../samples/Sample08_MockingClientTypes.md | 4 +- ...Azure.Messaging.EventHubs.Processor.csproj | 6 +- ...BlobCheckpointStoreInternal.Diagnostics.cs | 12 +- .../Diagnostics/BlobEventStoreEventSource.cs | 18 +- .../EventProcessorClientEventSource.cs | 94 +++++++-- .../src/EventProcessorClient.cs | 51 ++++- .../src/Primitives/BlobCheckpointStore.cs | 25 ++- ...CheckpointStoreInternalDiagnosticsTests.cs | 33 ++-- .../DiagnosticsActivitySourceTests.cs | 46 +++-- .../tests/Diagnostics/DiagnosticsTests.cs | 21 +- .../Primitives/BlobCheckpointStoreTests.cs | 38 +--- .../EventProcessorClientLiveTests.cs | 7 +- .../Processor/EventProcessorClientTests.cs | 92 ++++----- .../Sample08_MockingClientTypesLiveTests.cs | 4 +- .../BlobCheckpointStoreInternal.cs | 83 ++++---- .../src/Core/AttributeMessageText.cs | 57 ++++++ .../src/Resources.Designer.cs | 33 ++++ .../src/Resources.resx | 65 ++++--- .../src/Testing/EventDataExtensions.cs | 2 +- .../src/Testing/MockEventData.cs | 10 +- .../BlobsCheckpointStoreInternalLiveTests.cs | 96 ++-------- .../BlobsCheckpointStoreInternalTests.cs | 180 +++++------------- ...lobsCheckpointStoreInternal.Diagnostics.cs | 12 +- .../tests/Infrastructure/IBlobEventLogger.cs | 6 - .../tests/Testing/EventDataExtensionsTests.cs | 34 ++-- .../Testing/InMemoryCheckpointStoreTests.cs | 16 +- ...zure.Messaging.EventHubs.netstandard2.0.cs | 62 +++++- .../samples/Sample03_EventHubMetadata.md | 2 +- .../samples/Sample05_ReadingEvents.md | 4 +- .../samples/Sample11_MockingClientTypes.md | 12 +- .../Amqp/AmqpAnnotatedMessageExtensions.cs | 24 +-- .../src/Amqp/AmqpConnectionScope.cs | 6 + .../src/Amqp/AmqpConsumer.cs | 4 +- .../src/Amqp/AmqpFilter.cs | 6 +- .../src/Amqp/AmqpManagement.cs | 6 + .../src/Amqp/AmqpMessageConverter.cs | 13 +- .../src/Amqp/AmqpProperty.cs | 6 + .../src/Azure.Messaging.EventHubs.csproj | 2 +- .../src/Consumer/EventPosition.cs | 50 ++++- .../Consumer/LastEnqueuedEventProperties.cs | 95 ++++++++- .../src/EventData.cs | 93 +++++++-- .../src/EventHubProperties.cs | 26 ++- .../src/EventHubsModelFactory.cs | 112 ++++++++++- .../src/PartitionProperties.cs | 61 +++++- .../src/Primitives/CheckpointStore.cs | 10 +- .../src/Primitives/EventProcessor.cs | 38 ++-- .../PluggableCheckpointStoreEventProcessor.cs | 9 + .../src/Processor/CheckpointPosition.cs | 92 ++++++++- .../AmqpAnnotatedMessageExtensionsTests.cs | 26 +-- .../tests/Amqp/AmqpConnectionScopeTests.cs | 6 +- .../tests/Amqp/AmqpConsumerTests.cs | 18 +- .../tests/Amqp/AmqpFilterTests.cs | 8 +- .../tests/Amqp/AmqpMessageConverterTests.cs | 28 +-- .../Connection/EventHubConnectionLiveTests.cs | 2 +- .../Connection/EventHubConnectionTests.cs | 6 +- .../EventHubConsumerClientLiveTests.cs | 6 +- .../Consumer/EventHubConsumerClientTests.cs | 34 ++-- .../tests/Consumer/EventPositionTests.cs | 14 +- .../LastEnqueuedEventPropertiesTests.cs | 26 +-- .../tests/Consumer/PartitionContextTests.cs | 4 +- .../tests/Core/EventDataTests.cs | 24 +-- .../tests/Core/EventHubsModelFactoryTests.cs | 14 +- .../DiagnosticsActivitySourceTests.cs | 6 +- .../Primitives/EventProcessorOptionsTests.cs | 2 +- .../EventProcessorTests.Infrastructure.cs | 14 +- .../EventProcessorTests.MainProcessingLoop.cs | 94 +++++---- ...EventProcessorTests.PartitionProcessing.cs | 46 ++--- .../EventProcessorTests.StartStop.cs | 34 +++- .../Primitives/PartitionReceiverLiveTests.cs | 14 +- .../Primitives/PartitionReceiverTests.cs | 18 +- ...gableCheckpointStoreEventProcessorTests.cs | 20 +- .../Processor/CheckpointPositionTests.cs | 106 +++++++++-- ...EventHubBufferedProducerClientLiveTests.cs | 2 +- .../EventHubBufferedProducerClientTests.cs | 2 +- .../EventHubProducerClientLiveTests.cs | 9 +- .../Producer/EventHubProducerClientTests.cs | 2 +- .../Producer/TransportProducerPoolTests.cs | 2 +- .../Sample03_EventHubMetadataLiveTests.cs | 2 +- .../Sample05_ReadingEventsLiveTests.cs | 4 +- .../Sample11_MockingClientTypesLiveTests.cs | 12 +- .../EventHubListener.PartitionProcessor.cs | 18 +- ....Azure.WebJobs.Extensions.EventHubs.csproj | 5 +- .../src/Processor/CheckpointInfo.cs | 4 +- .../src/Processor/EventProcessorHost.cs | 2 +- .../Processor/EventProcessorHostPartition.cs | 2 +- .../EventHubTriggerBindingStrategy.cs | 6 +- .../src/Triggers/EventHubTriggerInput.cs | 4 +- .../tests/EventHubEndToEndTests.cs | 4 +- .../tests/EventHubListenerTests.cs | 8 +- .../tests/EventHubTests.cs | 4 +- .../tests/EventHubsMetricsProviderTests.cs | 26 +-- .../tests/TestPartitionProperties.cs | 2 +- 94 files changed, 1587 insertions(+), 886 deletions(-) create mode 100644 sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Core/AttributeMessageText.cs diff --git a/eng/ApiListing.exclude-attributes.txt b/eng/ApiListing.exclude-attributes.txt index 4fbcb9b4f4bfc..671f403ae17b1 100644 --- a/eng/ApiListing.exclude-attributes.txt +++ b/eng/ApiListing.exclude-attributes.txt @@ -3,6 +3,7 @@ T:System.Runtime.CompilerServices.AsyncStateMachineAttribute T:System.Runtime.CompilerServices.CompilerGeneratedAttribute T:System.Runtime.CompilerServices.NullableContextAttribute T:System.Runtime.CompilerServices.NullableAttribute +T:System.Runtime.CompilerServices.IsReadOnlyAttribute T:Azure.Core.CodeGenSuppressAttribute T:Azure.Core.CodeGenModelAttribute T:Azure.Core.CodeGenMemberAttribute diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.netstandard2.0.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.netstandard2.0.cs index 681137633b6d8..efc7c767e2f87 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.netstandard2.0.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.netstandard2.0.cs @@ -38,6 +38,8 @@ public EventProcessorClient(Azure.Storage.Blobs.BlobContainerClient checkpointSt [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task ValidateProcessingPreconditions(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -71,6 +73,8 @@ public BlobCheckpointStore(Azure.Storage.Blobs.BlobContainerClient blobContainer public override System.Threading.Tasks.Task> ClaimOwnershipAsync(System.Collections.Generic.IEnumerable desiredOwnership, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task GetCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task> ListOwnershipAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] public override System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/samples/Sample08_MockingClientTypes.md b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/samples/Sample08_MockingClientTypes.md index 8207da4d3aedd..9f83dce7b8b16 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/samples/Sample08_MockingClientTypes.md +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/samples/Sample08_MockingClientTypes.md @@ -49,7 +49,7 @@ EventData eventData = EventHubsModelFactory.EventData( systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:1:3344.1", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); // This creates a new instance of ProcessEventArgs to pass into the handler directly. @@ -110,7 +110,7 @@ TimerCallback dispatchEvent = async _ => systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:1:1111", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); ProcessEventArgs eventArgs = new( diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj index 8d86942d64056..0dee15d11d0f6 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj @@ -2,7 +2,7 @@ Azure Event Hubs is a highly scalable publish-subscribe service that can ingest millions of events per second and stream them to multiple consumers. This library extends its Event Processor with durable storage for checkpoint information using Azure Blob storage. For more information about Event Hubs, see https://azure.microsoft.com/en-us/services/event-hubs/ 5.12.0-beta.2 - + 5.11.5 Azure;Event Hubs;EventHubs;.NET;Event Processor;EventProcessor;$(PackageCommonTags) $(RequiredTargetFrameworks) @@ -16,7 +16,9 @@ - + + + diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobCheckpointStoreInternal.Diagnostics.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobCheckpointStoreInternal.Diagnostics.cs index a9e370843ac66..20db8c05e4fee 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobCheckpointStoreInternal.Diagnostics.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobCheckpointStoreInternal.Diagnostics.cs @@ -97,7 +97,6 @@ partial void InvalidCheckpointFound(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with the checkpoint. - /// The replication segment associated with the checkpoint. /// The offset associated with the checkpoint. /// The exception that occurred. /// @@ -107,10 +106,9 @@ partial void UpdateCheckpointError(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset, Exception exception) => - Logger.UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, exception.Message, sequenceNumber, replicationSegment, offset); + Logger.UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, exception.Message, sequenceNumber, offset); /// /// Indicates that an attempt to update a checkpoint has completed. @@ -122,7 +120,6 @@ partial void UpdateCheckpointError(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// partial void UpdateCheckpointComplete(string partitionId, @@ -131,9 +128,8 @@ partial void UpdateCheckpointComplete(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) => - Logger.UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, replicationSegment, offset); + Logger.UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, offset); /// /// Indicates that an attempt to create/update a checkpoint has started. @@ -145,7 +141,6 @@ partial void UpdateCheckpointComplete(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// partial void UpdateCheckpointStart(string partitionId, @@ -154,9 +149,8 @@ partial void UpdateCheckpointStart(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) => - Logger.UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, replicationSegment, offset); + Logger.UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, offset); /// /// Indicates that an attempt to retrieve claim partition ownership has completed. diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobEventStoreEventSource.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobEventStoreEventSource.cs index b68614332fee6..73af92afcc386 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobEventStoreEventSource.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/BlobEventStoreEventSource.cs @@ -254,22 +254,20 @@ public virtual void OwnershipClaimed(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// - [Event(32, Level = EventLevel.Verbose, Message = "Starting to create/update a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ClientIdentifier: '{4}'; at SequenceNumber: '{5}' ReplicationSegment: '{6}' Offset: '{7}'.")] + [Event(32, Level = EventLevel.Verbose, Message = "Starting to create/update a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ClientIdentifier: '{4}'; at SequenceNumber: '{5}' Offset: '{6}'.")] public virtual void UpdateCheckpointStart(string partitionId, string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) { if (IsEnabled()) { - WriteEvent(32, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, replicationSegment ?? string.Empty, offset ?? string.Empty); + WriteEvent(32, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } @@ -283,22 +281,20 @@ public virtual void UpdateCheckpointStart(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// - [Event(33, Level = EventLevel.Verbose, Message = "Completed the attempt to create/update a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ClientIdentifier: '{4}'; at SequenceNumber: '{5}' ReplicationSegment: '{6}' Offset: '{7}'.")] + [Event(33, Level = EventLevel.Verbose, Message = "Completed the attempt to create/update a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ClientIdentifier: '{4}'; at SequenceNumber: '{5}' Offset: '{6}'.")] public virtual void UpdateCheckpointComplete(string partitionId, string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) { if (IsEnabled()) { - WriteEvent(33, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, replicationSegment ?? string.Empty, offset ?? string.Empty); + WriteEvent(33, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } @@ -312,11 +308,10 @@ public virtual void UpdateCheckpointComplete(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the processor that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// The message for the exception that occurred. /// - [Event(34, Level = EventLevel.Error, Message = "An exception occurred when creating/updating a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ClientIdentifier: '{5}'; at SequenceNumber: '{6}' ReplicationSegment '{7}' Offset '{8}'. ErrorMessage: '{4}'.")] + [Event(34, Level = EventLevel.Error, Message = "An exception occurred when creating/updating a checkpoint for partition: `{0}` of FullyQualifiedNamespace: '{1}'; EventHubName: '{2}'; ConsumerGroup: '{3}'; ErrorMessage: '{4}'; ClientIdentifier: '{5}'; at SequenceNumber: '{6}' Offset '{7}'.")] public virtual void UpdateCheckpointError(string partitionId, string fullyQualifiedNamespace, string eventHubName, @@ -324,12 +319,11 @@ public virtual void UpdateCheckpointError(string partitionId, string clientIdentifier, string errorMessage, string sequenceNumber, - string replicationSegment, string offset) { if (IsEnabled()) { - WriteEvent(34, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, errorMessage ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, replicationSegment ?? string.Empty, offset ?? string.Empty); + WriteEvent(34, partitionId ?? string.Empty, fullyQualifiedNamespace ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, errorMessage ?? string.Empty, clientIdentifier ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs index d22b369482793..88fe9180a7359 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs @@ -120,16 +120,20 @@ public virtual void EventBatchProcessingError(string partitionId, /// A unique name used to identify the event processor. /// The name of the Event Hub that the processor is associated with. /// The name of the consumer group that the processor is associated with. + /// The sequence number associated with the checkpoint being written. + /// The offset associated with the checkpoint being written. /// - [Event(23, Level = EventLevel.Verbose, Message = "Starting to perform a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3}.")] + [Event(23, Level = EventLevel.Verbose, Message = "Starting to perform a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3} Sequence Number: {4} Offset {5}.")] public virtual void UpdateCheckpointStart(string partitionId, string identifier, string eventHubName, - string consumerGroup) + string consumerGroup, + string sequenceNumber, + string offset) { if (IsEnabled()) { - WriteEvent(23, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty); + WriteEvent(23, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } @@ -141,16 +145,20 @@ public virtual void UpdateCheckpointStart(string partitionId, /// A unique name used to identify the event processor. /// The name of the Event Hub that the processor is associated with. /// The name of the consumer group that the processor is associated with. + /// The sequence number associated with the checkpoint being written. + /// The offset associated with the checkpoint being written. /// - [Event(24, Level = EventLevel.Verbose, Message = "Completed performing a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3}.")] + [Event(24, Level = EventLevel.Verbose, Message = "Completed performing a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3} Sequence Number: {4} Offset: {5}.")] public virtual void UpdateCheckpointComplete(string partitionId, string identifier, string eventHubName, - string consumerGroup) + string consumerGroup, + string sequenceNumber, + string offset) { if (IsEnabled()) { - WriteEvent(24, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty); + WriteEvent(24, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } @@ -163,17 +171,21 @@ public virtual void UpdateCheckpointComplete(string partitionId, /// The name of the Event Hub that the processor is associated with. /// The name of the consumer group that the processor is associated with. /// The message for the exception that occurred. + /// The sequence number associated with the checkpoint being written. + /// The offset associated with the checkpoint being written. /// - [Event(25, Level = EventLevel.Error, Message = "An exception occurred while attempting to perform a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3}. Error Message: '{4}'")] + [Event(25, Level = EventLevel.Error, Message = "An exception occurred while attempting to perform a checkpoint update for partition '{0}' by processor instance with identifier '{1}' for Event Hub: {2} and Consumer Group: {3}. Error Message: '{4}. Sequence Number: {5} Offset: {6}'")] public virtual void UpdateCheckpointError(string partitionId, string identifier, string eventHubName, string consumerGroup, - string errorMessage) + string errorMessage, + string sequenceNumber, + string offset) { if (IsEnabled()) { - WriteEvent(25, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, errorMessage ?? string.Empty); + WriteEvent(25, partitionId ?? string.Empty, identifier ?? string.Empty, eventHubName ?? string.Empty, consumerGroup ?? string.Empty, errorMessage ?? string.Empty, sequenceNumber ?? string.Empty, offset ?? string.Empty); } } @@ -363,11 +375,71 @@ private unsafe void WriteEvent(int eventId, eventPayload[4].Size = (arg5.Length + 1) * sizeof(char); eventPayload[4].DataPointer = (IntPtr)arg5Ptr; - eventPayload[5].Size = (arg5.Length + 1) * sizeof(char); - eventPayload[5].DataPointer = (IntPtr)arg5Ptr; + eventPayload[5].Size = (arg6.Length + 1) * sizeof(char); + eventPayload[5].DataPointer = (IntPtr)arg6Ptr; WriteEventCore(eventId, 6, eventPayload); } } + + /// + /// Writes an event with five string arguments into a stack allocated + /// struct to avoid the parameter array allocation on the WriteEvent methods. + /// + /// + /// The identifier of the event. + /// The first argument. + /// The second argument. + /// The third argument. + /// The fourth argument. + /// The fifth argument. + /// The sixth argument. + /// The seventh argument. + /// + [NonEvent] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void WriteEvent(int eventId, + string arg1, + string arg2, + string arg3, + string arg4, + string arg5, + string arg6, + string arg7) + { + fixed (char* arg1Ptr = arg1) + fixed (char* arg2Ptr = arg2) + fixed (char* arg3Ptr = arg3) + fixed (char* arg4Ptr = arg4) + fixed (char* arg5Ptr = arg5) + fixed (char* arg6Ptr = arg6) + fixed (char* arg7Ptr = arg7) + { + var eventPayload = stackalloc EventData[7]; + + eventPayload[0].Size = (arg1.Length + 1) * sizeof(char); + eventPayload[0].DataPointer = (IntPtr)arg1Ptr; + + eventPayload[1].Size = (arg2.Length + 1) * sizeof(char); + eventPayload[1].DataPointer = (IntPtr)arg2Ptr; + + eventPayload[2].Size = (arg3.Length + 1) * sizeof(char); + eventPayload[2].DataPointer = (IntPtr)arg3Ptr; + + eventPayload[3].Size = (arg4.Length + 1) * sizeof(char); + eventPayload[3].DataPointer = (IntPtr)arg4Ptr; + + eventPayload[4].Size = (arg5.Length + 1) * sizeof(char); + eventPayload[4].DataPointer = (IntPtr)arg5Ptr; + + eventPayload[5].Size = (arg6.Length + 1) * sizeof(char); + eventPayload[5].DataPointer = (IntPtr)arg6Ptr; + + eventPayload[6].Size = (arg7.Length + 1) * sizeof(char); + eventPayload[6].DataPointer = (IntPtr)arg7Ptr; + + WriteEventCore(eventId, 7, eventPayload); + } + } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/EventProcessorClient.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/EventProcessorClient.cs index 6366e98724bb1..c967c8e4a5bd5 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/EventProcessorClient.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/EventProcessorClient.cs @@ -902,15 +902,23 @@ protected override async Task ValidateProcessingPreconditions(CancellationToken } /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// /// /// The identifier of the partition the checkpoint is for. - /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used for positioning if there is no value provided for . + /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used from positioning if there is no value provided for . /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// + /// + /// This method is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] protected override Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, @@ -919,14 +927,16 @@ protected override Task UpdateCheckpointAsync(string partitionId, cancellationToken.ThrowIfCancellationRequested(); Argument.AssertNotNull(partitionId, nameof(partitionId)); - Logger.UpdateCheckpointStart(partitionId, Identifier, EventHubName, ConsumerGroup); + var offsetString = offset.ToString(CultureInfo.InvariantCulture); + Logger.UpdateCheckpointStart(partitionId, Identifier, EventHubName, ConsumerGroup, sequenceNumber?.ToString(CultureInfo.InvariantCulture), offsetString); using var scope = ClientDiagnostics.CreateScope(DiagnosticProperty.EventProcessorCheckpointActivityName, ActivityKind.Internal); scope.Start(); try { - return CheckpointStore.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, partitionId, offset, sequenceNumber, cancellationToken); + var checkpointPosition = new CheckpointPosition(offsetString, sequenceNumber ?? long.MinValue); + return CheckpointStore.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, partitionId, Identifier, checkpointPosition, cancellationToken); } catch (Exception ex) { @@ -934,13 +944,13 @@ protected override Task UpdateCheckpointAsync(string partitionId, // be thrown directly to the caller here. scope.Failed(ex); - Logger.UpdateCheckpointError(partitionId, Identifier, EventHubName, ConsumerGroup, ex.Message); + Logger.UpdateCheckpointError(partitionId, Identifier, EventHubName, ConsumerGroup, ex.Message, sequenceNumber?.ToString(CultureInfo.InvariantCulture), offsetString); throw; } finally { - Logger.UpdateCheckpointComplete(partitionId, Identifier, EventHubName, ConsumerGroup); + Logger.UpdateCheckpointComplete(partitionId, Identifier, EventHubName, ConsumerGroup, sequenceNumber?.ToString(CultureInfo.InvariantCulture), offset.ToString(CultureInfo.InvariantCulture)); } } @@ -960,9 +970,19 @@ protected override Task UpdateCheckpointAsync(string partitionId, cancellationToken.ThrowIfCancellationRequested(); Argument.AssertNotNull(partitionId, nameof(partitionId)); - Argument.AssertAtLeast(startingPosition.SequenceNumber, 0, nameof(startingPosition.SequenceNumber)); + if (string.IsNullOrEmpty(startingPosition.OffsetString)) + { + if (EventHubProperties?.IsGeoReplicationEnabled ?? false) + { + var message = string.Format(CultureInfo.InvariantCulture, Resources.ProcessorAttemptingToWriteCheckpointWithoutOffset); + var updateCheckpointException = new EventHubsException(true, EventHubName, message, EventHubsException.FailureReason.GeneralError); + _ = InvokeOnProcessingErrorAsync(updateCheckpointException, Resources.OperationEventProcessingLoop, CancellationToken.None); + } - Logger.UpdateCheckpointStart(partitionId, Identifier, EventHubName, ConsumerGroup); + Argument.AssertAtLeast(startingPosition.SequenceNumber, 0, nameof(startingPosition.SequenceNumber)); + } + + Logger.UpdateCheckpointStart(partitionId, Identifier, EventHubName, ConsumerGroup, startingPosition.SequenceNumber.ToString(CultureInfo.InvariantCulture), startingPosition.OffsetString); using var scope = ClientDiagnostics.CreateScope(DiagnosticProperty.EventProcessorCheckpointActivityName, ActivityKind.Internal); scope.Start(); @@ -977,16 +997,29 @@ protected override Task UpdateCheckpointAsync(string partitionId, // be thrown directly to the caller here. scope.Failed(ex); - Logger.UpdateCheckpointError(partitionId, Identifier, EventHubName, ConsumerGroup, ex.Message); + Logger.UpdateCheckpointError(partitionId, Identifier, EventHubName, ConsumerGroup, ex.Message, startingPosition.SequenceNumber.ToString(CultureInfo.InvariantCulture), startingPosition.OffsetString); throw; } finally { - Logger.UpdateCheckpointComplete(partitionId, Identifier, EventHubName, ConsumerGroup); + Logger.UpdateCheckpointComplete(partitionId, Identifier, EventHubName, ConsumerGroup, startingPosition.SequenceNumber.ToString(), startingPosition.OffsetString); } } + /// + /// Performs the tasks needed invoke the method in the background, + /// as it is intended to be a fire-and-forget operation. + /// + /// + /// The exception that occurred during operation of the event processor. + /// A short textual description of the operation during which the exception occurred; intended to be informational only. + /// A instance to signal the request to cancel the processing. + /// + private Task InvokeOnProcessingErrorAsync(Exception exception, + string operationDescription, + CancellationToken cancellationToken) => Task.Run(() => OnProcessingErrorAsync(exception, null, operationDescription, cancellationToken), CancellationToken.None); + /// /// Creates an to use for communicating with the Event Hubs service. /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Primitives/BlobCheckpointStore.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Primitives/BlobCheckpointStore.cs index 78ae93f7f05df..dcaf741db69e7 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Primitives/BlobCheckpointStore.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Primitives/BlobCheckpointStore.cs @@ -1,10 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; +using Azure.Messaging.EventHubs.Core; using Azure.Messaging.EventHubs.Processor; using Azure.Storage.Blobs; @@ -95,6 +98,8 @@ public override Task GetCheckpointAsync(string fullyQu CancellationToken cancellationToken) => _checkpointStoreImplementation.GetCheckpointAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, cancellationToken); /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// @@ -103,17 +108,23 @@ public override Task GetCheckpointAsync(string fullyQu /// The name of the specific Event Hub the ownership are associated with, relative to the Event Hubs namespace that contains it. /// The name of the consumer group the checkpoint is associated with. /// The identifier of the partition the checkpoint is for. - /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used for positioning if there is no value provided for . + /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used from positioning if there is no value provided for . /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// + /// + /// This method is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public override Task UpdateCheckpointAsync(string fullyQualifiedNamespace, - string eventHubName, - string consumerGroup, - string partitionId, - long offset, - long? sequenceNumber, - CancellationToken cancellationToken) => _checkpointStoreImplementation.UpdateCheckpointAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, offset, sequenceNumber, cancellationToken); + string eventHubName, + string consumerGroup, + string partitionId, + long offset, + long? sequenceNumber, + CancellationToken cancellationToken) => _checkpointStoreImplementation.UpdateCheckpointAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, offset, sequenceNumber, cancellationToken); /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/BlobCheckpointStoreInternalDiagnosticsTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/BlobCheckpointStoreInternalDiagnosticsTests.cs index 5957c12a03f14..39769026c8320 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/BlobCheckpointStoreInternalDiagnosticsTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/BlobCheckpointStoreInternalDiagnosticsTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// Ignore Spelling: Etag + using System; using System.Collections.Generic; using System.IO; @@ -268,9 +270,10 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists() target.Logger = mockLog.Object; var expectedSequenceNumber = 0; - await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedSequenceNumber), CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), "-1", string.Empty)); - mockLog.Verify(log => log.UpdateCheckpointComplete(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), "-1", string.Empty)); + var expectedOffset = "10:1:9853.1"; + await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedOffset, expectedSequenceNumber), CancellationToken.None); + mockLog.Verify(log => log.UpdateCheckpointStart(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), expectedOffset)); + mockLog.Verify(log => log.UpdateCheckpointComplete(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), expectedOffset)); } /// @@ -296,11 +299,12 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobDoesNotExist() var mockLog = new Mock(); target.Logger = mockLog.Object; - var expectedSequenceNumber = 0; + var expectedOffset = "1:0:8833"; + var expectedSequence = 23432; - await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedSequenceNumber), CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), "-1", string.Empty)); - mockLog.Verify(log => log.UpdateCheckpointComplete(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequenceNumber.ToString(), "-1", string.Empty)); + await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedOffset, expectedSequence), CancellationToken.None); + mockLog.Verify(log => log.UpdateCheckpointStart(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequence.ToString(), expectedOffset)); + mockLog.Verify(log => log.UpdateCheckpointComplete(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedSequence.ToString(), expectedOffset)); } /// @@ -324,8 +328,9 @@ public void UpdateCheckpointLogsErrorsWhenTheBlobExists() target.Logger = mockLog.Object; var expectedSequenceNumber = 456; - Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedSequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedException.Message, expectedSequenceNumber.ToString(), "-1", string.Empty)); + var expectedOffset = "404"; + Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedOffset, expectedSequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); + mockLog.Verify(log => log.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedException.Message, expectedSequenceNumber.ToString(), expectedOffset)); } /// @@ -348,8 +353,9 @@ public void UpdateCheckpointLogsErrorsWhenTheBlobDoesNotExist() target.Logger = mockLog.Object; var expectedSequenceNumber = 6; - Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedSequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedException.Message, expectedSequenceNumber.ToString(), "-1", string.Empty)); + var expectedOffset = "9"; + Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedOffset, expectedSequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); + mockLog.Verify(log => log.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, expectedException.Message, expectedSequenceNumber.ToString(), expectedOffset)); } /// @@ -367,8 +373,9 @@ public void UpdateCheckpointForMissingContainerLogsCheckpointUpdateError() target.Logger = mockLog.Object; var expectedSequenceNumber = 999; - Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedSequenceNumber), CancellationToken.None), Throws.InstanceOf()); - mockLog.Verify(m => m.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, ex.Message, expectedSequenceNumber.ToString(), "-1", string.Empty)); + var expectedOffset = "777"; + Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(expectedOffset, expectedSequenceNumber), CancellationToken.None), Throws.InstanceOf()); + mockLog.Verify(m => m.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, ex.Message, expectedSequenceNumber.ToString(), expectedOffset)); } /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsActivitySourceTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsActivitySourceTests.cs index f7e8023731650..e26c88dd1d637 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsActivitySourceTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsActivitySourceTests.cs @@ -73,13 +73,14 @@ public async Task CheckpointStoreActivitySourceDisabled() var mockLogger = new Mock(); mockLogger - .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback(() => completionSource.TrySetResult(true)); mockProcessor.Object.Logger = mockLogger.Object; using var listener = new TestActivitySourceListener(source => source.Name.StartsWith(DiagnosticProperty.DiagnosticNamespace)); - await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, 998, default); + await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, "998", default); + await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, "1:0:556", default); Assert.IsEmpty(listener.Activities); } @@ -106,7 +107,7 @@ public async Task UpdateCheckpointAsyncCreatesScope() .Returns(Mock.Of()); mockLogger - .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback(() => completionSource.TrySetResult(true)); mockProcessor.Object.Logger = mockLogger.Object; @@ -114,7 +115,7 @@ public async Task UpdateCheckpointAsyncCreatesScope() using var _ = SetAppConfigSwitch(); using var listener = new TestActivitySourceListener(source => source.Name.StartsWith(DiagnosticProperty.DiagnosticNamespace)); - await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, 998, default); + await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, "1:0:998", default); 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."); @@ -141,8 +142,8 @@ public async Task EventProcessorClientCreatesScopeForEachEventProcessing() var enqueuedTime = DateTimeOffset.UtcNow; var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123, enqueuedTime: enqueuedTime), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456, enqueuedTime: enqueuedTime) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123, enqueuedTime: enqueuedTime), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456, enqueuedTime: enqueuedTime) }; for (int i = 0; i < eventBatch.Length; i++) @@ -199,8 +200,8 @@ public async Task EventProcessorClientCreatesScopeError() using var listener = new TestActivitySourceListener(source => source.Name.StartsWith(DiagnosticProperty.DiagnosticNamespace)); var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var mockLogger = new Mock(); @@ -252,8 +253,8 @@ public async Task EventProcessorClientCreatesScopeForEachEventProcessingWithoutR var enqueuedTime = DateTimeOffset.UtcNow; var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123, enqueuedTime: enqueuedTime), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456, enqueuedTime: enqueuedTime) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123, enqueuedTime: enqueuedTime), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456, enqueuedTime: enqueuedTime) }; var mockLogger = new Mock(); @@ -295,19 +296,38 @@ public async Task EventProcessorClientCreatesScopeForEachEventProcessingWithoutR /// The client whose method to invoke. /// The identifier of the partition the checkpoint is for. /// The offset to associate with the checkpoint, indicating that a processor should begin reading form the next event in the stream. - /// An optional sequence number to associate with the checkpoint, intended as informational metadata. The will be used for positioning when events are read. /// A instance to signal a request to cancel the operation. /// /// The translated options. /// private static Task InvokeUpdateCheckpointAsync(EventProcessorClient target, string partitionId, - long sequenceNumber, + string offset, CancellationToken cancellationToken) => (Task) typeof(EventProcessorClient) .GetMethod("UpdateCheckpointAsync", BindingFlags.Instance | BindingFlags.NonPublic, new Type[] { typeof(string), typeof(CheckpointPosition), typeof(CancellationToken) }) - .Invoke(target, new object[] { partitionId, new CheckpointPosition(sequenceNumber), cancellationToken }); + .Invoke(target, new object[] { partitionId, new CheckpointPosition(offset), cancellationToken }); + + /// + /// Invokes the protected UpdateCheckpointAsync method on the processor client. + /// + /// + /// The client whose method to invoke. + /// The identifier of the partition the checkpoint is for. + /// The offset to associate with the checkpoint, indicating that a processor should begin reading form the next event in the stream. + /// An optional sequence number to associate with the checkpoint, intended as informational metadata. The will be used for positioning when events are read. + /// A instance to signal a request to cancel the operation. + /// + private static Task InvokeOldUpdateCheckpointAsync(EventProcessorClient target, + string partitionId, + string offset, + long sequenceNumber, + CancellationToken cancellationToken) => + (Task) + typeof(EventProcessorClient) + .GetMethod("UpdateCheckpointAsync", BindingFlags.Instance | BindingFlags.NonPublic, new Type[] { typeof(string), typeof(string), typeof(long), typeof(CancellationToken) }) + .Invoke(target, new object[] { partitionId, offset, sequenceNumber, cancellationToken }); /// /// Sets and returns the app config switch to enable Activity Source. The switch must be disposed at the end of the test. diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsTests.cs index 371ff6910b0f8..2a7b794544045 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Diagnostics/DiagnosticsTests.cs @@ -62,13 +62,13 @@ public async Task UpdateCheckpointAsyncCreatesScope() .Returns(Mock.Of()); mockLogger - .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(log => log.UpdateCheckpointComplete(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback(() => completionSource.TrySetResult(true)); mockProcessor.Object.Logger = mockLogger.Object; using var listener = new ClientDiagnosticListener(DiagnosticProperty.DiagnosticNamespace); - await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, 998, default); + await InvokeUpdateCheckpointAsync(mockProcessor.Object, mockContext.Object.PartitionId, "998", default); 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."); @@ -93,8 +93,8 @@ public async Task EventProcessorClientCreatesScopeForEachEventProcessing() var enqueuedTime = DateTimeOffset.UtcNow; var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123, enqueuedTime: enqueuedTime), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456, enqueuedTime: enqueuedTime) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123, enqueuedTime: enqueuedTime), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456, enqueuedTime: enqueuedTime) }; for (int i = 0; i < eventBatch.Length; i++) @@ -150,8 +150,8 @@ public async Task EventProcessorClientCreatesScopeError() using var listener = new ClientDiagnosticListener(DiagnosticProperty.DiagnosticNamespace); var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var mockLogger = new Mock(); @@ -199,8 +199,8 @@ public async Task EventProcessorClientCreatesScopeForEachEventProcessingWithoutR var enqueuedTime = DateTimeOffset.UtcNow; var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123, enqueuedTime: enqueuedTime), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456, enqueuedTime: enqueuedTime) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123, enqueuedTime: enqueuedTime), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456, enqueuedTime: enqueuedTime) }; var mockLogger = new Mock(); @@ -252,18 +252,17 @@ public void EventProcessorClientDisablesBaseBatchTracing() /// The client whose method to invoke. /// The identifier of the partition the checkpoint is for. /// The offset to associate with the checkpoint, indicating that a processor should begin reading form the next event in the stream. - /// An optional sequence number to associate with the checkpoint, intended as informational metadata. The will be used for positioning when events are read. /// A instance to signal a request to cancel the operation. /// /// The translated options. /// private static Task InvokeUpdateCheckpointAsync(EventProcessorClient target, string partitionId, - long sequenceNumber, + string offset, CancellationToken cancellationToken) => (Task) typeof(EventProcessorClient) .GetMethod("UpdateCheckpointAsync", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(CheckpointPosition), typeof(CancellationToken) }, null) - .Invoke(target, new object[] { partitionId, new CheckpointPosition(sequenceNumber), cancellationToken }); + .Invoke(target, new object[] { partitionId, new CheckpointPosition(offset), cancellationToken }); } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Primitives/BlobCheckpointStoreTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Primitives/BlobCheckpointStoreTests.cs index e2b474a339942..a59c921dd1cef 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Primitives/BlobCheckpointStoreTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Primitives/BlobCheckpointStoreTests.cs @@ -119,11 +119,11 @@ public async Task UpdateCheckpointAsyncDelegatesTheCall() var expectedConsumerGroup = "fakeGroup"; var expectedPartition = "fakePart"; var expectedProcessorId = "Id"; - var expectedSequence = 999; + var expectedOffset = "999"; var mockCheckpointStore = new Mock(); var blobCheckpointStore = new BlobCheckpointStore(mockCheckpointStore.Object); - await blobCheckpointStore.UpdateCheckpointAsync(expectedNamespace, expectedHub, expectedConsumerGroup, expectedPartition, expectedProcessorId, new CheckpointPosition(expectedSequence), cancellationSource.Token); + await blobCheckpointStore.UpdateCheckpointAsync(expectedNamespace, expectedHub, expectedConsumerGroup, expectedPartition, expectedProcessorId, new CheckpointPosition(expectedOffset), cancellationSource.Token); mockCheckpointStore.Verify(store => store.UpdateCheckpointAsync( expectedNamespace, @@ -132,39 +132,7 @@ public async Task UpdateCheckpointAsyncDelegatesTheCall() expectedPartition, expectedProcessorId, It.Is(csp => - csp.SequenceNumber == expectedSequence), - cancellationSource.Token), - Times.Once); - } - - /// - /// Verifies functionality of the - /// method. - /// - /// - [Test] - public async Task UpdateCheckpointAsyncOldOverloadDelegatesTheCall() - { - using var cancellationSource = new CancellationTokenSource(); - - var expectedNamespace = "fakeNS"; - var expectedHub = "fakeHub"; - var expectedConsumerGroup = "fakeGroup"; - var expectedPartition = "fakePart"; - var expectedOffset = 123; - var expectedSequence = 999; - var mockCheckpointStore = new Mock(); - var blobCheckpointStore = new BlobCheckpointStore(mockCheckpointStore.Object); - - await blobCheckpointStore.UpdateCheckpointAsync(expectedNamespace, expectedHub, expectedConsumerGroup, expectedPartition, expectedOffset, expectedSequence, cancellationSource.Token); - - mockCheckpointStore.Verify(store => store.UpdateCheckpointAsync( - expectedNamespace, - expectedHub, - expectedConsumerGroup, - expectedPartition, - expectedOffset, - expectedSequence, + csp.OffsetString == expectedOffset), cancellationSource.Token), Times.Once); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientLiveTests.cs index 1a505588d2fb9..f6b5cb710543b 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientLiveTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// Ignore Spelling: Checkpointing + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -391,7 +393,7 @@ public async Task ProcessorClientCanStartFromAnInitialPosition() // Read the initial set back, marking the offset and sequence number of the last event in the initial set. - var startingOffset = 0L; + string startingOffset = null; await using (var consumer = new EventHubConsumerClient( scope.ConsumerGroups.First(), @@ -403,8 +405,7 @@ public async Task ProcessorClientCanStartFromAnInitialPosition() { if (partitionEvent.Data.IsEquivalentTo(lastSourceEvent)) { - startingOffset = partitionEvent.Data.Offset; - + startingOffset = partitionEvent.Data.OffsetString; break; } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientTests.cs index 3d31e9674b210..33ee543727b35 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Processor/EventProcessorClientTests.cs @@ -793,7 +793,7 @@ public async Task ProcessorRaisesInitializeEventHandlerWhenPartitionIsInitialize var capturedEventArgs = default(PartitionInitializingEventArgs); var partitionId = "0"; - var startingPosition = EventPosition.FromOffset(433); + var startingPosition = EventPosition.FromOffset("433"); var options = new EventProcessorOptions { DefaultStartingPosition = startingPosition }; var processorClient = new TestEventProcessorClient(Mock.Of(), "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), options); @@ -925,8 +925,8 @@ public async Task ProcessorRaisesProcessEventHandlerWhenEventsAreRead() var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var capturedEventArgs = new List(); @@ -1043,8 +1043,8 @@ public void EventProcessingToleratesAndSurfacesAnException() var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var invokeCount = 0; @@ -1089,7 +1089,7 @@ public async Task EventProcessingToleratesAndSurfacesMultipleExceptions() var eventBatch = Enumerable .Range(0, eventCount) - .Select(index => new MockEventData(Array.Empty(), offset: 1000 + index, sequenceNumber: 2000 + index)) + .Select(index => new MockEventData(Array.Empty(), offset: (1000 + index).ToString(), sequenceNumber: 2000 + index)) .ToList(); processorClient.ProcessEventAsync += eventArgs => @@ -1133,8 +1133,8 @@ public async Task EventProcessingLogsExecution() var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var partitionId = "3"; @@ -1179,8 +1179,8 @@ public async Task EventProcessingLogsExceptions() var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456) }; var partitionId = "3"; @@ -1218,10 +1218,10 @@ public async Task EventProcessingRespectsCancellation() var eventBatch = new[] { - new MockEventData(new byte[] { 0x11 }, offset: 123, sequenceNumber: 123), - new MockEventData(new byte[] { 0x22 }, offset: 456, sequenceNumber: 456), - new MockEventData(new byte[] { 0x33 }, offset: 789, sequenceNumber: 789), - new MockEventData(new byte[] { 0x44 }, offset: 000, sequenceNumber: 000) + new MockEventData(new byte[] { 0x11 }, offset: "123", sequenceNumber: 123), + new MockEventData(new byte[] { 0x22 }, offset: "456", sequenceNumber: 456), + new MockEventData(new byte[] { 0x33 }, offset: "789", sequenceNumber: 789), + new MockEventData(new byte[] { 0x44 }, offset: "000", sequenceNumber: 000) }; var processedEventsCount = 0; @@ -1291,7 +1291,7 @@ public async Task GetCheckpointIncludesInitializeEventHandlerStartingPositionWhe cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partitionId = "0"; - var startingPosition = EventPosition.FromOffset(433); + var startingPosition = EventPosition.FromOffset("433"); var options = new EventProcessorOptions { DefaultStartingPosition = EventPosition.Latest }; var mockCheckpointStore = new Mock(); var processorClient = new TestEventProcessorClient(mockCheckpointStore.Object, "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), options); @@ -1330,7 +1330,7 @@ public async Task GetCheckpointPrefersNaturalCheckpointOverInitializeEventHandle cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partitionId = "0"; - var startingPosition = EventPosition.FromOffset(433); + var startingPosition = EventPosition.FromOffset("433"); var checkpointStartingPosition = EventPosition.FromSequenceNumber(999); var options = new EventProcessorOptions { DefaultStartingPosition = EventPosition.Latest }; var mockCheckpointStore = new Mock(); @@ -1464,8 +1464,8 @@ public async Task UpdateCheckpointDelegatesToTheStorageManager() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partitionId = "3"; - var sequenceNumber = 789; - var checkpointStartingPosition = new CheckpointPosition(sequenceNumber); + var offset = "789"; + var checkpointStartingPosition = new CheckpointPosition(offset); var mockStorage = new Mock(); var processorClient = new TestEventProcessorClient(mockStorage.Object, "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), default); @@ -1479,41 +1479,7 @@ public async Task UpdateCheckpointDelegatesToTheStorageManager() partitionId, processorClient.Identifier, It.Is(csp => - csp.SequenceNumber == sequenceNumber), - It.IsAny()), - Times.Once); - - cancellationSource.Cancel(); - } - - /// - /// Verifies functionality of the - /// method. - /// - /// - [Test] - public async Task PreviousUpdateCheckpointCallsTheOldUpdateCheckpoint() - { - using var cancellationSource = new CancellationTokenSource(); - cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - - var partitionId = "3"; - var offset = 456; - var sequenceNumber = 789; - var checkpointPosition = new CheckpointPosition(sequenceNumber); - var mockStorage = new Mock(); - var processorClient = new TestEventProcessorClient(mockStorage.Object, "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), default); - - await processorClient.InvokeOldUpdateCheckpointAsync(partitionId, offset, sequenceNumber, cancellationSource.Token); - - mockStorage - .Verify(storage => storage.UpdateCheckpointAsync( - processorClient.FullyQualifiedNamespace, - processorClient.EventHubName, - processorClient.ConsumerGroup, - partitionId, - offset, - sequenceNumber, + csp.OffsetString == offset), It.IsAny()), Times.Once); @@ -1532,7 +1498,7 @@ public void UpdateCheckpointRespectsTheCancellationToken() cancellationSource.Cancel(); var processorClient = new TestEventProcessorClient(Mock.Of(), "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), default); - Assert.That(async () => await processorClient.InvokeUpdateCheckpointAsync("0", new CheckpointPosition(123), cancellationSource.Token), Throws.InstanceOf()); + Assert.That(async () => await processorClient.InvokeUpdateCheckpointAsync("0", new CheckpointPosition("123"), cancellationSource.Token), Throws.InstanceOf()); } /// @@ -1548,7 +1514,8 @@ public async Task UpdateCheckpointLogsExecution() var partitionId = "3"; var sequenceNumber = 789; - var checkpointStartingPosition = new CheckpointPosition(sequenceNumber); + var offset = "135"; + var checkpointStartingPosition = new CheckpointPosition(offset, sequenceNumber); var mockLogger = new Mock(); var processorClient = new TestEventProcessorClient(Mock.Of(), "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), default); @@ -1560,7 +1527,9 @@ public async Task UpdateCheckpointLogsExecution() partitionId, processorClient.Identifier, processorClient.EventHubName, - processorClient.ConsumerGroup), + processorClient.ConsumerGroup, + sequenceNumber.ToString(), + offset), Times.Once); mockLogger @@ -1568,7 +1537,9 @@ public async Task UpdateCheckpointLogsExecution() partitionId, processorClient.Identifier, processorClient.EventHubName, - processorClient.ConsumerGroup), + processorClient.ConsumerGroup, + sequenceNumber.ToString(), + offset), Times.Once); cancellationSource.Cancel(); @@ -1587,7 +1558,7 @@ public void UpdateCheckpointLogsExceptions() var expectedException = new NotImplementedException("This didn't work."); var partitionId = "3"; - var checkpointStartingPosition = new CheckpointPosition(789); + var checkpointStartingPosition = new CheckpointPosition("44", 789); var mockLogger = new Mock(); var mockStorage = new Mock(); var processorClient = new TestEventProcessorClient(mockStorage.Object, "consumerGroup", "namespace", "eventHub", Mock.Of(), Mock.Of(), default); @@ -1612,7 +1583,9 @@ public void UpdateCheckpointLogsExceptions() processorClient.Identifier, processorClient.EventHubName, processorClient.ConsumerGroup, - expectedException.Message), + expectedException.Message, + "789", + "44"), Times.Once); cancellationSource.Cancel(); @@ -1726,7 +1699,6 @@ internal TestEventProcessorClient(BlobContainerClient containerClient, public Task InvokeGetCheckpointAsync(string partitionId, CancellationToken cancellationToken) => base.GetCheckpointAsync(partitionId, cancellationToken); public Task> InvokeListOwnershipAsync(CancellationToken cancellationToken) => base.ListOwnershipAsync(cancellationToken); public Task> InvokeClaimOwnershipAsync(IEnumerable desiredOwnership, CancellationToken cancellationToken) => base.ClaimOwnershipAsync(desiredOwnership, cancellationToken); - public Task InvokeOldUpdateCheckpointAsync(string partitionId, long offset, long sequenceNumber, CancellationToken cancellationToken) => base.UpdateCheckpointAsync(partitionId, offset, sequenceNumber, cancellationToken); public Task InvokeUpdateCheckpointAsync(string partitionId, CheckpointPosition checkpointStartingPosition, CancellationToken cancellationToken) => base.UpdateCheckpointAsync(partitionId, checkpointStartingPosition, cancellationToken); protected override EventHubConnection CreateConnection() => InjectedConnection; protected override Task ValidateProcessingPreconditions(CancellationToken cancellationToken = default) => Task.CompletedTask; diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Snippets/Sample08_MockingClientTypesLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Snippets/Sample08_MockingClientTypesLiveTests.cs index fb002cd6c66c6..ce6f9020d25fa 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Snippets/Sample08_MockingClientTypesLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/tests/Snippets/Sample08_MockingClientTypesLiveTests.cs @@ -64,7 +64,7 @@ Task processErrorHandler(ProcessErrorEventArgs args) systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:1:3344.1", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); // This creates a new instance of ProcessEventArgs to pass into the handler directly. @@ -130,7 +130,7 @@ Task processEventHandler(ProcessEventArgs args) systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:1:1111", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); ProcessEventArgs eventArgs = new( diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/BlobCheckpointStore/BlobCheckpointStoreInternal.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/BlobCheckpointStore/BlobCheckpointStoreInternal.cs index 6915a4f70983e..daca314fa2d83 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/BlobCheckpointStore/BlobCheckpointStoreInternal.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/BlobCheckpointStore/BlobCheckpointStoreInternal.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.IO; using System.Text.Json; @@ -354,6 +355,8 @@ public override async Task GetCheckpointAsync(string f } /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// @@ -362,10 +365,16 @@ public override async Task GetCheckpointAsync(string f /// The name of the specific Event Hub the ownership are associated with, relative to the Event Hubs namespace that contains it. /// The name of the consumer group the checkpoint is associated with. /// The identifier of the partition the checkpoint is for. - /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used for positioning if there is no value provided for . + /// The offset to associate with the checkpoint, intended as informational metadata. This will only be used from positioning if there is no value provided for . /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// + /// + /// This method is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public override async Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, @@ -373,7 +382,7 @@ public override async Task UpdateCheckpointAsync(string fullyQualifiedNamespace, long offset, long? sequenceNumber, CancellationToken cancellationToken) - => await UpdateCheckpointInternalAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, string.Empty, offset, sequenceNumber, cancellationToken).ConfigureAwait(false); + => await UpdateCheckpointInternalAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, string.Empty, offset.ToString(CultureInfo.InvariantCulture), sequenceNumber, cancellationToken).ConfigureAwait(false); /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream @@ -395,7 +404,7 @@ public override async Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string clientIdentifier, CheckpointPosition startingPosition, CancellationToken cancellationToken) - => await UpdateCheckpointInternalAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, clientIdentifier, null, startingPosition.SequenceNumber, cancellationToken).ConfigureAwait(false); + => await UpdateCheckpointInternalAsync(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, clientIdentifier, startingPosition.OffsetString, startingPosition.SequenceNumber, cancellationToken).ConfigureAwait(false); /// /// Gets the name of the Storage Blob representing the checkpoint for a given partition. @@ -428,14 +437,22 @@ internal string GetCheckpointBlobName(string fullyQualifiedNamespace, /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// - private async Task UpdateCheckpointInternalAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, long? offset, long? sequenceNumber, CancellationToken cancellationToken) + private async Task UpdateCheckpointInternalAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, string offset, long? sequenceNumber, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber.ToString(), "-1", offset.ToString()); + UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber?.ToString(), offset); var blobName = GetCheckpointBlobName(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId); var blobClient = ContainerClient.GetBlobClient(blobName); + if (sequenceNumber == long.MinValue) + { + // We don't want to set the sequence number in the checkpoint to long.MinValue. This can break in-process upgrade since old SDKs don't have + // the long.MinValue check. Since CheckpointPosition.SequenceNumber is not nullable, the default value is long.MinValue. If we get + // the default, just set it to null. + sequenceNumber = null; + } + // Because the checkpoint format changed and offset is no longer populated by the EventProcessor, we need to ensure that a value is present for // the Functions scale controller which uses a null check on the offset to determine if a checkpoint is in the legacy format or current. Because // GetCheckpointAsync will only populate the offset if a long.TryParse is successful, adding a nonsense string value to satisfy the null check @@ -443,7 +460,7 @@ private async Task UpdateCheckpointInternalAsync(string fullyQualifiedNamespace, var metadata = new Dictionary() { - { BlobMetadataKey.Offset, offset.HasValue ? offset.Value.ToString(CultureInfo.InvariantCulture) : "no offset" }, + { BlobMetadataKey.Offset, offset ?? ""}, { BlobMetadataKey.SequenceNumber, sequenceNumber.HasValue ? sequenceNumber.Value.ToString(CultureInfo.InvariantCulture) : "" }, { BlobMetadataKey.ClientIdentifier, clientIdentifier } }; @@ -466,17 +483,17 @@ private async Task UpdateCheckpointInternalAsync(string fullyQualifiedNamespace, } catch (RequestFailedException ex) when (ex.ErrorCode == BlobErrorCode.ContainerNotFound) { - UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber.ToString(), "-1", offset.ToString(), ex); + UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber?.ToString(), offset, ex); throw new RequestFailedException(BlobsResourceDoesNotExist, ex); } catch (Exception ex) { - UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber.ToString(), "-1", offset.ToString(), ex); + UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber?.ToString(), offset, ex); throw; } finally { - UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber.ToString(), "-1", offset.ToString()); + UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber?.ToString(), offset); } } @@ -501,24 +518,21 @@ private EventProcessorCheckpoint CreateCheckpoint(string fullyQualifiedNamespace DateTimeOffset modifiedDate) { var startingPosition = default(EventPosition?); - var offset = default(long?); + var offset = default(string); var sequenceNumber = default(long?); var clientIdentifier = default(string); + if (metadata.TryGetValue(BlobMetadataKey.Offset, out var offsetStr) && !string.IsNullOrEmpty(offsetStr)) + { + offset = offsetStr; + startingPosition = EventPosition.FromOffset(offsetStr, false); + } if (metadata.TryGetValue(BlobMetadataKey.SequenceNumber, out var sequenceStr) && long.TryParse(sequenceStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out var sequenceResult)) { sequenceNumber = sequenceResult; if (sequenceNumber != long.MinValue) // If the sequence number is not equal to the default (long.MinValue), then a value was passed in. { - startingPosition = EventPosition.FromSequenceNumber(sequenceResult, false); - } - } - if (metadata.TryGetValue(BlobMetadataKey.Offset, out var offsetStr) && long.TryParse(offsetStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out var offsetResult)) - { - offset = offsetResult; - if (offset != long.MinValue) // If the offset is not equal to the default (long.MinValue), then a value was passed in. - { - startingPosition ??= EventPosition.FromOffset(offsetResult, false); + startingPosition ??= EventPosition.FromSequenceNumber(sequenceResult, false); } } if (metadata.TryGetValue(BlobMetadataKey.ClientIdentifier, out var idStr)) @@ -577,16 +591,16 @@ private async Task CreateLegacyCheckpoint(string fully if (TryReadLegacyCheckpoint( memoryStream.GetBuffer().AsSpan(0, (int)memoryStream.Length), - out long? offset, + out string offset, out long? sequenceNumber)) { - if (sequenceNumber.HasValue && sequenceNumber.Value != long.MinValue) + if (!string.IsNullOrEmpty(offset)) { - startingPosition = EventPosition.FromSequenceNumber(sequenceNumber.Value, false); + startingPosition ??= EventPosition.FromOffset(offset, false); } - else if (offset.HasValue) + else if (sequenceNumber.HasValue && sequenceNumber.Value != long.MinValue) { - startingPosition ??= EventPosition.FromOffset(offset.Value, false); + startingPosition = EventPosition.FromSequenceNumber(sequenceNumber.Value, false); } else { @@ -616,7 +630,7 @@ private async Task CreateLegacyCheckpoint(string fully } /// - /// Attempts to read a legacy checkpoint JSON format and extract an offset and a sequence number + /// Attempts to read a legacy checkpoint JSON format and extract an offset and a sequence number. /// /// /// The binary representation of the checkpoint JSON. @@ -636,7 +650,7 @@ private async Task CreateLegacyCheckpoint(string fully /// /// private static bool TryReadLegacyCheckpoint(Span data, - out long? offset, + out string offset, out long? sequenceNumber) { offset = null; @@ -666,14 +680,7 @@ private static bool TryReadLegacyCheckpoint(Span data, var offsetString = jsonReader.GetString(); if (!string.IsNullOrEmpty(offsetString)) { - if (long.TryParse(offsetString, out long offsetValue)) - { - offset = offsetValue; - } - else - { - return false; - } + offset = offsetString; } break; @@ -814,7 +821,6 @@ partial void InvalidCheckpointFound(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored the checkpoint. /// The sequence number associated with the checkpoint. - /// The replication segment associated with the checkpoint. /// The offset associated with the checkpoint. /// The message for the exception that occurred. /// @@ -824,7 +830,6 @@ partial void UpdateCheckpointError(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset, Exception exception); @@ -838,7 +843,6 @@ partial void UpdateCheckpointError(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored the checkpoint. /// The sequence number associated with the checkpoint. - /// The replication segment associated with the checkpoint. /// The offset associated with the checkpoint. /// partial void UpdateCheckpointComplete(string partitionId, @@ -847,7 +851,6 @@ partial void UpdateCheckpointComplete(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset); /// @@ -860,7 +863,6 @@ partial void UpdateCheckpointComplete(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with the checkpoint. - /// The replication segment associated with the checkpoint. /// The offset associated with the checkpoint. /// partial void UpdateCheckpointStart(string partitionId, @@ -869,7 +871,6 @@ partial void UpdateCheckpointStart(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset); /// @@ -975,7 +976,7 @@ partial void BlobsCheckpointStoreCreated(string typeName, /// internal class BlobStorageCheckpoint : EventProcessorCheckpoint { - public long? Offset { get; set; } + public string Offset { get; set; } public long? SequenceNumber { get; set; } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Core/AttributeMessageText.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Core/AttributeMessageText.cs new file mode 100644 index 0000000000000..df3b4a70e591e --- /dev/null +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Core/AttributeMessageText.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Messaging.EventHubs.Core +{ + /// + /// The set of common message text used in attributes, + /// which are unable to be represented in the shared + /// resources due to the constraint of needing to be + /// defined as a constant. + /// + /// + internal static class AttributeMessageText + { + /// + /// The message text detailing numeric offset deprecation. + /// + /// + public const string LongOffsetOffsetPropertyObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead."; + + /// + /// The message text detailing numeric offset deprecation. + /// + /// + public const string LongOffsetOffsetParameterObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead."; + + /// + /// The message text detailing numeric offset deprecation. + /// + /// + public const string LongLastEnqueuedOffsetOffsetObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'LastEnqueuedOffsetString' instead."; + + /// + /// The message text detailing numeric offset deprecation for the checkpoint-related operations. + /// + /// + public const string LongOffsetCheckpointObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset instead."; + + /// + /// The message text detailing numeric offset deprecation for the checkpoint-related operations. + /// + /// + public const string LongOffsetUpdateCheckpointObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead."; + + /// + /// The message text detailing numeric offset deprecation when positioning readers. + /// + /// + public const string LongOffsetEventPositionObsolete = "The Event Hubs service does not guarantee a numeric offset for all resource configurations. Reading events using a numeric offset may not work in all cases going forward. Please use a string-based offset instead."; + + /// + /// The message text detailing sequence-only checkpoints for processors. + /// + /// + public const string SequenceNumberOnlyCheckpointObsolete = "The Event Hubs service does not guarantee that a sequence number-only checkpoint can access the event stream for all resource configurations. Reading events may not work in all cases going forward. Please provide a string-based offset."; + } +} diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.Designer.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.Designer.cs index 889f5675c93d6..07cbef312b04c 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.Designer.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.Designer.cs @@ -847,6 +847,17 @@ internal static string ProcessorLoadBalancingCycleSlowMask } } + /// + /// Looks up a localized string similar to WARNING: A processor for a geo-replication-enabled Event Hub is trying to write a checkpoint without an offset. Readers of geo-replicated Event Hubs cannot be positioned without an offset.. + /// + internal static string ProcessorAttemptingToWriteCheckpointWithoutOffset + { + get + { + return ResourceManager.GetString("ProcessorLoadBalancingCycleSlowMask", resourceCulture); + } + } + /// /// Looks up a localized string similar to WARNING: The 'PartitionOwnershipExpirationInterval' and 'LoadBalancingUpdateInterval' are configured for intervals that may cause stability issues with partition ownership. It is recommended that the 'PartitionOwnershipExpirationInterval' be at least 3 times greater than the 'LoadBalancingUpdateInterval' and very strongly advised that it should be no less than twice as long. It is advised to adjust the intervals in the processor options. Load Balancing Interval '{1:0:00}' seconds. Partition Ownership Interval '{1:0:00}' seconds.. /// @@ -923,5 +934,27 @@ internal static string BufferedProducerStartupTimeout return ResourceManager.GetString("BufferedProducerStartupTimeout", resourceCulture); } } + + /// + /// Looks up a localized string similar to The Event Hubs services does not return a numeric offset for GeoDR-enabled namespaces. Please use 'OffsetString' instead.. + /// + internal static string LongOffsetOffsetUnsupported + { + get + { + return ResourceManager.GetString("LongOffsetOffsetUnsupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Event Hubs services does not return a numeric offset for GeoDR-enabled namespaces. Please use 'LastEnqueuedOffsetString' instead.. + /// + internal static string LongLastEnqueuedOffsetOffsetUnsupported + { + get + { + return ResourceManager.GetString("LongLastEnqueuedOffsetOffsetUnsupported", resourceCulture); + } + } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.resx b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.resx index 11f9364633207..078493abecd97 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.resx +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Resources.resx @@ -1,17 +1,17 @@  - @@ -351,4 +351,13 @@ FATAL: The processor has experienced a fatal error in its load balancing and health check task. Recovery is NOT possible; the processor is shutting down. Error message: {0} - \ No newline at end of file + + WARNING: A processor for a geo-replication-enabled Event Hub is trying to write a checkpoint without an offset. Readers of geo-replicated Event Hubs cannot be positioned without an offset. + + + The Event Hubs services does not return a numeric offset for GeoDR-enabled namespaces. Please use 'OffsetString' instead. + + + The Event Hubs services does not return a numeric offset for GeoDR-enabled namespaces. Please use 'LastEnqueuedOffsetString' instead. + + diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/EventDataExtensions.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/EventDataExtensions.cs index a799711350cb8..8fb2915c455f7 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/EventDataExtensions.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/EventDataExtensions.cs @@ -65,7 +65,7 @@ public static bool IsEquivalentTo(this EventData instance, // Verify that the stand-alone system properties are equivalent. if ((considerSystemProperties) - && ((instance.Offset != other.Offset) + && ((instance.OffsetString != other.OffsetString) || (instance.EnqueuedTime != other.EnqueuedTime) || (instance.PartitionKey != other.PartitionKey) || (instance.SequenceNumber != other.SequenceNumber))) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/MockEventData.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/MockEventData.cs index b1b2d59c1ac80..4fe5c82cfeb73 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/MockEventData.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/src/Testing/MockEventData.cs @@ -29,9 +29,15 @@ public MockEventData(ReadOnlyMemory eventBody, IDictionary properties = null, IReadOnlyDictionary systemProperties = null, long sequenceNumber = long.MinValue, - long offset = long.MinValue, + string offset = null, DateTimeOffset enqueuedTime = default, - string partitionKey = null) : base(eventBody, properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey) + string partitionKey = null) : base(BinaryData.FromBytes(eventBody), + properties, + systemProperties, + sequenceNumber, + offset, + enqueuedTime, + partitionKey) { } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalLiveTests.cs index 7d9a94dce1c36..68cd6c5e6ea78 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalLiveTests.cs @@ -131,8 +131,7 @@ public async Task BlobStorageManagerCanUpdateCheckpoint() }; await checkpointStore.ClaimOwnershipAsync(ownershipList, default); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10), default), Throws.Nothing); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId", 0, 10, default), Throws.Nothing); + Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("0", 10), default), Throws.Nothing); } } @@ -707,7 +706,7 @@ public async Task CheckpointUpdateFailsWhenContainerDoesNotExist() var containerClient = new BlobContainerClient(storageConnectionString, $"test-container-{Guid.NewGuid()}"); var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10), default), Throws.InstanceOf()); + Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("100"), default), Throws.InstanceOf()); } } @@ -736,7 +735,7 @@ public async Task CheckpointUpdateCreatesTheBlobOnFirstCall() var mockEvent = new MockEventData( eventBody: Array.Empty(), - offset: 10, + offset: "10", sequenceNumber: 20); // There should be no blobs or checkpoints before the first call. @@ -755,11 +754,11 @@ public async Task CheckpointUpdateCreatesTheBlobOnFirstCall() // Calling update should create the checkpoint. - await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(mockEvent.SequenceNumber), default); + await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(mockEvent.OffsetString, mockEvent.SequenceNumber), default); storedCheckpoint = await checkpointStore.GetCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, default); Assert.That(storedCheckpoint, Is.Not.Null); - Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(mockEvent.SequenceNumber, false))); + Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset(mockEvent.OffsetString, false))); Assert.That(storedCheckpoint.ClientIdentifier, Is.EqualTo("Id")); // There should be a single blob in the container. @@ -780,63 +779,6 @@ public async Task CheckpointUpdateCreatesTheBlobOnFirstCall() } } - /// - /// Verifies functionality of the - /// method. - /// - /// - [Test] - public async Task CheckpointUpdateWritesAnInvalidOffsetString() - { - await using (StorageScope storageScope = await StorageScope.CreateAsync()) - { - var storageConnectionString = StorageTestEnvironment.Instance.StorageConnectionString; - var containerClient = new BlobContainerClient(storageConnectionString, storageScope.ContainerName); - var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - - var checkpoint = new EventProcessorCheckpoint - { - FullyQualifiedNamespace = "namespace", - EventHubName = "eventHubName", - ConsumerGroup = "consumerGroup", - PartitionId = "partitionId", - ClientIdentifier = "Id" - }; - - var mockEvent = new MockEventData( - eventBody: Array.Empty(), - offset: 10, - sequenceNumber: 20); - - // Calling update should create the checkpoint. - - await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(mockEvent.SequenceNumber), default); - - var blobCount = 0; - var checkpointBlobName = default(string); - var checkpointBlob = default(BlobProperties); - var storedCheckpoint = await checkpointStore.GetCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, default); - - await foreach (var blob in containerClient.GetBlobsAsync()) - { - ++blobCount; - checkpointBlobName = blob.Name; - checkpointBlob = await containerClient.GetBlobClient(blob.Name).GetPropertiesAsync(); - - if (blobCount > 1) - { - break; - } - } - - Assert.That(blobCount, Is.EqualTo(1)); - Assert.That(checkpointBlobName, Is.EqualTo(checkpointStore.GetCheckpointBlobName(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId))); - Assert.That(checkpointBlob.Metadata.TryGetValue("offset", out var offset), Is.True); - Assert.That(offset, Is.Not.Null); - Assert.That(long.TryParse(offset, out _), Is.False); - } - } - /// /// Verifies functionality of the /// method. @@ -862,12 +804,12 @@ public async Task CheckpointUpdatesAnExistingBlob() var mockEvent = new MockEventData( eventBody: Array.Empty(), - offset: 10, + offset: "10", sequenceNumber: 20); // Calling update should create the checkpoint. - await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(mockEvent.SequenceNumber), default); + await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, CheckpointPosition.FromEvent(mockEvent), default); var blobCount = 0; var storedCheckpoint = await checkpointStore.GetCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, default); @@ -884,17 +826,17 @@ public async Task CheckpointUpdatesAnExistingBlob() Assert.That(blobCount, Is.EqualTo(1)); Assert.That(storedCheckpoint, Is.Not.Null); - Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(mockEvent.SequenceNumber, false))); + Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset(mockEvent.OffsetString, false))); Assert.That(storedCheckpoint.ClientIdentifier, Is.EqualTo("Id")); // Calling update again should update the existing checkpoint. mockEvent = new MockEventData( eventBody: Array.Empty(), - offset: 50, + offset: "50", sequenceNumber: 60); - await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(mockEvent.SequenceNumber), default); + await checkpointStore.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, CheckpointPosition.FromEvent(mockEvent), default); blobCount = 0; storedCheckpoint = await checkpointStore.GetCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, default); @@ -911,7 +853,7 @@ public async Task CheckpointUpdatesAnExistingBlob() Assert.That(blobCount, Is.EqualTo(1)); Assert.That(storedCheckpoint, Is.Not.Null); - Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(mockEvent.SequenceNumber, false))); + Assert.That(storedCheckpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset(mockEvent.OffsetString, false))); Assert.That(storedCheckpoint.ClientIdentifier, Is.EqualTo("Id")); } } @@ -930,8 +872,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherConsumerGroups() var containerClient = new BlobContainerClient(storageConnectionString, storageScope.ContainerName); var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId", "Id", new CheckpointPosition(20), default); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId", "Id", new CheckpointPosition(20), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId", "Id", new CheckpointPosition("10"), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId", "Id", new CheckpointPosition("444", 10), default); var storedCheckpoint1 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId", default); var storedCheckpoint2 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId", default); @@ -955,8 +897,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherEventHubs() var containerClient = new BlobContainerClient(storageConnectionString, storageScope.ContainerName); var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId", "Id", new CheckpointPosition(20), default); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId", "Id", new CheckpointPosition(20), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId", "Id", new CheckpointPosition("20"), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId", "Id", new CheckpointPosition("777", 10), default); var storedCheckpoint1 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId", default); var storedCheckpoint2 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId", default); @@ -980,8 +922,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherNamespaces() var containerClient = new BlobContainerClient(storageConnectionString, storageScope.ContainerName); var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - await checkpointStore.UpdateCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(20), default); - await checkpointStore.UpdateCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(20), default); + await checkpointStore.UpdateCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("20"), default); + await checkpointStore.UpdateCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("111", 20), default); var storedCheckpoint1 = await checkpointStore.GetCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId", default); var storedCheckpoint2 = await checkpointStore.GetCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId", default); @@ -1005,8 +947,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherPartitions() var containerClient = new BlobContainerClient(storageConnectionString, storageScope.ContainerName); var checkpointStore = new BlobCheckpointStoreInternal(containerClient); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1", "Id", new CheckpointPosition(20), default); - await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2", "Id", new CheckpointPosition(20), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1", "Id", new CheckpointPosition("20", 222), default); + await checkpointStore.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2", "Id", new CheckpointPosition("20"), default); var storedCheckpoint1 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1", default); var storedCheckpoint2 = await checkpointStore.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2", default); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalTests.cs index 3e9dd64c74c89..e821e99379c61 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/BlobCheckpointStore/BlobsCheckpointStoreInternalTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -362,7 +363,7 @@ public async Task GetCheckpointLogsStartAndComplete() [Test] public async Task GetCheckpointUsesOffsetAsTheStartingPositionWhenNoSequenceNumberIsPresent() { - var expectedOffset = 13; + var expectedOffset = "13"; var expectedStartingPosition = EventPosition.FromOffset(expectedOffset, false); var partition = Guid.NewGuid().ToString(); @@ -428,48 +429,10 @@ public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenNoOffs /// /// [Test] - public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenOffsetIsMinValue() + public async Task GetCheckpointUsesOffsetAsTheStartingPosition() { - var expectedSequence = 133; - var expectedStartingPosition = EventPosition.FromSequenceNumber(expectedSequence, false); - var partition = Guid.NewGuid().ToString(); - - var blobList = new List - { - BlobsModelFactory.BlobItem($"{FullyQualifiedNamespaceLowercase}/{EventHubNameLowercase}/{ConsumerGroupLowercase}/checkpoint/{partition}", - false, - BlobsModelFactory.BlobItemProperties(true, lastModified: DateTime.UtcNow, eTag: new ETag(MatchingEtag)), - "snapshot", - new Dictionary - { - {BlobMetadataKey.OwnerIdentifier, Guid.NewGuid().ToString()}, - {BlobMetadataKey.Offset, long.MinValue.ToString()}, - {BlobMetadataKey.SequenceNumber, expectedSequence.ToString()} - }) - }; - - var target = new BlobCheckpointStoreInternal(new MockBlobContainerClient() { Blobs = blobList }); - var checkpoint = await target.GetCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, partition, CancellationToken.None); - - Assert.That(checkpoint, Is.Not.Null, "A checkpoint should have been returned."); - Assert.That(checkpoint.StartingPosition, Is.EqualTo(expectedStartingPosition)); - - Assert.That(checkpoint, Is.InstanceOf(), "Checkpoint instance was not the expected type."); - var blobCheckpoint = (BlobCheckpointStoreInternal.BlobStorageCheckpoint)checkpoint; - Assert.That(blobCheckpoint.Offset, Is.EqualTo(long.MinValue), "The offset should have been long.MinValue."); - Assert.That(expectedSequence, Is.EqualTo(blobCheckpoint.SequenceNumber), "Checkpoint sequence number did not have the correct value."); - } - - /// - /// Verifies basic functionality of GetCheckpointAsync and ensures the starting position is set correctly. - /// - /// - [Test] - public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenInvalidOffsetIsPresent() - { - var expectedSequence = 133; - var expectedOffset = "invalid offset"; - var expectedStartingPosition = EventPosition.FromSequenceNumber(expectedSequence, false); + var expectedOffset = "133"; + var expectedStartingPosition = EventPosition.FromOffset(expectedOffset, false); var partition = Guid.NewGuid().ToString(); var blobList = new List @@ -482,7 +445,7 @@ public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenInvali { {BlobMetadataKey.OwnerIdentifier, Guid.NewGuid().ToString()}, {BlobMetadataKey.Offset, expectedOffset}, - {BlobMetadataKey.SequenceNumber, expectedSequence.ToString()} + {BlobMetadataKey.SequenceNumber, long.MinValue.ToString()} }) }; @@ -494,8 +457,8 @@ public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenInvali Assert.That(checkpoint, Is.InstanceOf(), "Checkpoint instance was not the expected type."); var blobCheckpoint = (BlobCheckpointStoreInternal.BlobStorageCheckpoint)checkpoint; - Assert.That(blobCheckpoint.Offset, Is.Null, $"The offset should not have been populated, as it was an invalid number."); - Assert.That(expectedSequence, Is.EqualTo(blobCheckpoint.SequenceNumber), "Checkpoint sequence number did not have the correct value."); + Assert.That(blobCheckpoint.SequenceNumber, Is.EqualTo(long.MinValue), "The offset should have been long.MinValue."); + Assert.That(expectedOffset, Is.EqualTo(blobCheckpoint.Offset), "Checkpoint sequence number did not have the correct value."); } /// @@ -503,11 +466,11 @@ public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenInvali /// /// [Test] - public async Task GetCheckpointPrefersSequenceNumberAsTheStartingPosition() + public async Task GetCheckpointPrefersOffsetAsTheStartingPosition() { - var offset = 13; + var offset = "13"; var sequenceNumber = 7777; - var expectedStartingPosition = EventPosition.FromSequenceNumber(sequenceNumber, false); + var expectedStartingPosition = EventPosition.FromOffset(offset, false); var partition = Guid.NewGuid().ToString(); var blobList = new List @@ -519,7 +482,7 @@ public async Task GetCheckpointPrefersSequenceNumberAsTheStartingPosition() new Dictionary { {BlobMetadataKey.OwnerIdentifier, Guid.NewGuid().ToString()}, - {BlobMetadataKey.Offset, offset.ToString()}, + {BlobMetadataKey.Offset, offset}, {BlobMetadataKey.SequenceNumber, sequenceNumber.ToString()} }) }; @@ -548,7 +511,7 @@ public async Task GetCheckpointConsidersDataInvalidWithNoOffsetOrSequenceNumber( new Dictionary { {BlobMetadataKey.OwnerIdentifier, Guid.NewGuid().ToString()}, - {BlobMetadataKey.Offset, "invalid"}, + {BlobMetadataKey.Offset, ""}, {BlobMetadataKey.SequenceNumber, long.MinValue.ToString()} }) }; @@ -569,7 +532,7 @@ public async Task GetCheckpointConsidersDataInvalidWithNoOffsetOrSequenceNumber( /// /// [Test] - public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenPresentInLegacyCheckpoint() + public async Task GetCheckpointUsesOffsetAsTheStartingPositionWhenPresentInLegacyCheckpoint() { var blobList = new List { @@ -596,7 +559,7 @@ public async Task GetCheckpointUsesSequenceNumberAsTheStartingPositionWhenPresen var checkpoint = await target.GetCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, "0", CancellationToken.None); Assert.That(checkpoint, Is.Not.Null, "A checkpoints should have been returned."); - Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(960180, false))); + Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset("13", false))); Assert.That(checkpoint.PartitionId, Is.EqualTo("0")); } @@ -629,11 +592,10 @@ public void GetCheckpointForMissingPartitionThrowsAndLogsGetCheckpointError() /// /// [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists(bool useCheckpointPositionOverload) + public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists() { - var sequenceNumber = 777; + var position = new CheckpointPosition("777", 89); + var expectedSequenceNumber = position.SequenceNumber.ToString(CultureInfo.InvariantCulture); var checkpoint = new EventProcessorCheckpoint { @@ -642,7 +604,7 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists(bool use ConsumerGroup = ConsumerGroup, PartitionId = PartitionId, ClientIdentifier = Identifier, - StartingPosition = EventPosition.FromSequenceNumber(sequenceNumber) + StartingPosition = EventPosition.FromOffset(position.OffsetString) }; var blobInfo = BlobsModelFactory.BlobInfo(new ETag($@"""{MatchingEtag}"""), DateTime.UtcNow); @@ -655,7 +617,8 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists(bool use "snapshot", new Dictionary { { BlobMetadataKey.OwnerIdentifier, Guid.NewGuid().ToString() }, - { BlobMetadataKey.SequenceNumber, sequenceNumber.ToString() }, + { BlobMetadataKey.Offset, position.OffsetString }, + { BlobMetadataKey.SequenceNumber, expectedSequenceNumber }, { BlobMetadataKey.ClientIdentifier, Identifier } }) }; @@ -673,18 +636,9 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists(bool use var mockLog = new Mock(); target.Logger = mockLog.Object; - if (useCheckpointPositionOverload) - { - await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(sequenceNumber), CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), "-1", string.Empty)); - mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), "-1", string.Empty)); - } - else - { - await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, 123, null, CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, string.Empty, "-1", "123")); - mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, string.Empty, "-1", "123")); - } + await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, position, CancellationToken.None); + mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, expectedSequenceNumber, position.OffsetString)); + mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, expectedSequenceNumber, position.OffsetString)); } /// @@ -692,12 +646,10 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobExists(bool use /// /// [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobDoesNotExist(bool useCheckpointPositionOverload) + public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobDoesNotExist() { var sequenceNumber = 1234; - var offset = 5678; + var offset = "5678"; var checkpoint = new EventProcessorCheckpoint { @@ -726,18 +678,9 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobDoesNotExist(bo var mockLog = new Mock(); target.Logger = mockLog.Object; - if (useCheckpointPositionOverload) - { - await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(sequenceNumber), CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), "-1", string.Empty)); - mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), "-1", string.Empty)); - } - else - { - await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, offset, sequenceNumber, CancellationToken.None); - mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, sequenceNumber.ToString(), "-1", offset.ToString())); - mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, sequenceNumber.ToString(), "-1", offset.ToString())); - } + await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(offset, sequenceNumber), CancellationToken.None); + mockLog.Verify(log => log.UpdateCheckpointStart(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), offset)); + mockLog.Verify(log => log.UpdateCheckpointComplete(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), offset)); } /// @@ -746,12 +689,10 @@ public async Task UpdateCheckpointLogsStartAndCompleteWhenTheBlobDoesNotExist(bo /// /// [Test] - [TestCase(true)] - [TestCase(false)] - public void UpdateCheckpointLogsErrorsWhenTheBlobExists(bool useCheckpointPositionOverload) + public void UpdateCheckpointLogsErrorsWhenTheBlobExists() { var sequenceNumber = 456; - var offset = 789; + var offset = "789"; var checkpoint = new EventProcessorCheckpoint { @@ -775,16 +716,8 @@ public void UpdateCheckpointLogsErrorsWhenTheBlobExists(bool useCheckpointPositi var target = new BlobCheckpointStoreInternal(mockContainerClient); target.Logger = mockLog.Object; - if (useCheckpointPositionOverload) - { - Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(sequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), "-1", string.Empty, expectedException.Message)); - } - else - { - Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, offset, sequenceNumber, CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, sequenceNumber.ToString(), "-1", offset.ToString(), expectedException.Message)); - } + Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(offset, sequenceNumber), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); + mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, sequenceNumber.ToString(), offset.ToString(), expectedException.Message)); } /// @@ -793,9 +726,7 @@ public void UpdateCheckpointLogsErrorsWhenTheBlobExists(bool useCheckpointPositi /// /// [Test] - [TestCase(true)] - [TestCase(false)] - public void UpdateCheckpointLogsErrorsWhenTheBlobDoesNotExist(bool useCheckpointPositionOverload) + public void UpdateCheckpointLogsErrorsWhenTheBlobDoesNotExist() { var checkpoint = new EventProcessorCheckpoint { @@ -817,16 +748,8 @@ public void UpdateCheckpointLogsErrorsWhenTheBlobDoesNotExist(bool useCheckpoint var target = new BlobCheckpointStoreInternal(mockContainerClient); target.Logger = mockLog.Object; - if (useCheckpointPositionOverload) - { - Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition(0), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, "0", "-1", string.Empty, expectedException.Message)); - } - else - { - Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, 0, 0, CancellationToken.None), Throws.Exception.EqualTo(expectedException)); - mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, string.Empty, "0", "-1", "0", expectedException.Message)); - } + Assert.That(async () => await target.UpdateCheckpointAsync(checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.PartitionId, checkpoint.ClientIdentifier, new CheckpointPosition("0", 0), CancellationToken.None), Throws.Exception.EqualTo(expectedException)); + mockLog.Verify(log => log.UpdateCheckpointError(checkpoint.PartitionId, checkpoint.FullyQualifiedNamespace, checkpoint.EventHubName, checkpoint.ConsumerGroup, checkpoint.ClientIdentifier, "0", "0", expectedException.Message)); } /// @@ -843,8 +766,8 @@ public void UpdateCheckpointForMissingContainerThrowsAndLogsCheckpointUpdateErro var mockLog = new Mock(); target.Logger = mockLog.Object; - Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition(0), CancellationToken.None), Throws.InstanceOf()); - mockLog.Verify(m => m.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, "0", "-1", string.Empty, ex.Message)); + Assert.That(async () => await target.UpdateCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, PartitionId, Identifier, new CheckpointPosition("111", 0), CancellationToken.None), Throws.InstanceOf()); + mockLog.Verify(m => m.UpdateCheckpointError(PartitionId, FullyQualifiedNamespace, EventHubName, ConsumerGroup, Identifier, "0", "111", ex.Message)); } /// @@ -1210,7 +1133,7 @@ public void UpdateCheckpointAsyncSurfacesNonRetriableExceptionsWhenTheBlobExists using var cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition(10), cancellationSource.Token), Throws.TypeOf(exception.GetType()), "The method call should surface the exception."); + Assert.That(async () => await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition("10"), cancellationSource.Token), Throws.TypeOf(exception.GetType()), "The method call should surface the exception."); Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The operation should have stopped without cancellation."); Assert.That(serviceCalls, Is.EqualTo(expectedServiceCalls), "The retry policy should have been applied."); } @@ -1239,7 +1162,7 @@ public void UpdateCheckpointAsyncSurfacesNonRetriableExceptionsWhenTheBlobDoesNo using var cancellationSource = new CancellationTokenSource(); cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition(10), cancellationSource.Token), Throws.TypeOf(exception.GetType()), "The method call should surface the exception."); + Assert.That(async () => await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition("10"), cancellationSource.Token), Throws.TypeOf(exception.GetType()), "The method call should surface the exception."); Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The operation should have stopped without cancellation."); } @@ -1249,9 +1172,7 @@ public void UpdateCheckpointAsyncSurfacesNonRetriableExceptionsWhenTheBlobDoesNo /// /// [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateCheckpointAsyncDelegatesTheCancellationTokenWhenTheBlobExists(bool useCheckpointPositionOverload) + public async Task UpdateCheckpointAsyncDelegatesTheCancellationTokenWhenTheBlobExists() { using var cancellationSource = new CancellationTokenSource(); @@ -1277,14 +1198,7 @@ public async Task UpdateCheckpointAsyncDelegatesTheCancellationTokenWhenTheBlobE var checkpointStore = new BlobCheckpointStoreInternal(mockContainerClient); - if (useCheckpointPositionOverload) - { - await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition(10), cancellationSource.Token); - } - else - { - await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, 0, 10, cancellationSource.Token); - } + await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition("10"), cancellationSource.Token); Assert.That(stateBeforeCancellation.HasValue, Is.True, "State before cancellation should have been captured."); Assert.That(stateBeforeCancellation.Value, Is.False, "The token should not have been canceled before cancellation request."); @@ -1320,7 +1234,7 @@ public async Task UpdateCheckpointAsyncDelegatesTheCancellationTokenWhenTheBlobD }); var checkpointStore = new BlobCheckpointStoreInternal(mockContainerClient); - await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition(10), cancellationSource.Token); + await checkpointStore.UpdateCheckpointAsync(FullyQualifiedNamespaceLowercase, EventHubNameLowercase, ConsumerGroupLowercase, partition, "Id", new CheckpointPosition("10"), cancellationSource.Token); Assert.That(stateBeforeCancellation.HasValue, Is.True, "State before cancellation should have been captured."); Assert.That(stateBeforeCancellation.Value, Is.False, "The token should not have been canceled before cancellation request."); @@ -1341,7 +1255,7 @@ public void AlreadyCanceledTokenMakesUpdateCheckpointAsyncThrow() using var cancellationSource = new CancellationTokenSource(); cancellationSource.Cancel(); - Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("ns", "eh", "cg", "p0", "Id", new CheckpointPosition(10), cancellationSource.Token), Throws.InstanceOf()); + Assert.That(async () => await checkpointStore.UpdateCheckpointAsync("ns", "eh", "cg", "p0", "Id", new CheckpointPosition("10"), cancellationSource.Token), Throws.InstanceOf()); } /// @@ -1388,12 +1302,12 @@ public async Task GetCheckpointPreferredNewCheckpointOverLegacy() var checkpoint = await target.GetCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, "0", CancellationToken.None); Assert.That(checkpoint, Is.Not.Null, "A single checkpoint should have been returned."); - Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(960182, false))); + Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset("14", false))); Assert.That(checkpoint.PartitionId, Is.EqualTo("0")); Assert.That(checkpoint, Is.InstanceOf(), "Checkpoint instance was not the expected type."); var blobCheckpoint = (BlobCheckpointStoreInternal.BlobStorageCheckpoint)checkpoint; - Assert.That(14, Is.EqualTo(blobCheckpoint.Offset), "Checkpoint offset did not have the correct value."); + Assert.That("14", Is.EqualTo(blobCheckpoint.Offset), "Checkpoint offset did not have the correct value."); Assert.That(960182, Is.EqualTo(blobCheckpoint.SequenceNumber), "Checkpoint sequence number did not have the correct value."); } @@ -1430,7 +1344,7 @@ public async Task GetCheckpointsUsesOffsetAsTheStartingPositionWhenPresentInLega var checkpoint = await target.GetCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, "0", CancellationToken.None); Assert.That(checkpoint, Is.Not.Null, "A set of checkpoints should have been returned."); - Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromSequenceNumber(960180, false))); + Assert.That(checkpoint.StartingPosition, Is.EqualTo(EventPosition.FromOffset("13", false))); Assert.That(checkpoint.PartitionId, Is.EqualTo("0")); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/BlobsCheckpointStoreInternal.Diagnostics.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/BlobsCheckpointStoreInternal.Diagnostics.cs index aaa66f163bce3..23d19e1c51c47 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/BlobsCheckpointStoreInternal.Diagnostics.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/BlobsCheckpointStoreInternal.Diagnostics.cs @@ -87,7 +87,6 @@ partial void InvalidCheckpointFound(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// The exception that occurred. /// @@ -97,10 +96,9 @@ partial void UpdateCheckpointError(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset, Exception exception) => - Logger.UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, replicationSegment, offset, exception.Message); + Logger.UpdateCheckpointError(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, offset, exception.Message); /// /// Indicates that an attempt to update a checkpoint has completed. @@ -112,7 +110,6 @@ partial void UpdateCheckpointError(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// partial void UpdateCheckpointComplete(string partitionId, @@ -121,9 +118,8 @@ partial void UpdateCheckpointComplete(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) => - Logger.UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, replicationSegment, offset); + Logger.UpdateCheckpointComplete(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, offset); /// /// Indicates that an attempt to create/update a checkpoint has started. @@ -135,7 +131,6 @@ partial void UpdateCheckpointComplete(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// partial void UpdateCheckpointStart(string partitionId, @@ -144,9 +139,8 @@ partial void UpdateCheckpointStart(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset) => - Logger.UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, replicationSegment, offset); + Logger.UpdateCheckpointStart(partitionId, fullyQualifiedNamespace, eventHubName, consumerGroup, clientIdentifier, sequenceNumber, offset); /// /// Indicates that an attempt to retrieve claim partition ownership has completed. diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/IBlobEventLogger.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/IBlobEventLogger.cs index 9f37487c033cb..e6ca8eaf158ae 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/IBlobEventLogger.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Infrastructure/IBlobEventLogger.cs @@ -116,7 +116,6 @@ void ListCheckpointsStart(string fullyQualifiedNamespace, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// The exception that occurred. /// @@ -126,7 +125,6 @@ void UpdateCheckpointError(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset, string exception); @@ -140,7 +138,6 @@ void UpdateCheckpointError(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// void UpdateCheckpointComplete(string partitionId, @@ -149,7 +146,6 @@ void UpdateCheckpointComplete(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset); /// @@ -162,7 +158,6 @@ void UpdateCheckpointComplete(string partitionId, /// The name of the consumer group the checkpoint is associated with. /// The unique identifier of the client that authored this checkpoint. /// The sequence number associated with this checkpoint. - /// The replication segment associated with this checkpoint. /// The offset associated with this checkpoint. /// void UpdateCheckpointStart(string partitionId, @@ -171,7 +166,6 @@ void UpdateCheckpointStart(string partitionId, string consumerGroup, string clientIdentifier, string sequenceNumber, - string replicationSegment, string offset); /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/EventDataExtensionsTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/EventDataExtensionsTests.cs index 0efbbccdc3cec..beca3c3f15e64 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/EventDataExtensionsTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/EventDataExtensionsTests.cs @@ -39,13 +39,13 @@ public void IsEquivalentToDetectsDifferentDifferentOffset() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); var secondEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 111, + offset: "111", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -62,13 +62,13 @@ public void IsEquivalentToDetectsDifferentDifferentSequenceNumbers() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); var secondEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -85,13 +85,13 @@ public void IsEquivalentToDetectsDifferentDifferentPartitionKey() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); var secondEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44 }, - offset: 1, + offset: "1", partitionKey: "goodbye", systemProperties: new Dictionary { { "test", new object() } }); @@ -285,8 +285,8 @@ public void IsEquivalentToIgnoresMappedSystemPropertiesByDefault() public void IsEquivalentToDetectsDifferentTypedSystemProperties() { var body = new byte[] { 0x22, 0x44, 0x88 }; - var firstEvent = new MockEventData((byte[])body.Clone(), offset: 1); - var secondEvent = new MockEventData((byte[])body.Clone(), offset: 2); + var firstEvent = new MockEventData((byte[])body.Clone(), offset: "1"); + var secondEvent = new MockEventData((byte[])body.Clone(), offset: "2"); Assert.That(firstEvent.IsEquivalentTo(secondEvent, true), Is.False); } @@ -380,13 +380,13 @@ public void IsEquivalentToDetectsEqualEventsWithPartitionMetrics() var firstEvent = new MockEventData( eventBody: (byte[])body.Clone(), - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); var secondEvent = new MockEventData( eventBody: (byte[])body.Clone(), - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -405,13 +405,13 @@ public void IsEquivalentToDetectsEqualEventsWithoutPartitionMetrics() var firstEvent = new MockEventData( eventBody: (byte[])body.Clone(), - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); var secondEvent = new MockEventData( eventBody: (byte[])body.Clone(), - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -428,7 +428,7 @@ public void IsEquivalentToDetectsSameInstance() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44, 0x88 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -456,7 +456,7 @@ public void IsEquivalentToDetectsNullInstance() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44, 0x88 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -473,7 +473,7 @@ public void IsEquivalentToDetectsNullArgument() { var firstEvent = new MockEventData( eventBody: new byte[] { 0x22, 0x44, 0x88 }, - offset: 1, + offset: "1", partitionKey: "hello", systemProperties: new Dictionary { { "test", new object() } }); @@ -493,9 +493,9 @@ public MockEventData(ReadOnlyMemory eventBody, IDictionary properties = null, IReadOnlyDictionary systemProperties = null, long sequenceNumber = long.MinValue, - long offset = long.MinValue, + string offset = null, DateTimeOffset enqueuedTime = default, - string partitionKey = null) : base(eventBody, properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey) + string partitionKey = null) : base(BinaryData.FromBytes(eventBody), properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey) { } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/InMemoryCheckpointStoreTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/InMemoryCheckpointStoreTests.cs index bbe83a006e177..3181742c2902c 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/InMemoryCheckpointStoreTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Shared/tests/Testing/InMemoryCheckpointStoreTests.cs @@ -432,8 +432,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherConsumerGroups() { var storageManager = new InMemoryCheckpointStore(); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId", "Id", new CheckpointPosition(10)); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId", "Id", new CheckpointPosition(10)); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId", "Id", new CheckpointPosition("10")); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId", "Id", new CheckpointPosition("444", 10)); var storedCheckpoint1 = await storageManager.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup1", "partitionId"); var storedCheckpoint2 = await storageManager.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup2", "partitionId"); @@ -452,8 +452,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherEventHubs() { var storageManager = new InMemoryCheckpointStore(); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10)); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10)); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId", "Id", new CheckpointPosition("10")); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId", "Id", new CheckpointPosition("444", 10)); var storedCheckpoint1 = await storageManager.GetCheckpointAsync("namespace", "eventHubName1", "consumerGroup", "partitionId"); var storedCheckpoint2 = await storageManager.GetCheckpointAsync("namespace", "eventHubName2", "consumerGroup", "partitionId"); @@ -472,8 +472,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherNamespaces() { var storageManager = new InMemoryCheckpointStore(); - await storageManager.UpdateCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10)); - await storageManager.UpdateCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition(10)); + await storageManager.UpdateCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("10")); + await storageManager.UpdateCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId", "Id", new CheckpointPosition("444", 10)); var storedCheckpoint1 = await storageManager.GetCheckpointAsync("namespace1", "eventHubName", "consumerGroup", "partitionId"); var storedCheckpoint2 = await storageManager.GetCheckpointAsync("namespace2", "eventHubName", "consumerGroup", "partitionId"); @@ -492,8 +492,8 @@ public async Task CheckpointUpdateDoesNotInterfereWithOtherPartitions() { var storageManager = new InMemoryCheckpointStore(); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1", "Id", new CheckpointPosition(10)); - await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2", "Id", new CheckpointPosition(10)); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1", "Id", new CheckpointPosition("111", 10)); + await storageManager.UpdateCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2", "Id", new CheckpointPosition("9")); var storedCheckpoint1 = await storageManager.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId1"); var storedCheckpoint2 = await storageManager.GetCheckpointAsync("namespace", "eventHubName", "consumerGroup", "partitionId2"); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.netstandard2.0.cs b/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.netstandard2.0.cs index 7d23614281bcd..3a9e0a4fa2cd4 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.netstandard2.0.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.netstandard2.0.cs @@ -6,9 +6,13 @@ public EventData() { } public EventData(Azure.Core.Amqp.AmqpAnnotatedMessage amqpMessage) { } public EventData(System.BinaryData eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] protected EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + protected EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, string offsetString = null, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } public EventData(System.ReadOnlyMemory eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.")] protected EventData(System.ReadOnlyMemory eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } public EventData(string eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -26,7 +30,10 @@ public EventData(string eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool IsReadOnly { get { throw null; } } public string MessageId { get { throw null; } set { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] public long Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public string PartitionKey { get { throw null; } } public System.Collections.Generic.IDictionary Properties { get { throw null; } } public long SequenceNumber { get { throw null; } } @@ -80,8 +87,11 @@ public EventHubConnectionOptions() { } } public partial class EventHubProperties { - protected internal EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + protected EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { } + protected internal EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds, bool isGeoReplicationEnabled) { } public System.DateTimeOffset CreatedOn { get { throw null; } } + public bool IsGeoReplicationEnabled { get { throw null; } } public string Name { get { throw null; } } public string[] PartitionIds { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -139,14 +149,26 @@ public enum FailureReason } public static partial class EventHubsModelFactory { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.EventData EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, string partitionKey = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset)) { throw null; } + public static Azure.Messaging.EventHubs.EventData EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, string partitionKey = null, long sequenceNumber = (long)-9223372036854775808, string offsetString = null, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset)) { throw null; } public static Azure.Messaging.EventHubs.Producer.EventDataBatch EventDataBatch(long batchSizeBytes, System.Collections.Generic.IList batchEventStore, Azure.Messaging.EventHubs.Producer.CreateBatchOptions batchOptions = null, System.Func tryAddCallback = null) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.EventHubProperties EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.Messaging.EventHubs.EventHubProperties EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds, bool isGeoReplicationEnabled) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + public static Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, string lastOffsetString, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.Consumer.PartitionContext PartitionContext(string partitionId, Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties lastEnqueuedEventProperties = default(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties)) { throw null; } public static Azure.Messaging.EventHubs.Consumer.PartitionContext PartitionContext(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties lastEnqueuedEventProperties = default(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.PartitionProperties PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, long lastOffset, System.DateTimeOffset lastEnqueuedTime) { throw null; } + public static Azure.Messaging.EventHubs.PartitionProperties PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, string lastOffsetString, System.DateTimeOffset lastEnqueuedTime) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.Producer.PartitionPublishingProperties PartitionPublishingProperties(bool isIdempotentPublishingEnabled, long? producerGroupId, short? ownerLevel, int? lastPublishedSequenceNumber) { throw null; } } @@ -184,12 +206,18 @@ public enum EventHubsTransportType } public partial class PartitionProperties { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] protected internal PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, long lastOffset, System.DateTimeOffset lastEnqueuedTime) { } + protected internal PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, string lastOffsetString, System.DateTimeOffset lastEnqueuedTime) { } public long BeginningSequenceNumber { get { throw null; } } public string EventHubName { get { throw null; } } public string Id { get { throw null; } } public bool IsEmpty { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'LastEnqueuedOffsetString' instead.", false)] public long LastEnqueuedOffset { get { throw null; } } + public string LastEnqueuedOffsetString { get { throw null; } } public long LastEnqueuedSequenceNumber { get { throw null; } } public System.DateTimeOffset LastEnqueuedTime { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -260,7 +288,10 @@ public partial struct EventPosition : System.IEquatable { + private object _dummy; + private int _dummyPrimitive; + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] + public LastEnqueuedEventProperties(long? lastSequenceNumber, long lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + public LastEnqueuedEventProperties(long? lastSequenceNumber, string lastOffsetString, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } public System.DateTimeOffset? EnqueuedTime { get { throw null; } } public System.DateTimeOffset? LastReceivedTime { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] public long? Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public long? SequenceNumber { get { throw null; } } public bool Equals(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -331,6 +373,8 @@ protected CheckpointStore() { } public abstract System.Threading.Tasks.Task> ClaimOwnershipAsync(System.Collections.Generic.IEnumerable desiredOwnership, System.Threading.CancellationToken cancellationToken); public abstract System.Threading.Tasks.Task GetCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, System.Threading.CancellationToken cancellationToken); public abstract System.Threading.Tasks.Task> ListOwnershipAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, System.Threading.CancellationToken cancellationToken); + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] public virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -393,6 +437,7 @@ protected EventProcessor(int eventBatchMaximumCount, string consumerGroup, strin public string ConsumerGroup { get { throw null; } } protected bool EnableBatchTracing { get { throw null; } set { } } public string EventHubName { get { throw null; } } + protected Azure.Messaging.EventHubs.EventHubProperties EventHubProperties { get { throw null; } } public string FullyQualifiedNamespace { get { throw null; } } public string Identifier { get { throw null; } } public bool IsRunning { get { throw null; } protected set { } } @@ -420,6 +465,8 @@ protected EventProcessor(int eventBatchMaximumCount, string consumerGroup, strin [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } protected virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } protected internal virtual System.Threading.Tasks.Task ValidateProcessingPreconditions(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -482,6 +529,8 @@ protected PluggableCheckpointStoreEventProcessor(Azure.Messaging.EventHubs.Primi protected override System.Threading.Tasks.Task GetCheckpointAsync(string partitionId, System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task> ListOwnershipAsync(System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } } } @@ -490,8 +539,19 @@ namespace Azure.Messaging.EventHubs.Processor [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct CheckpointPosition : System.IEquatable { + private object _dummy; private int _dummyPrimitive; + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee that a sequence number-only checkpoint can access the event stream for all resource configurations. Reading events may not work in all cases going forward. Please provide a string-based offset.", false)] public CheckpointPosition(long sequenceNumber) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset instead.", false)] + public CheckpointPosition(long offset, long sequenceNumber = (long)-9223372036854775808) { throw null; } + public CheckpointPosition(string offsetString, long sequenceNumber = (long)-9223372036854775808) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] + public long Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public long SequenceNumber { get { throw null; } } public bool Equals(Azure.Messaging.EventHubs.Processor.CheckpointPosition other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample03_EventHubMetadata.md b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample03_EventHubMetadata.md index 85759f4524b03..c2c6b7fbd3d56 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample03_EventHubMetadata.md +++ b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample03_EventHubMetadata.md @@ -100,7 +100,7 @@ try Debug.WriteLine($"\tThe partition contains no events: { partitionProperties.IsEmpty }"); Debug.WriteLine($"\tThe first sequence number is: { partitionProperties.BeginningSequenceNumber }"); Debug.WriteLine($"\tThe last sequence number is: { partitionProperties.LastEnqueuedSequenceNumber }"); - Debug.WriteLine($"\tThe last offset is: { partitionProperties.LastEnqueuedOffset }"); + Debug.WriteLine($"\tThe last offset is: { partitionProperties.LastEnqueuedOffsetString }"); Debug.WriteLine($"\tThe last enqueued time is: { partitionProperties.LastEnqueuedTime }, in UTC."); } finally diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample05_ReadingEvents.md b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample05_ReadingEvents.md index 7350b783ebd02..06cfc2c8cd772 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample05_ReadingEvents.md +++ b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample05_ReadingEvents.md @@ -386,7 +386,7 @@ try string firstPartition = (await consumer.GetPartitionIdsAsync(cancellationSource.Token)).First(); PartitionProperties properties = await consumer.GetPartitionPropertiesAsync(firstPartition, cancellationSource.Token); - EventPosition startingPosition = EventPosition.FromOffset(properties.LastEnqueuedOffset); + EventPosition startingPosition = EventPosition.FromOffset(properties.LastEnqueuedOffsetString); await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync( firstPartition, @@ -503,7 +503,7 @@ try Debug.WriteLine($"Partition: { partitionEvent.Partition.PartitionId }"); Debug.WriteLine($"\tThe last sequence number is: { properties.SequenceNumber }"); - Debug.WriteLine($"\tThe last offset is: { properties.Offset }"); + Debug.WriteLine($"\tThe last offset is: { properties.OffsetString }"); Debug.WriteLine($"\tThe last enqueued time is: { properties.EnqueuedTime }, in UTC."); Debug.WriteLine($"\tThe information was updated at: { properties.LastReceivedTime }, in UTC."); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample11_MockingClientTypes.md b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample11_MockingClientTypes.md index e148dd792149c..29a1e5cf96567 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample11_MockingClientTypes.md +++ b/sdk/eventhub/Azure.Messaging.EventHubs/samples/Sample11_MockingClientTypes.md @@ -220,7 +220,7 @@ Mock mockConsumer = new(); LastEnqueuedEventProperties lastEnqueueEventProperties = EventHubsModelFactory.LastEnqueuedEventProperties( lastSequenceNumber : 1234, - lastOffset : 234, + lastOffsetString : "234:1:954-2", lastEnqueuedTime : DateTimeOffset.Parse("1:24 AM"), lastReceivedTime : DateTimeOffset.Parse("1:26 AM")); @@ -246,7 +246,7 @@ async IAsyncEnumerable mockReturn() systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:44:59492", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); EventData eventData2 = EventHubsModelFactory.EventData( @@ -254,7 +254,7 @@ async IAsyncEnumerable mockReturn() systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:2:1111", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); // This creates a mock PartitionEvent to return from the consumer client. @@ -341,7 +341,7 @@ for (int index = 0; index < 10; index++) systemProperties: new Dictionary(), //arbitrary value partitionKey: $"sample-key-{index}", sequenceNumber: 1234, - offset: 234, + offsetString: "234:5:93928381.1", enqueuedTime: DateTimeOffset.Parse("9:25 AM")); receivedEvents.Add(eventData); @@ -431,7 +431,7 @@ Dictionary partitionProperties = new() isEmpty : true, beginningSequenceNumber: 1000, lastSequenceNumber : 1100, - lastOffset : 500, + lastOffsetString : "500:1:7863", lastEnqueuedTime : DateTime.UtcNow) }, // Empty partition @@ -441,7 +441,7 @@ Dictionary partitionProperties = new() isEmpty : false, beginningSequenceNumber : 2000, lastSequenceNumber : 2000, - lastOffset : 760, + lastOffsetString : "760:1:8800", lastEnqueuedTime : DateTime.UtcNow) } }; diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpAnnotatedMessageExtensions.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpAnnotatedMessageExtensions.cs index c42a363462a87..cd64f1480e620 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpAnnotatedMessageExtensions.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpAnnotatedMessageExtensions.cs @@ -33,11 +33,11 @@ internal static class AmqpAnnotatedMessageExtensions public static void PopulateFromEventProperties(this AmqpAnnotatedMessage instance, IDictionary properties = null, long? sequenceNumber = null, - long? offset = null, + string offset = null, DateTimeOffset? enqueuedTime = null, string partitionKey = null, long? lastPartitionSequenceNumber = null, - long? lastPartitionOffset = null, + string lastPartitionOffset = null, DateTimeOffset? lastPartitionEnqueuedTime = null, DateTimeOffset? lastPartitionPropertiesRetrievalTime = null) { @@ -51,9 +51,9 @@ public static void PopulateFromEventProperties(this AmqpAnnotatedMessage instanc instance.MessageAnnotations[AmqpProperty.SequenceNumber.ToString()] = sequenceNumber.Value; } - if (offset.HasValue) + if (!string.IsNullOrEmpty(offset)) { - instance.MessageAnnotations[AmqpProperty.Offset.ToString()] = offset.Value; + instance.MessageAnnotations[AmqpProperty.Offset.ToString()] = offset; } if (enqueuedTime.HasValue) @@ -71,9 +71,9 @@ public static void PopulateFromEventProperties(this AmqpAnnotatedMessage instanc instance.DeliveryAnnotations[AmqpProperty.PartitionLastEnqueuedSequenceNumber.ToString()] = lastPartitionSequenceNumber.Value; } - if (lastPartitionOffset.HasValue) + if (!string.IsNullOrEmpty(lastPartitionOffset)) { - instance.DeliveryAnnotations[AmqpProperty.PartitionLastEnqueuedOffset.ToString()] = lastPartitionOffset.Value; + instance.DeliveryAnnotations[AmqpProperty.PartitionLastEnqueuedOffset.ToString()] = lastPartitionOffset; } if (lastPartitionEnqueuedTime.HasValue) @@ -188,13 +188,13 @@ public static long GetSequenceNumber(this AmqpAnnotatedMessage instance, /// /// The offset, if represented in the ; otherwise, . /// - public static long GetOffset(this AmqpAnnotatedMessage instance, - long defaultValue = long.MinValue) + public static string GetOffset(this AmqpAnnotatedMessage instance, + string defaultValue = default) { if ((instance.HasSection(AmqpMessageSection.MessageAnnotations)) && (instance.MessageAnnotations.TryGetValue(AmqpProperty.Offset.ToString(), out var value))) { - return (long)value; + return (string)value; } return defaultValue; @@ -298,13 +298,13 @@ public static void SetPartitionKey(this AmqpAnnotatedMessage instance, /// /// The offset of the last event published to the partition, if represented in the ; otherwise, . /// - public static long? GetLastPartitionOffset(this AmqpAnnotatedMessage instance, - long? defaultValue = default) + public static string GetLastPartitionOffset(this AmqpAnnotatedMessage instance, + string defaultValue = default) { if ((instance.HasSection(AmqpMessageSection.DeliveryAnnotations)) && (instance.DeliveryAnnotations.TryGetValue(AmqpProperty.PartitionLastEnqueuedOffset.ToString(), out var value))) { - return (long)value; + return (string)value; } return defaultValue; diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConnectionScope.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConnectionScope.cs index 57ac47bd7a8b1..eb7025327ba5c 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConnectionScope.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConnectionScope.cs @@ -702,6 +702,9 @@ protected virtual async Task CreateReceivingLinkAsync(AmqpCon linkSettings.AddProperty(AmqpProperty.ConsumerIdentifier, linkIdentifier); } + linkSettings.DesiredCapabilities ??= new Multiple(); + linkSettings.DesiredCapabilities.Add(AmqpProperty.GeoReplication); + if (trackLastEnqueuedEventProperties) { linkSettings.DesiredCapabilities ??= new Multiple(); @@ -807,6 +810,9 @@ protected virtual async Task CreateSendingLinkAsync(AmqpConnect linkSettings.AddProperty(AmqpProperty.Timeout, (uint)linkTimeout.CalculateRemaining(stopWatch.GetElapsedTime()).TotalMilliseconds); linkSettings.AddProperty(AmqpProperty.EntityType, (int)AmqpProperty.Entity.EventHub); + linkSettings.DesiredCapabilities ??= new Multiple(); + linkSettings.DesiredCapabilities.Add(AmqpProperty.GeoReplication); + if ((features & TransportProducerFeatures.IdempotentPublishing) != 0) { linkSettings.DesiredCapabilities ??= new Multiple(); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConsumer.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConsumer.cs index 82bfb0b0bd1ea..828d100860a1b 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConsumer.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpConsumer.cs @@ -297,9 +297,9 @@ public override async Task> ReceiveAsync(int maximumEve firstReceivedEvent = receivedEvents[0]; lastReceivedEvent = receivedEvents[receivedEventCount - 1]; - if (lastReceivedEvent.Offset > long.MinValue) + if (!string.IsNullOrEmpty(lastReceivedEvent.OffsetString)) { - CurrentEventPosition = EventPosition.FromOffset(lastReceivedEvent.Offset, false); + CurrentEventPosition = EventPosition.FromOffset(lastReceivedEvent.OffsetString, false); } if (TrackLastEnqueuedEventProperties) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpFilter.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpFilter.cs index a9fb19fd33c64..09b70af4c2b57 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpFilter.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpFilter.cs @@ -57,9 +57,9 @@ public static string BuildFilterExpression(EventPosition eventPosition) { // Build the filter expression, in the order of significance. - if (!string.IsNullOrEmpty(eventPosition.Offset)) + if (!string.IsNullOrEmpty(eventPosition.OffsetString)) { - return $"{ OffsetName } { (eventPosition.IsInclusive ? ">=" : ">") } { eventPosition.Offset }"; + return $"{ OffsetName } { (eventPosition.IsInclusive ? ">=" : ">") } { eventPosition.OffsetString }"; } if (!string.IsNullOrEmpty(eventPosition.SequenceNumber)) @@ -72,7 +72,7 @@ public static string BuildFilterExpression(EventPosition eventPosition) return $"{ EnqueuedTimeName } > { eventPosition.EnqueuedTime.Value.ToUnixTimeMilliseconds() }"; } - // If no filter was built, than the event position is not valid for filtering. + // If no filter was built, then the event position is not valid for filtering. throw new ArgumentException(Resources.InvalidEventPositionForFilter, nameof(eventPosition)); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpManagement.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpManagement.cs index 682fa3be3b393..7d8c65603d34b 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpManagement.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpManagement.cs @@ -74,6 +74,12 @@ public static class ResponseMap /// public static MapKey PartitionIdentifiers { get; } = new MapKey("partition_ids"); + /// + /// The message property that identifies whether this Event Hub has geo-replication enabled. + /// + /// + public static MapKey GeoReplicationFactor { get; } = new MapKey("georeplication_factor"); + /// /// The message property that identifies the beginning sequence number in a partition. /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpMessageConverter.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpMessageConverter.cs index c24eb13643348..d6c879b296a15 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpMessageConverter.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpMessageConverter.cs @@ -39,9 +39,7 @@ internal class AmqpMessageConverter private static readonly HashSet SystemPropertyLongKeys = new() { AmqpProperty.SequenceNumber.ToString(), - AmqpProperty.Offset.ToString(), AmqpProperty.PartitionLastEnqueuedSequenceNumber.ToString(), - AmqpProperty.PartitionLastEnqueuedOffset.ToString() }; /// @@ -181,10 +179,17 @@ public virtual EventHubProperties CreateEventHubPropertiesFromResponse(AmqpMessa throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidMessageBody, typeof(AmqpMap).Name)); } + var geoReplicationEnabled = responseData[AmqpManagement.ResponseMap.GeoReplicationFactor] switch + { + int count when count > 1 => true, + _ => false + }; + return new EventHubProperties( (string)responseData[AmqpManagement.ResponseMap.Name], new DateTimeOffset((DateTime)responseData[AmqpManagement.ResponseMap.CreatedAt], TimeSpan.Zero), - (string[])responseData[AmqpManagement.ResponseMap.PartitionIdentifiers]); + (string[])responseData[AmqpManagement.ResponseMap.PartitionIdentifiers], + geoReplicationEnabled); } /// @@ -250,7 +255,7 @@ public virtual PartitionProperties CreatePartitionPropertiesFromResponse(AmqpMes (bool)responseData[AmqpManagement.ResponseMap.PartitionRuntimeInfoPartitionIsEmpty], (long)responseData[AmqpManagement.ResponseMap.PartitionBeginSequenceNumber], (long)responseData[AmqpManagement.ResponseMap.PartitionLastEnqueuedSequenceNumber], - long.Parse((string)responseData[AmqpManagement.ResponseMap.PartitionLastEnqueuedOffset], NumberStyles.Integer, CultureInfo.InvariantCulture), + (string)responseData[AmqpManagement.ResponseMap.PartitionLastEnqueuedOffset], new DateTimeOffset((DateTime)responseData[AmqpManagement.ResponseMap.PartitionLastEnqueuedTimeUtc], TimeSpan.Zero)); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpProperty.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpProperty.cs index 5ac1bb58d36cb..6906dd509084a 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpProperty.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Amqp/AmqpProperty.cs @@ -43,6 +43,12 @@ internal static class AmqpProperty /// public static AmqpSymbol TrackLastEnqueuedEventProperties { get; } = AmqpConstants.Vendor + ":enable-receiver-runtime-metric"; + /// + /// The capability for geo replication in a namespace, to associate with a link. + /// + /// + public static AmqpSymbol GeoReplication { get; } = AmqpConstants.Vendor + ":georeplication"; + /// /// The capability for opting-into idempotent publishing. /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Azure.Messaging.EventHubs.csproj b/sdk/eventhub/Azure.Messaging.EventHubs/src/Azure.Messaging.EventHubs.csproj index 6c36318bcf203..3886bff09ec5c 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Azure.Messaging.EventHubs.csproj +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Azure.Messaging.EventHubs.csproj @@ -2,7 +2,7 @@ Azure Event Hubs is a highly scalable publish-subscribe service that can ingest millions of events per second and stream them to multiple consumers. This client library allows for both publishing and consuming events using Azure Event Hubs. For more information about Event Hubs, see https://azure.microsoft.com/en-us/services/event-hubs/ 5.12.0-beta.2 - + 5.11.4 Azure;Event Hubs;EventHubs;.NET;AMQP;IoT;$(PackageCommonTags) $(RequiredTargetFrameworks) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/EventPosition.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/EventPosition.cs index fe0d8731e57a7..75bdbc21510fc 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/EventPosition.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/EventPosition.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Globalization; using Azure.Core; +using Azure.Messaging.EventHubs.Core; namespace Azure.Messaging.EventHubs.Consumer { @@ -27,7 +28,7 @@ public struct EventPosition : IEquatable /// which has not expired due to the retention policy. /// /// - public static EventPosition Earliest { get; } = new EventPosition { Offset = StartOfStream, IsInclusive = false }; + public static EventPosition Earliest { get; } = new EventPosition { OffsetString = StartOfStream, IsInclusive = false }; /// /// Corresponds to the end of the partition, where no more events are currently enqueued. Use this @@ -35,7 +36,7 @@ public struct EventPosition : IEquatable /// consumer begins reading with this position. /// /// - public static EventPosition Latest { get; } = new EventPosition { Offset = EndOfStream, IsInclusive = false }; + public static EventPosition Latest { get; } = new EventPosition { OffsetString = EndOfStream, IsInclusive = false }; /// /// The offset of the event identified by this position. @@ -43,7 +44,7 @@ public struct EventPosition : IEquatable /// /// Expected to be null if the event position represents a sequence number or enqueue time. /// - internal string Offset { get; set; } + internal string OffsetString { get; set; } /// /// Indicates if the specified offset is inclusive of the event which it identifies. This @@ -71,6 +72,8 @@ public struct EventPosition : IEquatable internal string SequenceNumber { get; set; } /// + /// Obsolete. + /// /// Corresponds to a specific offset in the partition event stream. By default, if an event is located /// at that offset, it will be read. Setting to false will skip the /// event at that offset and begin reading at the next available event. @@ -81,12 +84,41 @@ public struct EventPosition : IEquatable /// /// The specified position of an event in the partition. /// + /// + /// This method is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetEventPositionObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static EventPosition FromOffset(long offset, bool isInclusive = true) { return new EventPosition { - Offset = offset.ToString(CultureInfo.InvariantCulture), + OffsetString = (offset != long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : StartOfStream, + IsInclusive = isInclusive + }; + } + + /// + /// Corresponds to a specific offset in the partition event stream. By default, if an event is located + /// at that offset, it will be read. Setting to false will skip the + /// event at that offset and begin reading at the next available event. + /// + /// + /// The offset of an event with respect to its relative position in the partition. + /// When true, the event with the is included; otherwise the next event in sequence will be read. + /// + /// The specified position of an event in the partition. + /// + public static EventPosition FromOffset(string offsetString, + bool isInclusive = true) + { + Argument.AssertNotNullOrEmpty(offsetString, nameof(offsetString)); + + return new EventPosition + { + OffsetString = offsetString, IsInclusive = isInclusive }; } @@ -139,7 +171,7 @@ public static EventPosition FromEnqueuedTime(DateTimeOffset enqueuedTime) /// public bool Equals(EventPosition other) { - return (Offset == other.Offset) + return (OffsetString == other.OffsetString) && (SequenceNumber == other.SequenceNumber) && (EnqueuedTime == other.EnqueuedTime) && (IsInclusive == other.IsInclusive); @@ -171,7 +203,7 @@ public override bool Equals(object obj) => public override int GetHashCode() { var hashCode = new HashCodeBuilder(); - hashCode.Add(Offset); + hashCode.Add(OffsetString); hashCode.Add(SequenceNumber); hashCode.Add(EnqueuedTime); hashCode.Add(IsInclusive); @@ -188,9 +220,9 @@ public override int GetHashCode() public override string ToString() => this switch { - _ when (Offset == StartOfStream) => nameof(Earliest), - _ when (Offset == EndOfStream) => nameof(Latest), - _ when (!string.IsNullOrEmpty(Offset)) => $"Offset: [{ Offset }] | Inclusive: [{ IsInclusive }]", + _ when (OffsetString == StartOfStream) => nameof(Earliest), + _ when (OffsetString == EndOfStream) => nameof(Latest), + _ when (!string.IsNullOrEmpty(OffsetString)) => $"Offset: [{ OffsetString }] | Inclusive: [{ IsInclusive }]", _ when (!string.IsNullOrEmpty(SequenceNumber)) => $"Sequence Number: [{ SequenceNumber }] | Inclusive: [{ IsInclusive }]", _ when (EnqueuedTime.HasValue) => $"Enqueued: [{ EnqueuedTime }]", _ => base.ToString() diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/LastEnqueuedEventProperties.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/LastEnqueuedEventProperties.cs index 8736b79f1761d..3cd80c40f4037 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/LastEnqueuedEventProperties.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Consumer/LastEnqueuedEventProperties.cs @@ -3,7 +3,9 @@ using System; using System.ComponentModel; +using System.Globalization; using Azure.Core; +using Azure.Messaging.EventHubs.Core; namespace Azure.Messaging.EventHubs.Consumer { @@ -19,11 +21,40 @@ public struct LastEnqueuedEventProperties : IEquatable + /// Obsolete. + /// A numeric representation of the offset of the last observed event to be enqueued in the partition. + /// + /// + /// + /// This value is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetPropertyObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public long? Offset + { + get + { + if (string.IsNullOrEmpty(OffsetString)) + { + return null; + } + + if (long.TryParse(OffsetString, out var value)) + { + return value; + } + + throw new NotSupportedException(Resources.LongOffsetOffsetUnsupported); + } + } + /// /// The offset of the last observed event to be enqueued in the partition. /// /// - public long? Offset { get; } + public string OffsetString { get; } /// /// The date and time, in UTC, that the last observed event was enqueued in the partition. @@ -38,6 +69,35 @@ public struct LastEnqueuedEventProperties : IEquatable + /// Obsolete. + /// + /// Initializes a new instance of the class. + /// + /// + /// The sequence number observed the last event to be enqueued in the partition. + /// The offset of the last event to be enqueued in the partition. + /// The date and time, in UTC, that the last event was enqueued in the partition. + /// The date and time, in UTC, that the information was last received. + /// + /// + /// This constructor is obsolete and should no longer be used. Please use the string-based offset overload instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public LastEnqueuedEventProperties(long? lastSequenceNumber, + long lastOffset, + DateTimeOffset? lastEnqueuedTime, + DateTimeOffset? lastReceivedTime) : this(lastSequenceNumber, + (lastOffset > long.MinValue) ? lastOffset.ToString(CultureInfo.InvariantCulture) : null, + lastEnqueuedTime, + lastReceivedTime) + { + } + + /// + /// Obsolete. + /// /// Initializes a new instance of the class. /// /// @@ -46,13 +106,38 @@ public struct LastEnqueuedEventProperties : IEquatableThe date and time, in UTC, that the last event was enqueued in the partition. /// The date and time, in UTC, that the information was last received. /// + /// + /// This constructor is obsolete and should no longer be used. Please use the string-based offset overload instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, DateTimeOffset? lastEnqueuedTime, + DateTimeOffset? lastReceivedTime) : this(lastSequenceNumber, + lastOffset?.ToString(CultureInfo.InvariantCulture), + lastEnqueuedTime, + lastReceivedTime) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The sequence number observed the last event to be enqueued in the partition. + /// The offset of the last event to be enqueued in the partition. + /// The date and time, in UTC, that the last event was enqueued in the partition. + /// The date and time, in UTC, that the information was last received. + /// + public LastEnqueuedEventProperties(long? lastSequenceNumber, + string lastOffsetString, + DateTimeOffset? lastEnqueuedTime, DateTimeOffset? lastReceivedTime) { SequenceNumber = lastSequenceNumber; - Offset = lastOffset; + OffsetString = lastOffsetString; EnqueuedTime = lastEnqueuedTime; LastReceivedTime = lastReceivedTime; } @@ -81,7 +166,7 @@ internal LastEnqueuedEventProperties(EventData sourceEvent) : /// public bool Equals(LastEnqueuedEventProperties other) { - return (Offset == other.Offset) + return (OffsetString == other.OffsetString) && (SequenceNumber == other.SequenceNumber) && (EnqueuedTime == other.EnqueuedTime) && (LastReceivedTime == other.LastReceivedTime); @@ -113,7 +198,7 @@ public override bool Equals(object obj) => public override int GetHashCode() { var hashCode = new HashCodeBuilder(); - hashCode.Add(Offset); + hashCode.Add(OffsetString); hashCode.Add(SequenceNumber); hashCode.Add(EnqueuedTime); hashCode.Add(LastReceivedTime); @@ -128,7 +213,7 @@ public override int GetHashCode() /// A that represents this instance. /// [EditorBrowsable(EditorBrowsableState.Never)] - public override string ToString() => $"Sequence: [{ SequenceNumber }] | Offset: [{ Offset }] | Enqueued: [{ EnqueuedTime }] | Last Received: [{ LastReceivedTime }]"; + public override string ToString() => $"Sequence: [{ SequenceNumber }] | Offset: [{ OffsetString }] | Enqueued: [{ EnqueuedTime }] | Last Received: [{ LastReceivedTime }]"; /// /// Determines whether the specified instances are equal to each other. diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs index 69bf941fca6cb..1c2509f5ae574 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.IO; using System.Text; using Azure.Core; @@ -11,6 +12,7 @@ using Azure.Core.Serialization; using Azure.Messaging.EventHubs.Amqp; using Azure.Messaging.EventHubs.Consumer; +using Azure.Messaging.EventHubs.Core; using Azure.Messaging.EventHubs.Producer; namespace Azure.Messaging.EventHubs @@ -283,16 +285,45 @@ public string CorrelationId /// public long SequenceNumber => _amqpMessage.GetSequenceNumber(long.MinValue); + /// + /// Obsolete. + /// A numeric representation of the offset of an event when it was received from the associated Event Hub partition. + /// + /// + /// + /// This value is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetPropertyObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public long Offset + { + get + { + if (string.IsNullOrEmpty(OffsetString)) + { + return long.MinValue; + } + + if (long.TryParse(OffsetString, out var value)) + { + return value; + } + + throw new NotSupportedException(Resources.LongOffsetOffsetUnsupported); + } + } + /// /// The offset of the event when it was received from the associated Event Hub partition. /// /// /// /// This value is read-only and will only be populated for events that have been read from Event Hubs. The default value - /// when not populated is . + /// when not populated is null. /// /// - public long Offset => _amqpMessage.GetOffset(long.MinValue); + public string OffsetString => _amqpMessage.GetOffset(null); /// /// The date and time, in UTC, of when the event was enqueued in the Event Hub partition. @@ -383,7 +414,7 @@ public string CorrelationId /// populated is null. /// /// - internal long? LastPartitionOffset => _amqpMessage.GetLastPartitionOffset(); + internal string LastPartitionOffset => _amqpMessage.GetLastPartitionOffset(); /// /// The date and time, in UTC, that the last event was enqueued into the Event Hub partition from @@ -553,11 +584,11 @@ internal EventData(BinaryData eventBody, IDictionary properties = null, IReadOnlyDictionary systemProperties = null, long? sequenceNumber = null, - long? offset = null, + string offset = null, DateTimeOffset? enqueuedTime = null, string partitionKey = null, long? lastPartitionSequenceNumber = null, - long? lastPartitionOffset = null, + string lastPartitionOffset = null, DateTimeOffset? lastPartitionEnqueuedTime = null, DateTimeOffset? lastPartitionPropertiesRetrievalTime = null, int? publishedSequenceNumber = null, @@ -603,19 +634,54 @@ internal EventData(BinaryData eventBody, /// The set of free-form event properties to send with the event. /// The set of system properties received from the Event Hubs service. /// The sequence number assigned to the event when it was enqueued in the associated Event Hub partition. + /// The offset of the event when it was received from the associated Event Hub partition. + /// The date and time, in UTC, of when the event was enqueued in the Event Hub partition. + /// The partition hashing key associated with the event when it was published. + /// + /// + /// This constructor exists only for backwards compatibility and has been replaced by . + /// It should no longer be called. + /// + /// This overload was previously intended for mocking in support of testing efforts. It is recommended that + /// it not be used in production scenarios, as it allows setting of data that is broker-owned and is only + /// meaningful on events that have been read from the Event Hubs service.SSS + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected EventData(BinaryData eventBody, + IDictionary properties = null, + IReadOnlyDictionary systemProperties = null, + long sequenceNumber = long.MinValue, + string offsetString = default, + DateTimeOffset enqueuedTime = default, + string partitionKey = null) : this(eventBody, properties, systemProperties, sequenceNumber, offsetString, enqueuedTime, partitionKey, lastPartitionSequenceNumber: null) + { + } + + /// + /// Obsolete. + /// + /// Initializes a new instance of the class. + /// + /// + /// The raw data to use as the body of the event. + /// The set of free-form event properties to send with the event. + /// The set of system properties received from the Event Hubs service. + /// The sequence number assigned to the event when it was enqueued in the associated Event Hub partition. /// The offset of the event when it was received from the associated Event Hub partition. /// The date and time, in UTC, of when the event was enqueued in the Event Hub partition. /// The partition hashing key associated with the event when it was published. /// /// - /// This constructor has been superseded by the factory method. - /// It is strongly recommended that the model factory be preferred over use of this constructor. + /// This constructor is obsolete and has been replaced by . + /// It should no longer be called. /// /// This overload was previously intended for mocking in support of testing efforts. It is recommended that /// it not be used in production scenarios, as it allows setting of data that is broker-owned and is only - /// meaningful on events that have been read from the Event Hubs service. + /// meaningful on events that have been read from the Event Hubs service.SSS /// /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] [EditorBrowsable(EditorBrowsableState.Never)] protected EventData(BinaryData eventBody, IDictionary properties = null, @@ -623,11 +689,13 @@ protected EventData(BinaryData eventBody, long sequenceNumber = long.MinValue, long offset = long.MinValue, DateTimeOffset enqueuedTime = default, - string partitionKey = null) : this(eventBody, properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey, lastPartitionSequenceNumber: null) + string partitionKey = null) : this(eventBody, properties, systemProperties, sequenceNumber, (offset > long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : null, enqueuedTime, partitionKey, lastPartitionSequenceNumber: null) { } /// + /// Obsolete. + /// /// Initializes a new instance of the class. /// /// @@ -640,14 +708,15 @@ protected EventData(BinaryData eventBody, /// The partition hashing key associated with the event when it was published. /// /// - /// This constructor has been superseded by the factory method. - /// It is strongly recommended that the model factory be preferred over use of this constructor. + /// This constructor is obsolete and has been replaced by . + /// It should no longer be called. /// /// This overload was previously intended for mocking in support of testing efforts. It is recommended that /// it not be used in production scenarios, as it allows setting of data that is broker-owned and is only /// meaningful on events that have been read from the Event Hubs service. /// /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete)] [EditorBrowsable(EditorBrowsableState.Never)] protected EventData(ReadOnlyMemory eventBody, IDictionary properties = null, @@ -655,7 +724,7 @@ protected EventData(ReadOnlyMemory eventBody, long sequenceNumber = long.MinValue, long offset = long.MinValue, DateTimeOffset enqueuedTime = default, - string partitionKey = null) : this(new BinaryData(eventBody), properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey, lastPartitionSequenceNumber: null) + string partitionKey = null) : this(new BinaryData(eventBody), properties, systemProperties, sequenceNumber, (offset > long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : null, enqueuedTime, partitionKey, lastPartitionSequenceNumber: null) { } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs index 14a132c29a27e..013b2a5f408aa 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs @@ -31,6 +31,12 @@ public class EventHubProperties /// public string[] PartitionIds { get; } + /// + /// A flag indicating whether or not the Event Hub has geo-replication enabled. + /// + /// + public bool IsGeoReplicationEnabled { get; } + /// /// Initializes a new instance of the class. /// @@ -38,14 +44,32 @@ public class EventHubProperties /// The name of the Event Hub. /// The date and time at which the Event Hub was created. /// The set of unique identifiers for each partition. + /// A flag indicating whether or not the Event Hub has geo-replication enabled. /// protected internal EventHubProperties(string name, DateTimeOffset createdOn, - string[] partitionIds) + string[] partitionIds, + bool isGeoReplicationEnabled) { Name = name; CreatedOn = createdOn; PartitionIds = partitionIds; + IsGeoReplicationEnabled = isGeoReplicationEnabled; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of the Event Hub. + /// The date and time at which the Event Hub was created. + /// The set of unique identifiers for each partition. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected EventHubProperties(string name, + DateTimeOffset createdOn, + string[] partitionIds) : this(name, createdOn, partitionIds, false) + { } /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs index b4abe69e6b1fc..956ddc3de9fc1 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs @@ -22,20 +22,39 @@ namespace Azure.Messaging.EventHubs public static class EventHubsModelFactory { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The name of the Event Hub. /// The date and time at which the Event Hub was created. /// The set of unique identifiers for each partition. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static EventHubProperties EventHubProperties(string name, DateTimeOffset createdOn, string[] partitionIds) => - new EventHubProperties(name, createdOn, partitionIds); + new EventHubProperties(name, createdOn, partitionIds, false); + + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of the Event Hub. + /// The date and time at which the Event Hub was created. + /// The set of unique identifiers for each partition. + /// A flag indicating whether or not the Event Hub has geo-replication enabled. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static EventHubProperties EventHubProperties(string name, + DateTimeOffset createdOn, + string[] partitionIds, + bool isGeoReplicationEnabled) => + new EventHubProperties(name, createdOn, partitionIds, isGeoReplicationEnabled); /// - /// Initializes a new instance of the class. + /// Obsolete. + /// + /// Initializes a new instance of the class. /// /// /// The name of the Event Hub that contains the partitions. @@ -46,6 +65,12 @@ public static EventHubProperties EventHubProperties(string name, /// The offset of the last event to be enqueued in the partition. /// The date and time, in UTC, that the last event was enqueued in the partition. /// + /// + /// This method is obsolete and should no longer be used. Please use the overload with a string-based offset instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static PartitionProperties PartitionProperties(string eventHubName, string partitionId, bool isEmpty, @@ -53,10 +78,31 @@ public static PartitionProperties PartitionProperties(string eventHubName, long lastSequenceNumber, long lastOffset, DateTimeOffset lastEnqueuedTime) => - new PartitionProperties(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, lastOffset, lastEnqueuedTime); + new PartitionProperties(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, (lastOffset > long.MinValue) ? lastOffset.ToString(CultureInfo.InvariantCulture) : null, lastEnqueuedTime); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + /// + /// The name of the Event Hub that contains the partitions. + /// The identifier of the partition. + /// Indicates whether or not the partition is currently empty. + /// The first sequence number available for events in the partition. + /// The sequence number observed the last event to be enqueued in the partition. + /// The offset of the last event to be enqueued in the partition. + /// The date and time, in UTC, that the last event was enqueued in the partition. + /// + public static PartitionProperties PartitionProperties(string eventHubName, + string partitionId, + bool isEmpty, + long beginningSequenceNumber, + long lastSequenceNumber, + string lastOffsetString, + DateTimeOffset lastEnqueuedTime) => + new PartitionProperties(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, lastOffsetString, lastEnqueuedTime); + + /// + /// Initializes a new instance of the class. /// /// /// Indicates whether idempotent publishing is enabled. @@ -71,8 +117,8 @@ public static PartitionPublishingProperties PartitionPublishingProperties(bool i int? lastPublishedSequenceNumber) => new PartitionPublishingProperties(isIdempotentPublishingEnabled, producerGroupId, ownerLevel, lastPublishedSequenceNumber); - /// - /// Initializes a new instance of the class. + /// + /// Initializes a new instance of the class. /// /// /// The sequence number observed the last event to be enqueued in the partition. @@ -80,11 +126,28 @@ public static PartitionPublishingProperties PartitionPublishingProperties(bool i /// The date and time, in UTC, that the last event was enqueued in the partition. /// The date and time, in UTC, that the information was last received. /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, DateTimeOffset? lastEnqueuedTime, DateTimeOffset? lastReceivedTime) => - new LastEnqueuedEventProperties(lastSequenceNumber, lastOffset, lastEnqueuedTime, lastReceivedTime); + new LastEnqueuedEventProperties(lastSequenceNumber, lastOffset?.ToString(CultureInfo.InvariantCulture), lastEnqueuedTime, lastReceivedTime); + + /// + /// Initializes a new instance of the class. + /// + /// + /// The sequence number observed the last event to be enqueued in the partition. + /// The offset of the last event to be enqueued in the partition. + /// The date and time, in UTC, that the last event was enqueued in the partition. + /// The date and time, in UTC, that the information was last received. + /// + public static LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, + string lastOffsetString, + DateTimeOffset? lastEnqueuedTime, + DateTimeOffset? lastReceivedTime) => + new LastEnqueuedEventProperties(lastSequenceNumber, lastOffsetString, lastEnqueuedTime, lastReceivedTime); /// /// Initializes a new instance of the class. @@ -116,7 +179,9 @@ public static PartitionContext PartitionContext(string partitionId, new FactoryPartitionContext("<< NULL >>", "<< NULL >>", "<< NULL >>", partitionId, lastEnqueuedEventProperties); /// - /// Initializes a new instance of the class. + /// Obsolete. + /// + /// Initializes a new instance of the class. /// /// /// The data to use as the body of the event. @@ -127,6 +192,12 @@ public static PartitionContext PartitionContext(string partitionId, /// The offset of the event when it was received from the associated Event Hub partition. /// The date and time, in UTC, of when the event was enqueued in the Event Hub partition. /// + /// + /// This method is obsolete and should no longer be used. Please use the overload with a string-based offset instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public static EventData EventData(BinaryData eventBody, IDictionary properties = null, IReadOnlyDictionary systemProperties = null, @@ -134,7 +205,28 @@ public static EventData EventData(BinaryData eventBody, long sequenceNumber = long.MinValue, long offset = long.MinValue, DateTimeOffset enqueuedTime = default) => - new EventData(eventBody, properties, systemProperties, sequenceNumber, offset, enqueuedTime, partitionKey); + EventData(eventBody, properties, systemProperties, partitionKey, sequenceNumber, (offset > long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : null, enqueuedTime); + + /// + /// Initializes a new instance of the class. + /// + /// + /// The data to use as the body of the event. + /// The set of free-form event properties to send with the event. + /// The set of system properties that accompany events read from the Event Hubs service. + /// The partition hashing key associated with the event when it was published. + /// The sequence number assigned to the event when it was enqueued in the associated Event Hub partition. + /// The offset of the event when it was received from the associated Event Hub partition. + /// The date and time, in UTC, of when the event was enqueued in the Event Hub partition. + /// + public static EventData EventData(BinaryData eventBody, + IDictionary properties = null, + IReadOnlyDictionary systemProperties = null, + string partitionKey = null, + long sequenceNumber = long.MinValue, + string offsetString = default, + DateTimeOffset enqueuedTime = default) => + new EventData(eventBody, properties, systemProperties, sequenceNumber, offsetString, enqueuedTime, partitionKey); /// /// Initializes a new instance of the class. diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/PartitionProperties.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/PartitionProperties.cs index 122cd4d023dde..6643be32592a9 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/PartitionProperties.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/PartitionProperties.cs @@ -3,6 +3,8 @@ using System; using System.ComponentModel; +using System.Globalization; +using Azure.Messaging.EventHubs.Core; namespace Azure.Messaging.EventHubs { @@ -41,7 +43,26 @@ public class PartitionProperties /// The offset of the last observed event to be enqueued in the partition. /// /// - public long LastEnqueuedOffset { get; } + [Obsolete(AttributeMessageText.LongLastEnqueuedOffsetOffsetObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public long LastEnqueuedOffset + { + get + { + if (long.TryParse(LastEnqueuedOffsetString, out var value)) + { + return value; + } + + throw new NotSupportedException(Resources.LongLastEnqueuedOffsetOffsetUnsupported); + } + } + + /// + /// The offset of the last observed event to be enqueued in the partition. + /// + /// + public string LastEnqueuedOffsetString { get; } /// /// The date and time, in UTC, that the last observed event was enqueued in the partition. @@ -58,6 +79,8 @@ public class PartitionProperties public bool IsEmpty { get; } /// + /// Obsolete. + /// /// Initializes a new instance of the class. /// /// @@ -69,19 +92,53 @@ public class PartitionProperties /// The offset of the last event to be enqueued in the partition. /// The date and time, in UTC, that the last event was enqueued in the partition. /// + /// + /// This constructor is obsolete and should no longer be used. Please use the string-based offset overload instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetParameterObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] protected internal PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, long lastOffset, + DateTimeOffset lastEnqueuedTime) : this(eventHubName, + partitionId, + isEmpty, + beginningSequenceNumber, + lastSequenceNumber, + (lastOffset > long.MinValue) ? lastOffset.ToString(CultureInfo.InvariantCulture) : null, + lastEnqueuedTime) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of the Event Hub that contains the partitions. + /// The identifier of the partition. + /// Indicates whether or not the partition is currently empty. + /// The first sequence number available for events in the partition. + /// The sequence number observed the last event to be enqueued in the partition. + /// The offset of the last event to be enqueued in the partition. + /// The date and time, in UTC, that the last event was enqueued in the partition. + /// + protected internal PartitionProperties(string eventHubName, + string partitionId, + bool isEmpty, + long beginningSequenceNumber, + long lastSequenceNumber, + string lastOffsetString, DateTimeOffset lastEnqueuedTime) { EventHubName = eventHubName; Id = partitionId; BeginningSequenceNumber = beginningSequenceNumber; LastEnqueuedSequenceNumber = lastSequenceNumber; - LastEnqueuedOffset = lastOffset; + LastEnqueuedOffsetString = lastOffsetString; LastEnqueuedTime = lastEnqueuedTime; IsEmpty = isEmpty; } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/CheckpointStore.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/CheckpointStore.cs index bdfb33b061e6c..aba12ce74d8e8 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/CheckpointStore.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/CheckpointStore.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Azure.Messaging.EventHubs.Core; using Azure.Messaging.EventHubs.Processor; namespace Azure.Messaging.EventHubs.Primitives @@ -70,6 +70,8 @@ public abstract Task GetCheckpointAsync(string fullyQu CancellationToken cancellationToken); /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// @@ -82,6 +84,12 @@ public abstract Task GetCheckpointAsync(string fullyQu /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// + /// + /// This method is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public virtual Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/EventProcessor.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/EventProcessor.cs index 80cfd4a2d43b5..d0d2e8650bd10 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/EventProcessor.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/EventProcessor.cs @@ -206,6 +206,12 @@ internal EventProcessorStatus Status /// protected EventHubsRetryPolicy RetryPolicy { get; } + /// + /// The properties associated with the Event Hub being read from. This value is updated in each load balancing cycle. + /// + /// + protected EventHubProperties EventHubProperties { get; private set; } + /// /// Indicates whether or not this event processor should instrument batch event processing calls with distributed tracing. /// Implementations that instrument event processing themselves should set this to false. @@ -829,9 +835,9 @@ async Task performProcessing() lastEvent = (eventBatch != null && eventBatch.Count > 0) ? eventBatch[eventBatch.Count - 1] : null; - if ((lastEvent != null) && (lastEvent.Offset != long.MinValue)) + if (!string.IsNullOrEmpty(lastEvent?.OffsetString)) { - startingPosition = EventPosition.FromOffset(lastEvent.Offset, false); + startingPosition = EventPosition.FromOffset(lastEvent.OffsetString, false); } // If event batches are successfully processed, then the need for forward progress is @@ -1088,6 +1094,8 @@ protected virtual async Task GetCheckpointAsync(string } /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// @@ -1098,22 +1106,17 @@ protected virtual async Task GetCheckpointAsync(string /// A instance to signal a request to cancel the operation. /// /// - /// This overload exists to preserve backwards compatibility; it is highly recommended that - /// be called instead. + /// This method is obsolete and should no longer be used. Please use + /// instead. /// /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] protected virtual Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, - CancellationToken cancellationToken) - { - if (sequenceNumber.HasValue) - { - return UpdateCheckpointAsync(partitionId, new CheckpointPosition(sequenceNumber.Value), cancellationToken); - } - - throw new NotImplementedException(); - } + CancellationToken cancellationToken) => + UpdateCheckpointAsync(partitionId, new CheckpointPosition((offset != long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : null, sequenceNumber.Value), cancellationToken); /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream @@ -1309,9 +1312,11 @@ protected virtual LastEnqueuedEventProperties ReadLastEnqueuedEventProperties(st /// /// The set of identifiers for the Event Hub partitions. /// - protected virtual async Task ListPartitionIdsAsync(EventHubConnection connection, - CancellationToken cancellationToken) => - await connection.GetPartitionIdsAsync(RetryPolicy, cancellationToken).ConfigureAwait(false); + protected virtual Task ListPartitionIdsAsync(EventHubConnection connection, + CancellationToken cancellationToken) + { + return Task.FromResult(EventHubProperties.PartitionIds); + } /// /// Allows reporting that a partition was stolen by another event consumer causing ownership @@ -1648,6 +1653,7 @@ private async Task RunProcessingAsync(CancellationToken cancellationToken) try { + EventHubProperties = await connection.GetPropertiesAsync(RetryPolicy, cancellationToken).ConfigureAwait(false); partitionIds = await ListPartitionIdsAsync(connection, cancellationToken).ConfigureAwait(false); } catch (Exception ex) when (ex.IsNotType()) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/PluggableCheckpointStoreEventProcessor.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/PluggableCheckpointStoreEventProcessor.cs index a99f56b4d82cb..571289924cf35 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/PluggableCheckpointStoreEventProcessor.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Primitives/PluggableCheckpointStoreEventProcessor.cs @@ -220,6 +220,8 @@ protected override Task GetCheckpointAsync(string part _checkpointStore.GetCheckpointAsync(FullyQualifiedNamespace, EventHubName, ConsumerGroup, partitionId, cancellationToken); /// + /// Obsolete. + /// /// Creates or updates a checkpoint for a specific partition, identifying a position in the partition's event stream /// that an event processor should begin reading from. /// @@ -229,6 +231,13 @@ protected override Task GetCheckpointAsync(string part /// The sequence number to associate with the checkpoint, indicating that a processor should begin reading from the next event in the stream. /// A instance to signal a request to cancel the operation. /// + /// + /// This method is obsolete and should no longer be used. Please use + /// instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetUpdateCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] protected override Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/Processor/CheckpointPosition.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/Processor/CheckpointPosition.cs index 43dbfe0b4a277..1db828bc6b074 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/Processor/CheckpointPosition.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/Processor/CheckpointPosition.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.Text; using Azure.Core; +using Azure.Messaging.EventHubs.Core; namespace Azure.Messaging.EventHubs.Processor { @@ -16,33 +18,107 @@ namespace Azure.Messaging.EventHubs.Processor public struct CheckpointPosition : IEquatable { /// - /// The sequence number to associate with the checkpoint. This indicates that a processor should begin reading from the next event in the stream. + /// The sequence number to associate with the checkpoint. The sequence number is intended to be informational, and will only be used for + /// positioning if no is set. /// /// public long SequenceNumber { get; } + /// + /// Obsolete. + /// A numeric representation of the offset to associate with the checkpoint. This indicates that a processor + /// should begin reading from the next event in the stream. + /// + /// + /// + /// This value is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetOffsetPropertyObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public long Offset + { + get + { + if (long.TryParse(OffsetString, out var value)) + { + return value; + } + + throw new NotSupportedException(Resources.LongOffsetOffsetUnsupported); + } + } + + /// + /// The offset to associate with the checkpoint. This indicates that a processor should begin reading from the next event in the stream. + /// + /// + public string OffsetString { get; } + /// /// Initializes a new instance of the struct. /// /// /// The sequence number to associate with the checkpoint. This indicates that a processor should begin reading from the next event in the stream. /// + /// + /// This constructor is not compatible when processing a geo-replicated Event Hub. Use or + /// instead. + /// + /// + [Obsolete(AttributeMessageText.SequenceNumberOnlyCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] public CheckpointPosition(long sequenceNumber) { SequenceNumber = sequenceNumber; } /// - /// Initializes a new instance of the from an instance. + /// Obsolete. + /// + /// Initializes a new instance of the struct. /// /// - /// The to use to determine the starting point of a checkpoint, indicating that an event processor should begin reading from the next event in the stream. + /// The offset to associate with the checkpoint. This indicates that a processor should begin reading from the next event in the stream. + /// The sequence number to associate with the checkpoint. This is used as informational metadata. /// - public static CheckpointPosition FromEvent(EventData eventData) + /// + /// This constructor is obsolete and should no longer be used. Please use instead. + /// + /// + [Obsolete(AttributeMessageText.LongOffsetCheckpointObsolete, false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public CheckpointPosition(long offset, + long sequenceNumber = long.MinValue) { - return new CheckpointPosition(eventData.SequenceNumber); + SequenceNumber = sequenceNumber; + OffsetString = (offset != long.MinValue) ? offset.ToString(CultureInfo.InvariantCulture) : null; } + /// + /// Initializes a new instance of the struct. + /// + /// The offset to associate with the checkpoint. This indicates that a processor should begin reading from the next event in the stream. + /// The sequence number to associate with the checkpoint. This is used as informational metadata. + /// + public CheckpointPosition(string offsetString, + long sequenceNumber = long.MinValue) + { + Argument.AssertNotNullOrEmpty(offsetString, nameof(offsetString)); + + SequenceNumber = sequenceNumber; + OffsetString = offsetString; + } + + /// + /// Initializes a new instance of the from an instance. + /// + /// + /// The to use to determine the starting point of a checkpoint, indicating that an event processor should begin reading from the next event in the stream. + /// + public static CheckpointPosition FromEvent(EventData eventData) => + new CheckpointPosition(eventData.OffsetString, eventData.SequenceNumber); + /// /// Determines whether the specified is equal to this instance. /// @@ -53,7 +129,8 @@ public static CheckpointPosition FromEvent(EventData eventData) /// public bool Equals(CheckpointPosition other) { - return (SequenceNumber == other.SequenceNumber); + return ((SequenceNumber == other.SequenceNumber) + && (OffsetString == other.OffsetString)); } /// @@ -83,6 +160,7 @@ public override int GetHashCode() { var hashCode = new HashCodeBuilder(); hashCode.Add(SequenceNumber); + hashCode.Add(OffsetString); return hashCode.ToHashCode(); } @@ -95,7 +173,7 @@ public override int GetHashCode() /// public override string ToString() { - return $"Sequence Number: [{SequenceNumber}]"; + return $"Offset: [{OffsetString}] Sequence Number: [{SequenceNumber}]"; } /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpAnnotatedMessageExtensionsTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpAnnotatedMessageExtensionsTests.cs index 9c64cadd26b88..8dc0d83bd4456 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpAnnotatedMessageExtensionsTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpAnnotatedMessageExtensionsTests.cs @@ -295,11 +295,11 @@ public void GetEventBodyDoesNotAllowNonDataBodyTypes(AmqpMessageBodyType bodyTyp public void SystemPropertiesCanBeRead() { var sequenceNumber = 123L; - var offset = 456L; + var offset = "456L"; var enqueueTime = new DateTimeOffset(2015, 10, 27, 00, 00, 00, TimeSpan.Zero); var partitionKey = "fake-key"; var lastSequence = 321L; - var lastOffset = 654L; + var lastOffset = "654L"; var lastEnqueue = new DateTimeOffset(2012, 03, 04, 08, 00, 00, TimeSpan.Zero); var lastRetrieve = new DateTimeOffset(2020, 01, 01, 05, 15, 37, TimeSpan.Zero); var message = CreateDataBodyMessageWithSystemProperties(sequenceNumber, lastSequence, offset, lastOffset, partitionKey, enqueueTime, lastEnqueue, lastRetrieve); @@ -330,11 +330,11 @@ public void MessageIsPopulatedFromEventHubProperties() }; var sequenceNumber = 123L; - var offset = 456L; + var offset = "456L"; var enqueueTime = new DateTimeOffset(2015, 10, 27, 00, 00, 00, TimeSpan.Zero); var partitionKey = "fake-key"; var lastSequence = 321L; - var lastOffset = 654L; + var lastOffset = "654L"; var lastEnqueue = new DateTimeOffset(2012, 03, 04, 08, 00, 00, TimeSpan.Zero); var lastRetrieve = new DateTimeOffset(2020, 01, 01, 05, 15, 37, TimeSpan.Zero); @@ -361,11 +361,11 @@ public void MessageIsPopulatedFromEventHubProperties() public void SystemPropertiesReturnCustomDefaultValuesWhenNotInTheMessage() { var sequenceNumber = 123L; - var offset = 456L; + var offset = "456L"; var enqueueTime = new DateTimeOffset(2015, 10, 27, 00, 00, 00, TimeSpan.Zero); var partitionKey = "fake-key"; var lastSequence = 321L; - var lastOffset = 654L; + var lastOffset = "654L"; var lastEnqueue = new DateTimeOffset(2012, 03, 04, 08, 00, 00, TimeSpan.Zero); var lastRetrieve = new DateTimeOffset(2020, 01, 01, 05, 15, 37, TimeSpan.Zero); var message = CreateDataBodyMessageWithSystemProperties(default, default, default, default, default, default, default, default); @@ -391,7 +391,7 @@ public void SystemPropertiesReturnExpectedDefaultValuesWhenNotInTheMessageAndNoC var message = CreateDataBodyMessageWithSystemProperties(default, default, default, default, default, default, default, default); Assert.That(message.GetSequenceNumber(), Is.EqualTo(long.MinValue), "The sequence number should match."); - Assert.That(message.GetOffset(), Is.EqualTo(long.MinValue), "The offset should match."); + Assert.That(message.GetOffset(), Is.EqualTo(null), "The offset should match."); Assert.That(message.GetEnqueuedTime(), Is.EqualTo(default(DateTimeOffset)), "The enqueue time should match."); Assert.That(message.GetPartitionKey(), Is.EqualTo(null), "The partition key should match."); Assert.That(message.GetLastPartitionSequenceNumber(), Is.EqualTo(null), "The last sequence number should match."); @@ -510,8 +510,8 @@ private static AmqpAnnotatedMessage CreateFullyPopulatedDataBodyMessage() /// private static AmqpAnnotatedMessage CreateDataBodyMessageWithSystemProperties(long? sequenceNumber, long? lastSequenceNumber, - long? offset, - long? lastOffset, + string offset, + string lastOffset, string partitionKey, DateTimeOffset? enqueueTime, DateTimeOffset? lastEnqueueTime, @@ -526,9 +526,9 @@ private static AmqpAnnotatedMessage CreateDataBodyMessageWithSystemProperties(lo message.MessageAnnotations.Add(AmqpProperty.SequenceNumber.ToString(), sequenceNumber.Value); } - if (offset.HasValue) + if (!string.IsNullOrEmpty(offset)) { - message.MessageAnnotations.Add(AmqpProperty.Offset.ToString(), offset.Value); + message.MessageAnnotations.Add(AmqpProperty.Offset.ToString(), offset); } if (enqueueTime.HasValue) @@ -550,9 +550,9 @@ private static AmqpAnnotatedMessage CreateDataBodyMessageWithSystemProperties(lo message.DeliveryAnnotations.Add(AmqpProperty.PartitionLastEnqueuedSequenceNumber.ToString(), lastSequenceNumber.Value); } - if (lastOffset.HasValue) + if (!string.IsNullOrEmpty(lastOffset)) { - message.DeliveryAnnotations.Add(AmqpProperty.PartitionLastEnqueuedOffset.ToString(), lastOffset.Value); + message.DeliveryAnnotations.Add(AmqpProperty.PartitionLastEnqueuedOffset.ToString(), lastOffset); } if (lastEnqueueTime.HasValue) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConnectionScopeTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConnectionScopeTests.cs index 7dcc2d3559872..b2c90be18137d 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConnectionScopeTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConnectionScopeTests.cs @@ -778,7 +778,7 @@ public async Task OpenConsumerLinkAsyncRespectsTheTrackLastEventOption() var link = await mockScope.Object.OpenConsumerLinkAsync(consumerGroup, partitionId, position, TimeSpan.FromDays(1), TimeSpan.FromDays(1), prefetchCount, prefetchSizeInBytes, ownerLevel, trackLastEvent, identifier, cancellationSource.Token); Assert.That(link, Is.Not.Null, "The link produced was null"); - Assert.That(link.Settings.DesiredCapabilities, Is.Null, "There should have not have been a set of desired capabilities created, as we're not tracking the last event."); + Assert.That(link.Settings.DesiredCapabilities.Contains(AmqpProperty.GeoReplication), Is.True, "Geo replication should always be requested."); } /// @@ -1292,6 +1292,7 @@ public async Task OpenProducerLinkAsyncConfiguresTheLink() Assert.That(link.Settings.Properties.Any(item => item.Key.Key.ToString() == AmqpProperty.EntityType.ToString()), Is.True, "There should be an entity type specified."); Assert.That(link.Settings.Properties[AmqpProperty.ProducerGroupId], Is.EqualTo(options.ProducerGroupId), "The producer group should have been set."); Assert.That(link.Settings.Properties[AmqpProperty.ProducerOwnerLevel], Is.EqualTo(options.OwnerLevel), "The owner level should have been set."); + Assert.That(link.Settings.DesiredCapabilities.Contains(AmqpProperty.GeoReplication), Is.True, "Geo replication should always be requested."); Assert.That(link.Settings.Properties[AmqpProperty.ProducerSequenceNumber], Is.EqualTo(options.StartingSequenceNumber), "The published sequence number should have been set."); } @@ -1435,6 +1436,7 @@ public async Task OpenProducerLinkAsyncConfiguresTheLinkWhenOptionsAreEmpty(obje var linkTarget = (Target)link.Settings.Target; Assert.That(linkTarget.Address.ToString(), Contains.Substring($"/{ partitionId }"), "The partition identifier should have been part of the link address."); Assert.That(link.Settings.DesiredCapabilities.Contains(AmqpProperty.EnableIdempotentPublishing), Is.True, "The idempotent publishing capability should have been set."); + Assert.That(link.Settings.DesiredCapabilities.Contains(AmqpProperty.GeoReplication), Is.True, "Geo replication should always be requested."); Assert.That(link.Settings.Properties.Any(item => item.Key.Key.ToString() == AmqpProperty.EntityType.ToString()), Is.True, "There should be an entity type specified."); Assert.That(link.Settings.Properties.Any(item => item.Key.Key.ToString() == AmqpProperty.ProducerGroupId.ToString()), Is.True, "The producer group should have been set."); Assert.That(link.Settings.Properties.Any(item => item.Key.Key.ToString() == AmqpProperty.ProducerOwnerLevel.ToString()), Is.True, "The owner level should have been set."); @@ -1513,7 +1515,7 @@ public async Task OpenProducerLinkAsyncConfiguresTheLinkWhenNoFeaturesAreActive( var linkTarget = (Target)link.Settings.Target; Assert.That(linkTarget.Address.ToString(), Contains.Substring($"/{ partitionId }"), "The partition identifier should have been part of the link address."); - Assert.That(link.Settings.DesiredCapabilities, Is.Null, "The idempotent publishing capability should not have been set."); + Assert.That(link.Settings.DesiredCapabilities.Contains(AmqpProperty.GeoReplication), Is.True, "Geo replication should always be requested."); Assert.That(link.Settings.Properties.Any(item => item.Key.Key.ToString() == AmqpProperty.EntityType.ToString()), Is.True, "There should be an entity type specified."); Assert.That(link.Settings.Properties[AmqpProperty.ProducerGroupId], Is.EqualTo(options.ProducerGroupId), "The producer group should have been set."); Assert.That(link.Settings.Properties[AmqpProperty.ProducerOwnerLevel], Is.EqualTo(options.OwnerLevel), "The owner level should have been set."); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConsumerTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConsumerTests.cs index c32237bdfdcba..8128f95cbe332 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConsumerTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpConsumerTests.cs @@ -162,7 +162,7 @@ public void ReceiveAsyncValidatesTheMaximumMessageCount(int count) var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var retryPolicy = new BasicRetryPolicy(new EventHubsRetryOptions()); var retriableException = new EventHubsException(true, "Test"); var mockConverter = new Mock(); @@ -187,7 +187,7 @@ public void ReceiveAsyncRespectsTheCancellationTokenIfSetWhenCalled() var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var retryPolicy = new BasicRetryPolicy(new EventHubsRetryOptions()); var retriableException = new EventHubsException(true, "Test"); var mockConverter = new Mock(); @@ -215,7 +215,7 @@ public void ReceiveAsyncAppliesTheRetryPolicy(EventHubsRetryOptions retryOptions var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var trackLastEnqueued = false; var invalidateOnSteal = true; var ownerLevel = 123L; @@ -279,7 +279,7 @@ public void ReceiveAsyncConsidersOperationCanceledExceptionAsRetriable(EventHubs var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var trackLastEnqueued = false; var invalidateOnSteal = true; var ownerLevel = 123L; @@ -344,7 +344,7 @@ public void ReceiveAsyncAppliesTheRetryPolicyForAmqpErrors(EventHubsRetryOptions var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var trackLastEnqueued = false; var invalidateOnSteal = true; var ownerLevel = 123L; @@ -407,7 +407,7 @@ public void ReceiveAsyncDetectsAnEmbeddedErrorForOperationCanceled() var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var trackLastEnqueued = false; var invalidateOnSteal = true; var ownerLevel = 123L; @@ -470,7 +470,7 @@ public void ReceiveAsyncDetectsAnEmbeddedAmqpErrorForOperationCanceled() var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var trackLastEnqueued = false; var invalidateOnSteal = true; var ownerLevel = 123L; @@ -533,7 +533,7 @@ public async Task ReceiveAsyncValidatesClosed() var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var options = new EventHubConsumerClientOptions(); var retryPolicy = new BasicRetryPolicy(new EventHubsRetryOptions()); var retriableException = new EventHubsException(true, "Test"); @@ -562,7 +562,7 @@ public void ReceiveAsyncValidatesConnectionClosed() var consumerGroup = "$DEFAULT"; var partition = "3"; var identifier = "cusTOM-1D"; - var eventPosition = EventPosition.FromOffset(123); + var eventPosition = EventPosition.FromOffset("123"); var options = new EventHubConsumerClientOptions(); var retryPolicy = new BasicRetryPolicy(new EventHubsRetryOptions()); var mockCredential = new EventHubTokenCredential(Mock.Of()); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpFilterTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpFilterTests.cs index 1d7223e9aaf2b..91f5147d76275 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpFilterTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpFilterTests.cs @@ -26,8 +26,8 @@ public void BuildFilterExpressionEnsuresAnEventPositionIsFilterable() { // Unset all properties for the event position. - var position = EventPosition.FromOffset(1); - position.Offset = null; + var position = EventPosition.FromOffset("1"); + position.OffsetString = null; Assert.That(() => AmqpFilter.BuildFilterExpression(position), Throws.ArgumentException); } @@ -42,7 +42,7 @@ public void BuildFilterExpressionPrefersOffset() { // Set all properties for the event position. - var offset = 1; + var offset = "1"; var position = EventPosition.FromOffset(offset); position.SequenceNumber = "222"; position.EnqueuedTime = DateTimeOffset.Parse("2015-10-27T12:00:00Z"); @@ -122,7 +122,7 @@ public void BuildFilterExpressionAllowsLatest() public void BuildFilterExpressionHonorsInclusiveFlagForOffset(bool inclusive) { var comparison = (inclusive) ? ">=" : ">"; - var position = EventPosition.FromOffset(1); + var position = EventPosition.FromOffset("1"); position.IsInclusive = inclusive; var filter = AmqpFilter.BuildFilterExpression(position); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpMessageConverterTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpMessageConverterTests.cs index 6acfccfec21f8..4883d4a9b1c7c 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpMessageConverterTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Amqp/AmqpMessageConverterTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// Ignore Spelling: Accessor + using System; using System.Collections.Generic; using System.IO; @@ -1214,7 +1216,7 @@ public void CreateEventFromMessageDoesNotIncludeUnknownApplicationPropertyType() [Test] public void CreateEventFromMessagePopulatesTypedSystemProperties() { - var offset = 123; + var offset = "123"; var sequenceNumber = (long.MaxValue - 10); var enqueuedTime = DateTimeOffset.Parse("2015-10-27T12:00:00Z"); var partitionKey = "OMG! partition!"; @@ -1225,7 +1227,7 @@ public void CreateEventFromMessagePopulatesTypedSystemProperties() message.ApplicationProperties.Map.Add("First", 1); message.ApplicationProperties.Map.Add("Second", "2"); - message.MessageAnnotations.Map.Add(AmqpProperty.Offset, offset.ToString()); + message.MessageAnnotations.Map.Add(AmqpProperty.Offset, offset); message.MessageAnnotations.Map.Add(AmqpProperty.SequenceNumber, sequenceNumber); message.MessageAnnotations.Map.Add(AmqpProperty.EnqueuedTime, enqueuedTime.Ticks); message.MessageAnnotations.Map.Add(AmqpProperty.PartitionKey, partitionKey); @@ -1236,11 +1238,11 @@ public void CreateEventFromMessagePopulatesTypedSystemProperties() Assert.That(eventData, Is.Not.Null, "The event should have been created."); Assert.That(eventData.EventBody, Is.Not.Null, "The event should have a body."); Assert.That(eventData.Properties.Count, Is.EqualTo(message.ApplicationProperties.Map.Count()), "The event should have a set of properties."); - Assert.That(eventData.Offset, Is.EqualTo(offset), "The offset should match."); + Assert.That(eventData.OffsetString, Is.EqualTo(offset), "The offset should match."); Assert.That(eventData.SequenceNumber, Is.EqualTo(sequenceNumber), "The sequence number should match."); Assert.That(eventData.EnqueuedTime, Is.EqualTo(enqueuedTime), "The enqueue time should match."); Assert.That(eventData.PartitionKey, Is.EqualTo(partitionKey), "The partition key should match."); - Assert.That(eventData.LastPartitionOffset.HasValue, Is.False, "The last offset should not be set."); + Assert.That(eventData.LastPartitionOffset, Is.Null, "The last offset should not be set."); Assert.That(eventData.LastPartitionSequenceNumber.HasValue, Is.False, "The last sequence number should not be set."); Assert.That(eventData.LastPartitionEnqueuedTime.HasValue, Is.False, "The last enqueued time should not be set."); Assert.That(eventData.LastPartitionPropertiesRetrievalTime.HasValue, Is.False, "The last retrieval time should not be set."); @@ -1291,8 +1293,8 @@ public void CreateEventFromMessagePopulatesMappedSystemProperties() [Test] public void CreateEventFromMessagePopulatesTypedSystemPropertiesAndMetrics() { - var offset = 123; - var lastOffset = 987; + string offset = "123"; + var lastOffset = "987"; var sequenceNumber = (long.MaxValue - 10); var lastSequenceNumber = (long.MaxValue - 100); var enqueuedTime = DateTimeOffset.Parse("2015-10-27T12:00:00Z"); @@ -1322,7 +1324,7 @@ public void CreateEventFromMessagePopulatesTypedSystemPropertiesAndMetrics() Assert.That(eventData, Is.Not.Null, "The event should have been created."); Assert.That(eventData.EventBody, Is.Not.Null, "The event should have a body."); Assert.That(eventData.Properties.Count, Is.EqualTo(message.ApplicationProperties.Map.Count()), "The event should have a set of properties."); - Assert.That(eventData.Offset, Is.EqualTo(offset), "The offset should match."); + Assert.That(eventData.OffsetString, Is.EqualTo(offset), "The offset should match."); Assert.That(eventData.SequenceNumber, Is.EqualTo(sequenceNumber), "The sequence number should match."); Assert.That(eventData.EnqueuedTime, Is.EqualTo(enqueuedTime), "The enqueue time should match."); Assert.That(eventData.PartitionKey, Is.EqualTo(partitionKey), "The partition key should match."); @@ -1564,17 +1566,17 @@ void validateMap(AmqpMap expected, IDictionary dictionary, strin [Test] public void CreateEventFromMessageAllowsAnEmptyMessageWithProperties() { - var propertyValue = 1; + var propertyValue = "1"; using var message = AmqpMessage.Create(); message.ApplicationProperties.Map.Add("Test", propertyValue); - message.MessageAnnotations.Map.Add(AmqpProperty.Offset, propertyValue.ToString()); + message.MessageAnnotations.Map.Add(AmqpProperty.Offset, propertyValue); var eventData = new AmqpMessageConverter().CreateEventFromMessage(message); Assert.That(eventData, Is.Not.Null, "The event should have been created."); Assert.That(eventData.Properties.Count, Is.EqualTo(message.ApplicationProperties.Map.Count()), "There should have been properties present."); Assert.That(eventData.Properties.First().Value, Is.EqualTo(propertyValue), "The application property should have been populated."); - Assert.That(eventData.Offset, Is.EqualTo(propertyValue), "The offset should have been populated."); + Assert.That(eventData.OffsetString, Is.EqualTo(propertyValue), "The offset should have been populated."); } /// @@ -2046,7 +2048,7 @@ public void CreatePartitionPropertiesFromResponseCreatesTheProperties() var partition = "55"; var beginSequenceNumber = 555L; var lastSequenceNumber = 666L; - var lastOffset = 777L; + var lastOffset = "777L"; var lastEnqueueTime = DateTimeOffset.Parse("2015-10-27T00:00:00z"); var isEmpty = false; var converter = new AmqpMessageConverter(); @@ -2056,7 +2058,7 @@ public void CreatePartitionPropertiesFromResponseCreatesTheProperties() { AmqpManagement.ResponseMap.PartitionIdentifier, partition }, { AmqpManagement.ResponseMap.PartitionBeginSequenceNumber, beginSequenceNumber }, { AmqpManagement.ResponseMap.PartitionLastEnqueuedSequenceNumber, lastSequenceNumber }, - { AmqpManagement.ResponseMap.PartitionLastEnqueuedOffset, lastOffset.ToString() }, + { AmqpManagement.ResponseMap.PartitionLastEnqueuedOffset, lastOffset }, { AmqpManagement.ResponseMap.PartitionLastEnqueuedTimeUtc, lastEnqueueTime.UtcDateTime }, { AmqpManagement.ResponseMap.PartitionRuntimeInfoPartitionIsEmpty, isEmpty } }; @@ -2069,7 +2071,7 @@ public void CreatePartitionPropertiesFromResponseCreatesTheProperties() Assert.That(properties.Id, Is.EqualTo(partition), "The partition should match"); Assert.That(properties.BeginningSequenceNumber, Is.EqualTo(beginSequenceNumber), "The beginning sequence number should match"); Assert.That(properties.LastEnqueuedSequenceNumber, Is.EqualTo(lastSequenceNumber), "The last sequence number should match"); - Assert.That(properties.LastEnqueuedOffset, Is.EqualTo(lastOffset), "The offset should match"); + Assert.That(properties.LastEnqueuedOffsetString, Is.EqualTo(lastOffset), "The offset should match"); Assert.That(properties.LastEnqueuedTime, Is.EqualTo(lastEnqueueTime), "The last enqueued time should match"); Assert.That(properties.IsEmpty, Is.EqualTo(isEmpty), "The empty flag should match"); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionLiveTests.cs index bd9bb804f9228..16f87c1bb537f 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionLiveTests.cs @@ -246,7 +246,7 @@ public async Task ConnectionTransportCanRetrievePartitionProperties(EventHubsTra Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match."); Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated."); Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated."); - Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); + Assert.That(partitionProperties.LastEnqueuedOffsetString, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionTests.cs index c8560b0aa7e6c..c401886436d53 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Connection/EventHubConnectionTests.cs @@ -746,7 +746,7 @@ public async Task GetPartitionIdsAsyncDelegatesToGetProperties() { var date = DateTimeOffset.Parse("2015-10-27T12:00:00Z"); var partitionIds = new[] { "first", "second", "third" }; - var properties = new EventHubProperties("dummy", date, partitionIds); + var properties = new EventHubProperties("dummy", date, partitionIds, false); var mockConnection = new Mock { CallBase = true }; mockConnection @@ -835,7 +835,7 @@ public void CreateConsumerInvokesTheTransportClient() { var transportClient = new ObservableTransportClientMock(); var connection = new InjectableTransportClientMock(transportClient, "Endpoint=sb://not-real.servicebus.windows.net/;SharedAccessKeyName=DummyKey;SharedAccessKey=[not_real];EntityPath=fake"); - var expectedPosition = EventPosition.FromOffset(65); + var expectedPosition = EventPosition.FromOffset("65"); var expectedPartition = "2123"; var expectedIdentifier = "OMG-ID"; var expectedConsumerGroup = EventHubConsumerClient.DefaultConsumerGroupName; @@ -851,7 +851,7 @@ public void CreateConsumerInvokesTheTransportClient() Assert.That(actualPartition, Is.EqualTo(expectedPartition), "The partition should have been passed."); Assert.That(actualConsumerGroup, Is.EqualTo(expectedConsumerGroup), "The consumer group should match."); Assert.That(actualIdentifier, Is.EqualTo(expectedIdentifier), "The identifier should match."); - Assert.That(actualPosition.Offset, Is.EqualTo(expectedPosition.Offset), "The event position to receive should match."); + Assert.That(actualPosition.OffsetString, Is.EqualTo(expectedPosition.OffsetString), "The event position to receive should match."); Assert.That(actualRetry, Is.SameAs(expectedRetryPolicy), "The retryPolicy should match."); Assert.That(actualOwnerLevel, Is.EqualTo(expectedOwnerLevel), "The owner levels should match."); Assert.That(actualPrefetch, Is.EqualTo(expectedPrefetch), "The prefetch counts should match."); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientLiveTests.cs index b322cbc6777d3..b3ed9df576edd 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientLiveTests.cs @@ -856,7 +856,7 @@ public async Task ConsumerCanReadFromOffset(bool isInclusive) // Query the partition and determine the offset of the last enqueued event, then send the new set // of events that should appear after the starting position. - var lastOffset = (await consumer.GetPartitionPropertiesAsync(partition, cancellationSource.Token)).LastEnqueuedOffset; + var lastOffset = (await consumer.GetPartitionPropertiesAsync(partition, cancellationSource.Token)).LastEnqueuedOffsetString; var startingPosition = EventPosition.FromOffset(lastOffset, isInclusive); await SendEventsAsync(scope.EventHubName, sourceEvents, new CreateBatchOptions { PartitionId = partition }, cancellationSource.Token); @@ -876,7 +876,7 @@ public async Task ConsumerCanReadFromOffset(bool isInclusive) Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled."); Assert.That(readState.Events.Count, Is.EqualTo(expectedCount), "The wrong number of events was read for the value of the inclusive flag."); - Assert.That(readState.Events.Values.Any(readEvent => readEvent.Data.Offset == lastOffset), Is.EqualTo(isInclusive), $"The event with offset [{ lastOffset }] was { ((isInclusive) ? "not" : "") } in the set of read events, which is inconsistent with the inclusive flag."); + Assert.That(readState.Events.Values.Any(readEvent => readEvent.Data.OffsetString == lastOffset), Is.EqualTo(isInclusive), $"The event with offset [{ lastOffset }] was { ((isInclusive) ? "not" : "") } in the set of read events, which is inconsistent with the inclusive flag."); foreach (var sourceEvent in sourceEvents) { @@ -2099,7 +2099,7 @@ public async Task ConsumerCanQueryPartitionProperties(EventHubsTransportType tra Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match."); Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated."); Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated."); - Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); + Assert.That(partitionProperties.LastEnqueuedOffsetString, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientTests.cs index d9600187c2e8b..a1d5b8c134526 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventHubConsumerClientTests.cs @@ -684,7 +684,7 @@ public async Task ReadEventsFromPartitionAsyncWithNoOptionsReturnsAnEnumerable() var mockConnection = new MockConnection(() => transportConsumer); var consumer = new EventHubConsumerClient(EventHubConsumerClient.DefaultConsumerGroupName, mockConnection); - IAsyncEnumerable enumerable = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12)); + IAsyncEnumerable enumerable = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12")); Assert.That(enumerable, Is.Not.Null, "An enumerable should have been returned."); Assert.That(enumerable, Is.InstanceOf>(), "The enumerable should be of the correct type."); @@ -709,7 +709,7 @@ public async Task ReadEventsFromPartitionAsyncWithOptionsReturnsAnEnumerable() var consumer = new EventHubConsumerClient(EventHubConsumerClient.DefaultConsumerGroupName, mockConnection); var options = new ReadEventOptions { MaximumWaitTime = TimeSpan.FromMilliseconds(25) }; - var enumerable = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), options); + var enumerable = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), options); Assert.That(enumerable, Is.Not.Null, "An enumerable should have been returned."); Assert.That(enumerable, Is.InstanceOf>(), "The enumerable should be of the correct type."); @@ -734,7 +734,7 @@ public async Task ReadEventsFromPartitionAsyncRespectsTheCacheEventCount() var consumer = new EventHubConsumerClient(EventHubConsumerClient.DefaultConsumerGroupName, mockConnection); var options = new ReadEventOptions { MaximumWaitTime = TimeSpan.FromMilliseconds(25), CacheEventCount = 999 }; - await using var enumerator = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), options).GetAsyncEnumerator(); + await using var enumerator = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), options).GetAsyncEnumerator(); for (var index = 0; index < 5; ++index) { @@ -776,7 +776,7 @@ public async Task ReadEventsFromPartitionAsyncRespectsThePrefetchCount() It.IsAny())) .Returns(transportConsumer); - await using var enumerator = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), options).GetAsyncEnumerator(); + await using var enumerator = consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), options).GetAsyncEnumerator(); await enumerator.MoveNextAsync(); mockConnection @@ -824,7 +824,7 @@ public void ReadEventsFromPartitionAsyncThrowsIfConsumerClosedBeforeRead() { await consumer.CloseAsync(cancellationSource.Token); - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellationSource.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellationSource.Token)) { receivedEvents = true; break; @@ -862,7 +862,7 @@ public void ReadEventsFromPartitionAsyncThrowsIfCanceledBeforeRead() Assert.That(async () => { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { if (partitionEvent.Data == null) { @@ -905,7 +905,7 @@ public void ReadEventsFromPartitionAsyncThrowsIfCanceledDuringRead() Assert.That(async () => { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { if (++receivedEvents >= expectedEvents) { @@ -946,7 +946,7 @@ public void ReadEventsFromPartitionAsyncDoesNotThrowIfNotCanceled() Assert.That(async () => { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { if (++receivedEvents >= expectedEvents) { @@ -983,7 +983,7 @@ public async Task ReadEventsFromPartitionAsyncStopsReceivingWhenCanceled() try { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { if (++receivedEvents >= expectedEvents) { @@ -1024,7 +1024,7 @@ public async Task ReadEventsFromPartitionAsyncStopsReceivingWhenIterationStops() try { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { if (++receivedEvents >= expectedEvents) { @@ -1075,7 +1075,7 @@ public async Task ReadEventsFromPartitionAsyncStopsReceivingOnException() try { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { ++receivedEvents; } @@ -1114,7 +1114,7 @@ public async Task ReadEventsFromPartitionAsyncEmitsEventsToOneIteratorIteratorAn using var cancellation = new CancellationTokenSource(); cancellation.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), cancellation.Token)) { receivedEvents.Add(partitionEvent.Data); @@ -1147,7 +1147,7 @@ public async Task ReadEventsFromPartitionAsyncEmitsEventsWithMultipleIteratorsAn var options = new ReadEventOptions { MaximumWaitTime = TimeSpan.FromMilliseconds(5) }; var partition = "0"; - var position = EventPosition.FromOffset(22); + var position = EventPosition.FromOffset("22"); var mockConnection = new MockConnection(() => new PublishingTransportConsumerMock(events)); var consumer = new EventHubConsumerClient(EventHubConsumerClient.DefaultConsumerGroupName, mockConnection); var firstSubscriberEvents = new List(); @@ -1337,7 +1337,7 @@ public async Task ReadEventsFromPartitionAsyncRespectsWaitTimeWhenEmittingEvents using var cancellation = new CancellationTokenSource(); cancellation.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(12), options, cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("12"), options, cancellation.Token)) { receivedEvents.Add(partitionEvent.Data); consecutiveEmptyCount = (partitionEvent.Data == null) ? consecutiveEmptyCount + 1 : 0; @@ -1377,7 +1377,7 @@ public void ReadEventsFromPartitionAsyncPropagatesExceptions(Type exceptionType) Assert.That(async () => { - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset(2), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync("0", EventPosition.FromOffset("2"), cancellation.Token)) { ++receivedEvents; break; @@ -1445,7 +1445,7 @@ public async Task ReadEventsFromPartitionAsyncSetsThePartitionContext() using var cancellation = new CancellationTokenSource(); cancellation.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); - await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync(expectedPartition, EventPosition.FromOffset(12), cancellation.Token)) + await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync(expectedPartition, EventPosition.FromOffset("12"), cancellation.Token)) { Assert.That(partitionEvent.Partition.PartitionId, Is.EqualTo(expectedPartition), $"The event in position: { actualCount } was not associated with the expected partition."); Assert.That(partitionEvent.Partition.FullyQualifiedNamespace, Is.EqualTo(mockConnection.FullyQualifiedNamespace), $"The event in position: { actualCount } was not associated with the expected namespace."); @@ -2774,7 +2774,7 @@ internal override Task GetPropertiesAsync(EventHubsRetryPoli CancellationToken cancellationToken = default) { GetPropertiesInvokedWith = retryPolicy; - return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" })); + return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" }, false)); } internal override async Task GetPartitionIdsAsync(EventHubsRetryPolicy retryPolicy, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventPositionTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventPositionTests.cs index 0eb31ffdd8ccb..830cebb61a1ae 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventPositionTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/EventPositionTests.cs @@ -40,8 +40,8 @@ public void EarliestAndLatestAreNotEqual() [Test] public void TheSameOffsetAreEqual() { - var first = EventPosition.FromOffset(12); - var second = EventPosition.FromOffset(12); + var first = EventPosition.FromOffset("12"); + var second = EventPosition.FromOffset("12"); Assert.That(first.Equals((object)second), Is.True, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.True, "The IEquatable comparison is incorrect."); @@ -57,8 +57,8 @@ public void TheSameOffsetAreEqual() [Test] public void DifferentOffsetsAreNotEqual() { - var first = EventPosition.FromOffset(12); - var second = EventPosition.FromOffset(34); + var first = EventPosition.FromOffset("12"); + var second = EventPosition.FromOffset("34"); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -179,7 +179,7 @@ public void DifferentInclusiveFlagsAreNotEqual() public void DifferentMembersAreNotEqual() { var first = EventPosition.FromSequenceNumber(234234); - var second = EventPosition.FromOffset(12); + var second = EventPosition.FromOffset("12"); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -195,7 +195,7 @@ public void DifferentMembersAreNotEqual() [Test] public void GetHashCodeReturnsDifferentValuesForDifferentMembers() { - var first = EventPosition.FromOffset(12); + var first = EventPosition.FromOffset("12"); var second = EventPosition.FromSequenceNumber(123); Assert.That(first.GetHashCode(), Is.Not.EqualTo(second.GetHashCode())); @@ -210,7 +210,7 @@ public void GetHashCodeReturnsDifferentValuesForDifferentMembers() public void ToStringReflectsTheState() { var inclusive = true; - var offset = 123; + var offset = "123"; var sequence = 778; var enqueued = DateTimeOffset.Now.AddHours(1); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/LastEnqueuedEventPropertiesTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/LastEnqueuedEventPropertiesTests.cs index 8a3b638b46c8e..3d57b18d0e525 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/LastEnqueuedEventPropertiesTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/LastEnqueuedEventPropertiesTests.cs @@ -24,8 +24,8 @@ public class LastEnqueuedEventPropertiesTests public void TheSameValuesAreEqual() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); Assert.That(first.Equals((object)second), Is.True, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.True, "The IEquatable comparison is incorrect."); @@ -42,8 +42,8 @@ public void TheSameValuesAreEqual() public void DifferentOffsetsAreNotEqual() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 999, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 888, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "999", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "888", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -60,8 +60,8 @@ public void DifferentOffsetsAreNotEqual() public void DifferentEnqueueTimesAreNotEqual() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("1974-12-09T21:30:00Z"), lastReceivedTime: now); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("1974-12-09T21:30:00Z"), lastReceivedTime: now); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -78,8 +78,8 @@ public void DifferentEnqueueTimesAreNotEqual() public void DifferentSequenceNumbersAreNotEqual() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 333, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 444, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 333, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 444, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -96,8 +96,8 @@ public void DifferentSequenceNumbersAreNotEqual() public void DifferentLastReceiveTimesAreNotEqual() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now.AddHours(1)); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffsetString: "887", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now.AddHours(1)); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -114,8 +114,8 @@ public void DifferentLastReceiveTimesAreNotEqual() public void GetHashCodeReturnsDifferentValuesForDifferentMembers() { var now = DateTimeOffset.UtcNow; - var first = new LastEnqueuedEventProperties(lastSequenceNumber: 333, lastOffset: 888, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); - var second = new LastEnqueuedEventProperties(lastSequenceNumber: 555, lastOffset: 777, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var first = new LastEnqueuedEventProperties(lastSequenceNumber: 333, lastOffsetString: "888", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); + var second = new LastEnqueuedEventProperties(lastSequenceNumber: 555, lastOffsetString: "777", lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now); Assert.That(first.GetHashCode(), Is.Not.EqualTo(second.GetHashCode())); } @@ -128,7 +128,7 @@ public void GetHashCodeReturnsDifferentValuesForDifferentMembers() [Test] public void ToStringReflectsTheState() { - var offset = 123; + var offset = "123"; var sequence = 778; var enqueued = DateTimeOffset.Now.AddHours(1); var received = DateTimeOffset.Now.AddHours(7); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/PartitionContextTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/PartitionContextTests.cs index dd54b8c35fa93..4d38cd9a7eeef 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/PartitionContextTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Consumer/PartitionContextTests.cs @@ -89,7 +89,7 @@ public void ReadLastEnqueuedEventPropertiesDelegatesToTheConsumer() ( eventBody: new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 1234, - lastPartitionOffset: 42, + lastPartitionOffset: "42", lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T00:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2012-03-04T08:42Z") ); @@ -99,7 +99,7 @@ public void ReadLastEnqueuedEventPropertiesDelegatesToTheConsumer() var information = context.ReadLastEnqueuedEventProperties(); Assert.That(information.SequenceNumber, Is.EqualTo(lastEvent.LastPartitionSequenceNumber), "The sequence number should match."); - Assert.That(information.Offset, Is.EqualTo(lastEvent.LastPartitionOffset), "The offset should match."); + Assert.That(information.OffsetString, Is.EqualTo(lastEvent.LastPartitionOffset), "The offset should match."); Assert.That(information.EnqueuedTime, Is.EqualTo(lastEvent.LastPartitionEnqueuedTime), "The last enqueue time should match."); Assert.That(information.LastReceivedTime, Is.EqualTo(lastEvent.LastPartitionPropertiesRetrievalTime), "The retrieval time should match."); Assert.That(mockConsumer.IsClosed, Is.False, "The consumer should not have been closed or disposed of."); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventDataTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventDataTests.cs index f2265ad863b7a..be7d09080b4d7 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventDataTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventDataTests.cs @@ -142,14 +142,14 @@ public void MutablePropertySettersPopulateTheAmqpMessage() /// /// [Test] - public void NonIdempotentStatePropertyAcessorsDeferToTheAmqpMessage() + public void NonIdempotentStatePropertyAccessorsDeferToTheAmqpMessage() { var sequenceNumber = 123L; - var offset = 456L; + var offset = "456L"; var enqueueTime = new DateTimeOffset(2015, 10, 27, 00, 00, 00, TimeSpan.Zero); var partitionKey = "fake-key"; var lastSequence = 321L; - var lastOffset = 654L; + var lastOffset = "654L"; var lastEnqueue = new DateTimeOffset(2012, 03, 04, 08, 00, 00, TimeSpan.Zero); var lastRetrieve = new DateTimeOffset(2020, 01, 01, 05, 15, 37, TimeSpan.Zero); var message = CreateFullyPopulatedAmqpMessage(sequenceNumber, lastSequence, offset, lastOffset, partitionKey, enqueueTime, lastEnqueue, lastRetrieve); @@ -158,7 +158,7 @@ public void NonIdempotentStatePropertyAcessorsDeferToTheAmqpMessage() Assert.That(message.Body.TryGetData(out var messageBody), Is.True, "The message body should have been read."); Assert.That(eventData.EventBody.ToArray(), Is.EquivalentTo(messageBody.First().ToArray()), "The message body should match."); Assert.That(eventData.Properties, Is.EquivalentTo(message.ApplicationProperties), "The application properties should match."); - Assert.That(eventData.Offset, Is.EqualTo(offset), "The offset should match."); + Assert.That(eventData.OffsetString, Is.EqualTo(offset), "The offset should match."); Assert.That(eventData.SequenceNumber, Is.EqualTo(sequenceNumber), "The sequence number should match."); Assert.That(eventData.EnqueuedTime, Is.EqualTo(enqueueTime), "The enqueued time should match."); Assert.That(eventData.PartitionKey, Is.EqualTo(partitionKey), "The partition key should match."); @@ -245,11 +245,11 @@ public void CloneProducesACopyWhenPropertyDictionariesAreSet() new Dictionary { { "Test", 123 } }, new Dictionary { { "System", "Hello" } }, 33334444, - 666777, + "666777", DateTimeOffset.Parse("2015-10-27T00:00:00Z"), "TestKey", 111222, - 999888, + "999888", DateTimeOffset.Parse("2012-03-04T09:00:00Z"), DateTimeOffset.Parse("2003-09-27T15:00:00Z"), 787878, @@ -275,11 +275,11 @@ public void CloneProducesACopyWhenPropertyDictionariesAreNotSet() null, null, 33334444, - 666777, + "666777", DateTimeOffset.Parse("2015-10-27T00:00:00Z"), "TestKey", 111222, - 999888, + "999888", DateTimeOffset.Parse("2012-03-04T09:00:00Z"), DateTimeOffset.Parse("2003-09-27T15:00:00Z"), 787878, @@ -304,11 +304,11 @@ public void CloneIsolatesPropertyChanges() new Dictionary { { "Test", 123 } }, new Dictionary { { "System", "Hello" } }, 33334444, - 666777, + "666777", DateTimeOffset.Parse("2015-10-27T00:00:00Z"), "TestKey", 111222, - 999888, + "999888", DateTimeOffset.Parse("2012-03-04T09:00:00Z"), DateTimeOffset.Parse("2003-09-27T15:00:00Z"), 787878, @@ -341,8 +341,8 @@ public void CloneIsolatesPropertyChanges() /// private static AmqpAnnotatedMessage CreateFullyPopulatedAmqpMessage(long sequenceNumber, long lastSequenceNumber, - long offset, - long lastOffset, + string offset, + string lastOffset, string partitionKey, DateTimeOffset enqueueTime, DateTimeOffset lastEnqueueTime, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventHubsModelFactoryTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventHubsModelFactoryTests.cs index 737cf9b0c6826..9fe753126fa80 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventHubsModelFactoryTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Core/EventHubsModelFactoryTests.cs @@ -52,7 +52,7 @@ public void PartitionPropertiesInitializesProperties() var isEmpty = false; var beginningSequenceNumber = 123; var lastSequenceNumber = 9999; - var lastOffset = 767; + var lastOffset = "767"; var lastEnqueuedTime = new DateTimeOffset(2015, 10, 27, 12, 0, 0, TimeSpan.Zero); var properties = EventHubsModelFactory.PartitionProperties(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, lastOffset, lastEnqueuedTime); @@ -62,7 +62,7 @@ public void PartitionPropertiesInitializesProperties() Assert.That(properties.IsEmpty, Is.EqualTo(isEmpty), "The `is empty` flag should have been set."); Assert.That(properties.BeginningSequenceNumber, Is.EqualTo(beginningSequenceNumber), "The beginning sequence number should have been set."); Assert.That(properties.LastEnqueuedSequenceNumber, Is.EqualTo(lastSequenceNumber), "The last sequence number should have been set."); - Assert.That(properties.LastEnqueuedOffset, Is.EqualTo(lastOffset), "The last offset should have been set."); + Assert.That(properties.LastEnqueuedOffsetString, Is.EqualTo(lastOffset), "The last offset should have been set."); Assert.That(properties.LastEnqueuedTime, Is.EqualTo(lastEnqueuedTime), "The last enqueue date/time should have been set."); } @@ -96,14 +96,14 @@ public void PartitionPublishingPropertiesInitializesProperties() public void LastEnqueuedEventPropertiesInitializesProperties() { var lastSequence = long.MaxValue - 100; - var lastOffset = long.MaxValue - 10; + var lastOffset = (long.MaxValue - 10).ToString(); var lastEnqueued = new DateTimeOffset(2015, 10, 27, 12, 0, 0, TimeSpan.Zero); var lastReceived = new DateTimeOffset(2012, 03, 04, 08, 0, 0, TimeSpan.Zero); var properties = EventHubsModelFactory.LastEnqueuedEventProperties(lastSequence, lastOffset, lastEnqueued, lastReceived); Assert.That(properties, Is.Not.Null, "The properties should have been created."); Assert.That(properties.SequenceNumber, Is.EqualTo(lastSequence), "The sequence number should have been set."); - Assert.That(properties.Offset, Is.EqualTo(lastOffset), "The offset should have been set."); + Assert.That(properties.OffsetString, Is.EqualTo(lastOffset), "The offset should have been set."); Assert.That(properties.EnqueuedTime, Is.EqualTo(lastEnqueued), "The enqueued date/time should have been set."); Assert.That(properties.LastReceivedTime, Is.EqualTo(lastReceived), "The last received date/time should have been set."); } @@ -121,7 +121,7 @@ public void PartitionContextInitializesProperties() var eventHubName = "fakeHub"; var consumerGroup = "fakeConsumerGroup"; var partition = "0"; - var properties = EventHubsModelFactory.LastEnqueuedEventProperties(465, 988, fakeDate, fakeDate); + var properties = EventHubsModelFactory.LastEnqueuedEventProperties(465, "988", fakeDate, fakeDate); var context = EventHubsModelFactory.PartitionContext(fullyQualifiedNamespace, eventHubName, consumerGroup, partition, properties); Assert.That(context, Is.Not.Null, "The context should have been created."); @@ -166,7 +166,7 @@ public void EventDataInitializesProperties() var properties = new Dictionary { { "id", 12 } }; var systemProperties = new Dictionary { { "custom", "sys-value" } }; var sequenceNumber = long.MaxValue - 512; - var offset = long.MaxValue - 1024; + string offset = (long.MaxValue - 1024).ToString(); var enqueueTime = new DateTimeOffset(2015, 10, 27, 12, 0, 0, TimeSpan.Zero); var partitionKey = "omghai!"; var eventData = EventHubsModelFactory.EventData(body, properties, systemProperties, partitionKey, sequenceNumber, offset, enqueueTime); @@ -177,7 +177,7 @@ public void EventDataInitializesProperties() Assert.That(eventData.SystemProperties, Is.EquivalentTo(systemProperties), "The system properties should have been set."); Assert.That(eventData.PartitionKey, Is.EqualTo(partitionKey), "The partition key should have been set."); Assert.That(eventData.SequenceNumber, Is.EqualTo(sequenceNumber), "The sequence number should have been set."); - Assert.That(eventData.Offset, Is.EqualTo(offset), "The offset should have been set."); + Assert.That(eventData.OffsetString, Is.EqualTo(offset), "The offset should have been set."); Assert.That(eventData.EnqueuedTime, Is.EqualTo(enqueueTime), "The sequence number should have been set."); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Diagnostics/DiagnosticsActivitySourceTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Diagnostics/DiagnosticsActivitySourceTests.cs index 1b45b52614a2a..4ef49a4cbbab9 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Diagnostics/DiagnosticsActivitySourceTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Diagnostics/DiagnosticsActivitySourceTests.cs @@ -620,11 +620,11 @@ public async Task UpdateCheckpointAsyncCreatesScope(bool useOldOverload) if (useOldOverload) { - await mockProcessor.InvokeOldUpdateCheckpointAsync("65", 12345, 67890, cancellationSource.Token); + await mockProcessor.InvokeOldUpdateCheckpointAsync("65", "12345", 67890, cancellationSource.Token); } else { - await mockProcessor.InvokeUpdateCheckpointAsync("65", new CheckpointPosition(12345), cancellationSource.Token); + await mockProcessor.InvokeUpdateCheckpointAsync("65", new CheckpointPosition("12345"), cancellationSource.Token); } Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled."); @@ -783,7 +783,7 @@ public MockCheckpointStoreProcessor(CheckpointStore checkpointStore, protected override Task OnProcessingErrorAsync(Exception exception, EventProcessorPartition partition, string operationDescription, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task InvokeGetCheckpointAsync(string partitionId, CancellationToken cancellationToken) => GetCheckpointAsync(partitionId, cancellationToken); - public Task InvokeOldUpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, CancellationToken cancellationToken) => UpdateCheckpointAsync(partitionId, offset, sequenceNumber, cancellationToken); + public Task InvokeOldUpdateCheckpointAsync(string partitionId, string offset, long? sequenceNumber, CancellationToken cancellationToken) => UpdateCheckpointAsync(partitionId, new CheckpointPosition(offset, sequenceNumber ?? long.MinValue), cancellationToken); public Task InvokeUpdateCheckpointAsync(string partitionId, CheckpointPosition checkpointPosition, CancellationToken cancellationToken) => UpdateCheckpointAsync(partitionId, checkpointPosition, cancellationToken); public Task> InvokeListOwnershipAsync(CancellationToken cancellationToken) => ListOwnershipAsync(cancellationToken); public Task> InvokeClaimOwnershipAsync(IEnumerable desiredOwnership, CancellationToken cancellationToken) => ClaimOwnershipAsync(desiredOwnership, cancellationToken); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorOptionsTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorOptionsTests.cs index 01734cdf07e8d..4859885cac3ed 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorOptionsTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorOptionsTests.cs @@ -35,7 +35,7 @@ public void CloneProducesACopy() PartitionOwnershipExpirationInterval = TimeSpan.FromHours(16), Identifier = "Rick Springfield is a bad friend", TrackLastEnqueuedEventProperties = false, - DefaultStartingPosition = EventPosition.FromOffset(555) + DefaultStartingPosition = EventPosition.FromOffset("555") }; EventProcessorOptions clone = options.Clone(); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.Infrastructure.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.Infrastructure.cs index 9516f4e8d4375..e7bc8d93abcb8 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.Infrastructure.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.Infrastructure.cs @@ -42,7 +42,7 @@ public async Task ReadLastEnqueuedEventPropertiesReadsPropertiesWhenThePartition var partitionId = "27"; var partitionIds = new[] { "0", partitionId }; var ownedPartitions = new List(); - var lastEventProperties = new LastEnqueuedEventProperties(1234, 9876, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), DateTimeOffset.Parse("2012-03-04T08:30:00Z")); + var lastEventProperties = new LastEnqueuedEventProperties(1234, "9876", DateTimeOffset.Parse("2015-10-27T00:00:00Z"), DateTimeOffset.Parse("2012-03-04T08:30:00Z")); var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMinutes(5), TrackLastEnqueuedEventProperties = true }; var mockLoadBalancer = new Mock(); @@ -72,8 +72,8 @@ public async Task ReadLastEnqueuedEventPropertiesReadsPropertiesWhenThePartition .Returns(() => default); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockProcessor .Setup(processor => processor.CreateConnection()) @@ -112,7 +112,7 @@ public async Task ReadLastEnqueuedEventPropertiesThrowsWhenThePartitionIsNotOwne var partitionId = "27"; var partitionIds = new[] { "0", partitionId }; - var lastEventProperties = new LastEnqueuedEventProperties(1234, 9876, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), DateTimeOffset.Parse("2012-03-04T08:30:00Z")); + var lastEventProperties = new LastEnqueuedEventProperties(1234, "9876", DateTimeOffset.Parse("2015-10-27T00:00:00Z"), DateTimeOffset.Parse("2012-03-04T08:30:00Z")); var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMinutes(5), TrackLastEnqueuedEventProperties = true }; var mockLoadBalancer = new Mock(); @@ -129,8 +129,8 @@ public async Task ReadLastEnqueuedEventPropertiesThrowsWhenThePartitionIsNotOwne .Callback(() => completionSource.TrySetResult(true)); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockProcessor .Setup(processor => processor.CreateConnection()) @@ -231,7 +231,7 @@ public void ProcessorCheckpointStoreDoesNotAllowCheckpointUpdate() var checkpointStore = EventProcessor.CreateCheckpointStore(mockProcessor.Object); Assert.That(checkpointStore, Is.Not.Null, "The storage manager should have been created."); - Assert.That(() => checkpointStore.UpdateCheckpointAsync(fqNamespace, eventHub, consumerGroup, partitionId, "Id", new CheckpointPosition(0), CancellationToken.None), Throws.InstanceOf(), "Calling to update checkpoints should not be implemented."); + Assert.That(() => checkpointStore.UpdateCheckpointAsync(fqNamespace, eventHub, consumerGroup, partitionId, "Id", new CheckpointPosition("0"), CancellationToken.None), Throws.InstanceOf(), "Calling to update checkpoints should not be implemented."); } /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.MainProcessingLoop.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.MainProcessingLoop.cs index 4bcfd5f5acf5a..8ad26f0f6b608 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.MainProcessingLoop.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.MainProcessingLoop.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -142,11 +143,16 @@ public async Task BackgroundProcessingToleratesPartitionIdQueryFailure() var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = new Mock(); var mockProcessor = new Mock(65, "consumerGroup", "namespace", "eventHub", Mock.Of(), default(EventProcessorOptions)) { CallBase = true }; + var partitionIds = new[] { "0", "1" }; mockConnection - .SetupSequence(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) + .SetupSequence(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) .Throws(expectedException) - .ReturnsAsync(new[] { "0", "1" }); + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); + + mockConnection + .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(partitionIds); mockProcessor .Setup(processor => processor.CreateConnection()) @@ -217,6 +223,10 @@ public async Task BackgroundProcessingToleratesALoadBalancingRunFailure() .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); + mockProcessor.Object.Logger = mockLogger.Object; mockProcessor @@ -301,8 +311,8 @@ public async Task BackgroundProcessingToleratesAnOwnershipClaimFailureWhenThePar .Throws(expectedException); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockProcessor.Object.Logger = mockLogger.Object; @@ -387,8 +397,8 @@ public async Task BackgroundProcessingToleratesAnOwnershipClaimFailureWhenThePar .Throws(expectedException); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockProcessor.Object.Logger = mockLogger.Object; @@ -486,8 +496,8 @@ public async Task BackgroundProcessingToleratesAnOwnershipClaimFailureWhenThePar .Returns(Mock.Of()); mockConnection - .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -604,8 +614,8 @@ public async Task BackgroundProcessingStartsProcessingForClaimedPartitions() .Returns(mockConsumer.Object); mockConnection - .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -744,7 +754,7 @@ public async Task BackgroundProcessingStopsProcessingAllPartitionsWhenShutdown() mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -875,7 +885,7 @@ public async Task BackgroundProcessingLogsHandlerErrorWhenPartitionProcessingSto mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1000,7 +1010,7 @@ public async Task BackgroundProcessingStopsProcessingForPartitionsWithLostOwners mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1090,6 +1100,10 @@ public async Task BackgroundProcessingRestartsProcessingForFaultedPartitions() .Returns(new ValueTask(new EventProcessorPartitionOwnership { PartitionId = partitionId })) .Returns(() => default); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -1149,7 +1163,7 @@ public async Task BackgroundProcessingUsesCheckpointsWhenProcessingPartitions() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var createConsumerCalls = 0; - var expectedStartingPosition = EventPosition.FromOffset(775, true); + var expectedStartingPosition = EventPosition.FromOffset("775", true); var partitionId = "27"; var partitionIds = new[] { "0", partitionId, "11" }; var ownedPartitions = new List { partitionId }; @@ -1174,7 +1188,7 @@ public async Task BackgroundProcessingUsesCheckpointsWhenProcessingPartitions() mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1253,7 +1267,7 @@ public async Task BackgroundProcessingDelegatesInitializationWhenProcessingClaim mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1372,7 +1386,7 @@ public async Task BackgroundProcessingLogsWhenStartingToProcessClaimedPartitions mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1484,6 +1498,10 @@ public async Task BackgroundProcessingLogsWhenStartingToProcessClaimedPartitions .Returns(new ValueTask(new EventProcessorPartitionOwnership { PartitionId = partitionId })) .Returns(() => default); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -1582,6 +1600,10 @@ public async Task BackgroundProcessingDispatchesExceptionsWhenStartingToProcessC .Returns(new ValueTask(new EventProcessorPartitionOwnership { PartitionId = partitionId })) .Returns(() => default); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -1701,7 +1723,7 @@ public async Task BackgroundProcessingLogsWhenSurrenderingClaimedPartitions() mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1814,7 +1836,7 @@ public async Task BackgroundProcessingDelegatesStopNotificationWhenSurrenderingC mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -1877,7 +1899,7 @@ public async Task BackgroundProcessingLogsWhenLoadBalancingIsSlow() var partitionId = "27"; var partitionIds = new[] { "0", partitionId, "111" }; var ownedPartitions = new List { partitionId }; - var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMilliseconds(250), PartitionOwnershipExpirationInterval = TimeSpan.FromMilliseconds(750), LoadBalancingStrategy = LoadBalancingStrategy.Balanced }; + var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMilliseconds(250), PartitionOwnershipExpirationInterval = TimeSpan.FromMilliseconds(750), LoadBalancingStrategy = LoadBalancingStrategy.Balanced }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockLogger = new Mock(); var mockLoadBalancer = new Mock(); @@ -1902,6 +1924,10 @@ public async Task BackgroundProcessingLogsWhenLoadBalancingIsSlow() return default; }); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -1959,7 +1985,7 @@ public async Task BackgroundProcessingEmitsAnErrorWhenLoadBalancingIsSlow() var partitionId = "27"; var partitionIds = new[] { "0", partitionId, "111" }; var ownedPartitions = new List { partitionId }; - var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMilliseconds(250), PartitionOwnershipExpirationInterval = TimeSpan.FromMilliseconds(750), LoadBalancingStrategy = LoadBalancingStrategy.Balanced }; + var options = new EventProcessorOptions { LoadBalancingUpdateInterval = TimeSpan.FromMilliseconds(250), PartitionOwnershipExpirationInterval = TimeSpan.FromMilliseconds(750), LoadBalancingStrategy = LoadBalancingStrategy.Balanced }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockLogger = new Mock(); var mockLoadBalancer = new Mock(); @@ -2000,7 +2026,7 @@ public async Task BackgroundProcessingEmitsAnErrorWhenLoadBalancingIsSlow() mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -2100,8 +2126,8 @@ public async Task BackgroundProcessingLogsWhenLargeNumberOfOwnedPartitions() .Returns(new ValueTask(default(EventProcessorPartitionOwnership))); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockConsumer .Setup(consumer => consumer.ReceiveAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -2187,6 +2213,10 @@ public async Task BackgroundProcessingDoesNotLogWarningWhenOwnedCountIsStable() } }); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -2282,11 +2312,11 @@ public async Task LoadBalancingAppliesTheGreedyStrategy() }); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) .Returns(async () => { await Task.Yield(); - return partitionIds; + return new EventHubProperties("", DateTime.Now, partitionIds, false); }); mockConsumer @@ -2377,8 +2407,8 @@ public async Task LoadBalancingWhenGreedyAppliesTheTimeoutAfterBalance() }); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockConsumer .Setup(consumer => consumer.ReceiveAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -2475,8 +2505,8 @@ public async Task LoadBalancingAppliesTheBalancedStrategy() }); mockConnection - .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(partitionIds); + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("", DateTime.Now, partitionIds, false)); mockConsumer .Setup(consumer => consumer.ReceiveAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -2584,7 +2614,7 @@ public async Task LoadBalancingIsNotBlockedByLostPartitionOwnership() mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds)); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), partitionIds, false)); mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) @@ -2617,7 +2647,7 @@ public async Task LoadBalancingIsNotBlockedByLostPartitionOwnership() ItExpr.IsAny>(), ItExpr.Is(part => part.PartitionId == firstPartiton), ItExpr.IsAny()) - .Callback (() => + .Callback(() => { if (loadBalancingCountAtDelay == 0) { diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.PartitionProcessing.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.PartitionProcessing.cs index 2ece62a997a58..fe44aeacab837 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.PartitionProcessing.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.PartitionProcessing.cs @@ -132,8 +132,8 @@ public async Task ProcessEventBatchAsyncLogsWhenBatchesAreDispatched() var eventBatch = new[] { - EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), sequenceNumber: 12345), - EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), sequenceNumber: 67890), + EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), sequenceNumber: 12345, offsetString: default), + EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), sequenceNumber: 67890, offsetString: default), }; var partition = new EventProcessorPartition { PartitionId = "123" }; @@ -364,7 +364,7 @@ public async Task CreatePartitionProcessorCanReadLastEventProperties() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = true }; - var lastEvent = new EventData(new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 123, lastPartitionOffset: 887, lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2021-03-04T08:30:00Z")); + var lastEvent = new EventData(new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 123, lastPartitionOffset: "887", lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2021-03-04T08:30:00Z")); var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = Mock.Of(); var mockConsumer = new Mock(); @@ -413,7 +413,7 @@ public async Task CreatePartitionProcessorCanReadLastEventPropertiesWhenTheConsu var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = true, RetryOptions = retryOptions }; - var lastEvent = new EventData(new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 123, lastPartitionOffset: 887, lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2021-03-04T08:30:00Z")); + var lastEvent = new EventData(new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 123, lastPartitionOffset: "887", lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2021-03-04T08:30:00Z")); var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = Mock.Of(); var mockConsumer = new Mock(); @@ -482,7 +482,7 @@ public async Task CreatePartitionProcessorCreatesTheTransportConsumer() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { Identifier = "fake", TrackLastEnqueuedEventProperties = false, PrefetchCount = 37, PrefetchSizeInBytes = 44, LoadBalancingUpdateInterval = TimeSpan.FromMinutes(1) }; var expectedOwnerLevel = 0; var expectedInvalidationOnSteal = true; @@ -542,7 +542,7 @@ public async Task CreatePartitionProcessorStartsTheProcessingTask() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = Mock.Of(); @@ -596,7 +596,7 @@ public async Task CreatePartitionProcessorProcessingTaskRespectsCancellation() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = Mock.Of(); @@ -644,7 +644,7 @@ public async Task CreatePartitionProcessorProcessingTaskClosesTheConsumerOnCance cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false }; var receiveCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var closeCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -754,7 +754,7 @@ public async Task CreatePartitionProcessorProcessingTaskDoesNotInvokeTheErrorHan cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false }; var receiveCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var closeCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -846,7 +846,7 @@ public async Task CreatePartitionProcessorProcessingTaskDispatchesEvents() cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -903,7 +903,7 @@ public async Task CreatePartitionProcessorProcessingLogsEachCycle() var startSequenceNumber = "4444"; var endSequenceNumber = "8888"; var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -914,8 +914,8 @@ public async Task CreatePartitionProcessorProcessingLogsEachCycle() var eventBatch = new List { - EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), offset: 0, sequenceNumber: long.Parse(startSequenceNumber)), - EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), offset: 1, sequenceNumber: long.Parse(endSequenceNumber)) + EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), offsetString: "0", sequenceNumber: long.Parse(startSequenceNumber)), + EventHubsModelFactory.EventData(new BinaryData(Array.Empty()), offsetString: "1", sequenceNumber: long.Parse(endSequenceNumber)) }; mockConsumer @@ -994,7 +994,7 @@ public async Task CreatePartitionProcessorProcessingTaskDispatchesExceptionsWhen var expectedException = new DivideByZeroException(); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1048,7 +1048,7 @@ public async Task CreatePartitionProcessorProcessingTaskDispatchesExceptions() var expectedException = new DivideByZeroException(); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var receiveCompletion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1111,7 +1111,7 @@ public async Task CreatePartitionProcessorProcessingTaskLogsExceptions() var expectedException = new DivideByZeroException("OMG FAIL!"); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var receiveCompletion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1176,7 +1176,7 @@ public async Task CreatePartitionProcessorProcessingTaskSurfacesExceptions() var expectedException = new DivideByZeroException("I'm special!"); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var receiveCompletion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1232,7 +1232,7 @@ public async Task CreatePartitionProcessorProcessingTaskWarnsForDeveloperCodeExc var expectedException = new DeveloperCodeException(new DivideByZeroException()); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 1, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1293,7 +1293,7 @@ public async Task CreatePartitionProcessorProcessingTaskLogsDeveloperCodeExcepti var expectedException = new DeveloperCodeException(new DivideByZeroException("Yay, I'm on the inside!")); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 1, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1356,7 +1356,7 @@ public async Task CreatePartitionProcessorProcessingTaskSurfacesDeveloperCodeExc var expectedException = new InvalidOperationException("BOOM!"); var developerException = new DeveloperCodeException(expectedException); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 1, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1403,7 +1403,7 @@ public async Task CreatePartitionProcessorProcessingTaskHonorsTheRetryPolicy() var expectedException = new EventHubsException(true, "frank", "BOOM!", EventHubsException.FailureReason.GeneralError); var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var retryOptions = new EventHubsRetryOptions { MaximumRetries = 2, MaximumDelay = TimeSpan.FromMilliseconds(15) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -1658,7 +1658,7 @@ public async Task CreatePartitionProcessorProcessingTaskStartsTheConsumerAtTheCo var retryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) }; var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = retryOptions }; var partition = new EventProcessorPartition { PartitionId = "4" }; - var lastEventBatch = new List { new EventData(new BinaryData(Array.Empty())), new EventData(new BinaryData(Array.Empty()), offset: 9987) }; + var lastEventBatch = new List { new EventData(new BinaryData(Array.Empty())), new EventData(new BinaryData(Array.Empty()), offset: "9987") }; var initialStartingPosition = EventPosition.FromSequenceNumber(332); var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockConnection = Mock.Of(); @@ -1711,7 +1711,7 @@ public async Task CreatePartitionProcessorProcessingTaskStartsTheConsumerAtTheCo mockProcessor.Object.ConsumerGroup, partition.PartitionId, It.IsAny(), - EventPosition.FromOffset(lastEventBatch.Last().Offset, false), + EventPosition.FromOffset(lastEventBatch.Last().OffsetString, false), mockConnection, It.IsAny(), It.IsAny()), diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.StartStop.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.StartStop.cs index a22005b0c94cd..def35104ea189 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.StartStop.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/EventProcessorTests.StartStop.cs @@ -132,6 +132,10 @@ public async Task StartProcessingStartsTheLoadBalancer(bool async) .Callback(() => completionSource.TrySetResult(true)) .ReturnsAsync(default(EventProcessorPartitionOwnership)); + mockConnection + .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitions, false)); + mockConnection .Setup(connection => connection.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitions); @@ -362,7 +366,7 @@ public async Task StartProcessingValidatesCheckpointsCanBeQueried(bool async) mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), new[] { "0" })); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), new[] { "0" }, false)); mockProcessor .Setup(processor => processor.CreateConnection()) @@ -429,9 +433,9 @@ public async Task StartProcessingSurfacesMultipleValidationFailures(bool async) var mockConnection = new Mock(); var mockProcessor = new Mock>(4, "consumerGroup", "namespace", "eventHub", Mock.Of(), default(EventProcessorOptions)) { CallBase = true }; - mockProcessor - .Setup(processor => processor.CreateConnection()) - .Returns(mockConnection.Object); + mockProcessor + .Setup(processor => processor.CreateConnection()) + .Returns(mockConnection.Object); mockProcessor .Protected() @@ -629,6 +633,10 @@ public async Task StartProcessingWarnsWhenConfiguredIntervalsAreTooClose() .Setup(lb => lb.RunLoadBalancingAsync(partitionIds, It.IsAny())) .Returns(new ValueTask(default(EventProcessorPartitionOwnership))); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -730,6 +738,10 @@ public async Task StartProcessingDoesNotWarnForAppropriateIntervals(double owner .Setup(lb => lb.RunLoadBalancingAsync(partitionIds, It.IsAny())) .Returns(new ValueTask(default(EventProcessorPartitionOwnership))); + mockConnection + .Setup(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, partitionIds, false)); + mockConnection .Setup(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(partitionIds); @@ -923,6 +935,10 @@ public async Task StopProcessingStopsTheLoadBalancer(bool async) .Returns(Task.CompletedTask) .Callback(() => stopCompletionSource.TrySetResult(true)); + mockConnection + .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, Array.Empty(), false)); + mockConnection .Setup(connection => connection.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(Array.Empty()); @@ -1268,9 +1284,9 @@ public async Task StopProcessingIsSafeToCallInTheErrorHandler(bool async) .Callback(() => stopCompletionSource.TrySetResult(true)); mockConnection - .SetupSequence(conn => conn.GetPartitionIdsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(Array.Empty()) - .ReturnsAsync(Array.Empty()) + .SetupSequence(conn => conn.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, Array.Empty(), false)) + .ReturnsAsync(new EventHubProperties("eventHub", DateTimeOffset.Now, Array.Empty(), false)) .Returns(async () => { await startCompletionSource.Task; @@ -1364,7 +1380,7 @@ public async Task StopProcessingLogsWarningForTokenCancellationErrors(bool async var firstCall = true; var partition = new EventProcessorPartition { PartitionId = "99" }; - var position = EventPosition.FromOffset(12); + var position = EventPosition.FromOffset("12"); var options = new EventProcessorOptions { TrackLastEnqueuedEventProperties = false, RetryOptions = new EventHubsRetryOptions { MaximumRetries = 0, MaximumDelay = TimeSpan.FromMilliseconds(5) } }; var handlerCompletion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockEventSource = new Mock(); @@ -1374,7 +1390,7 @@ public async Task StopProcessingLogsWarningForTokenCancellationErrors(bool async mockConnection .Setup(connection => connection.GetPropertiesAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), new[] { "0" })); + .ReturnsAsync(new EventHubProperties(mockProcessor.Object.EventHubName, new DateTimeOffset(2015, 10, 27, 12, 0, 0, 0, TimeSpan.Zero), new[] { "0" }, false)); mockConsumer .Setup(consumer => consumer.ReceiveAsync(It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverLiveTests.cs index ef02b92dff63b..3a5665f9b4b89 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverLiveTests.cs @@ -647,7 +647,7 @@ public async Task ReceiverCanReadFromOffset(bool isInclusive) // Once sent, query the partition and determine the offset of the last enqueued event, then send the new set // of events that should appear after the starting position. - long lastOffset; + string lastOffset; EventPosition startingPosition; await using (var producer = new EventHubProducerClient( @@ -658,7 +658,7 @@ public async Task ReceiverCanReadFromOffset(bool isInclusive) await SendEventsAsync(scope.EventHubName, seedEvents, new CreateBatchOptions { PartitionId = partition }, cancellationSource.Token); await Task.Delay(250); - lastOffset = (await producer.GetPartitionPropertiesAsync(partition, cancellationSource.Token)).LastEnqueuedOffset; + lastOffset = (await producer.GetPartitionPropertiesAsync(partition, cancellationSource.Token)).LastEnqueuedOffsetString; startingPosition = EventPosition.FromOffset(lastOffset, isInclusive); await SendEventsAsync(scope.EventHubName, sourceEvents, new CreateBatchOptions { PartitionId = partition }, cancellationSource.Token); @@ -687,7 +687,7 @@ public async Task ReceiverCanReadFromOffset(bool isInclusive) Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled."); Assert.That(readState.Events.Count, Is.EqualTo(expectedCount), "The wrong number of events was read for the value of the inclusive flag."); - Assert.That(readState.Events.Values.Any(readEvent => readEvent.Offset == lastOffset), Is.EqualTo(isInclusive), $"The event with offset [{ lastOffset }] was { ((isInclusive) ? "not" : "") } in the set of read events, which is inconsistent with the inclusive flag."); + Assert.That(readState.Events.Values.Any(readEvent => readEvent.OffsetString == lastOffset), Is.EqualTo(isInclusive), $"The event with offset [{ lastOffset }] was { ((isInclusive) ? "not" : "") } in the set of read events, which is inconsistent with the inclusive flag."); foreach (var sourceEvent in sourceEvents) { @@ -1527,7 +1527,7 @@ public async Task ReceiverCanReadFromMultipleConsumerGroupsWithDifferentActiveOw /// /// [Test] - public async Task ExclusiveReceiverSupercedesNonExclusiveActiveReader() + public async Task ExclusiveReceiverSupersedesNonExclusiveActiveReader() { await using (EventHubScope scope = await EventHubScope.CreateAsync(1)) { @@ -1598,7 +1598,7 @@ public async Task ExclusiveReceiverSupercedesNonExclusiveActiveReader() /// /// [Test] - public async Task ReceiverWithHigherOwnerLevelSupercedesActiveReader() + public async Task ReceiverWithHigherOwnerLevelSupersedesActiveReader() { await using (EventHubScope scope = await EventHubScope.CreateAsync(1)) { @@ -1818,7 +1818,7 @@ public async Task ExclusiveReceiverDoesNotSupersedeNonExclusiveActiveReaderOnAno /// /// [Test] - public async Task ReceiverIsNotCompromisedByBeingSupercededByAnotherReaderWithHigherLevel() + public async Task ReceiverIsNotCompromisedByBeingSupersededByAnotherReaderWithHigherLevel() { await using (EventHubScope scope = await EventHubScope.CreateAsync(2)) { @@ -2436,7 +2436,7 @@ public async Task ReceiverCanRetrievePartitionProperties(EventHubsTransportType Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match."); Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated."); Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated."); - Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); + Assert.That(partitionProperties.LastEnqueuedOffsetString, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverTests.cs index e6edb55d04053..4f3808ba636ac 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PartitionReceiverTests.cs @@ -582,7 +582,7 @@ public void ConnectionConstructorSetsThePartitionId() [Test] public void ConnectionStringConstructorSetsTheInitialPosition() { - var expectedPosition = EventPosition.FromOffset(999); + var expectedPosition = EventPosition.FromOffset("999"); var connectionString = "Endpoint=sb://somehost.com;SharedAccessKeyName=ABC;SharedAccessKey=123;EntityPath=somehub"; var receiver = new PartitionReceiver("cg", "pid", expectedPosition, connectionString); @@ -596,7 +596,7 @@ public void ConnectionStringConstructorSetsTheInitialPosition() [Test] public void TokenCredentialConstructorSetsTheInitialPosition() { - var expectedPosition = EventPosition.FromOffset(999); + var expectedPosition = EventPosition.FromOffset("999"); var receiver = new PartitionReceiver("cg", "pid", expectedPosition, "fqns", "eh", Mock.Of()); Assert.That(receiver.InitialPosition, Is.EqualTo(expectedPosition)); @@ -609,7 +609,7 @@ public void TokenCredentialConstructorSetsTheInitialPosition() [Test] public void SharedKeyCredentialConstructorSetsTheInitialPosition() { - var expectedPosition = EventPosition.FromOffset(999); + var expectedPosition = EventPosition.FromOffset("999"); var receiver = new PartitionReceiver("cg", "pid", expectedPosition, "fqns", "eh", new AzureNamedKeyCredential("key", "value")); Assert.That(receiver.InitialPosition, Is.EqualTo(expectedPosition)); @@ -622,7 +622,7 @@ public void SharedKeyCredentialConstructorSetsTheInitialPosition() [Test] public void SasCredentialConstructorSetsTheInitialPosition() { - var expectedPosition = EventPosition.FromOffset(999); + var expectedPosition = EventPosition.FromOffset("999"); var receiver = new PartitionReceiver("cg", "pid", expectedPosition, "fqns", "eh", new AzureSasCredential(new SharedAccessSignature("sb://this.is.Fake/blah", "key", "value").Value)); Assert.That(receiver.InitialPosition, Is.EqualTo(expectedPosition)); @@ -635,7 +635,7 @@ public void SasCredentialConstructorSetsTheInitialPosition() [Test] public void ConnectionConstructorSetsTheInitialPosition() { - var expectedPosition = EventPosition.FromOffset(999); + var expectedPosition = EventPosition.FromOffset("999"); var receiver = new PartitionReceiver("cg", "pid", expectedPosition, Mock.Of()); Assert.That(receiver.InitialPosition, Is.EqualTo(expectedPosition)); @@ -730,7 +730,7 @@ public void CreateTransportConsumerDelegatesToTheConnection() var expectedConsumerGroup = "consumerGroup"; var expectedPartitionId = "partitionId"; var expectedIdentifier = "customIdent!fi3r!"; - var expectedPosition = EventPosition.FromOffset(55); + var expectedPosition = EventPosition.FromOffset("55"); var expectedInvalidateOnSteal = false; var expectedOptions = new PartitionReceiverOptions @@ -771,7 +771,7 @@ public void ReadLastEnqueuedEventPropertiesDelegatesToTheTransportConsumer() ( eventBody: new BinaryData(Array.Empty()), lastPartitionSequenceNumber: 1234, - lastPartitionOffset: 42, + lastPartitionOffset: "42", lastPartitionEnqueuedTime: DateTimeOffset.Parse("2015-10-27T00:00:00Z"), lastPartitionPropertiesRetrievalTime: DateTimeOffset.Parse("2012-03-04T08:42Z") ); @@ -798,7 +798,7 @@ public void ReadLastEnqueuedEventPropertiesDelegatesToTheTransportConsumer() var information = receiver.ReadLastEnqueuedEventProperties(); Assert.That(information.SequenceNumber, Is.EqualTo(lastEvent.LastPartitionSequenceNumber), "The sequence number should match."); - Assert.That(information.Offset, Is.EqualTo(lastEvent.LastPartitionOffset), "The offset should match."); + Assert.That(information.OffsetString, Is.EqualTo(lastEvent.LastPartitionOffset), "The offset should match."); Assert.That(information.EnqueuedTime, Is.EqualTo(lastEvent.LastPartitionEnqueuedTime), "The last enqueue time should match."); Assert.That(information.LastReceivedTime, Is.EqualTo(lastEvent.LastPartitionPropertiesRetrievalTime), "The retrieval time should match."); } @@ -834,7 +834,7 @@ public void ReadLastEnqueuedEventPropertiesAllowsTheOperationWhenTheOptionIsUnse var information = receiver.ReadLastEnqueuedEventProperties(); Assert.That(information.SequenceNumber, Is.EqualTo(defaultProperties.SequenceNumber), "The sequence number should match."); - Assert.That(information.Offset, Is.EqualTo(defaultProperties.Offset), "The offset should match."); + Assert.That(information.OffsetString, Is.EqualTo(defaultProperties.OffsetString), "The offset should match."); Assert.That(information.EnqueuedTime, Is.EqualTo(defaultProperties.EnqueuedTime), "The last enqueue time should match."); Assert.That(information.LastReceivedTime, Is.EqualTo(defaultProperties.LastReceivedTime), "The retrieval time should match."); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PluggableCheckpointStoreEventProcessorTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PluggableCheckpointStoreEventProcessorTests.cs index 5ae1ae7be4993..900d604dda888 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PluggableCheckpointStoreEventProcessorTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Primitives/PluggableCheckpointStoreEventProcessorTests.cs @@ -78,7 +78,7 @@ public void CheckpointStoreIsUsedByUpdateCheckpointAsync() var expectedException = new DivideByZeroException(); var expectedExceptionOld = new FormatException(); var partitionId = "fakePart"; - var offset = 12345; + var offset = "12345"; var sequence = 9987; var mockCheckpointStore = new Mock(); var mockProcessor = new MockCheckpointStoreProcessor(mockCheckpointStore.Object, 100, "fakeConsumer", "fakeNamespace", "fakeHub", Mock.Of()); @@ -90,24 +90,11 @@ public void CheckpointStoreIsUsedByUpdateCheckpointAsync() mockProcessor.ConsumerGroup, partitionId, mockProcessor.Identifier, - It.Is(csp => - csp.SequenceNumber == sequence), + It.Is(csp => csp.OffsetString == offset && csp.SequenceNumber == sequence), cancellationSource.Token)) .ThrowsAsync(expectedException); - mockCheckpointStore - .Setup(store => store.UpdateCheckpointAsync( - mockProcessor.FullyQualifiedNamespace, - mockProcessor.EventHubName, - mockProcessor.ConsumerGroup, - partitionId, - offset, - sequence, - cancellationSource.Token)) - .ThrowsAsync(expectedExceptionOld); - - Assert.That(async () => await mockProcessor.InvokeOldUpdateCheckpointAsync(partitionId, offset, sequence, cancellationSource.Token), Throws.Exception.EqualTo(expectedExceptionOld)); - Assert.That(async () => await mockProcessor.InvokeUpdateCheckpointAsync(partitionId, new CheckpointPosition(sequence), cancellationSource.Token), Throws.Exception.EqualTo(expectedException)); + Assert.That(async () => await mockProcessor.InvokeUpdateCheckpointAsync(partitionId, new CheckpointPosition(offset, sequence), cancellationSource.Token), Throws.Exception.EqualTo(expectedException)); } /// @@ -229,7 +216,6 @@ public MockCheckpointStoreProcessor(CheckpointStore checkpointStore, protected override Task OnProcessingErrorAsync(Exception exception, EventProcessorPartition partition, string operationDescription, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task InvokeGetCheckpointAsync(string partitionId, CancellationToken cancellationToken) => GetCheckpointAsync(partitionId, cancellationToken); - public Task InvokeOldUpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, CancellationToken cancellationToken) => UpdateCheckpointAsync(partitionId, offset, sequenceNumber, cancellationToken); public Task InvokeUpdateCheckpointAsync(string partitionId, CheckpointPosition checkpointPosition, CancellationToken cancellationToken) => UpdateCheckpointAsync(partitionId, checkpointPosition, cancellationToken); public Task> InvokeListOwnershipAsync(CancellationToken cancellationToken) => ListOwnershipAsync(cancellationToken); public Task> InvokeClaimOwnershipAsync(IEnumerable desiredOwnership, CancellationToken cancellationToken) => ClaimOwnershipAsync(desiredOwnership, cancellationToken); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Processor/CheckpointPositionTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Processor/CheckpointPositionTests.cs index 6eeba1708c4bd..164d8a57ac7c6 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Processor/CheckpointPositionTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Processor/CheckpointPositionTests.cs @@ -15,6 +15,25 @@ namespace Azure.Messaging.EventHubs.Tests [TestFixture] public class CheckpointPositionTests { + /// + /// Verifies functionality of the constructor. + /// + /// + [Test] + [TestCase(null)] + [TestCase("")] + public void ConstructorValidatesOffset(string offset) + { + if (offset == null) + { + Assert.That(() => new CheckpointPosition(offset), Throws.InstanceOf()); + } + else + { + Assert.That(() => new CheckpointPosition(offset), Throws.InstanceOf()); + } + } + /// /// Verifies functionality of the /// equality. @@ -23,8 +42,24 @@ public class CheckpointPositionTests [Test] public void TheSamePositionAreEqual() { - var first = new CheckpointPosition(121); - var second = new CheckpointPosition(121); + var first = new CheckpointPosition("121"); + var second = new CheckpointPosition("121"); + + Assert.That(first.Equals((object)second), Is.True, "The default Equals comparison is incorrect."); + Assert.That(first.Equals(second), Is.True, "The IEquatable comparison is incorrect."); + Assert.That((first == second), Is.True, "The == operator comparison is incorrect."); + Assert.That((first != second), Is.False, "The != operator comparison is incorrect."); + } + + /// + /// Verifies functionality of the + /// equality. + /// + [Test] + public void TheSamePositionAreEqualWithSequenceNumber() + { + var first = new CheckpointPosition("44", 121); + var second = new CheckpointPosition("44", 121); Assert.That(first.Equals((object)second), Is.True, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.True, "The IEquatable comparison is incorrect."); @@ -40,8 +75,42 @@ public void TheSamePositionAreEqual() [Test] public void DifferentPositionsAreNotEqual() { - var first = new CheckpointPosition(10); - var second = new CheckpointPosition(121); + var first = new CheckpointPosition("10"); + var second = new CheckpointPosition("121"); + + Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); + Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); + Assert.That((first == second), Is.False, "The == operator comparison is incorrect."); + Assert.That((first != second), Is.True, "The != operator comparison is incorrect."); + } + + /// + /// Verifies functionality of the + /// equality. + /// + /// + [Test] + public void DifferentPositionsAreNotEqualWithDifferentMembers() + { + var first = new CheckpointPosition("121"); + var second = new CheckpointPosition("121", 10); + + Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); + Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); + Assert.That((first == second), Is.False, "The == operator comparison is incorrect."); + Assert.That((first != second), Is.True, "The != operator comparison is incorrect."); + } + + /// + /// Verifies functionality of the + /// equality. + /// + /// + [Test] + public void DifferentPositionsAreNotEqualWithOffset() + { + var first = new CheckpointPosition("10"); + var second = new CheckpointPosition("121"); Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect."); Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect."); @@ -57,8 +126,8 @@ public void DifferentPositionsAreNotEqual() [Test] public void GetHashCodeReturnsDifferentValuesForDifferentMembers() { - var first = new CheckpointPosition(10); - var second = new CheckpointPosition(121); + var first = new CheckpointPosition("10", 12); + var second = new CheckpointPosition("121"); Assert.That(first.GetHashCode(), Is.Not.EqualTo(second.GetHashCode())); } @@ -72,7 +141,7 @@ public void GetHashCodeReturnsDifferentValuesForDifferentMembers() public void FromEventSetsProperties() { var sequence = 4566; - var eventData = new EventData(new BinaryData("Hello"), sequenceNumber: sequence, offset: 123); + var eventData = new EventData(new BinaryData("Hello"), sequenceNumber: sequence, offset: "123"); var checkpoint = CheckpointPosition.FromEvent(eventData); @@ -87,11 +156,26 @@ public void FromEventSetsProperties() [Test] public void ToStringReflectsTheState() { - var sequence = 121; + var offset = "121"; + var checkpoint = new CheckpointPosition(offset); - var checkpoint = new CheckpointPosition(sequence); + Assert.That(checkpoint.ToString(), Contains.Substring($"[{offset}]"), "The offset should be represented."); + } - Assert.That(checkpoint.ToString(), Contains.Substring($"[{sequence}]"), "The sequence should be represented."); + /// + /// Verifies functionality of the + /// method. + /// + /// + [Test] + public void ToStringReflectsTheStateWithSequenceNumber() + { + var offset = "121"; + var sequence = 10; + var checkpoint = new CheckpointPosition(offset, sequence); + + Assert.That(checkpoint.ToString(), Contains.Substring($"[{offset}]"), "The offset should be represented."); + Assert.That(checkpoint.ToString(), Contains.Substring($"[{sequence}]"), "The sequence number should be represented."); } /// @@ -102,7 +186,7 @@ public void ToStringReflectsTheState() [Test] public void ToStringReflectsTheStateFromEventData() { - var offset = 400; + var offset = "400"; var sequence = 121; var eventData = new EventData(new BinaryData("Hello"), sequenceNumber: sequence, offset: offset); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientLiveTests.cs index 18882e31e5301..e78b5f44c0ce6 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientLiveTests.cs @@ -1601,7 +1601,7 @@ public async Task ProducerCanRetrievePartitionProperties(EventHubsTransportType Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match."); Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated."); Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated."); - Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); + Assert.That(partitionProperties.LastEnqueuedOffsetString, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); } /// diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientTests.cs index 2616c99758267..bc78073b0d426 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubBufferedProducerClientTests.cs @@ -412,7 +412,7 @@ public async Task GetPartitionPropertiesAsyncIsDelegated() .Setup(producer => producer.GetPartitionPropertiesAsync( It.IsAny(), It.IsAny())) - .ReturnsAsync(new PartitionProperties("test", "1", true, 12345, 6789, 22, new DateTimeOffset(2015, 10, 27, 0, 0, 0, TimeSpan.Zero))); + .ReturnsAsync(new PartitionProperties("test", "1", true, 12345, 6789, "22", new DateTimeOffset(2015, 10, 27, 0, 0, 0, TimeSpan.Zero))); await bufferedProducer.GetPartitionPropertiesAsync(expectedPartition, cancellationSource.Token); mockProducer.Verify(producer => producer.GetPartitionPropertiesAsync(expectedPartition, cancellationSource.Token), Times.Once); diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientLiveTests.cs index 38bb0890967f1..70d643635bd1b 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientLiveTests.cs @@ -869,7 +869,7 @@ public async Task SendSetUpdatesPartitionProperties() // The following properties should have been updated. Assert.That(newPartitionProperties.LastEnqueuedSequenceNumber, Is.GreaterThan(oldPartitionProperties.LastEnqueuedSequenceNumber)); - Assert.That(newPartitionProperties.LastEnqueuedOffset, Is.GreaterThan(oldPartitionProperties.LastEnqueuedOffset)); + Assert.That(newPartitionProperties.LastEnqueuedOffsetString, Is.GreaterThan(oldPartitionProperties.LastEnqueuedOffsetString)); } } } @@ -920,7 +920,8 @@ public async Task SendBatchUpdatesPartitionProperties() // The following properties should have been updated. Assert.That(newPartitionProperties.LastEnqueuedSequenceNumber, Is.GreaterThan(oldPartitionProperties.LastEnqueuedSequenceNumber)); - Assert.That(newPartitionProperties.LastEnqueuedOffset, Is.GreaterThan(oldPartitionProperties.LastEnqueuedOffset)); + Assert.That(newPartitionProperties.LastEnqueuedOffsetString, Is.Not.EqualTo(oldPartitionProperties.LastEnqueuedOffsetString)); + Assert.That(newPartitionProperties.LastEnqueuedOffsetString, Is.Not.Null.And.Not.Empty); } } } @@ -964,7 +965,7 @@ public async Task SendDoesNotUpdatePartitionPropertiesWhenSendingToDifferentPart Assert.That(newPartitionProperties.EventHubName, Is.EqualTo(oldPartitionProperties.EventHubName)); Assert.That(newPartitionProperties.BeginningSequenceNumber, Is.EqualTo(oldPartitionProperties.BeginningSequenceNumber)); Assert.That(newPartitionProperties.LastEnqueuedSequenceNumber, Is.EqualTo(oldPartitionProperties.LastEnqueuedSequenceNumber)); - Assert.That(newPartitionProperties.LastEnqueuedOffset, Is.EqualTo(oldPartitionProperties.LastEnqueuedOffset)); + Assert.That(newPartitionProperties.LastEnqueuedOffsetString, Is.EqualTo(oldPartitionProperties.LastEnqueuedOffsetString)); } } } @@ -1375,7 +1376,7 @@ public async Task ProducerCanRetrievePartitionProperties(EventHubsTransportType Assert.That(partitionProperties.EventHubName, Is.EqualTo(scope.EventHubName).Using((IEqualityComparer)StringComparer.InvariantCultureIgnoreCase), "The Event Hub path should match."); Assert.That(partitionProperties.BeginningSequenceNumber, Is.Not.EqualTo(default(long)), "The beginning sequence number should have been populated."); Assert.That(partitionProperties.LastEnqueuedSequenceNumber, Is.Not.EqualTo(default(long)), "The last sequence number should have been populated."); - Assert.That(partitionProperties.LastEnqueuedOffset, Is.Not.EqualTo(default(long)), "The last offset should have been populated."); + Assert.That(partitionProperties.LastEnqueuedOffsetString, Is.Not.Null.And.Not.Empty, "The last offset should have been populated."); } } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientTests.cs index 95eb0d6d34f0c..dfc0786557e7f 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/EventHubProducerClientTests.cs @@ -2888,7 +2888,7 @@ internal override Task GetPropertiesAsync(EventHubsRetryPoli CancellationToken cancellationToken = default) { GetPropertiesInvokedWith = retryPolicy; - return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" })); + return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" }, false)); } internal override async Task GetPartitionIdsAsync(EventHubsRetryPolicy retryPolicy, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/TransportProducerPoolTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/TransportProducerPoolTests.cs index a1a8c9c47a901..c4847f9d9be5b 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/TransportProducerPoolTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Producer/TransportProducerPoolTests.cs @@ -398,7 +398,7 @@ internal override Task GetPropertiesAsync(EventHubsRetryPoli CancellationToken cancellationToken = default) { GetPropertiesInvokedWith = retryPolicy; - return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" })); + return Task.FromResult(new EventHubProperties(EventHubName, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), new string[] { "0", "1" }, false)); } internal async override Task GetPartitionIdsAsync(EventHubsRetryPolicy retryPolicy, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample03_EventHubMetadataLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample03_EventHubMetadataLiveTests.cs index c758b7b90efe7..c50916b7ddf87 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample03_EventHubMetadataLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample03_EventHubMetadataLiveTests.cs @@ -142,7 +142,7 @@ public async Task InspectPartition() Debug.WriteLine($"\tThe partition contains no events: { partitionProperties.IsEmpty }"); Debug.WriteLine($"\tThe first sequence number is: { partitionProperties.BeginningSequenceNumber }"); Debug.WriteLine($"\tThe last sequence number is: { partitionProperties.LastEnqueuedSequenceNumber }"); - Debug.WriteLine($"\tThe last offset is: { partitionProperties.LastEnqueuedOffset }"); + Debug.WriteLine($"\tThe last offset is: { partitionProperties.LastEnqueuedOffsetString }"); Debug.WriteLine($"\tThe last enqueued time is: { partitionProperties.LastEnqueuedTime }, in UTC."); } finally diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample05_ReadingEventsLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample05_ReadingEventsLiveTests.cs index 0f7cf87fe79f8..c90f15978dbc9 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample05_ReadingEventsLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample05_ReadingEventsLiveTests.cs @@ -453,7 +453,7 @@ public async Task ReadPartitionFromOffset() string firstPartition = (await consumer.GetPartitionIdsAsync(cancellationSource.Token)).First(); PartitionProperties properties = await consumer.GetPartitionPropertiesAsync(firstPartition, cancellationSource.Token); - EventPosition startingPosition = EventPosition.FromOffset(properties.LastEnqueuedOffset); + EventPosition startingPosition = EventPosition.FromOffset(properties.LastEnqueuedOffsetString); await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync( firstPartition, @@ -592,7 +592,7 @@ public async Task ReadPartitionTrackLastEnqueued() Debug.WriteLine($"Partition: { partitionEvent.Partition.PartitionId }"); Debug.WriteLine($"\tThe last sequence number is: { properties.SequenceNumber }"); - Debug.WriteLine($"\tThe last offset is: { properties.Offset }"); + Debug.WriteLine($"\tThe last offset is: { properties.OffsetString }"); Debug.WriteLine($"\tThe last enqueued time is: { properties.EnqueuedTime }, in UTC."); Debug.WriteLine($"\tThe information was updated at: { properties.LastReceivedTime }, in UTC."); } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample11_MockingClientTypesLiveTests.cs b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample11_MockingClientTypesLiveTests.cs index 747da578b3491..079fc42748d81 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample11_MockingClientTypesLiveTests.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/tests/Snippets/Sample11_MockingClientTypesLiveTests.cs @@ -88,7 +88,7 @@ public async Task MockingProducerProperties() isEmpty : true, beginningSequenceNumber: 1000, lastSequenceNumber : 1100, - lastOffset : 500, + lastOffsetString : "500:1:7863", lastEnqueuedTime : DateTime.UtcNow) }, // Empty partition @@ -98,7 +98,7 @@ public async Task MockingProducerProperties() isEmpty : false, beginningSequenceNumber : 2000, lastSequenceNumber : 2000, - lastOffset : 760, + lastOffsetString : "760:1:8800", lastEnqueuedTime : DateTime.UtcNow) } }; @@ -360,7 +360,7 @@ public async Task MockingConsumerClient() LastEnqueuedEventProperties lastEnqueueEventProperties = EventHubsModelFactory.LastEnqueuedEventProperties( lastSequenceNumber : 1234, - lastOffset : 234, + lastOffsetString : "234:1:954-2", lastEnqueuedTime : DateTimeOffset.Parse("1:24 AM"), lastReceivedTime : DateTimeOffset.Parse("1:26 AM")); @@ -386,7 +386,7 @@ async IAsyncEnumerable mockReturn() systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:44:59492", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); EventData eventData2 = EventHubsModelFactory.EventData( @@ -394,7 +394,7 @@ async IAsyncEnumerable mockReturn() systemProperties: new Dictionary(), //arbitrary value partitionKey: "sample-key", sequenceNumber: 1000, - offset: 1500, + offsetString: "1500:2:1111", enqueuedTime: DateTimeOffset.Parse("11:36 PM")); // This creates a mock PartitionEvent to return from the consumer client. @@ -480,7 +480,7 @@ public async Task PartitionReceiverMock() systemProperties: new Dictionary(), //arbitrary value partitionKey: $"sample-key-{index}", sequenceNumber: 1234, - offset: 234, + offsetString: "234:5:93928381.1", enqueuedTime: DateTimeOffset.Parse("9:25 AM")); receivedEvents.Add(eventData); diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs index 5f22ee4831d60..246edb38c0deb 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs @@ -463,19 +463,21 @@ private static string GetOperationDetails(EventProcessorHostPartition context, s // leave the property name as lease for backcompat with T1 writer.WritePropertyName("lease"); writer.WriteStartObject(); - WritePropertyIfNotNull(writer, "offset", context.Checkpoint.Value.Offset.ToString(CultureInfo.InvariantCulture)); + WritePropertyIfNotNull(writer, "offset", context.Checkpoint.Value.Offset); WritePropertyIfNotNull(writer, "sequenceNumber", context.Checkpoint.Value.SequenceNumber.ToString(CultureInfo.InvariantCulture)); writer.WriteEndObject(); } // Log RuntimeInformation if EnableReceiverRuntimeMetric is enabled - writer.WritePropertyName("runtimeInformation"); - writer.WriteStartObject(); - WritePropertyIfNotNull(writer, "lastEnqueuedOffset", context.LastEnqueuedEventProperties.Offset?.ToString(CultureInfo.InvariantCulture)); - WritePropertyIfNotNull(writer, "lastSequenceNumber", context.LastEnqueuedEventProperties.SequenceNumber?.ToString(CultureInfo.InvariantCulture)); - WritePropertyIfNotNull(writer, "lastEnqueuedTimeUtc", context.LastEnqueuedEventProperties.EnqueuedTime?.ToString("o", CultureInfo.InvariantCulture)); - writer.WriteEndObject(); - + if (context.LastEnqueuedEventProperties != null) + { + writer.WritePropertyName("runtimeInformation"); + writer.WriteStartObject(); + WritePropertyIfNotNull(writer, "lastEnqueuedOffset", context.LastEnqueuedEventProperties.OffsetString); + WritePropertyIfNotNull(writer, "lastSequenceNumber", context.LastEnqueuedEventProperties.SequenceNumber?.ToString(CultureInfo.InvariantCulture)); + WritePropertyIfNotNull(writer, "lastEnqueuedTimeUtc", context.LastEnqueuedEventProperties.EnqueuedTime?.ToString("o", CultureInfo.InvariantCulture)); + writer.WriteEndObject(); + } writer.WriteEndObject(); return sw.ToString(); diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Microsoft.Azure.WebJobs.Extensions.EventHubs.csproj b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Microsoft.Azure.WebJobs.Extensions.EventHubs.csproj index a192b65d8a04e..539e414cb53fd 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Microsoft.Azure.WebJobs.Extensions.EventHubs.csproj +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Microsoft.Azure.WebJobs.Extensions.EventHubs.csproj @@ -12,7 +12,9 @@ - + + + @@ -22,6 +24,7 @@ + diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/CheckpointInfo.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/CheckpointInfo.cs index e1854f2609f14..2558c84f31495 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/CheckpointInfo.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/CheckpointInfo.cs @@ -7,11 +7,11 @@ namespace Microsoft.Azure.WebJobs.EventHubs.Processor { internal struct CheckpointInfo { - public long Offset { get; } + public string Offset { get; } public long SequenceNumber { get; } public DateTimeOffset? LastModified { get; } - public CheckpointInfo(long offset, long sequenceNumber, DateTimeOffset? lastModified) + public CheckpointInfo(string offset, long sequenceNumber, DateTimeOffset? lastModified) { Offset = offset; SequenceNumber = sequenceNumber; diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHost.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHost.cs index ea2ee5e3cfacf..339f2e1564483 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHost.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHost.cs @@ -66,7 +66,7 @@ protected override async Task GetCheckpointAsync(strin if (checkpoint is BlobCheckpointStoreInternal.BlobStorageCheckpoint blobCheckpoint && blobCheckpoint is not null) { - _lastReadCheckpoint[partitionId] = new CheckpointInfo(blobCheckpoint.Offset ?? -1, blobCheckpoint.SequenceNumber ?? -1, + _lastReadCheckpoint[partitionId] = new CheckpointInfo(blobCheckpoint.Offset, blobCheckpoint.SequenceNumber ?? -1, blobCheckpoint.LastModified); } diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHostPartition.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHostPartition.cs index 2487fa852d1cc..5a128fc2cca5e 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHostPartition.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Processor/EventProcessorHostPartition.cs @@ -62,7 +62,7 @@ public LastEnqueuedEventProperties LastEnqueuedEventProperties public async Task CheckpointAsync(EventData checkpointEvent) { await ProcessorHost.CheckpointAsync(PartitionId, checkpointEvent).ConfigureAwait(false); - Checkpoint = new CheckpointInfo(checkpointEvent.Offset, checkpointEvent.SequenceNumber, DateTimeOffset.UtcNow); + Checkpoint = new CheckpointInfo(checkpointEvent.OffsetString, checkpointEvent.SequenceNumber, DateTimeOffset.UtcNow); } private class EventProcessorHostPartitionContext : TriggerPartitionContext diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerBindingStrategy.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerBindingStrategy.cs index c6c1d17dd9cad..d91f39de57c7c 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerBindingStrategy.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerBindingStrategy.cs @@ -114,7 +114,7 @@ internal static void AddBindingData(Dictionary bindingData, Even for (int i = 0; i < events.Length; i++) { partitionKeys[i] = events[i].PartitionKey; - offsets[i] = events[i].Offset.ToString(CultureInfo.InvariantCulture); + offsets[i] = events[i].OffsetString; sequenceNumbers[i] = events[i].SequenceNumber; enqueuedTimesUtc[i] = events[i].EnqueuedTime.DateTime; properties[i] = events[i].Properties; @@ -125,7 +125,7 @@ internal static void AddBindingData(Dictionary bindingData, Even private static void AddBindingData(Dictionary bindingData, EventData eventData) { SafeAddValue(() => bindingData.Add("PartitionKey", eventData.PartitionKey)); - SafeAddValue(() => bindingData.Add("Offset", eventData.Offset)); + SafeAddValue(() => bindingData.Add("Offset", eventData.OffsetString)); SafeAddValue(() => bindingData.Add("SequenceNumber", eventData.SequenceNumber)); SafeAddValue(() => bindingData.Add("EnqueuedTimeUtc", eventData.EnqueuedTime.DateTime)); SafeAddValue(() => bindingData.Add("Properties", eventData.Properties)); @@ -155,7 +155,7 @@ private static IDictionary GetSystemPropertiesForBinding(EventDa // Following is needed to maintain structure of bindingdata: https://github.com/Azure/azure-webjobs-sdk/pull/1849 modifiedDictionary["SequenceNumber"] = eventData.SequenceNumber; - modifiedDictionary["Offset"] = eventData.Offset; + modifiedDictionary["Offset"] = eventData.OffsetString; modifiedDictionary["PartitionKey"] = eventData.PartitionKey; modifiedDictionary["EnqueuedTimeUtc"] = eventData.EnqueuedTime.DateTime; return modifiedDictionary; diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerInput.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerInput.cs index 5400435d0c5ed..576f8b4abcbc2 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerInput.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Triggers/EventHubTriggerInput.cs @@ -67,7 +67,7 @@ public Dictionary GetTriggerDetails(EventProcessorPartition cont string offset, enqueueTimeUtc, sequenceNumber; if (IsSingleDispatch) { - offset = Events[0].Offset.ToString(CultureInfo.InvariantCulture); + offset = Events[0].OffsetString; enqueueTimeUtc = Events[0].EnqueuedTime.ToString("o", CultureInfo.InvariantCulture); sequenceNumber = Events[0].SequenceNumber.ToString(CultureInfo.InvariantCulture); } @@ -76,7 +76,7 @@ public Dictionary GetTriggerDetails(EventProcessorPartition cont EventData first = Events[0]; EventData last = Events[Events.Length - 1]; - offset = $"{first.Offset}-{last.Offset}"; + offset = $"{first.OffsetString}-{last.OffsetString}"; enqueueTimeUtc = $"{first.EnqueuedTime.ToString("o", CultureInfo.InvariantCulture)}-{last.EnqueuedTime.ToString("o", CultureInfo.InvariantCulture)}"; sequenceNumber = $"{first.SequenceNumber}-{last.SequenceNumber}"; } diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs index ad193b8cf8412..2414e2d15397a 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +// Ignore Spelling: Poco evt + using System; using System.Collections.Generic; using System.Linq; @@ -925,7 +927,7 @@ await checkpointStore.UpdateCheckpointAsync( EventHubConsumerClient.DefaultConsumerGroupName, partition, producer.Identifier, - new CheckpointPosition(-1), + new CheckpointPosition("-1", -1), CancellationToken.None); } } diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubListenerTests.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubListenerTests.cs index 2b7b44bd3fd0a..a94cbe6e534ad 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubListenerTests.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubListenerTests.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +// Ignore Spelling: Checkpointing Rebalancing + using System; using System.Collections.Generic; using System.Linq; @@ -186,7 +188,7 @@ public async Task ProcessEvents_MultipleDispatch_MinBatch_CheckpointsCorrectly_R var processor = new Mock(MockBehavior.Strict); processor.Setup(p => p.CheckpointAsync(partitionContext.PartitionId, It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo(123, 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromSeconds(1)))); + processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo("123:1:11", 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromSeconds(1)))); partitionContext.ProcessorHost = processor.Object; var loggerMock = new Mock(); @@ -240,7 +242,7 @@ public async Task ProcessEvents_MultipleDispatch_MinBatch_CheckpointsCorrectly_O var processor = new Mock(MockBehavior.Strict); processor.Setup(p => p.CheckpointAsync(partitionContext.PartitionId, It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo(123, 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)))); + processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo("123", 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)))); partitionContext.ProcessorHost = processor.Object; var loggerMock = new Mock(); @@ -292,7 +294,7 @@ public async Task ProcessEvents_MultipleDispatch_MinBatch_BackgroundInvokesParti var processor = new Mock(MockBehavior.Strict); processor.Setup(p => p.CheckpointAsync(partitionContext.PartitionId, It.IsAny(), It.IsAny())).Returns(Task.CompletedTask); - processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo(123, 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)))); + processor.Setup(p => p.GetLastReadCheckpoint(partitionContext.PartitionId)).Returns(() => new CheckpointInfo("123", 45678, DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)))); partitionContext.ProcessorHost = processor.Object; // Because the first invocation will allow a partial batch due to the old checkpoint, diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubTests.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubTests.cs index b41229d684641..9dd2cb29949e2 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubTests.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubTests.cs @@ -84,7 +84,7 @@ public void GetBindingData_SingleDispatch_ReturnsExpectedValue() Assert.AreSame(input.ProcessorPartition.PartitionContext, bindingData["TriggerPartitionContext"]); Assert.AreSame(input.ProcessorPartition.PartitionContext, bindingData["PartitionContext"]); Assert.AreEqual(evt.PartitionKey, bindingData["PartitionKey"]); - Assert.AreEqual(evt.Offset, bindingData["Offset"]); + Assert.AreEqual(evt.OffsetString, bindingData["Offset"]); Assert.AreEqual(evt.SequenceNumber, bindingData["SequenceNumber"]); Assert.AreEqual(evt.EnqueuedTime.DateTime, bindingData["EnqueuedTimeUtc"]); Assert.AreSame(evt.Properties, bindingData["Properties"]); @@ -99,7 +99,7 @@ public void GetBindingData_SingleDispatch_ReturnsExpectedValue() } private static EventData GetSystemProperties(byte[] body, string partitionKey = "TestKey") => - EventHubsModelFactory.EventData(new BinaryData(body), partitionKey: partitionKey, offset: 140, enqueuedTime: DateTimeOffset.MinValue, sequenceNumber: 4294967296, systemProperties: new Dictionary() + EventHubsModelFactory.EventData(new BinaryData(body), partitionKey: partitionKey, offsetString: "140:1:12", enqueuedTime: DateTimeOffset.MinValue, sequenceNumber: 4294967296, systemProperties: new Dictionary() { {"iothub-connection-device-id", "testDeviceId"}, {"iothub-enqueuedtime", DateTime.MinValue} diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubsMetricsProviderTests.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubsMetricsProviderTests.cs index 80e6803b7e041..a8ebba897d8ad 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubsMetricsProviderTests.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubsMetricsProviderTests.cs @@ -72,7 +72,7 @@ public async Task CreateTriggerMetrics_ReturnsExpectedResult() this._checkpoints = new EventProcessorCheckpoint[] { - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 0 } + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 0 } }; var metrics = await _metricsProvider.GetMetricsAsync(); @@ -108,7 +108,7 @@ public async Task CreateTriggerMetrics_ReturnsExpectedResult() // Checkpointing is ahead of partition info and invalid (SequenceNumber > LastEnqueued) this._checkpoints = new EventProcessorCheckpoint[] { - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 999, SequenceNumber = 11 } + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "999", SequenceNumber = 11 } }; _partitions = new List @@ -129,9 +129,9 @@ public async Task CreateTriggerMetrics_MultiplePartitions_ReturnsExpectedResult( // No messages processed, no messages in queue this._checkpoints = new EventProcessorCheckpoint[] { - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 0, PartitionId = "1" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 0, PartitionId = "2" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 0, PartitionId = "3" } + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 0, PartitionId = "1" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 0, PartitionId = "2" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 0, PartitionId = "3" } }; _partitions = new List @@ -150,9 +150,9 @@ public async Task CreateTriggerMetrics_MultiplePartitions_ReturnsExpectedResult( // Messages processed, Messages in queue this._checkpoints = new EventProcessorCheckpoint[] { - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 2, PartitionId = "1" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 3, PartitionId = "2" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 4, PartitionId = "3" } + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 2, PartitionId = "1" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 3, PartitionId = "2" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 4, PartitionId = "3" } }; _partitions = new List @@ -171,9 +171,9 @@ public async Task CreateTriggerMetrics_MultiplePartitions_ReturnsExpectedResult( // One invalid sample this._checkpoints = new EventProcessorCheckpoint[] { - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 2, PartitionId = "1" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 3, PartitionId = "2" }, - new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = 0, SequenceNumber = 4, PartitionId = "3" } + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 2, PartitionId = "1" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 3, PartitionId = "2" }, + new BlobCheckpointStoreInternal.BlobStorageCheckpoint { Offset = "0", SequenceNumber = 4, PartitionId = "3" } }; _partitions = new List @@ -245,7 +245,7 @@ public async Task CreateTriggerMetrics_DifferentCheckpointFormats_ReturnsExpecte long checkpointSequenceNumber, long partitionBeginningSequenceNumber, long partitionLastSequenceNumber, - long expectedUprocessedMessageCount) + long expectedUnprocessedMessageCount) { if (!isNullCheckpoint) { @@ -265,7 +265,7 @@ public async Task CreateTriggerMetrics_DifferentCheckpointFormats_ReturnsExpecte }; var metrics = await _metricsProvider.GetMetricsAsync(); - Assert.AreEqual(expectedUprocessedMessageCount, metrics.EventCount); + Assert.AreEqual(expectedUnprocessedMessageCount, metrics.EventCount); } } } diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/TestPartitionProperties.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/TestPartitionProperties.cs index be5fb9527b82e..b3952349a74e5 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/TestPartitionProperties.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/TestPartitionProperties.cs @@ -8,7 +8,7 @@ namespace Microsoft.Azure.WebJobs.EventHubs.UnitTests { public class TestPartitionProperties : PartitionProperties { - public TestPartitionProperties(string eventHubName = default, string partitionId = default, bool isEmpty = default, long beginningSequenceNumber = default, long lastSequenceNumber = default, long lastOffset = default, DateTimeOffset lastEnqueuedTime = default) : base(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, lastOffset, lastEnqueuedTime) + public TestPartitionProperties(string eventHubName = default, string partitionId = default, bool isEmpty = default, long beginningSequenceNumber = default, long lastSequenceNumber = default, string lastOffset = default, DateTimeOffset lastEnqueuedTime = default) : base(eventHubName, partitionId, isEmpty, beginningSequenceNumber, lastSequenceNumber, lastOffset, lastEnqueuedTime) { } } From 5aa47a1d8d4c4d15529f2dec3e6ba619cb52b5f8 Mon Sep 17 00:00:00 2001 From: Jesse Squire Date: Wed, 13 Nov 2024 14:50:10 -0800 Subject: [PATCH 2/4] Fixing AOT warning --- .../src/Diagnostics/EventProcessorClientEventSource.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs index 88fe9180a7359..da596dbb86884 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Diagnostics/EventProcessorClientEventSource.cs @@ -398,6 +398,7 @@ private unsafe void WriteEvent(int eventId, /// [NonEvent] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = EventSourceSuppressMessage)] private unsafe void WriteEvent(int eventId, string arg1, string arg2, From 5f97d17c7380643ac52879362fee457ee59b0352 Mon Sep 17 00:00:00 2001 From: Jesse Squire Date: Sat, 23 Nov 2024 10:12:38 -0800 Subject: [PATCH 3/4] Fixing build error from rebase and regenerating net8.0 API listings. --- ...re.Messaging.EventHubs.Processor.net8.0.cs | 4 ++ .../api/Azure.Messaging.EventHubs.net8.0.cs | 61 ++++++++++++++++++- .../EventHubListener.PartitionProcessor.cs | 16 +++-- 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.net8.0.cs b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.net8.0.cs index 681137633b6d8..efc7c767e2f87 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.net8.0.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/api/Azure.Messaging.EventHubs.Processor.net8.0.cs @@ -38,6 +38,8 @@ public EventProcessorClient(Azure.Storage.Blobs.BlobContainerClient checkpointSt [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task ValidateProcessingPreconditions(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -71,6 +73,8 @@ public BlobCheckpointStore(Azure.Storage.Blobs.BlobContainerClient blobContainer public override System.Threading.Tasks.Task> ClaimOwnershipAsync(System.Collections.Generic.IEnumerable desiredOwnership, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task GetCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task> ListOwnershipAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] public override System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } } diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.net8.0.cs b/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.net8.0.cs index 12662ec7eec23..091f1b9a6203b 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.net8.0.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/api/Azure.Messaging.EventHubs.net8.0.cs @@ -6,9 +6,13 @@ public EventData() { } public EventData(Azure.Core.Amqp.AmqpAnnotatedMessage amqpMessage) { } public EventData(System.BinaryData eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] protected EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + protected EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, string offsetString = null, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } public EventData(System.ReadOnlyMemory eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.")] protected EventData(System.ReadOnlyMemory eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset), string partitionKey = null) { } public EventData(string eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -26,7 +30,10 @@ public EventData(string eventBody) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool IsReadOnly { get { throw null; } } public string MessageId { get { throw null; } set { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] public long Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public string PartitionKey { get { throw null; } } public System.Collections.Generic.IDictionary Properties { get { throw null; } } public long SequenceNumber { get { throw null; } } @@ -80,8 +87,11 @@ public EventHubConnectionOptions() { } } public partial class EventHubProperties { - protected internal EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + protected EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { } + protected internal EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds, bool isGeoReplicationEnabled) { } public System.DateTimeOffset CreatedOn { get { throw null; } } + public bool IsGeoReplicationEnabled { get { throw null; } } public string Name { get { throw null; } } public string[] PartitionIds { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -139,14 +149,26 @@ public enum FailureReason } public static partial class EventHubsModelFactory { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.EventData EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, string partitionKey = null, long sequenceNumber = (long)-9223372036854775808, long offset = (long)-9223372036854775808, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset)) { throw null; } + public static Azure.Messaging.EventHubs.EventData EventData(System.BinaryData eventBody, System.Collections.Generic.IDictionary properties = null, System.Collections.Generic.IReadOnlyDictionary systemProperties = null, string partitionKey = null, long sequenceNumber = (long)-9223372036854775808, string offsetString = null, System.DateTimeOffset enqueuedTime = default(System.DateTimeOffset)) { throw null; } public static Azure.Messaging.EventHubs.Producer.EventDataBatch EventDataBatch(long batchSizeBytes, System.Collections.Generic.IList batchEventStore, Azure.Messaging.EventHubs.Producer.CreateBatchOptions batchOptions = null, System.Func tryAddCallback = null) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.EventHubProperties EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.Messaging.EventHubs.EventHubProperties EventHubProperties(string name, System.DateTimeOffset createdOn, string[] partitionIds, bool isGeoReplicationEnabled) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + public static Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties LastEnqueuedEventProperties(long? lastSequenceNumber, string lastOffsetString, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.Consumer.PartitionContext PartitionContext(string partitionId, Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties lastEnqueuedEventProperties = default(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties)) { throw null; } public static Azure.Messaging.EventHubs.Consumer.PartitionContext PartitionContext(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties lastEnqueuedEventProperties = default(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public static Azure.Messaging.EventHubs.PartitionProperties PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, long lastOffset, System.DateTimeOffset lastEnqueuedTime) { throw null; } + public static Azure.Messaging.EventHubs.PartitionProperties PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, string lastOffsetString, System.DateTimeOffset lastEnqueuedTime) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Messaging.EventHubs.Producer.PartitionPublishingProperties PartitionPublishingProperties(bool isIdempotentPublishingEnabled, long? producerGroupId, short? ownerLevel, int? lastPublishedSequenceNumber) { throw null; } } @@ -184,12 +206,18 @@ public enum EventHubsTransportType } public partial class PartitionProperties { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] protected internal PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, long lastOffset, System.DateTimeOffset lastEnqueuedTime) { } + protected internal PartitionProperties(string eventHubName, string partitionId, bool isEmpty, long beginningSequenceNumber, long lastSequenceNumber, string lastOffsetString, System.DateTimeOffset lastEnqueuedTime) { } public long BeginningSequenceNumber { get { throw null; } } public string EventHubName { get { throw null; } } public string Id { get { throw null; } } public bool IsEmpty { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'LastEnqueuedOffsetString' instead.", false)] public long LastEnqueuedOffset { get { throw null; } } + public string LastEnqueuedOffsetString { get { throw null; } } public long LastEnqueuedSequenceNumber { get { throw null; } } public System.DateTimeOffset LastEnqueuedTime { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -260,7 +288,10 @@ public partial struct EventPosition : System.IEquatable { + private object _dummy; private int _dummyPrimitive; + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] + public LastEnqueuedEventProperties(long? lastSequenceNumber, long lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use the overload with a string-based offset instead.", false)] public LastEnqueuedEventProperties(long? lastSequenceNumber, long? lastOffset, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } + public LastEnqueuedEventProperties(long? lastSequenceNumber, string lastOffsetString, System.DateTimeOffset? lastEnqueuedTime, System.DateTimeOffset? lastReceivedTime) { throw null; } public System.DateTimeOffset? EnqueuedTime { get { throw null; } } public System.DateTimeOffset? LastReceivedTime { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] public long? Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public long? SequenceNumber { get { throw null; } } public bool Equals(Azure.Messaging.EventHubs.Consumer.LastEnqueuedEventProperties other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -332,6 +373,8 @@ protected CheckpointStore() { } public abstract System.Threading.Tasks.Task> ClaimOwnershipAsync(System.Collections.Generic.IEnumerable desiredOwnership, System.Threading.CancellationToken cancellationToken); public abstract System.Threading.Tasks.Task GetCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, System.Threading.CancellationToken cancellationToken); public abstract System.Threading.Tasks.Task> ListOwnershipAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, System.Threading.CancellationToken cancellationToken); + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] public virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string fullyQualifiedNamespace, string eventHubName, string consumerGroup, string partitionId, string clientIdentifier, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -394,6 +437,7 @@ protected EventProcessor(int eventBatchMaximumCount, string consumerGroup, strin public string ConsumerGroup { get { throw null; } } protected bool EnableBatchTracing { get { throw null; } set { } } public string EventHubName { get { throw null; } } + protected Azure.Messaging.EventHubs.EventHubProperties EventHubProperties { get { throw null; } } public string FullyQualifiedNamespace { get { throw null; } } public string Identifier { get { throw null; } } public bool IsRunning { get { throw null; } protected set { } } @@ -421,6 +465,8 @@ protected EventProcessor(int eventBatchMaximumCount, string consumerGroup, strin [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } protected virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected virtual System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } protected internal virtual System.Threading.Tasks.Task ValidateProcessingPreconditions(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -483,6 +529,8 @@ protected PluggableCheckpointStoreEventProcessor(Azure.Messaging.EventHubs.Primi protected override System.Threading.Tasks.Task GetCheckpointAsync(string partitionId, System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task> ListOwnershipAsync(System.Threading.CancellationToken cancellationToken) { throw null; } protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, Azure.Messaging.EventHubs.Processor.CheckpointPosition startingPosition, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset via the overload accepting 'CheckpointPosition' instead.", false)] protected override System.Threading.Tasks.Task UpdateCheckpointAsync(string partitionId, long offset, long? sequenceNumber, System.Threading.CancellationToken cancellationToken) { throw null; } } } @@ -491,8 +539,19 @@ namespace Azure.Messaging.EventHubs.Processor [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct CheckpointPosition : System.IEquatable { + private object _dummy; private int _dummyPrimitive; + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee that a sequence number-only checkpoint can access the event stream for all resource configurations. Reading events may not work in all cases going forward. Please provide a string-based offset.", false)] public CheckpointPosition(long sequenceNumber) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Checkpoints created from a numeric offset may not work in all cases going forward. Please use a string-based offset instead.", false)] + public CheckpointPosition(long offset, long sequenceNumber = (long)-9223372036854775808) { throw null; } + public CheckpointPosition(string offsetString, long sequenceNumber = (long)-9223372036854775808) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("The Event Hubs service does not guarantee a numeric offset for all resource configurations. Please use 'OffsetString' instead.", false)] + public long Offset { get { throw null; } } + public string OffsetString { get { throw null; } } public long SequenceNumber { get { throw null; } } public bool Equals(Azure.Messaging.EventHubs.Processor.CheckpointPosition other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs index 246edb38c0deb..d4fa786d05355 100644 --- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs +++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/src/Listeners/EventHubListener.PartitionProcessor.cs @@ -469,15 +469,13 @@ private static string GetOperationDetails(EventProcessorHostPartition context, s } // Log RuntimeInformation if EnableReceiverRuntimeMetric is enabled - if (context.LastEnqueuedEventProperties != null) - { - writer.WritePropertyName("runtimeInformation"); - writer.WriteStartObject(); - WritePropertyIfNotNull(writer, "lastEnqueuedOffset", context.LastEnqueuedEventProperties.OffsetString); - WritePropertyIfNotNull(writer, "lastSequenceNumber", context.LastEnqueuedEventProperties.SequenceNumber?.ToString(CultureInfo.InvariantCulture)); - WritePropertyIfNotNull(writer, "lastEnqueuedTimeUtc", context.LastEnqueuedEventProperties.EnqueuedTime?.ToString("o", CultureInfo.InvariantCulture)); - writer.WriteEndObject(); - } + writer.WritePropertyName("runtimeInformation"); + writer.WriteStartObject(); + WritePropertyIfNotNull(writer, "lastEnqueuedOffset", context.LastEnqueuedEventProperties.OffsetString); + WritePropertyIfNotNull(writer, "lastSequenceNumber", context.LastEnqueuedEventProperties.SequenceNumber?.ToString(CultureInfo.InvariantCulture)); + WritePropertyIfNotNull(writer, "lastEnqueuedTimeUtc", context.LastEnqueuedEventProperties.EnqueuedTime?.ToString("o", CultureInfo.InvariantCulture)); + writer.WriteEndObject(); + writer.WriteEndObject(); return sw.ToString(); From 94100a33a4ed5404f91df94c71c09e1c5e4f7584 Mon Sep 17 00:00:00 2001 From: Jesse Squire Date: Wed, 27 Nov 2024 12:27:48 -0800 Subject: [PATCH 4/4] Fixing comments --- .../Azure.Messaging.EventHubs/src/EventHubProperties.cs | 4 ++-- .../Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs index 013b2a5f408aa..08ff4a2053725 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubProperties.cs @@ -32,7 +32,7 @@ public class EventHubProperties public string[] PartitionIds { get; } /// - /// A flag indicating whether or not the Event Hub has geo-replication enabled. + /// A flag indicating whether or not the Event Hubs namespace has geo-replication enabled. /// /// public bool IsGeoReplicationEnabled { get; } @@ -44,7 +44,7 @@ public class EventHubProperties /// The name of the Event Hub. /// The date and time at which the Event Hub was created. /// The set of unique identifiers for each partition. - /// A flag indicating whether or not the Event Hub has geo-replication enabled. + /// A flag indicating whether or not the Event Hubs namespace has geo-replication enabled. /// protected internal EventHubProperties(string name, DateTimeOffset createdOn, diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs index 956ddc3de9fc1..22b79d4ba4523 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventHubsModelFactory.cs @@ -42,7 +42,7 @@ public static EventHubProperties EventHubProperties(string name, /// The name of the Event Hub. /// The date and time at which the Event Hub was created. /// The set of unique identifiers for each partition. - /// A flag indicating whether or not the Event Hub has geo-replication enabled. + /// A flag indicating whether or not the Event Hubs namespace has geo-replication enabled. /// [EditorBrowsable(EditorBrowsableState.Never)] public static EventHubProperties EventHubProperties(string name,