예제 #1
0
        /// <summary> Read state data function for this storage provider. </summary>
        /// <see cref="IGrainStorage.ReadStateAsync"/>
        public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            if (tableDataManager == null)
            {
                throw new ArgumentException("GrainState-Table property not initialized");
            }

            string pk = GetKeyString(grainReference);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace((int)AzureProviderErrorCode.AzureTableProvider_ReadingData, "Reading: GrainType={0} Pk={1} Grainid={2} from Table={3}", grainType, pk, grainReference, this.options.TableName);
            }
            string           partitionKey = pk;
            string           rowKey       = AzureTableUtils.SanitizeTableProperty(grainType);
            GrainStateRecord record       = await tableDataManager.Read(partitionKey, rowKey).ConfigureAwait(false);

            if (record != null)
            {
                var entity = record.Entity;
                if (entity != null)
                {
                    var loadedState = ConvertFromStorageFormat(entity, grainState.Type);
                    grainState.State = loadedState ?? Activator.CreateInstance(grainState.Type);
                    grainState.ETag  = record.ETag;
                }
            }

            // Else leave grainState in previous default condition
        }
예제 #2
0
        /// <summary> Read state data function for this storage provider. </summary>
        /// <see cref="IGrainStorage.ReadStateAsync{T}"/>
        public async Task ReadStateAsync <T>(string grainType, GrainReference grainReference, IGrainState <T> grainState)
        {
            if (tableDataManager == null)
            {
                throw new ArgumentException("GrainState-Table property not initialized");
            }

            string pk = GetKeyString(grainReference);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.LogTrace((int)AzureProviderErrorCode.AzureTableProvider_ReadingData,
                                "Reading: GrainType={GrainType} Pk={PartitionKey} Grainid={GrainId} from Table={TableName}",
                                grainType,
                                pk,
                                grainReference,
                                this.options.TableName);
            }
            string partitionKey = pk;
            string rowKey       = AzureTableUtils.SanitizeTableProperty(grainType);
            var    entity       = await tableDataManager.Read(partitionKey, rowKey).ConfigureAwait(false);

            if (entity is not null)
            {
                var loadedState = ConvertFromStorageFormat <T>(entity);
                grainState.RecordExists = loadedState != null;
                grainState.State        = loadedState ?? Activator.CreateInstance <T>();
                grainState.ETag         = entity.ETag.ToString();
            }
            // Else leave grainState in previous default condition
        }
        /// <summary>
        /// Conditionally update the row for this entry, but only if the eTag matches with the current record in data store
        /// </summary>
        /// <param name="siloEntry">Silo Entry to be written</param>
        /// <param name="entryEtag">ETag value for the entry being updated</param>
        /// <param name="tableVersionEntry">Version row to update</param>
        /// <param name="versionEtag">ETag value for the version row</param>
        /// <returns></returns>
        internal async Task <bool> UpdateSiloEntryConditionally(SiloInstanceTableEntry siloEntry, string entryEtag, SiloInstanceTableEntry tableVersionEntry, string versionEtag)
        {
            try
            {
                await storage.UpdateTwoTableEntriesConditionallyAsync(siloEntry, entryEtag, tableVersionEntry, versionEtag);

                return(true);
            }
            catch (Exception exc)
            {
                HttpStatusCode httpStatusCode;
                string         restStatus;
                if (!AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus))
                {
                    throw;
                }

                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace("UpdateSiloEntryConditionally failed with httpStatusCode={0}, restStatus={1}", httpStatusCode, restStatus);
                }
                if (AzureTableUtils.IsContentionError(httpStatusCode))
                {
                    return(false);
                }

                throw;
            }
        }
