/// <summary> /// Set a custom state on the session which can be later retrieved using <see cref="GetSessionStateAsync"/> /// </summary> /// /// <param name="sessionState">A <see cref="BinaryData"/> of session state</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <remarks>This state is stored on Service Bus forever unless you set an empty state on it.</remarks> /// /// <returns>A task to be resolved on when the operation has completed.</returns> public virtual async Task SetSessionStateAsync( BinaryData sessionState, CancellationToken cancellationToken = default) { Argument.AssertNotDisposed(IsClosed, nameof(ServiceBusSessionReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.SetSessionStateStart(Identifier, SessionId); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.SetSessionStateActivityName, sessionId: SessionId); scope.Start(); try { await InnerReceiver.SetStateAsync(sessionState, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.SetSessionStateException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.SetSessionStateComplete(Identifier); }
/// <summary> /// Renews the lock on the message. The lock will be renewed based on the setting specified on the queue. /// </summary> /// /// <remarks> /// When a message is received in <see cref="ReceiveMode.PeekLock"/> mode, the message is locked on the server for this /// receiver instance for a duration as specified during the Queue/Subscription creation (LockDuration). /// If processing of the message requires longer than this duration, the lock needs to be renewed. /// For each renewal, it resets the time the message is locked by the LockDuration set on the Entity. /// </remarks> /// /// <param name="lockToken">The lockToken of the <see cref="ServiceBusReceivedMessage"/> to renew the lock for.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> public virtual async Task <DateTimeOffset> RenewMessageLockAsync( string lockToken, CancellationToken cancellationToken = default) { ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); ThrowIfNotPeekLockMode(); ThrowIfSessionReceiver(); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.RenewMessageLockStart(Identifier, 1, lockToken); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.RenewMessageLockActivityName, lockToken: lockToken); scope.Start(); DateTimeOffset lockedUntil; try { lockedUntil = await InnerReceiver.RenewMessageLockAsync( lockToken, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.RenewMessageLockException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.RenewMessageLockComplete(Identifier); scope.AddAttribute(DiagnosticProperty.LockedUntilAttribute, lockedUntil); return(lockedUntil); }
/// <summary> Indicates that the receiver wants to defer the processing for the message.</summary> /// /// <param name="lockToken">The lockToken of the <see cref="ServiceBusReceivedMessage"/> to defer.</param> /// <param name="propertiesToModify">The properties of the message to modify while deferring the message.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <remarks> /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>, /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>. /// In order to receive this message again in the future, you will need to save the /// <see cref="ServiceBusReceivedMessage.SequenceNumber"/> /// and receive it using <see cref="ReceiveDeferredMessageAsync(long, CancellationToken)"/>. /// Deferring messages does not impact message's expiration, meaning that deferred messages can still expire. /// This operation can only be performed on messages that were received by this receiver. /// </remarks> /// /// <returns>A task to be resolved on when the operation has completed.</returns> public virtual async Task DeferMessageAsync( string lockToken, IDictionary <string, object> propertiesToModify = null, CancellationToken cancellationToken = default) { ThrowIfLockTokenIsEmpty(lockToken); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); ThrowIfNotPeekLockMode(); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.DeferMessageStart(Identifier, 1, lockToken); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.DeferActivityName, lockToken: lockToken); scope.Start(); try { await InnerReceiver.DeferAsync( lockToken, propertiesToModify, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.DeferMessageException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.DeferMessageComplete(Identifier); }
/// <summary> /// Receives a <see cref="IList{ServiceBusReceivedMessage}"/> of deferred messages identified by <paramref name="sequenceNumbers"/>. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// <param name="sequenceNumbers">An <see cref="IEnumerable{T}"/> containing the sequence numbers to receive.</param> /// /// <returns>Messages identified by sequence number are returned. Returns null if no messages are found. /// Throws if the messages have not been deferred.</returns> /// <seealso cref="DeferMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, CancellationToken)"/> /// <seealso cref="DeferMessageAsync(string, IDictionary{string, object}, CancellationToken)"/> public virtual async Task <IList <ServiceBusReceivedMessage> > ReceiveDeferredMessagesAsync( IEnumerable <long> sequenceNumbers, CancellationToken cancellationToken = default) { Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); Argument.AssertNotNullOrEmpty(sequenceNumbers, nameof(sequenceNumbers)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var sequenceNumbersList = sequenceNumbers.ToList(); Logger.ReceiveDeferredMessageStart(Identifier, sequenceNumbersList); using DiagnosticScope scope = ScopeFactory.CreateScope(DiagnosticProperty.ReceiveDeferredActivityName); scope.AddAttribute( DiagnosticProperty.SequenceNumbersAttribute, string.Join(",", sequenceNumbers)); scope.Start(); IList <ServiceBusReceivedMessage> deferredMessages = null; try { deferredMessages = await InnerReceiver.ReceiveDeferredMessagesAsync( sequenceNumbersList, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.ReceiveDeferredMessageException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.ReceiveDeferredMessageComplete(Identifier, deferredMessages.Count); scope.SetMessageData(deferredMessages); return(deferredMessages); }
/// <summary> /// Fetches a list of active messages without changing the state of the receiver or the message source. /// </summary> /// <param name="sequenceNumber">The sequence number from where to peek the message.</param> /// <param name="maxMessages">The maximum number of messages that will be fetched.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// <returns>An <see cref="IList{ServiceBusReceivedMessage}" /> of messages that were peeked.</returns> private async Task <IList <ServiceBusReceivedMessage> > PeekMessagesInternalAsync( long?sequenceNumber, int maxMessages, CancellationToken cancellationToken) { Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.PeekMessageStart(Identifier, sequenceNumber, maxMessages); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.PeekActivityName, requestedMessageCount: maxMessages); scope.Start(); IList <ServiceBusReceivedMessage> messages = new List <ServiceBusReceivedMessage>(); try { messages = await InnerReceiver.PeekMessagesAsync( sequenceNumber, maxMessages, cancellationToken) .ConfigureAwait(false); } catch (Exception exception) { Logger.PeekMessageException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.PeekMessageComplete(Identifier, messages.Count); scope.SetMessageData(messages); return(messages); }
public virtual async ValueTask DisposeAsync() { IsDisposed = true; Type clientType = GetType(); Logger.ClientDisposeStart(clientType, Identifier); try { await InnerReceiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { Logger.ClientDisposeException(clientType, Identifier, ex); throw; } Logger.ClientDisposeComplete(clientType, Identifier); }
/// <summary> /// Renews the lock on the session specified by the <see cref="SessionId"/>. The lock will be renewed based on the setting specified on the entity. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <remarks> /// <para> /// When you get session receiver, the session is locked for this receiver by the service for a duration as specified during the Queue/Subscription creation. /// If processing of the session requires longer than this duration, the session-lock needs to be renewed. /// For each renewal, it resets the time the session is locked by the LockDuration set on the Entity. /// </para> /// <para> /// Renewal of session renews all the messages in the session as well. Each individual message need not be renewed. /// </para> /// </remarks> public virtual async Task RenewSessionLockAsync(CancellationToken cancellationToken = default) { Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusSessionReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); ServiceBusEventSource.Log.RenewSessionLockStart(Identifier, SessionId); try { await InnerReceiver.RenewSessionLockAsync(cancellationToken).ConfigureAwait(false); } catch (Exception exception) { ServiceBusEventSource.Log.RenewSessionLockException(Identifier, exception); throw; } cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); ServiceBusEventSource.Log.RenewSessionLockComplete(Identifier); }
/// <summary> /// Receives a list of <see cref="ServiceBusReceivedMessage" /> from the entity using <see cref="ReceiveMode"/> mode. /// <see cref="ReceiveMode"/> defaults to PeekLock mode. /// This method doesn't guarantee to return exact `maxMessages` messages, /// even if there are `maxMessages` messages available in the queue or topic. /// </summary> /// /// <param name="maxMessages">The maximum number of messages that will be received.</param> /// <param name="maxWaitTime">An optional <see cref="TimeSpan"/> specifying the maximum time to wait for the first message before returning an empty list if no messages are available. /// If not specified, the <see cref="ServiceBusRetryOptions.TryTimeout"/> will be used.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>List of messages received. Returns an empty list if no message is found.</returns> public virtual async Task <IList <ServiceBusReceivedMessage> > ReceiveMessagesAsync( int maxMessages, TimeSpan?maxWaitTime = default, CancellationToken cancellationToken = default) { Argument.AssertAtLeast(maxMessages, 1, nameof(maxMessages)); Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusReceiver)); if (maxWaitTime.HasValue) { Argument.AssertPositive(maxWaitTime.Value, nameof(maxWaitTime)); } cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.ReceiveMessageStart(Identifier, maxMessages); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.ReceiveActivityName, requestedMessageCount: maxMessages); scope.Start(); IList <ServiceBusReceivedMessage> messages = null; try { messages = await InnerReceiver.ReceiveMessagesAsync( maxMessages, maxWaitTime, cancellationToken).ConfigureAwait(false); await ApplyPlugins(messages).ConfigureAwait(false); } catch (Exception exception) { Logger.ReceiveMessageException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.ReceiveMessageComplete(Identifier, messages.Count); scope.SetMessageData(messages); return(messages); }
/// <summary> /// Gets the session state. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The session state as byte array.</returns> public virtual async Task <byte[]> GetStateAsync(CancellationToken cancellationToken = default) { Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusSessionReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); ServiceBusEventSource.Log.GetSessionStateStart(Identifier, SessionId); byte[] sessionState = null; try { sessionState = await InnerReceiver.GetStateAsync(cancellationToken).ConfigureAwait(false); } catch (Exception exception) { ServiceBusEventSource.Log.GetSessionStateException(Identifier, exception); throw; } cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); ServiceBusEventSource.Log.GetSessionStateComplete(Identifier); return(sessionState); }
/// <summary> /// Set a custom state on the session which can be later retrieved using <see cref="GetSessionStateAsync"/> /// </summary> /// /// <param name="sessionState">A byte array of session state</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <remarks>This state is stored on Service Bus forever unless you set an empty state on it.</remarks> /// /// <returns>A task to be resolved on when the operation has completed.</returns> public virtual async Task SetSessionStateAsync( byte[] sessionState, CancellationToken cancellationToken = default) { Argument.AssertNotClosed(IsDisposed, nameof(ServiceBusSessionReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.SetSessionStateStart(Identifier, SessionId); try { await InnerReceiver.SetStateAsync(sessionState, cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.SetSessionStateException(Identifier, exception.ToString()); throw; } cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.SetSessionStateComplete(Identifier); }
/// <summary> /// Renews the lock on the session specified by the <see cref="SessionId"/>. The lock will be renewed based on the setting specified on the entity. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <remarks> /// <para> /// When you accept a session, the session is locked for this receiver by the service for a duration as specified during the Queue/Subscription creation. /// If processing of the session requires longer than this duration, the session-lock needs to be renewed. /// For each renewal, it resets the time the session is locked by the LockDuration set on the Entity. /// </para> /// <para> /// Renewal of session renews all the messages in the session as well. Each individual message need not be renewed. /// </para> /// </remarks> /// <exception cref="ServiceBusException"> /// The lock for the session has expired. /// The <see cref="ServiceBusException.Reason" /> will be set to <see cref="ServiceBusFailureReason.SessionLockLost"/> in this case. /// </exception> public virtual async Task RenewSessionLockAsync(CancellationToken cancellationToken = default) { Argument.AssertNotDisposed(IsClosed, nameof(ServiceBusSessionReceiver)); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); Logger.RenewSessionLockStart(Identifier, SessionId); using DiagnosticScope scope = ScopeFactory.CreateScope( DiagnosticProperty.RenewSessionLockActivityName, DiagnosticScope.ActivityKind.Client); scope.Start(); try { await InnerReceiver.RenewSessionLockAsync(cancellationToken).ConfigureAwait(false); } catch (Exception exception) { Logger.RenewSessionLockException(Identifier, exception.ToString()); scope.Failed(exception); throw; } Logger.RenewSessionLockComplete(Identifier); }
/// <summary> /// Opens an AMQP link for use with receiver operations. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> internal async Task OpenLinkAsync(CancellationToken cancellationToken) => await InnerReceiver.OpenLinkAsync(cancellationToken).ConfigureAwait(false);