/// <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 }
/// <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; } }
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); }
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)); }
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()); } }
/// <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; } }
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; } }
/// <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); } }
/// <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); }
/// <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); }
/// <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); }
/// <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; }
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); }; }
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); }
/// <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; } }
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)); }
/// <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)); }
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); }
/// <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); }
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); } }
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()); } }
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; } }
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); }
/// <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); } }
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); } }