예제 #4
0
        public async Task AzureTableDataManager_CreateTableEntryAsync()
        {
            var data = GenerateNewData();
            await manager.CreateTableEntryAsync(data);

            try
            {
                var data2 = data.Clone();
                data2.StringData = "NewData";
                await manager.CreateTableEntryAsync(data2);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.Conflict, exc.Status);  // "Creating an already existing entry."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.Conflict, httpStatusCode);
                Assert.Equal("EntityAlreadyExists", restStatus);
            }
            var tuple = await manager.ReadSingleTableEntryAsync(data.PartitionKey, data.RowKey);

            Assert.Equal(data.StringData, tuple.Entity.StringData);
        }
예제 #5
0
        private async Task <bool> TryOperation(Func <Task> func, string operation = null)
        {
            try
            {
                await func().ConfigureAwait(false);

                return(true);
            }
            catch (Exception exc)
            {
                HttpStatusCode httpStatusCode;
                string         restStatus;
                if (!AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus))
                {
                    throw;
                }

                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace("{0} failed with httpStatusCode={1}, restStatus={2}", operation, httpStatusCode, restStatus);
                }
                if (AzureTableUtils.IsContentionError(httpStatusCode))
                {
                    return(false);
                }

                throw;
            }
        }
        public void AzureTableErrorCode_BadTableName()
        {
            string tableName = "abc-123";

            Assert.Throws <ArgumentException>(() =>
                                              AzureTableUtils.ValidateTableName(tableName));
        }
예제 #7
0
        public async Task AzureTableDataManager_UpdateTableEntryAsync()
        {
            var data = GenerateNewData();

            try
            {
                await manager.UpdateTableEntryAsync(data, AzureTableUtils.ANY_ETAG);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.Status);  // "Update before insert."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(TableErrorCode.ResourceNotFound.ToString(), restStatus);
            }

            await manager.UpsertTableEntryAsync(data);

            var tuple = await manager.ReadSingleTableEntryAsync(data.PartitionKey, data.RowKey);

            Assert.Equal(data.StringData, tuple.Entity.StringData);

            var data2 = data.Clone();

            data2.StringData = "NewData";
            string eTag1 = await manager.UpdateTableEntryAsync(data2, AzureTableUtils.ANY_ETAG);

            tuple = await manager.ReadSingleTableEntryAsync(data2.PartitionKey, data2.RowKey);

            Assert.Equal(data2.StringData, tuple.Entity.StringData);

            var data3 = data.Clone();

            data3.StringData = "EvenNewerData";
            _ = await manager.UpdateTableEntryAsync(data3, eTag1);

            tuple = await manager.ReadSingleTableEntryAsync(data3.PartitionKey, data3.RowKey);

            Assert.Equal(data3.StringData, tuple.Entity.StringData);

            try
            {
                string eTag3 = await manager.UpdateTableEntryAsync(data3.Clone(), eTag1);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.PreconditionFailed, exc.Status);  // "Wrong eTag"
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.PreconditionFailed, httpStatusCode);
                Assert.True(restStatus == TableErrorCode.UpdateConditionNotSatisfied.ToString());
            }
        }
예제 #8
0
        /// <summary> Write state data function for this storage provider. </summary>
        /// <see cref="IGrainStorage.WriteStateAsync"/>
        public async Task WriteStateAsync <T>(string grainType, GrainReference grainReference, IGrainState <T> grainState)
        {
            if (tableDataManager == null)
            {
                throw new ArgumentException("GrainState-Table property not initialized");
            }

            string pk = GetKeyString(grainReference);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace((int)AzureProviderErrorCode.AzureTableProvider_WritingData, "Writing: GrainType={0} Pk={1} Grainid={2} ETag={3} to Table={4}", grainType, pk, grainReference, grainState.ETag, this.options.TableName);
            }

            var rowKey = AzureTableUtils.SanitizeTableProperty(grainType);
            var entity = new TableEntity(pk, rowKey)
            {
                ETag = new ETag(grainState.ETag)
            };

            ConvertToStorageFormat(grainState.State, entity);
            try
            {
                await DoOptimisticUpdate(() => tableDataManager.Write(entity), grainType, grainReference.GrainId, this.options.TableName, grainState.ETag).ConfigureAwait(false);

                grainState.ETag         = entity.ETag.ToString();
                grainState.RecordExists = true;
            }
            catch (Exception exc)
            {
                logger.Error((int)AzureProviderErrorCode.AzureTableProvider_WriteError,
                             $"Error Writing: GrainType={grainType} GrainId={grainReference.GrainId} ETag={grainState.ETag} to Table={this.options.TableName} Exception={exc.Message}", exc);
                throw;
            }
        }
