public async Task <(string QueueName, string MessageId)> EnqueueAsync(BlobTriggerMessage message, CancellationToken cancellationToken) { string contents = JsonConvert.SerializeObject(message, JsonSerialization.Settings); var receipt = await _queue.AddMessageAndCreateIfNotExistsAsync(BinaryData.FromString(contents), cancellationToken).ConfigureAwait(false); _watcher.Notify(_queue.Name); return(QueueName : _queue.Name, MessageId : receipt.MessageId); }
private static QueueMessage CreateMessage(string functionId, string eTag) { BlobTriggerMessage triggerMessage = new BlobTriggerMessage { FunctionId = functionId, // BlobType = StorageBlobType.BlockBlob, $$$ ContainerName = TestContainerName, BlobName = TestBlobName, ETag = eTag }; return(CreateMessage(triggerMessage)); }
private static QueueMessage CreateMessage(BlobTriggerMessage triggerMessage) { return(CreateMessage(JsonConvert.SerializeObject(triggerMessage))); }
public async Task <FunctionResult> ExecuteAsync(QueueMessage value, CancellationToken cancellationToken) { BlobTriggerMessage message = JsonConvert.DeserializeObject <BlobTriggerMessage>(value.Body.ToValidUTF8String(), JsonSerialization.Settings); if (message == null) { throw new InvalidOperationException("Invalid blob trigger message."); } string functionId = message.FunctionId; if (functionId == null) { throw new InvalidOperationException("Invalid function ID."); } // Ensure that the function ID is still valid. Otherwise, ignore this message. FunctionResult successResult = new FunctionResult(true); if (!_registrations.TryGetValue(functionId, out BlobQueueRegistration registration)) { Logger.FunctionNotFound(_logger, message.BlobName, functionId, value.MessageId); return(successResult); } var container = registration.BlobServiceClient.GetBlobContainerClient(message.ContainerName); string blobName = message.BlobName; BlobBaseClient blob; BlobProperties blobProperties; try { (blob, blobProperties) = await container.GetBlobReferenceFromServerAsync(blobName, cancellationToken).ConfigureAwait(false); } catch (RequestFailedException exception) when(exception.IsNotFound() || exception.IsOk()) { // If the blob no longer exists, just ignore this message. Logger.BlobNotFound(_logger, blobName, value.MessageId); return(successResult); } // Ensure the blob still exists with the same ETag. string possibleETag = blobProperties.ETag.ToString(); // If the blob still exists but the ETag is different, delete the message but do a fast path notification. if (!string.Equals(message.ETag, possibleETag, StringComparison.Ordinal)) { _blobWrittenWatcher.Notify(new Extensions.Storage.Blobs.BlobWithContainer <BlobBaseClient>(container, blob)); return(successResult); } // If the blob still exists and its ETag is still valid, execute. // Note: it's possible the blob could change/be deleted between now and when the function executes. Guid?parentId = await _causalityReader.GetWriterAsync(blob, cancellationToken).ConfigureAwait(false); // Include the queue details here. IDictionary <string, string> details = PopulateTriggerDetails(value); if (blobProperties.CreatedOn != null) { details[BlobCreatedKey] = blobProperties.CreatedOn.ToString(Constants.DateTimeFormatString, CultureInfo.InvariantCulture); } if (blobProperties.LastModified != null) { details[BlobLastModifiedKey] = blobProperties.LastModified.ToString(Constants.DateTimeFormatString, CultureInfo.InvariantCulture); } TriggeredFunctionData input = new TriggeredFunctionData { ParentId = parentId, TriggerValue = blob, TriggerDetails = details }; return(await registration.Executor.TryExecuteAsync(input, cancellationToken).ConfigureAwait(false)); }
public async Task <FunctionResult> ExecuteAsync(BlobTriggerExecutorContext context, CancellationToken cancellationToken) { BlobBaseClient value = context.Blob.BlobClient; BlobTriggerScanSource triggerSource = context.TriggerSource; string pollId = context.PollId; // Avoid unnecessary network calls for non-matches. First, check to see if the blob matches this trigger. IReadOnlyDictionary <string, object> bindingData = _input.CreateBindingData(value.ToBlobPath()); if (bindingData == null) { string pattern = new BlobPath(_input.ContainerNamePattern, _input.BlobNamePattern).ToString(); Logger.BlobDoesNotMatchPattern(_logger, _functionDescriptor.LogName, value.Name, pattern, pollId, triggerSource); // Blob is not a match for this trigger. return(new FunctionResult(true)); } // Next, check to see if the blob currently exists (and, if so, what the current ETag is). BlobProperties blobProperties = await value.FetchPropertiesOrNullIfNotExistAsync(cancellationToken).ConfigureAwait(false); string possibleETag = null; if (blobProperties != null) { possibleETag = blobProperties.ETag.ToString(); } if (possibleETag == null) { Logger.BlobHasNoETag(_logger, _functionDescriptor.LogName, value.Name, pollId, triggerSource); // If the blob doesn't exist and have an ETag, don't trigger on it. return(new FunctionResult(true)); } var receiptBlob = _receiptManager.CreateReference(_hostId, _functionDescriptor.Id, value.BlobContainerName, value.Name, possibleETag); // Check for the completed receipt. If it's already there, noop. BlobReceipt unleasedReceipt = await _receiptManager.TryReadAsync(receiptBlob, cancellationToken).ConfigureAwait(false); if (unleasedReceipt != null && unleasedReceipt.IsCompleted) { Logger.BlobAlreadyProcessed(_logger, _functionDescriptor.LogName, value.Name, possibleETag, pollId, triggerSource); return(new FunctionResult(true)); } else if (unleasedReceipt == null) { // Try to create (if not exists) an incomplete receipt. if (!await _receiptManager.TryCreateAsync(receiptBlob, cancellationToken).ConfigureAwait(false)) { // Someone else just created the receipt; wait to try to trigger until later. // Alternatively, we could just ignore the return result and see who wins the race to acquire the // lease. return(new FunctionResult(false)); } } string leaseId = await _receiptManager.TryAcquireLeaseAsync(receiptBlob, cancellationToken).ConfigureAwait(false); if (leaseId == null) { // If someone else owns the lease and just took over this receipt or deleted it; // wait to try to trigger until later. return(new FunctionResult(false)); } try { // Check again for the completed receipt. If it's already there, noop. BlobReceipt receipt = await _receiptManager.TryReadAsync(receiptBlob, cancellationToken).ConfigureAwait(false); Debug.Assert(receipt != null); // We have a (30 second) lease on the blob; it should never disappear on us. if (receipt.IsCompleted) { Logger.BlobAlreadyProcessed(_logger, _functionDescriptor.LogName, value.Name, possibleETag, pollId, triggerSource); await _receiptManager.ReleaseLeaseAsync(receiptBlob, leaseId, cancellationToken).ConfigureAwait(false); return(new FunctionResult(true)); } // We've successfully acquired a lease to enqueue the message for this blob trigger. Enqueue the message, // complete the receipt and release the lease. // Enqueue a message: function ID + blob path + ETag BlobTriggerMessage message = new BlobTriggerMessage { FunctionId = _functionDescriptor.Id, BlobType = blobProperties.BlobType, ContainerName = value.BlobContainerName, BlobName = value.Name, ETag = possibleETag }; var(queueName, messageId) = await _queueWriter.EnqueueAsync(message, cancellationToken).ConfigureAwait(false); Logger.BlobMessageEnqueued(_logger, _functionDescriptor.LogName, value.Name, messageId, queueName, pollId, triggerSource); // Complete the receipt await _receiptManager.MarkCompletedAsync(receiptBlob, leaseId, cancellationToken).ConfigureAwait(false); return(new FunctionResult(true)); } finally { await _receiptManager.ReleaseLeaseAsync(receiptBlob, leaseId, cancellationToken).ConfigureAwait(false); } }