Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pause and resume, and check for cancel while uploading #3712

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
232 changes: 154 additions & 78 deletions Duplicati/Library/Main/Operation/Backup/BackendUploader.cs

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions Duplicati/Library/Main/Operation/Backup/FileProgressThrottler.cs
Expand Up @@ -30,6 +30,7 @@ internal class FileProgressThrottler
private readonly object m_Lock = new object();
private readonly StatsCollector m_Stats;
private long m_MaxBytesPerSecond;
private bool m_Paused;
private long m_TotalSize;

public FileProgressThrottler(StatsCollector stats, long maxBytesPerSecond)
Expand All @@ -48,6 +49,16 @@ public void EndFileProgress(string path)
}
}

public void Pause()
{
m_Paused = true;
}

public void Resume()
{
m_Paused = false;
}

public void Run(CancellationToken cancelToken)
{
Task.Run(() => UpdateProgressAndThrottle(cancelToken));
Expand Down Expand Up @@ -149,6 +160,9 @@ private async Task UpdateProgressAndThrottle(CancellationToken cancelToken)
if (cancelToken.IsCancellationRequested)
return;

if (m_Paused)
continue;

lock (m_Lock)
{
if (!m_Files.Any())
Expand Down
5 changes: 2 additions & 3 deletions Duplicati/Library/Main/Operation/BackupHandler.cs
Expand Up @@ -31,8 +31,6 @@
using Duplicati.Library.Common.IO;
using Duplicati.Library.Common;
using Duplicati.Library.Logging;
using Duplicati.Library.Main.Operation.Backup;
using Duplicati.Library.Main.Operation.Common;

namespace Duplicati.Library.Main.Operation
{
Expand Down Expand Up @@ -429,7 +427,8 @@ private async Task RunAsync(string[] sources, Library.Utility.IFilter filter, Ca
{
long filesetid;
var counterToken = new CancellationTokenSource();
var uploader = new Backup.BackendUploader(() => DynamicLoader.BackendLoader.GetBackend(m_backendurl, m_options.RawOptions), m_options, db, m_result.TaskReader, stats);
Func<IBackend> backendFactory = () => DynamicLoader.BackendLoader.GetBackend(m_backendurl, m_options.RawOptions);
var uploader = new Backup.BackendUploader(backendFactory, m_options, db, m_result.TaskReader, stats, m_result.OperationProgressUpdater);
using (var snapshot = GetSnapshot(sources, m_options))
{
try
Expand Down
15 changes: 8 additions & 7 deletions Duplicati/Library/Main/Operation/Common/TaskControl.cs
Expand Up @@ -30,6 +30,7 @@ public interface ITaskReader
/// The return value is <c>true</c> if the program should continue and <c>false</c> if a stop is requested
/// </summary>
Task<bool> ProgressAsync { get; }

/// <summary>
/// Gets the transfer progress async control.
/// The transfer handler should await this instead of the <see cref="ProgressAsync"/> event.
Expand Down Expand Up @@ -159,8 +160,8 @@ public void Resume()
/// <summary>
/// Requests that progress should be paused
/// </summary>
/// <param name="alsoTransfers">If set to <c>true</c> also transfers.</param>
public void Pause(bool alsoTransfers = false)
/// <param name="alsoNewTransfers">If set to <c>true</c> also pauses new transfers. Transfers in progress will complete.</param>
public void Pause(bool alsoNewTransfers = false)
{
lock(m_lock)
{
Expand All @@ -170,7 +171,7 @@ public void Pause(bool alsoTransfers = false)
m_progressstate = State.Paused;
}

if (alsoTransfers && m_transferstate == State.Active)
if (alsoNewTransfers && m_transferstate == State.Active)
{
m_transfer = new TaskCompletionSource<bool>();
m_transferstate = State.Paused;
Expand All @@ -182,8 +183,8 @@ public void Pause(bool alsoTransfers = false)
/// Requests that the progress should be stopped in an orderly manner,
/// which allows current transfers to be completed.
/// </summary>
/// <param name="alsoTransfers">If set to <c>true</c> also transfers.</param>
public void Stop(bool alsoTransfers = false)
/// <param name="alsoNewTransfers">If set to <c>true</c> stops any new transfers and completes existing transfers.</param>
public void Stop(bool alsoNewTransfers = false)
{
lock(m_lock)
{
Expand All @@ -196,7 +197,7 @@ public void Stop(bool alsoTransfers = false)
m_progressstate = State.Stopped;
}

if (alsoTransfers && (m_transferstate == State.Active || m_transferstate == State.Paused))
if (alsoNewTransfers && (m_transferstate == State.Active || m_transferstate == State.Paused))
{
if (m_transferstate != State.Paused)
m_transfer = new TaskCompletionSource<bool>();
Expand All @@ -208,7 +209,7 @@ public void Stop(bool alsoTransfers = false)
}

/// <summary>
/// Terminates the progress without allowing a flush
/// Terminates the progress and all existing transfers without allowing them to complete.
/// </summary>
public void Terminate()
{
Expand Down
4 changes: 3 additions & 1 deletion Duplicati/Library/Main/OperationPhase.cs
Expand Up @@ -59,7 +59,9 @@ public enum OperationPhase
PurgeFiles_Compact,
PurgeFiles_Complete,

Paused,
Paused_WaitForUpload,

Error
}
}

11 changes: 7 additions & 4 deletions Duplicati/Library/Main/ProgressClasses.cs
Expand Up @@ -313,6 +313,8 @@ public interface IOperationProgress
/// Occurs when the phase has changed
/// </summary>
event PhaseChangedDelegate PhaseChanged;

OperationPhase CurrentPhase { get; }
}

/// <summary>
Expand All @@ -334,9 +336,10 @@ internal interface IOperationProgressUpdaterAndReporter : IOperationProgressUpda

internal class OperationProgressUpdater : IOperationProgressUpdaterAndReporter
{
public OperationPhase CurrentPhase { get; private set; }

private readonly object m_lock = new object();

private OperationPhase m_phase;
private float m_progress;
private string m_curfilename;
private long m_curfilesize;
Expand All @@ -357,8 +360,8 @@ public void UpdatePhase(OperationPhase phase)
OperationPhase prev_phase;
lock(m_lock)
{
prev_phase = m_phase;
m_phase = phase;
prev_phase = CurrentPhase;
CurrentPhase = phase;
m_curfilename = null;
m_curfilesize = 0;
m_curfileoffset = 0;
Expand Down Expand Up @@ -426,7 +429,7 @@ public void UpdateOverall(out OperationPhase phase, out float progress, out long
{
lock(m_lock)
{
phase = m_phase;
phase = CurrentPhase;
filesize = m_filesize;
progress = m_progress;
filesprocessed = m_filesprocessed;
Expand Down
10 changes: 9 additions & 1 deletion Duplicati/Library/Main/ResultClasses.cs
Expand Up @@ -178,6 +178,8 @@ internal abstract class BasicResults : IBasicResults, ISetCommonOptions, ITaskCo
/// </summary>
protected static readonly int SERIALIZATION_LIMIT = 20;

private OperationPhase m_PreviousPhase;

protected class DbMessage
{
public readonly string Type;
Expand Down Expand Up @@ -409,6 +411,9 @@ public void Pause()
{
m_pauseEvent.Reset();
m_controlState = TaskControlState.Pause;
m_PreviousPhase = m_operationProgressUpdater.CurrentPhase;
m_operationProgressUpdater.UpdatePhase(OperationPhase.Paused_WaitForUpload);
m_taskController.Pause(alsoNewTransfers: true);
}

if (StateChangedEvent != null)
Expand All @@ -430,6 +435,8 @@ public void Resume()
{
m_pauseEvent.Set();
m_controlState = TaskControlState.Run;
m_operationProgressUpdater.UpdatePhase(m_PreviousPhase);
m_taskController.Resume();
}

if (StateChangedEvent != null)
Expand All @@ -453,7 +460,7 @@ public void Stop(bool allowCurrentFileToFinish)
m_pauseEvent.Set();
if (!allowCurrentFileToFinish)
{
m_taskController.Stop(true);
m_taskController.Stop(alsoNewTransfers: true);
}
}

Expand All @@ -475,6 +482,7 @@ public void Abort()
{
m_controlState = TaskControlState.Abort;
m_pauseEvent.Set();
m_taskController.Terminate();
}

if (StateChangedEvent != null)
Expand Down
6 changes: 5 additions & 1 deletion Duplicati/Server/webroot/ngax/index.html
Expand Up @@ -175,9 +175,13 @@
</div>
</div>
</span>
<span ng-show="StopReqId == activeTaskID">

<span ng-show="StopReqId == activeTaskID &amp;&amp; stopEvent == 'stopaftercurrentfile'">
<strong translate>Stopping after the current file:</strong> {{activeBackup.Backup.Name}}
</span>
<span ng-show="StopReqId == activeTaskID &amp;&amp; stopEvent == 'stopnow'">
<strong translate>Stopping backup ...</strong>
</span>
</span>
<span ng-show="activeBackup == null">
<strong ng-hide="StopReqId == activeTaskID" translate>Running task:</strong>
Expand Down
Expand Up @@ -67,6 +67,11 @@ backupApp.controller('StateController', function($scope, $timeout, ServerStatus,
{
pg = 1;
}
else if ($scope.state.lastPgEvent != null && $scope.state.lastPgEvent.Phase == 'Paused_WaitForUpload')
{
var speed_txt = ($scope.state.lastPgEvent.BackendSpeed < 0) ? "" : " at "+AppUtils.formatSizeString($scope.state.lastPgEvent.BackendSpeed)+"/s";
text = text + speed_txt;
}
else if ($scope.state.lastPgEvent.OverallProgress > 0) {
pg = $scope.state.lastPgEvent.OverallProgress;
}
Expand All @@ -91,10 +96,12 @@ backupApp.controller('StateController', function($scope, $timeout, ServerStatus,
{
AppService.post('/task/' + taskId + '/stopaftercurrentfile');
$scope.StopReqId = taskId;
$scope.stopEvent = 'stopaftercurrentfile';
}
else if (ix == 1) {
AppService.post('/task/' + taskId + '/stopnow');
$scope.StopReqId = taskId;
$scope.stopEvent = 'stopnow';
}
};

Expand Down
Expand Up @@ -22,7 +22,8 @@ backupApp.service('ServerStatus', function($rootScope, $timeout, AppService, App
updateReady: false,
updateDownloadProgress: 0,
proposedSchedule: [],
schedulerQueueIds: []
schedulerQueueIds: [],
stopEvent: ''
};

this.state = state;
Expand All @@ -42,6 +43,7 @@ backupApp.service('ServerStatus', function($rootScope, $timeout, AppService, App
'Backup_VerificationUpload': gettextCatalog.getString('Uploading verification file …'),
'Backup_PostBackupVerify': gettextCatalog.getString('Verifying backend data …'),
'Backup_Complete': gettextCatalog.getString('Backup complete!'),
'Backup_StopNow': gettextCatalog.getString('Stopping backup ...'),
'Restore_Begin': gettextCatalog.getString('Starting restore …'),
'Restore_RecreateDatabase': gettextCatalog.getString('Rebuilding local database …'),
'Restore_PreRestoreVerify': gettextCatalog.getString('Verifying remote data …'),
Expand All @@ -64,6 +66,8 @@ backupApp.service('ServerStatus', function($rootScope, $timeout, AppService, App
'PurgeFiles_Process,': gettextCatalog.getString('Purging files …'),
'PurgeFiles_Compact,': gettextCatalog.getString('Compacting remote data …'),
'PurgeFiles_Complete,': gettextCatalog.getString('Purging files complete!'),
'Paused': gettextCatalog.getString('Paused'),
'Paused_WaitForUpload': gettextCatalog.getString('Pausing, waiting for uploads to finish'),
'Error': gettextCatalog.getString('Error!')
};
};
Expand Down