Esempio n. 1
0
        /// <summary>
        ///   Updates the checkpoint using the given information for the associated partition and consumer group in the storage blob service.
        /// </summary>
        ///
        /// <param name="checkpoint">The checkpoint containing the information to be stored.</param>
        ///
        /// <returns>A task to be resolved on when the operation has completed.</returns>
        ///
        public override async Task UpdateCheckpointAsync(Checkpoint checkpoint)
        {
            var        blobName   = $"{ checkpoint.FullyQualifiedNamespace }/{ checkpoint.EventHubName }/{ checkpoint.ConsumerGroup }/{ checkpoint.PartitionId }";
            BlobClient blobClient = _containerClient.GetBlobClient(blobName);

            BlobProperties currentBlob;

            try
            {
                currentBlob = (await blobClient.GetPropertiesAsync().ConfigureAwait(false)).Value;
            }
            catch (StorageRequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.BlobNotFound)
            {
                Log($"Checkpoint with partition id = '{ checkpoint.PartitionId }' could not be updated because no associated ownership was found.");
                return;
            }

            // In case this key does not exist, ownerIdentifier is set to null.  The OwnerIdentifier in Checkpoint cannot
            // be null as well, so we won't be able to update the associated ownership.

            currentBlob.Metadata.TryGetValue(BlobMetadataKey.OwnerIdentifier, out var ownerIdentifier);

            if (ownerIdentifier == checkpoint.OwnerIdentifier)
            {
                var metadata = new Dictionary <string, string>()
                {
                    { BlobMetadataKey.OwnerIdentifier, checkpoint.OwnerIdentifier },
                    { BlobMetadataKey.Offset, checkpoint.Offset.ToString() },
                    { BlobMetadataKey.SequenceNumber, checkpoint.SequenceNumber.ToString() }
                };

                var accessConditions = new BlobAccessConditions
                {
                    HttpAccessConditions = new HttpAccessConditions {
                        IfMatch = currentBlob.ETag
                    }
                };

                try
                {
                    await blobClient.SetMetadataAsync(metadata, accessConditions).ConfigureAwait(false);

                    Log($"Checkpoint with partition id = '{ checkpoint.PartitionId }' updated.");
                }
                catch (StorageRequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.ConditionNotMet)
                {
                    Log($"Checkpoint with partition id = '{ checkpoint.PartitionId }' could not be updated because eTag has changed.");
                }
            }
            else
            {
                Log($"Checkpoint with partition id = '{ checkpoint.PartitionId }' could not be updated because owner has changed.");
            }
        }
Esempio n. 2
0
        /// <summary>
        ///   Attempts to claim ownership of partitions for processing.
        /// </summary>
        ///
        /// <param name="partitionOwnership">An enumerable containing all the ownership to claim.</param>
        ///
        /// <returns>An enumerable containing the successfully claimed ownership.</returns>
        ///
        public override async Task <IEnumerable <PartitionOwnership> > ClaimOwnershipAsync(IEnumerable <PartitionOwnership> partitionOwnership)
        {
            var claimedOwnership = new List <PartitionOwnership>();
            var metadata         = new Dictionary <string, string>();

            Response <BlobContentInfo> contentInfoResponse;
            Response <BlobInfo>        infoResponse;

            foreach (var ownership in partitionOwnership)
            {
                metadata[BlobMetadataKey.OwnerIdentifier] = ownership.OwnerIdentifier;
                metadata[BlobMetadataKey.Offset]          = ownership.Offset?.ToString() ?? String.Empty;
                metadata[BlobMetadataKey.SequenceNumber]  = ownership.SequenceNumber?.ToString() ?? String.Empty;

                var blobAccessConditions = new BlobAccessConditions();

                var blobName   = $"{ ownership.EventHubName }/{ ownership.ConsumerGroup }/{ ownership.PartitionId }";
                var blobClient = ContainerClient.GetBlobClient(blobName);

                try
                {
                    // Even though documentation states otherwise, we cannot use UploadAsync when the blob already exists in
                    // the current storage SDK.  For this reason, we are using the specified ETag as an indication of what
                    // method to use.

                    if (ownership.ETag == null)
                    {
                        blobAccessConditions.HttpAccessConditions = new HttpAccessConditions {
                            IfNoneMatch = new ETag("*")
                        };

                        MemoryStream blobContent = null;

                        try
                        {
                            blobContent         = new MemoryStream(new byte[0]);
                            contentInfoResponse = await blobClient.UploadAsync(blobContent, metadata : metadata, blobAccessConditions : blobAccessConditions).ConfigureAwait(false);
                        }
                        catch (StorageRequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.BlobAlreadyExists)
                        {
                            // A blob could have just been created by another Event Processor that claimed ownership of this
                            // partition.  In this case, there's no point in retrying because we don't have the correct ETag.

                            Log($"Ownership with partition id = '{ ownership.PartitionId }' is not claimable.");
                            continue;
                        }
                        finally
                        {
                            blobContent?.Dispose();
                        }

                        ownership.LastModifiedTime = contentInfoResponse.Value.LastModified;
                        ownership.ETag             = contentInfoResponse.Value.ETag.ToString();
                    }
                    else
                    {
                        blobAccessConditions.HttpAccessConditions = new HttpAccessConditions {
                            IfMatch = new ETag(ownership.ETag)
                        };

                        try
                        {
                            infoResponse = await blobClient.SetMetadataAsync(metadata, blobAccessConditions).ConfigureAwait(false);
                        }
                        catch (StorageRequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.BlobNotFound)
                        {
                            // No ownership was found, which means the ETag should have been set to null in order to
                            // claim this ownership.  For this reason, we consider it a failure and don't try again.

                            Log($"Ownership with partition id = '{ ownership.PartitionId }' is not claimable.");
                            continue;
                        }

                        ownership.LastModifiedTime = infoResponse.Value.LastModified;
                        ownership.ETag             = infoResponse.Value.ETag.ToString();
                    }

                    // Small workaround to retrieve the eTag.  The current storage SDK returns it enclosed in
                    // double quotes ('"ETAG_VALUE"' instead of 'ETAG_VALUE').

                    var match = DoubleQuotesExpression.Match(ownership.ETag);

                    if (match.Success)
                    {
                        ownership.ETag = match.Groups[1].ToString();
                    }

                    claimedOwnership.Add(ownership);

                    Log($"Ownership with partition id = '{ ownership.PartitionId }' claimed.");
                }
                catch (StorageRequestFailedException ex) when(ex.ErrorCode == BlobErrorCode.ConditionNotMet)
                {
                    Log($"Ownership with partition id = '{ ownership.PartitionId }' is not claimable.");
                }
            }

            return(claimedOwnership);
        }