/// <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_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()); } }
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; } }
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 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); }; }
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 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()); } }
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); }
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); } }
/// <summary> /// Insert (create new) row entry /// </summary> internal async Task <bool> TryCreateTableVersionEntryAsync() { try { var versionRow = await storage.ReadSingleTableEntryAsync(DeploymentId, SiloInstanceTableEntry.TABLE_VERSION_ROW); if (versionRow != null && versionRow.Item1 != null) { return(false); } SiloInstanceTableEntry entry = CreateTableVersionEntry(0); await storage.CreateTableEntryAsync(entry); 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("InsertSiloEntryConditionally failed with httpStatusCode={0}, restStatus={1}", httpStatusCode, restStatus); } if (AzureTableUtils.IsContentionError(httpStatusCode)) { return(false); } throw; } }
internal async Task <string> UpsertRow(ReminderTableEntry reminderEntry) { try { return(await UpsertTableEntryAsync(reminderEntry)); } catch (Exception exc) { HttpStatusCode httpStatusCode; string restStatus; if (AzureTableUtils.EvaluateException(exc, out httpStatusCode, out restStatus)) { if (Logger.IsEnabled(LogLevel.Trace)) { Logger.Trace("UpsertRow failed with httpStatusCode={0}, restStatus={1}", httpStatusCode, restStatus); } if (AzureTableUtils.IsContentionError(httpStatusCode)) { return(null); // false; } } throw; } }
/// <summary> Decodes Storage exceptions.</summary> public bool DecodeException(Exception e, out HttpStatusCode httpStatusCode, out string restStatus, bool getRESTErrors = false) { return(AzureTableUtils.EvaluateException(e, out httpStatusCode, out restStatus, getRESTErrors)); }