예제 #9
0
        internal async Task <bool> DeleteReminderEntryConditionally(ReminderTableEntry reminderEntry, string eTag)
        {
            try
            {
                await DeleteTableEntryAsync(reminderEntry, eTag);

                return(true);
            }catch (Exception exc)
            {
                HttpStatusCode httpStatusCode;
                string         restStatus;
                if (AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus))
                {
                    if (Logger.IsEnabled(LogLevel.Trace))
                    {
                        Logger.Trace("DeleteReminderEntryConditionally failed with httpStatusCode={0}, restStatus={1}", httpStatusCode, restStatus);
                    }
                    if (AzureTableUtils.IsContentionError(httpStatusCode))
                    {
                        return(false);
                    }
                }
                throw;
            }
        }
예제 #10
0
        /// <summary>
        /// Read data entries and their corresponding eTags from the Azure table.
        /// </summary>
        /// <param name="filter">Filter string to use for querying the table and filtering the results.</param>
        /// <returns>Enumeration of entries in the table which match the query condition.</returns>
        public async Task <IEnumerable <Tuple <T, string> > > ReadTableEntriesAndEtagsAsync(string filter)
        {
            const string operation = "ReadTableEntriesAndEtags";
            var          startTime = DateTime.UtcNow;

            try
            {
                TableQuery <T> cloudTableQuery = filter == null
                    ? new TableQuery <T>()
                    : new TableQuery <T>().Where(filter);

                try
                {
                    Func <Task <List <T> > > executeQueryHandleContinuations = async() =>
                    {
                        TableQuerySegment <T> querySegment = null;
                        var list = new List <T>();
                        //ExecuteSegmentedAsync not supported in "WindowsAzure.Storage": "7.2.1" yet
                        while (querySegment == null || querySegment.ContinuationToken != null)
                        {
                            querySegment = await Table.ExecuteQuerySegmentedAsync(cloudTableQuery, querySegment?.ContinuationToken);

                            list.AddRange(querySegment);
                        }

                        return(list);
                    };

#if !ORLEANS_TRANSACTIONS
                    IBackoffProvider backoff = new FixedBackoff(this.StoragePolicyOptions.PauseBetweenOperationRetries);

                    List <T> results = await AsyncExecutorWithRetries.ExecuteWithRetries(
                        counter => executeQueryHandleContinuations(),
                        this.StoragePolicyOptions.MaxOperationRetries,
                        (exc, counter) => AzureTableUtils.AnalyzeReadException(exc.GetBaseException(), counter, TableName, Logger),
                        this.StoragePolicyOptions.OperationTimeout,
                        backoff);
#else
                    List <T> results = await executeQueryHandleContinuations();
#endif
                    // Data was read successfully if we got to here
                    return(results.Select(i => Tuple.Create(i, i.ETag)).ToList());
                }
                catch (Exception exc)
                {
                    // Out of retries...
                    var errorMsg = $"Failed to read Azure storage table {TableName}: {exc.Message}";
                    if (!AzureTableUtils.TableStorageDataNotFound(exc))
                    {
                        Logger.Warn((int)Utilities.ErrorCode.AzureTable_09, errorMsg, exc);
                    }
                    throw new OrleansException(errorMsg, exc);
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }
예제 #11
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName">Name of the table to be connected to.</param>
        /// <param name="storageConnectionString">Connection string for the Azure storage account used to host this table.</param>
        /// <param name="loggerFactory">Logger factory to use.</param>
        public AzureTableDataManager(string tableName, string storageConnectionString, ILoggerFactory loggerFactory)
        {
            Logger           = loggerFactory.CreateLogger <AzureTableDataManager <T> >();
            TableName        = tableName;
            ConnectionString = storageConnectionString;

            AzureTableUtils.ValidateTableName(tableName);
        }
예제 #12
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName">Name of the table to be connected to.</param>
        /// <param name="storageConnectionString">Connection string for the Azure storage account used to host this table.</param>
        /// <param name="logger">Logger to use.</param>
        public AzureTableDataManager(string tableName, string storageConnectionString, ILogger logger)
        {
            Logger           = logger;
            TableName        = tableName;
            ConnectionString = storageConnectionString;

            AzureTableUtils.ValidateTableName(tableName);
        }
예제 #13
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName">Name of the table to be connected to.</param>
        /// <param name="storageConnectionString">Connection string for the Azure storage account used to host this table.</param>
        /// <param name="logger">Logger to use.</param>
        /// <param name="storagePolicyOptions">Optional Storage Policy Configuration.</param>
        public AzureTableDataManager(string tableName, string storageConnectionString, ILogger logger, AzureStoragePolicyOptions storagePolicyOptions)
        {
            Logger                    = logger ?? throw new ArgumentNullException(nameof(logger));
            TableName                 = tableName ?? throw new ArgumentNullException(nameof(tableName));
            ConnectionString          = storageConnectionString ?? throw new ArgumentNullException(nameof(storageConnectionString));
            this.StoragePolicyOptions = storagePolicyOptions ?? throw new ArgumentNullException(nameof(storagePolicyOptions));

            AzureTableUtils.ValidateTableName(tableName);
        }
예제 #14
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName">Name of the table to be connected to.</param>
        /// <param name="storageConnectionString">Connection string for the Azure storage account used to host this table.</param>
        /// <param name="logger">Logger to use.</param>
        /// <param name="storagePolicyOptions">Optional Storage Policy Configuration.</param>
        public AzureTableDataManager(string tableName, string storageConnectionString, ILogger logger, AzureStoragePolicyOptions storagePolicyOptions = default)
        {
            Logger           = logger;
            TableName        = tableName;
            ConnectionString = storageConnectionString;

            AzureTableUtils.ValidateTableName(tableName);
            this.StoragePolicyOptions ??= storagePolicyOptions;
        }
예제 #15
0
        public async Task AzureTableDataManager_InsertTwoTableEntriesConditionallyAsync()
        {
            StorageEmulatorUtilities.EnsureEmulatorIsNotUsed();

            var data1 = GenerateNewData();
            var data2 = GenerateNewData();

            try
            {
                await manager.InsertTwoTableEntriesConditionallyAsync(data1, data2, AzureTableUtils.ANY_ETAG);
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.Status);  // "Upadte item 2 before created it."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(TableErrorCode.ResourceNotFound.ToString(), restStatus);
            }

            string etag = await manager.CreateTableEntryAsync(data2.Clone());

            var tuple = await manager.InsertTwoTableEntriesConditionallyAsync(data1, data2, etag);

            try
            {
                await manager.InsertTwoTableEntriesConditionallyAsync(data1.Clone(), data2.Clone(), tuple.Item2);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.Conflict, exc.Status);  // "Inserting an already existing item 1."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.Conflict, httpStatusCode);
                Assert.Equal("EntityAlreadyExists", restStatus);
            }

            try
            {
                await manager.InsertTwoTableEntriesConditionallyAsync(data1.Clone(), data2.Clone(), AzureTableUtils.ANY_ETAG);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.Conflict, exc.Status);  // "Inserting an already existing item 1 AND wring eTag"
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.Conflict, httpStatusCode);
                Assert.Equal("EntityAlreadyExists", restStatus);
            };
        }
