/// <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> /// Read data entries and their corresponding eTags from the Azure table. /// </summary> /// <param name="predicate">Predicate function 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(Expression <Func <T, bool> > predicate) { const string operation = "ReadTableEntriesAndEtags"; var startTime = DateTime.UtcNow; try { TableQuery <T> cloudTableQuery = predicate == null ? tableReference.CreateQuery <T>() : tableReference.CreateQuery <T>().Where(predicate).AsTableQuery(); try { Func <Task <List <T> > > executeQueryHandleContinuations = async() => { TableQuerySegment <T> querySegment = null; var list = new List <T>(); while (querySegment == null || querySegment.ContinuationToken != null) { querySegment = await cloudTableQuery.ExecuteSegmentedAsync(querySegment?.ContinuationToken); list.AddRange(querySegment); } return(list); }; IBackoffProvider backoff = new FixedBackoff(AzureTableDefaultPolicies.PauseBetweenTableOperationRetries); List <T> results = await AsyncExecutorWithRetries.ExecuteWithRetries( counter => executeQueryHandleContinuations(), AzureTableDefaultPolicies.MaxTableOperationRetries, (exc, counter) => AzureStorageUtils.AnalyzeReadException(exc.GetBaseException(), counter, TableName, Logger), AzureTableDefaultPolicies.TableOperationTimeout, backoff); // 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 (!AzureStorageUtils.TableStorageDataNotFound(exc)) { Logger.Warn(ErrorCode.AzureTable_09, errorMsg, exc); } throw new OrleansException(errorMsg, exc); } } finally { CheckAlertSlowAccess(startTime, operation); } }
/// <summary> /// Read data entries and their corresponding eTags from the Azure table. /// </summary> /// <param name="predicate">Predicate function to use for querying the table and filtering the results.</param> /// <returns>Enumeration of entries in the table which match the query condition.</returns> internal async Task <IEnumerable <Tuple <T, string> > > ReadTableEntriesAndEtagsAsync(Expression <Func <T, bool> > predicate) { const string operation = "ReadTableEntriesAndEtags"; var startTime = DateTime.UtcNow; try { TableServiceContext svc = tableOperationsClient.GetDataServiceContext(); // Improve performance when table name differs from class name // http://www.gtrifonov.com/2011/06/15/improving-performance-for-windows-azure-tables/ svc.ResolveType = ResolveEntityType; //IQueryable<T> query = svc.CreateQuery<T>(TableName).Where(predicate); CloudTableQuery <T> cloudTableQuery = svc.CreateQuery <T>(TableName).Where(predicate).AsTableServiceQuery(); // turn IQueryable into CloudTableQuery try { Func <Task <List <T> > > executeQueryHandleContinuations = async() => { // Read table with continuation token // http://convective.wordpress.com/2013/11/03/queries-in-the-windows-azure-storage-client-library-v2-1/ // 1) First wrong sync way to read: // List<T> queryResults = query.ToList(); // ToList will actually execute the query and add entities to svc. However, this will not handle continuation tokens. // 2) Second correct sync way to read: // http://convective.wordpress.com/2010/02/06/queries-in-azure-tables/ // CloudTableQuery.Execute will properly retrieve all the records from a table through the automatic handling of continuation tokens: Task <ResultSegment <T> > firstSegmentPromise = Task <ResultSegment <T> > .Factory.FromAsync( cloudTableQuery.BeginExecuteSegmented, cloudTableQuery.EndExecuteSegmented, null); // 3) Third wrong async way to read: // return firstSegmentPromise; // 4) Forth correct async way to read - handles continuation tokens: var list = new List <T>(); Task <ResultSegment <T> > nextSegmentAsync = firstSegmentPromise; while (true) { ResultSegment <T> resultSegment = await nextSegmentAsync; var capture = resultSegment.Results; if (capture != null) // don't call Count or Any or anything else that can potentialy cause multiple evaluations of the IEnumerable { list.AddRange(capture); } if (!resultSegment.HasMoreResults) { // All data was read successfully if we got to here break; } // ask to read the next segment nextSegmentAsync = Task <ResultSegment <T> > .Factory.FromAsync( resultSegment.BeginGetNext, resultSegment.EndGetNext, null); } return(list); }; IBackoffProvider backoff = new FixedBackoff(AzureTableDefaultPolicies.PauseBetweenTableOperationRetries); List <T> results = await AsyncExecutorWithRetries.ExecuteWithRetries( counter => executeQueryHandleContinuations(), AzureTableDefaultPolicies.MaxTableOperationRetries, (exc, counter) => AzureStorageUtils.AnalyzeReadException(exc.GetBaseException(), counter, TableName, Logger), AzureTableDefaultPolicies.TableOperationTimeout, backoff); // Data was read successfully if we got to here return(PairEntitiesWithEtags(svc, results)); } catch (Exception exc) { // Out of retries... var errorMsg = string.Format("Failed to read Azure storage table {0}: {1}", TableName, exc.Message); if (!AzureStorageUtils.TableStorageDataNotFound(exc)) { Logger.Warn(ErrorCode.AzureTable_09, errorMsg, exc); } throw new OrleansException(errorMsg, exc); } } finally { CheckAlertSlowAccess(startTime, operation); } }
/// <summary> /// Inserts a data entry in the Azure table: creates a new one if does not exists or overwrites (without eTag) an already existing version (the "update in place" semantincs). /// </summary> /// <param name="data">Data to be inserted or replaced in the table.</param> /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns> public async Task <string> UpsertTableEntryAsync(T data) { const string operation = "UpsertTableEntry"; var startTime = DateTime.UtcNow; if (Logger.IsVerbose2) { Logger.Verbose2("{0} entry {1} into table {2}", operation, data, TableName); } try { TableServiceContext svc = tableOperationsClient.GetDataServiceContext(); try { Task <DataServiceResponse> savePromise; Func <int, Task <DataServiceResponse> > doSaveChanges = retryNum => { if (retryNum > 0) { svc.Detach(data); } // Try to do update first svc.AttachTo(TableName, data, ANY_ETAG); svc.UpdateObject(data); return(Task <DataServiceResponse> .Factory.FromAsync( svc.BeginSaveChangesWithRetries, svc.EndSaveChangesWithRetries, SaveChangesOptions.ReplaceOnUpdate, null)); }; if (AzureTableDefaultPolicies.MaxBusyRetries > 0) { IBackoffProvider backoff = new FixedBackoff(AzureTableDefaultPolicies.PauseBetweenBusyRetries); savePromise = AsyncExecutorWithRetries.ExecuteWithRetries( doSaveChanges, AzureTableDefaultPolicies.MaxBusyRetries, // Retry automatically iff we get ServerBusy reply from Azure storage (exc, retryNum) => IsServerBusy(exc), AzureTableDefaultPolicies.BusyRetriesTimeout, backoff); } else { // Try single Write only once savePromise = doSaveChanges(0); } await savePromise; EntityDescriptor result = svc.GetEntityDescriptor(data); return(result.ETag); } catch (Exception exc) { Logger.Warn(ErrorCode.AzureTable_06, String.Format("Intermediate error upserting entry {0} to the table {1}", (data == null ? "null" : data.ToString()), TableName), exc); throw; } } finally { CheckAlertSlowAccess(startTime, operation); } }