/// <summary> /// Sends the transfer payload to the target providers. /// </summary> /// <param name="file"></param> /// <param name="source"></param> /// <param name="payload"></param> /// <param name="cancelToken"></param> /// <returns></returns> private async Task SendTransferPayloadToFileTargetsAsync(BackupFile file, SourceLocation source, TransferPayload payload, CancellationToken cancelToken) { var directory = await Database.GetDirectoryMapItemAsync(file.Directory).ConfigureAwait(false); foreach (var providerName in payload.DestinationProviders) { var destination = Providers[providerName]; try { // upload this chunk to the destination cloud provider. // note: the provider implementation will automatically handle retries of transient issues. await destination.UploadFileBlockAsync(file, source, directory, payload.Data, (int)payload.CurrentBlockNumber, (int)payload.TotalBlocks, cancelToken).ConfigureAwait(false); // flag the chunk as sent in the file status. file.SetBlockAsSent((int)payload.CurrentBlockNumber, providerName); } catch (OperationCanceledException) { // cancellation was requested. // bubble up to the next level. throw; } catch (Exception ex) { Logger.WriteTraceError("An error occurred during a file transfer.", ex, Logger.GenerateFullContextStackTrace(), InstanceID); file.SetProviderToFailed(providerName); // sets the error message/stack trace await Database.SetBackupFileAsFailedAsync(file, ex.ToString()).ConfigureAwait(false); await Database.RemoveFileFromBackupQueueAsync(file).ConfigureAwait(false); } finally { // commit the status changes to the local state database. await Database.UpdateBackupFileCopyStateAsync(file).ConfigureAwait(false); } } }