예제 #16
0
        private async ValueTask <CloudTableClient> GetCloudTableClientAsync()
        {
            CloudStorageAccount storageAccount = options.TokenCredential != null
                ? new CloudStorageAccount(new StorageCredentials(accountName: "ignored", await GetAccountKeyUsingAad()), options.TableEndpoint)
                : AzureTableUtils.GetCloudStorageAccount(options.ConnectionString);

            CloudTableClient creationClient = storageAccount.CreateCloudTableClient();

            return(creationClient);
        }
        /// <summary>
        /// Creates a new <see cref="AzureTableDataManager{T}"/> instance.
        /// </summary>
        /// <param name="options">Storage configuration.</param>
        /// <param name="logger">Logger to use.</param>
        public AzureTableDataManager(AzureStorageOperationOptions options, ILogger logger)
        {
            this.options = options ?? throw new ArgumentNullException(nameof(options));

            Logger               = logger ?? throw new ArgumentNullException(nameof(logger));
            TableName            = options.TableName ?? throw new ArgumentNullException(nameof(options.TableName));
            StoragePolicyOptions = options.StoragePolicyOptions ?? throw new ArgumentNullException(nameof(options.StoragePolicyOptions));

            AzureTableUtils.ValidateTableName(TableName);
        }
예제 #18
0
        /// <summary> Clear / Delete state data function for this storage provider. </summary>
        /// <remarks>
        /// If the <c>DeleteStateOnClear</c> is set to <c>true</c> then the table row
        /// for this grain will be deleted / removed, otherwise the table row will be
        /// cleared by overwriting with default / null values.
        /// </remarks>
        /// <see cref="IGrainStorage.ClearStateAsync{T}"/>
        public async Task ClearStateAsync <T>(string grainType, GrainReference grainReference, IGrainState <T> grainState)
        {
            if (tableDataManager == null)
            {
                throw new ArgumentException("GrainState-Table property not initialized");
            }

            string pk = GetKeyString(grainReference);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.LogTrace((int)AzureProviderErrorCode.AzureTableProvider_WritingData,
                                "Clearing: GrainType={GrainType} Pk={PartitionKey} Grainid={GrainId} ETag={ETag} DeleteStateOnClear={DeleteStateOnClear} from Table={TableName}",
                                grainType,
                                pk,
                                grainReference,
                                grainState.ETag,
                                this.options.DeleteStateOnClear,
                                this.options.TableName);
            }
            var rowKey = AzureTableUtils.SanitizeTableProperty(grainType);
            var entity = new TableEntity(pk, rowKey)
            {
                ETag = new ETag(grainState.ETag)
            };
            string operation = "Clearing";

            try
            {
                if (this.options.DeleteStateOnClear)
                {
                    operation = "Deleting";
                    await DoOptimisticUpdate(() => tableDataManager.Delete(entity), grainType, grainReference.GrainId, this.options.TableName, grainState.ETag).ConfigureAwait(false);
                }
                else
                {
                    await DoOptimisticUpdate(() => tableDataManager.Write(entity), grainType, grainReference.GrainId, this.options.TableName, grainState.ETag).ConfigureAwait(false);
                }

                grainState.ETag         = entity.ETag.ToString(); // Update in-memory data to the new ETag
                grainState.RecordExists = false;
            }
            catch (Exception exc)
            {
                logger.LogError((int)AzureProviderErrorCode.AzureTableProvider_DeleteError,
                                exc,
                                "Error {Operation}: GrainType={GrainType} Grainid={GrainId} ETag={ETag} from Table={TableName}",
                                operation,
                                grainType,
                                grainReference,
                                grainState.ETag,
                                this.options.TableName);
                throw;
            }
        }
