private static async Task <KeyValuePair <string, TableTransferStatistics> > CopyTableAsync(this CloudTable sourceTable, CloudTableClient targetClient, TableCopyOptions copyOptions, Func <bool> stopCalled) { var targetTableName = sourceTable.Name; if (stopCalled()) { return(sourceTable.Name.PairWithValue(TableTransferStatistics.Default.Concat(new[] { $"copy stopped on {targetTableName}" }))); } return(await await sourceTable.CreateIfNotExistTargetTableForCopyAsync(targetClient, targetTableName, copyOptions.partitionKeys, copyOptions.maxSegmentDownloadConcurrencyPerTable, async (targetTable, findExistingAsync, getNextSegmentAsync) => { EastFiveAzureStorageBackupService.Log.Info($"starting {targetTableName}"); try { var existingTargetRows = await findExistingAsync(stopCalled); EastFiveAzureStorageBackupService.Log.Info($"{existingTargetRows.Value.Count} entities already backed up for {targetTableName}"); var pair = new SegmentedQuery[] { }.PairWithValue(TableTransferStatistics.Default.Concat(existingTargetRows.Key)); // just copies errors while (true) { if (stopCalled()) { return sourceTable.Name.PairWithValue(pair.Value); } var nextSourceRows = await getNextSegmentAsync(pair.Key, stopCalled); pair = nextSourceRows.Value.details .PairWithValue(pair.Value.Concat(nextSourceRows.Key) // just copies errors .Concat(await nextSourceRows.Value.rows.CopyRowsAsync(targetTable, existingTargetRows.Value, copyOptions.maxRowUploadConcurrencyPerTable))); // gets result of work pair.Value.LogProgress( msg => EastFiveAzureStorageBackupService.Log.Info($"(progress) {targetTableName} -> {msg}")); if (!pair.Key.Any()) // wait until all the tokens are gone { if (pair.Value.retries.Any()) { EastFiveAzureStorageBackupService.Log.Info($"retrying {targetTableName}"); var copyRetries = copyOptions.copyRetries; existingTargetRows = await findExistingAsync(stopCalled); EastFiveAzureStorageBackupService.Log.Info($"{existingTargetRows.Value.Count} entities already backed up for {targetTableName}"); pair = new SegmentedQuery[] { }.PairWithValue(pair.Value.Concat(existingTargetRows.Key)); // just copies errors while (copyRetries-- > 0) { if (stopCalled()) { break; } var stats = await pair.Value.retries .Select(x => x.Key) .ToArray() .CopyRowsAsync(targetTable, existingTargetRows.Value, copyOptions.maxRowUploadConcurrencyPerTable); pair = new SegmentedQuery[] { }.PairWithValue(new TableTransferStatistics { errors = pair.Value.errors.Concat(stats.errors).ToArray(), successes = pair.Value.successes + stats.successes, retries = stats.retries }); if (!pair.Value.retries.Any()) { break; } } } pair.Value.LogProgress( msg => EastFiveAzureStorageBackupService.Log.Info($"finished {targetTableName} -> {msg}"), TableTransferStatistics.logFrequency); return sourceTable.Name.PairWithValue(pair.Value); } } } catch (Exception e) { return sourceTable.Name.PairWithValue(TableTransferStatistics.Default.Concat(new[] { $"Exception copying table, Detail: {e.Message}" })); } }, why => sourceTable.Name.PairWithValue(TableTransferStatistics.Default.Concat(new[] { why })).ToTask())); }