/// <summary>IoC constructor.</summary>
 /// <param name="observer">Can be <see langword="null"/>.</param>
 public TableStorageProvider(CloudTableClient tableStorage, IDataSerializer serializer = null, IStorageObserver observer = null)
 {
     _policies     = new RetryPolicies(observer);
     _tableStorage = tableStorage;
     _serializer   = serializer ?? new CloudFormatter();
     _observer     = observer;
 }
示例#2
0
        CloudTableClient TableClient()
        {
            var policies    = new Azure.RetryPolicies(Observer);
            var tableClient = _storageAccount.CreateCloudTableClient();

            tableClient.RetryPolicy = policies.ForAzureStorageClient();
            return(tableClient);
        }
示例#3
0
        CloudBlobClient BlobClient()
        {
            var policies   = new Azure.RetryPolicies(Observer);
            var blobClient = _storageAccount.CreateCloudBlobClient();

            blobClient.RetryPolicy = policies.ForAzureStorageClient();
            return(blobClient);
        }
示例#4
0
        CloudQueueClient QueueClient()
        {
            var policies    = new Azure.RetryPolicies(Observer);
            var queueClient = _storageAccount.CreateCloudQueueClient();

            queueClient.RetryPolicy   = policies.ForAzureStorageClient();
            queueClient.ServerTimeout = TimeSpan.FromSeconds(300);
            return(queueClient);
        }
 CloudTableClient TableClient()
 {
     var policies = new Azure.RetryPolicies(Observer);
     var tableClient = _storageAccount.CreateCloudTableClient();
     tableClient.RetryPolicy = policies.ForAzureStorageClient;
     return tableClient;
 }
 CloudQueueClient QueueClient()
 {
     var policies = new Azure.RetryPolicies(Observer);
     var queueClient = _storageAccount.CreateCloudQueueClient();
     queueClient.RetryPolicy = policies.ForAzureStorageClient;
     queueClient.Timeout = TimeSpan.FromSeconds(300);
     return queueClient;
 }
 CloudBlobClient BlobClient()
 {
     var policies = new Azure.RetryPolicies(Observer);
     var blobClient = _storageAccount.CreateCloudBlobClient();
     blobClient.RetryPolicy = policies.ForAzureStorageClient;
     return blobClient;
 }
        /// <remarks></remarks>
        void DeleteInternal <T>(string tableName, string partitionKey, IEnumerable <Tuple <string, string> > rowKeysAndETags, bool force)
        {
            var context = _tableStorage.GetTableServiceContext();

            var stopwatch = new Stopwatch();

            // CAUTION: make sure to get rid of potential duplicate in rowkeys.
            // (otherwise insertion in 'context' is likely to fail)
            foreach (var s in Slice(rowKeysAndETags
                                    // Similar effect than 'Distinct' based on 'RowKey'
                                    .ToLookup(p => p.Item1, p => p).Select(g => g.First()),
                                    MaxEntityTransactionCount))
            {
                stopwatch.Restart();

                var slice = s;

DeletionStart:  // 'slice' might have been refreshed if some entities were already deleted

                foreach (var rowKeyAndETag in slice)
                {
                    // Deleting entities in 1 roundtrip
                    // http://blog.smarx.com/posts/deleting-entities-from-windows-azure-without-querying-first
                    var mock = new FatEntity
                    {
                        PartitionKey = partitionKey,
                        RowKey       = rowKeyAndETag.Item1
                    };

                    context.AttachTo(tableName, mock, rowKeyAndETag.Item2);
                    context.DeleteObject(mock);
                }

                try     // HACK: [vermorel] if a single entity is missing, then the whole batch operation is aborded
                {
                    try // HACK: nested try/catch to handle the special case where the table is missing
                    {
                        Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () => context.SaveChanges(SaveChangesOptions.Batch));
                    }
                    catch (DataServiceRequestException ex)
                    {
                        // if the table is missing, no need to go on with the deletion
                        var errorCode = RetryPolicies.GetErrorCode(ex);
                        if (TableErrorCodeStrings.TableNotFound == errorCode)
                        {
                            NotifySucceeded(StorageOperationType.TableDelete, stopwatch);
                            return;
                        }

                        throw;
                    }
                }
                // if some entities exist
                catch (DataServiceRequestException ex)
                {
                    var errorCode = RetryPolicies.GetErrorCode(ex);

                    // HACK: Table Storage both implement a bizarre non-idempotent semantic
                    // but in addition, it throws a non-documented exception as well.
                    if (errorCode != "ResourceNotFound")
                    {
                        throw;
                    }

                    slice = Get <T>(tableName, partitionKey, slice.Select(p => p.Item1))
                            .Select(e => Tuple.Create(e.RowKey, MapETag(e.ETag, force))).ToArray();

                    // entities with same name will be added again
                    context = _tableStorage.GetTableServiceContext();

                    // HACK: [vermorel] yes, gotos are horrid, but other solutions are worst here.
                    goto DeletionStart;
                }

                NotifySucceeded(StorageOperationType.TableDelete, stopwatch);
            }
        }
        /// <remarks>Upsert is making several storage calls to emulate the
        /// missing semantic from the Table Storage.</remarks>
        void UpsertInternal <T>(string tableName, IEnumerable <CloudEntity <T> > entities)
        {
            var context = _tableStorage.GetTableServiceContext();

            context.MergeOption = MergeOption.AppendOnly;
            context.ResolveType = ResolveFatEntityType;

            var stopwatch = new Stopwatch();

            var fatEntities = entities.Select(e => Tuple.Create(FatEntity.Convert(e, _serializer), e));

            var noBatchMode = false;

            foreach (var slice in SliceEntities(fatEntities, e => e.Item1.GetPayload()))
            {
                stopwatch.Restart();

                var cloudEntityOfFatEntity = new Dictionary <object, CloudEntity <T> >();
                foreach (var fatEntity in slice)
                {
                    // entities should be updated in a single round-trip
                    context.AttachTo(tableName, fatEntity.Item1);
                    context.UpdateObject(fatEntity.Item1);
                    cloudEntityOfFatEntity.Add(fatEntity.Item1, fatEntity.Item2);
                }

                Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () =>
                {
                    try
                    {
                        context.SaveChanges(noBatchMode ? SaveChangesOptions.ReplaceOnUpdate : SaveChangesOptions.ReplaceOnUpdate | SaveChangesOptions.Batch);
                        ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                    }
                    catch (DataServiceRequestException ex)
                    {
                        var errorCode = RetryPolicies.GetErrorCode(ex);

                        if (errorCode == StorageErrorCodeStrings.OperationTimedOut)
                        {
                            // if batch does not work, then split into elementary requests
                            // PERF: it would be better to split the request in two and retry
                            context.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
                            ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            noBatchMode = true;
                        }
                        else if (errorCode == TableErrorCodeStrings.TableNotFound)
                        {
                            Retry.Do(_policies.SlowInstantiation(), CancellationToken.None, () =>
                            {
                                try
                                {
                                    var table = _tableStorage.GetTableReference(tableName);
                                    table.CreateIfNotExists();
                                }
                                // HACK: incorrect behavior of the StorageClient (2010-09)
                                // Fails to behave properly in multi-threaded situations
                                catch (StorageException cex)
                                {
                                    var extended = cex.RequestInformation != null ? cex.RequestInformation.ExtendedErrorInformation : null;
                                    if (extended == null || extended.ErrorCode != TableErrorCodeStrings.TableAlreadyExists)
                                    {
                                        throw;
                                    }
                                }
                                context.SaveChanges(noBatchMode ? SaveChangesOptions.ReplaceOnUpdate : SaveChangesOptions.ReplaceOnUpdate | SaveChangesOptions.Batch);
                                ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            });
                        }
                        else if (errorCode == StorageErrorCodeStrings.ResourceNotFound)
                        {
                            throw new InvalidOperationException("Cannot call update on a resource that does not exist", ex);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    catch (DataServiceQueryException ex)
                    {
                        // HACK: code duplicated

                        var errorCode = RetryPolicies.GetErrorCode(ex);

                        if (errorCode == StorageErrorCodeStrings.OperationTimedOut)
                        {
                            // if batch does not work, then split into elementary requests
                            // PERF: it would be better to split the request in two and retry
                            context.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
                            ReadETagsAndDetach(context, (entity, etag) => cloudEntityOfFatEntity[entity].ETag = etag);
                            noBatchMode = true;
                        }
                        else
                        {
                            throw;
                        }
                    }
                });

                NotifySucceeded(StorageOperationType.TableUpsert, stopwatch);
            }
        }
        /// <remarks></remarks>
        private IEnumerable <CloudEntity <T> > GetInternal <T>(TableServiceContext context, string tableName, Maybe <string> filter)
        {
            string continuationRowKey       = null;
            string continuationPartitionKey = null;

            var stopwatch = Stopwatch.StartNew();

            context.MergeOption = MergeOption.AppendOnly;
            context.ResolveType = ResolveFatEntityType;

            do
            {
                var query = context.CreateQuery <FatEntity>(tableName);

                if (filter.HasValue)
                {
                    query = query.AddQueryOption("$filter", filter.Value);
                }

                if (null != continuationRowKey)
                {
                    query = query.AddQueryOption(NextRowKeyToken, continuationRowKey)
                            .AddQueryOption(NextPartitionKeyToken, continuationPartitionKey);
                }

                QueryOperationResponse response    = null;
                FatEntity[]            fatEntities = null;

                Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () =>
                {
                    try
                    {
                        response    = query.Execute() as QueryOperationResponse;
                        fatEntities = ((IEnumerable <FatEntity>)response).ToArray();
                    }
                    catch (DataServiceQueryException ex)
                    {
                        // if the table does not exist, there is nothing to return
                        var errorCode = RetryPolicies.GetErrorCode(ex);
                        if (TableErrorCodeStrings.TableNotFound == errorCode ||
                            StorageErrorCodeStrings.ResourceNotFound == errorCode)
                        {
                            fatEntities = new FatEntity[0];
                            return;
                        }

                        throw;
                    }
                });

                NotifySucceeded(StorageOperationType.TableQuery, stopwatch);

                foreach (var fatEntity in fatEntities)
                {
                    var etag = context.Entities.First(e => e.Entity == fatEntity).ETag;
                    context.Detach(fatEntity);
                    yield return(FatEntity.Convert <T>(fatEntity, _serializer, etag));
                }

                Debug.Assert(context.Entities.Count == 0);

                if (null != response && response.Headers.ContainsKey(ContinuationNextRowKeyToken))
                {
                    continuationRowKey       = response.Headers[ContinuationNextRowKeyToken];
                    continuationPartitionKey = response.Headers[ContinuationNextPartitionKeyToken];

                    stopwatch.Restart();
                }
                else
                {
                    continuationRowKey       = null;
                    continuationPartitionKey = null;
                }
            } while (null != continuationRowKey);
        }