예제 #19
0
        public static string ConstructPartitionKey(string serviceId, uint number)
        {
            // IMPORTANT NOTE: Other code using this return data is very sensitive to format changes,
            //       so take great care when making any changes here!!!

            // this format of partition key makes sure that the comparisons in FindReminderEntries(begin, end) work correctly
            // the idea is that when converting to string, negative numbers start with 0, and positive start with 1. Now,
            // when comparisons will be done on strings, this will ensure that positive numbers are always greater than negative
            // string grainHash = number < 0 ? string.Format("0{0}", number.ToString("X")) : string.Format("1{0:d16}", number);

            return(AzureTableUtils.SanitizeTableProperty($"{serviceId}_{number:X8}"));
        }
        public void AzureTableErrorCode_IsRetriableHttpError()
        {
            Assert.True(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)503, null));
            Assert.True(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)504, null));
            Assert.True(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)408, null));

            Assert.True(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)500, "OperationTimedOut"));
            Assert.False(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)500, null));
            Assert.False(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)500, "SomeOtherStatusValue"));

            // Current behaviour is to ignore successes as not retriable:
            Assert.False(AzureTableUtils.IsRetriableHttpError((HttpStatusCode)200, null));
        }
예제 #21
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="options">Storage configuration.</param>
        /// <param name="logger">Logger to use.</param>
        public AzureTableDataManager(AzureStorageOperationOptions options, ILogger logger)
        {
            this.options = options ?? throw new ArgumentNullException(nameof(options));

            Logger               = logger ?? throw new ArgumentNullException(nameof(logger));
            TableName            = options.TableName ?? throw new ArgumentNullException(nameof(options.TableName));
            StoragePolicyOptions = options.StoragePolicyOptions ?? throw new ArgumentNullException(nameof(options.StoragePolicyOptions));

            AzureTableUtils.ValidateTableName(TableName);

            if (options.TokenCredential != null)
            {
                tokenLock = new SemaphoreSlim(1, 1);
            }
        }
        public void AzureTableErrorCode_IsContentionError()
        {
            Assert.True(AzureTableUtils.IsContentionError(HttpStatusCode.PreconditionFailed));
            Assert.True(AzureTableUtils.IsContentionError(HttpStatusCode.Conflict));
            Assert.True(AzureTableUtils.IsContentionError(HttpStatusCode.NotFound));
            Assert.True(AzureTableUtils.IsContentionError(HttpStatusCode.NotImplemented));

            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)503));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)504));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)408));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)500));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)500));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)500));
            Assert.False(AzureTableUtils.IsContentionError((HttpStatusCode)200));
        }
예제 #23
0
        public async Task AzureTableDataManager_MergeTableAsync()
        {
            StorageEmulatorUtilities.EnsureEmulatorIsNotUsed();
            var data = GenerateNewData();

            try
            {
                await manager.MergeTableEntryAsync(data, AzureTableUtils.ANY_ETAG);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.Status);  // "Merge before create."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(TableErrorCode.ResourceNotFound.ToString(), restStatus);
            }

            string eTag1 = await manager.UpsertTableEntryAsync(data);

            var data2 = data.Clone();

            data2.StringData = "NewData";
            await manager.MergeTableEntryAsync(data2, eTag1);

            try
            {
                await manager.MergeTableEntryAsync(data, eTag1);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.PreconditionFailed, exc.Status);  // "Wrong eTag."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.PreconditionFailed, httpStatusCode);
                Assert.True(restStatus == TableErrorCode.UpdateConditionNotSatisfied.ToString());
            }

            var tuple = await manager.ReadSingleTableEntryAsync(data.PartitionKey, data.RowKey);

            Assert.Equal("NewData", tuple.Entity.StringData);
        }
예제 #24
0
        /// <summary>
        /// Inspect an exception returned from Azure storage libraries to check whether it means that attempt was made to read some data that does not exist in storage table.
        /// </summary>
        /// <param name="exc">Exception that was returned by Azure storage library operation</param>
        /// <returns><c>True</c> if this exception means the data being read was not present in Azure table storage</returns>
        public static bool TableStorageDataNotFound(Exception exc)
        {
            HttpStatusCode httpStatusCode;
            string         restStatus;

            if (AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true))
            {
                if (AzureTableUtils.IsNotFoundError(httpStatusCode)
                    /* New table: Azure table schema not yet initialized, so need to do first create */)
                {
                    return(true);
                }
                return(StorageErrorCodeStrings.ResourceNotFound.Equals(restStatus));
            }
            return(false);
        }
예제 #25
0
 public void ValidateConfiguration()
 {
     if (!CloudStorageAccount.TryParse(this.options.ConnectionString, out var ignore))
     {
         throw new OrleansConfigurationException(
                   $"Configuration for AzureTableStorageProvider {name} is invalid. {nameof(this.options.ConnectionString)} is not valid.");
     }
     try
     {
         AzureTableUtils.ValidateTableName(this.options.TableName);
     }
     catch (Exception e)
     {
         throw new OrleansConfigurationException(
                   $"Configuration for AzureTableStorageProvider {name} is invalid. {nameof(this.options.TableName)} is not valid", e);
     }
 }
예제 #26
0
        public async Task AzureTableDataManager_UpdateTwoTableEntriesConditionallyAsync()
        {
            StorageEmulatorUtilities.EnsureEmulatorIsNotUsed();

            var data1 = GenerateNewData();
            var data2 = GenerateNewData();

            try
            {
                await manager.UpdateTwoTableEntriesConditionallyAsync(data1, AzureTableUtils.ANY_ETAG, data2, AzureTableUtils.ANY_ETAG);

                Assert.True(false, "Update should have failed since the data has not been created yet");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.Status);  // "Update before insert."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(TableErrorCode.ResourceNotFound.ToString(), restStatus);
            }

            string etag = await manager.CreateTableEntryAsync(data2.Clone());

            var tuple1 = await manager.InsertTwoTableEntriesConditionallyAsync(data1, data2, etag);

            _ = await manager.UpdateTwoTableEntriesConditionallyAsync(data1, tuple1.Item1, data2, tuple1.Item2);

            try
            {
                await manager.UpdateTwoTableEntriesConditionallyAsync(data1, tuple1.Item1, data2, tuple1.Item2);

                Assert.True(false, "Should have thrown RequestFailedException.");
            }
            catch (RequestFailedException exc)
            {
                Assert.Equal((int)HttpStatusCode.PreconditionFailed, exc.Status);  // "Wrong eTag"
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.PreconditionFailed, httpStatusCode);
                Assert.True(restStatus == TableErrorCode.UpdateConditionNotSatisfied.ToString());
            }
        }
예제 #27
0
 private CloudTableClient GetCloudTableCreationClient()
 {
     try
     {
         CloudStorageAccount storageAccount = AzureTableUtils.GetCloudStorageAccount(ConnectionString);
         CloudTableClient    creationClient = storageAccount.CreateCloudTableClient();
         creationClient.DefaultRequestOptions.RetryPolicy   = this.StoragePolicyOptions.CreationRetryPolicy;
         creationClient.DefaultRequestOptions.ServerTimeout = this.StoragePolicyOptions.CreationTimeout;
         // Values supported can be AtomPub, Json, JsonFullMetadata or JsonNoMetadata with Json being the default value
         creationClient.DefaultRequestOptions.PayloadFormat = TablePayloadFormat.JsonNoMetadata;
         return(creationClient);
     }
     catch (Exception exc)
     {
         Logger.Error((int)Utilities.ErrorCode.AzureTable_18, "Error creating CloudTableCreationClient.", exc);
         throw;
     }
 }
예제 #28
0
        public async Task AzureTableDataManager_DeleteTableAsync()
        {
            var data = GenerateNewData();

            try
            {
                await manager.DeleteTableEntryAsync(data, AzureTableUtils.ANY_ETAG);

                Assert.True(false, "Should have thrown StorageException.");
            }
            catch (StorageException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.RequestInformation.HttpStatusCode);  // "Delete before create."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(StorageErrorCodeStrings.ResourceNotFound, restStatus);
            }

            string eTag1 = await manager.UpsertTableEntryAsync(data);

            await manager.DeleteTableEntryAsync(data, eTag1);

            try
            {
                await manager.DeleteTableEntryAsync(data, eTag1);

                Assert.True(false, "Should have thrown StorageException.");
            }
            catch (StorageException exc)
            {
                Assert.Equal((int)HttpStatusCode.NotFound, exc.RequestInformation.HttpStatusCode);  // "Deleting an already deleted item."
                HttpStatusCode httpStatusCode;
                string         restStatus;
                AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus, true);
                Assert.Equal(HttpStatusCode.NotFound, httpStatusCode);
                Assert.Equal(StorageErrorCodeStrings.ResourceNotFound, restStatus);
            }

            var tuple = await manager.ReadSingleTableEntryAsync(data.PartitionKey, data.RowKey);

            Assert.Null(tuple);
        }
예제 #29
0
        /// <summary>
        /// Read a single table entry from the storage table.
        /// </summary>
        /// <param name="partitionKey">The partition key for the entry.</param>
        /// <param name="rowKey">The row key for the entry.</param>
        /// <returns>Value promise for tuple containing the data entry and its corresponding etag.</returns>
        public async Task <Tuple <T, string> > ReadSingleTableEntryAsync(string partitionKey, string rowKey)
        {
            const string operation = "ReadSingleTableEntryAsync";
            var          startTime = DateTime.UtcNow;

            if (Logger.IsEnabled(LogLevel.Trace))
            {
                Logger.Trace("{0} table {1} partitionKey {2} rowKey = {3}", operation, TableName, partitionKey, rowKey);
            }
            T retrievedResult = default(T);

            try
            {
                try
                {
                    string queryString            = TableQueryFilterBuilder.MatchPartitionKeyAndRowKeyFilter(partitionKey, rowKey);
                    var    query                  = new TableQuery <T>().Where(queryString);
                    TableQuerySegment <T> segment = await Table.ExecuteQuerySegmentedAsync(query, null);

                    retrievedResult = segment.Results.SingleOrDefault();
                }
                catch (StorageException exception)
                {
                    if (!AzureTableUtils.TableStorageDataNotFound(exception))
                    {
                        throw;
                    }
                }
                //The ETag of data is needed in further operations.
                if (retrievedResult != null)
                {
                    return(new Tuple <T, string>(retrievedResult, retrievedResult.ETag));
                }
                if (Logger.IsEnabled(LogLevel.Debug))
                {
                    Logger.Debug("Could not find table entry for PartitionKey={0} RowKey={1}", partitionKey, rowKey);
                }
                return(null);  // No data
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }
예제 #30
0
        private void CheckAlertWriteError(string operation, object data1, string data2, Exception exc)
        {
            HttpStatusCode httpStatusCode;

            if (AzureTableUtils.EvaluateException(exc, out httpStatusCode, out _) && AzureTableUtils.IsContentionError(httpStatusCode))
            {
                // log at Verbose, since failure on conditional is not not an error. Will analyze and warn later, if required.
                if (Logger.IsEnabled(LogLevel.Debug))
                {
                    Logger.Debug((int)Utilities.ErrorCode.AzureTable_13,
                                 $"Intermediate Azure table write error {operation} to table {TableName} data1 {(data1 ?? "null")} data2 {(data2 ?? "null")}", exc);
                }
            }
            else
            {
                Logger.Error((int)Utilities.ErrorCode.AzureTable_14,
                             $"Azure table access write error {operation} to table {TableName} entry {data1}", exc);
            }
        }