Merge pull request #6173 from duplicati/feature/dont-upload-dlist-on-failure
Simple fix to prevent uploading dlist on failures
This commit is contained in:
@@ -75,6 +75,10 @@ partial class BackendManager
|
||||
/// </summary>
|
||||
public required bool TrackedInDb { get; init; }
|
||||
/// <summary>
|
||||
/// A callback that is invoked when the database needs to be updated
|
||||
/// </summary>
|
||||
public required Func<Task> OnDbUpdate { get; init; }
|
||||
/// <summary>
|
||||
/// The encryption and hashing task, the return value indicates if the hash and size was updated
|
||||
/// </summary>
|
||||
private Task<(string Hash, long Size)>? encryptionAndHashingTask;
|
||||
@@ -94,6 +98,10 @@ partial class BackendManager
|
||||
/// A callback that is invoked when the index volume is finished
|
||||
/// </summary>
|
||||
public required Action? IndexVolumeFinishedCallback { get; init; }
|
||||
/// <summary>
|
||||
/// The default callback for database updates (no-op)
|
||||
/// </summary>
|
||||
public static Func<Task> OnDbUpdateDefault = () => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new put operation
|
||||
@@ -201,12 +209,16 @@ partial class BackendManager
|
||||
|
||||
// On first upload attempt, calculate the hash and size
|
||||
if (operationState == OperationState.None && TrackedInDb)
|
||||
{
|
||||
Context.Database.LogRemoteVolumeUpdated(RemoteFilename, RemoteVolumeState.Uploading, size, hash);
|
||||
await OnDbUpdate().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// First attempt here, finish the index file now that all information is known
|
||||
if (OriginalIndexFile != null && indexOperation == null)
|
||||
{
|
||||
Context.Database.LogRemoteVolumeUpdated(OriginalIndexFile.RemoteFilename, RemoteVolumeState.Uploading, -1, null);
|
||||
await OnDbUpdate().ConfigureAwait(false);
|
||||
|
||||
// Prepare an upload operation for the index file
|
||||
var req2 = new PutOperation(OriginalIndexFile.RemoteFilename, Context, false, cancelToken)
|
||||
@@ -215,9 +227,11 @@ partial class BackendManager
|
||||
TrackedInDb = TrackedInDb,
|
||||
Unencrypted = Unencrypted,
|
||||
IndexVolumeFinishedCallback = null,
|
||||
OriginalIndexFile = null
|
||||
OriginalIndexFile = null,
|
||||
OnDbUpdate = OnDbUpdate
|
||||
};
|
||||
|
||||
|
||||
OriginalIndexFile.FinishVolume(hash, size);
|
||||
IndexVolumeFinishedCallback?.Invoke();
|
||||
OriginalIndexFile.Close();
|
||||
@@ -226,8 +240,11 @@ partial class BackendManager
|
||||
}
|
||||
|
||||
// If we have previously attempted to upload, we need to rename the file
|
||||
if (operationState == OperationState.Uploading)
|
||||
if (operationState == OperationState.Uploading && TrackedInDb)
|
||||
{
|
||||
RenameFileAfterError(size);
|
||||
await OnDbUpdate().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Flag for next attempt, if any
|
||||
operationState = OperationState.Uploading;
|
||||
@@ -263,6 +280,8 @@ partial class BackendManager
|
||||
{
|
||||
Context.Database.LogRemoteOperation("put", RemoteFilename, JsonConvert.SerializeObject(new { Size = Size, Hash = Hash }));
|
||||
Context.Statwriter.SendEvent(BackendActionType.Put, BackendEventType.Started, RemoteFilename, Size);
|
||||
if (TrackedInDb)
|
||||
await OnDbUpdate().ConfigureAwait(false);
|
||||
|
||||
var begin = DateTime.Now;
|
||||
|
||||
@@ -280,7 +299,10 @@ partial class BackendManager
|
||||
Logging.Log.WriteProfilingMessage(LOGTAG, "UploadSpeed", "Uploaded {0} in {1}, {2}/s", Library.Utility.Utility.FormatSizeString(Size), duration, Library.Utility.Utility.FormatSizeString((long)(Size / duration.TotalSeconds)));
|
||||
|
||||
if (TrackedInDb)
|
||||
{
|
||||
Context.Database.LogRemoteVolumeUpdated(RemoteFilename, RemoteVolumeState.Uploaded, Size, Hash);
|
||||
await OnDbUpdate().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Context.Statwriter.SendEvent(BackendActionType.Put, BackendEventType.Completed, RemoteFilename, Size);
|
||||
|
||||
|
||||
@@ -249,9 +249,10 @@ internal partial class BackendManager : IBackendManager
|
||||
/// <param name="indexVolume">The index volume to upload, if any</param>
|
||||
/// <param name="indexVolumeFinished">The callback to call when the index volume is finished</param>
|
||||
/// <param name="waitForComplete">True if the operation should wait for the file to actually be uploaded. If this argument is false, the task will complete once the operation is queued</param>
|
||||
/// <param name="onDbUpdate">The callback to call when the database is updated</param>
|
||||
/// <param name="cancelToken">The cancellation token</param>
|
||||
/// <returns>An awaitable task</returns>
|
||||
public async Task PutAsync(VolumeWriterBase volume, IndexVolumeWriter? indexVolume, Action? indexVolumeFinished, bool waitForComplete, CancellationToken cancelToken)
|
||||
public async Task PutAsync(VolumeWriterBase volume, IndexVolumeWriter? indexVolume, Action? indexVolumeFinished, bool waitForComplete, Func<Task>? onDbUpdate, CancellationToken cancelToken)
|
||||
{
|
||||
volume.Close();
|
||||
|
||||
@@ -261,7 +262,8 @@ internal partial class BackendManager : IBackendManager
|
||||
OriginalIndexFile = indexVolume,
|
||||
Unencrypted = false,
|
||||
TrackedInDb = true,
|
||||
IndexVolumeFinishedCallback = indexVolumeFinished
|
||||
IndexVolumeFinishedCallback = indexVolumeFinished,
|
||||
OnDbUpdate = onDbUpdate ?? PutOperation.OnDbUpdateDefault,
|
||||
};
|
||||
|
||||
// Prepare encryption
|
||||
@@ -285,7 +287,8 @@ internal partial class BackendManager : IBackendManager
|
||||
Unencrypted = true, // Avoid encrypting
|
||||
TrackedInDb = false, // Not tracked
|
||||
OriginalIndexFile = null,
|
||||
IndexVolumeFinishedCallback = null
|
||||
IndexVolumeFinishedCallback = null,
|
||||
OnDbUpdate = PutOperation.OnDbUpdateDefault,
|
||||
};
|
||||
|
||||
// Sets the task as already completed
|
||||
@@ -294,20 +297,43 @@ internal partial class BackendManager : IBackendManager
|
||||
await op.GetResult().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the database messages to the database
|
||||
/// </summary>
|
||||
/// <param name="database">The database to write to</param>
|
||||
/// <param name="transaction">The transaction to use</param>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <returns></returns>
|
||||
public Task FlushPendingMessagesAsync(LocalDatabase database, IDbTransaction? transaction, CancellationToken cancellationToken)
|
||||
{
|
||||
context.Database.FlushPendingMessages(database, transaction);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the backend queue to be empty
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <returns>An awaitable task</returns>
|
||||
public async Task WaitForEmptyAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var op = new WaitForEmptyOperation(context, cancellationToken);
|
||||
await QueueTask(op).ConfigureAwait(false);
|
||||
await op.GetResult().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the backend queue to be empty and flushes the database messages
|
||||
/// </summary>
|
||||
/// <param name="database">The database to write to</param>
|
||||
/// <param name="transaction">The transaction to use</param>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <returns>An awaitable task</returns>
|
||||
public async Task WaitForEmptyAsync(LocalDatabase database, IDbTransaction? transaction, CancellationToken cancellationToken)
|
||||
{
|
||||
context.Database.FlushPendingMessages(database, transaction);
|
||||
|
||||
var op = new WaitForEmptyOperation(context, cancellationToken);
|
||||
await QueueTask(op).ConfigureAwait(false);
|
||||
await op.GetResult().ConfigureAwait(false);
|
||||
|
||||
context.Database.FlushPendingMessages(database, transaction);
|
||||
await FlushPendingMessagesAsync(database, transaction, cancellationToken).ConfigureAwait(false);
|
||||
await WaitForEmptyAsync(cancellationToken).ConfigureAwait(false);
|
||||
await FlushPendingMessagesAsync(database, transaction, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -318,7 +344,8 @@ internal partial class BackendManager : IBackendManager
|
||||
public async Task StopRunnerAndFlushMessages(LocalDatabase database, IDbTransaction? transaction)
|
||||
{
|
||||
await requestChannel.RetireAsync().ConfigureAwait(false);
|
||||
context.Database.FlushPendingMessages(database, transaction);
|
||||
await FlushPendingMessagesAsync(database, transaction, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (queueRunner.IsFaulted)
|
||||
Logging.Log.WriteWarningMessage(LOGTAG, "BackendManagerShutdown", queueRunner.Exception, "Backend manager queue runner crashed");
|
||||
}
|
||||
|
||||
@@ -25,9 +25,10 @@ internal interface IBackendManager : IDisposable
|
||||
/// <param name="indexVolume">The index volume to upload, if any</param>
|
||||
/// <param name="indexVolumeFinished">The action to call when the index volume is finished</param>
|
||||
/// <param name="waitForComplete">Whether to wait for the upload to complete</param>
|
||||
/// <param name="onDbUpdate">The action to call when the database should be updated</param>
|
||||
/// <param name="cancelToken">The cancellation token</param>
|
||||
/// <returns>An awaitable task</returns>
|
||||
Task PutAsync(VolumeWriterBase blockVolume, IndexVolumeWriter? indexVolume, Action? indexVolumeFinished, bool waitForComplete, CancellationToken cancelToken);
|
||||
Task PutAsync(VolumeWriterBase blockVolume, IndexVolumeWriter? indexVolume, Action? indexVolumeFinished, bool waitForComplete, Func<Task>? onDbUpdate, CancellationToken cancelToken);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads a file to the backend without encryption
|
||||
@@ -38,6 +39,13 @@ internal interface IBackendManager : IDisposable
|
||||
/// <returns>An awaitable task</returns>
|
||||
Task PutVerificationFileAsync(string remotename, TempFile tempFile, CancellationToken cancelToken);
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the backend queue to be empty
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <returns>An awaitable task</returns>
|
||||
Task WaitForEmptyAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the backend queue to be empty and flushes any pending messages to the database
|
||||
/// </summary>
|
||||
@@ -128,4 +136,13 @@ internal interface IBackendManager : IDisposable
|
||||
/// </summary>
|
||||
long LastWriteSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the database messages to the database
|
||||
/// </summary>
|
||||
/// <param name="database">The database to write to</param>
|
||||
/// <param name="transaction">The transaction to use</param>
|
||||
/// <param name="cancellationToken">The cancellation token</param>
|
||||
/// <returns></returns>
|
||||
Task FlushPendingMessagesAsync(LocalDatabase database, IDbTransaction? transaction, CancellationToken cancellationToken);
|
||||
|
||||
}
|
||||
|
||||
@@ -127,8 +127,6 @@ namespace Duplicati.Library.Main.Operation.Backup
|
||||
|
||||
blockvolume.Close();
|
||||
|
||||
await database.CommitTransactionAsync("CommitAddBlockToOutputFlush");
|
||||
|
||||
IndexVolumeWriter indexVolumeCopy = null;
|
||||
if (indexvolume != null)
|
||||
{
|
||||
@@ -143,7 +141,8 @@ namespace Duplicati.Library.Main.Operation.Backup
|
||||
blockvolume = null;
|
||||
indexvolume = null;
|
||||
|
||||
await backendManager.PutAsync(blockVolumeCopy, indexVolumeCopy, null, false, taskreader.ProgressToken);
|
||||
await database.CommitTransactionAsync("CommitAddBlockToOutputFlush");
|
||||
await backendManager.PutAsync(blockVolumeCopy, indexVolumeCopy, null, false, () => database.FlushBackendMessagesAndCommitAsync(backendManager), taskreader.ProgressToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ internal static class RecreateMissingIndexFiles
|
||||
return;
|
||||
|
||||
await database.UpdateRemoteVolumeAsync(w.RemoteFilename, RemoteVolumeState.Uploading, -1, null).ConfigureAwait(false);
|
||||
await backendManager.PutAsync(w, null, null, false, taskreader.ProgressToken).ConfigureAwait(false);
|
||||
await database.CommitTransactionAsync("RecreateMissingIndexFile").ConfigureAwait(false);
|
||||
await backendManager.PutAsync(w, null, null, false, () => database.FlushBackendMessagesAndCommitAsync(backendManager), taskreader.ProgressToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,8 @@ namespace Duplicati.Library.Main.Operation.Backup
|
||||
await database.AddIndexBlockLinkAsync(indexVolumeCopy.VolumeID, target.BlockVolume.VolumeID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await backendManager.PutAsync(target.BlockVolume, indexVolumeCopy, null, false, taskreader.ProgressToken).ConfigureAwait(false);
|
||||
await database.CommitTransactionAsync("UploadSpillVolume").ConfigureAwait(false);
|
||||
await backendManager.PutAsync(target.BlockVolume, indexVolumeCopy, null, false, () => database.FlushBackendMessagesAndCommitAsync(backendManager), taskreader.ProgressToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ internal static class UploadRealFilelist
|
||||
await db.UpdateRemoteVolumeAsync(filesetvolume.RemoteFilename, RemoteVolumeState.Uploading, -1, null).ConfigureAwait(false);
|
||||
await db.CommitTransactionAsync("CommitUpdateRemoteVolume").ConfigureAwait(false);
|
||||
|
||||
await backendManager.PutAsync(filesetvolume, null, null, false, taskreader.ProgressToken).ConfigureAwait(false);
|
||||
await backendManager.PutAsync(filesetvolume, null, null, false, () => db.FlushBackendMessagesAndCommitAsync(backendManager), taskreader.ProgressToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -115,8 +115,7 @@ namespace Duplicati.Library.Main.Operation.Backup
|
||||
if (!string.IsNullOrWhiteSpace(lastTempFilelist.Name) && (lastTempFilelist.State == RemoteVolumeState.Uploading || lastTempFilelist.State == RemoteVolumeState.Temporary))
|
||||
await database.UpdateRemoteVolumeAsync(lastTempFilelist.Name, RemoteVolumeState.Deleting, -1, null);
|
||||
await database.CommitTransactionAsync("CommitUpdateFilelistVolume");
|
||||
|
||||
await backendManager.PutAsync(fsw, null, null, false, taskreader.ProgressToken);
|
||||
await backendManager.PutAsync(fsw, null, null, true, () => database.FlushBackendMessagesAndCommitAsync(backendManager), taskreader.ProgressToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -375,7 +375,8 @@ namespace Duplicati.Library.Main.Operation
|
||||
throw;
|
||||
}
|
||||
|
||||
return new PreBackupVerifyResult(database, lastTempFilelist);
|
||||
// Return the updated version of the fileset, in case it was actually complete
|
||||
return new PreBackupVerifyResult(database, database.GetLastIncompleteFilesetVolume(null));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -594,14 +595,16 @@ namespace Duplicati.Library.Main.Operation
|
||||
return new AggregateException(ex.First().Message, ex);
|
||||
}
|
||||
|
||||
private static async Task<long> FlushBackend(LocalDatabase database, IDbTransaction transaction, BackupResults result, IBackendManager backendManager)
|
||||
private static async Task<long> FlushBackend(Backup.BackupDatabase database, BackupResults result, IBackendManager backendManager)
|
||||
{
|
||||
// Wait for upload completion
|
||||
result.OperationProgressUpdater.UpdatePhase(OperationPhase.Backup_WaitForUpload);
|
||||
|
||||
try
|
||||
{
|
||||
await backendManager.WaitForEmptyAsync(database, transaction, result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
await database.FlushBackendMessagesAndCommitAsync(backendManager).ConfigureAwait(false);
|
||||
await backendManager.WaitForEmptyAsync(result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
await database.FlushBackendMessagesAndCommitAsync(backendManager).ConfigureAwait(false);
|
||||
// Grab the size of the last uploaded volume
|
||||
return backendManager.LastWriteSize;
|
||||
}
|
||||
@@ -709,12 +712,14 @@ namespace Duplicati.Library.Main.Operation
|
||||
using (new Logging.Timer(LOGTAG, "VerifyConsistency", "VerifyConsistency"))
|
||||
await db.VerifyConsistencyAsync(m_options.Blocksize, m_options.BlockhashSize, false);
|
||||
|
||||
await FlushBackend(db, m_result, backendManager).ConfigureAwait(false);
|
||||
|
||||
// Send the actual filelist
|
||||
await Backup.UploadRealFilelist.Run(m_result, db, backendManager, m_options, filesetvolume, filesetid, m_result.TaskControl, lastTempVolumeIncomplete);
|
||||
|
||||
// Wait for upload completion
|
||||
m_result.OperationProgressUpdater.UpdatePhase(OperationPhase.Backup_WaitForUpload);
|
||||
var lastVolumeSize = await FlushBackend(m_database, null, m_result, backendManager).ConfigureAwait(false);
|
||||
var lastVolumeSize = await FlushBackend(db, m_result, backendManager).ConfigureAwait(false);
|
||||
|
||||
if (!m_options.Dryrun)
|
||||
database.TerminatedWithActiveUploads = false;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.Library.Main.Database;
|
||||
|
||||
@@ -66,6 +67,15 @@ namespace Duplicati.Library.Main.Operation.Common
|
||||
return RunOnMain(() => m_db.UpdateRemoteVolume(name, state, size, hash, suppressCleanup, deleteGraceTime, GetTransaction()));
|
||||
}
|
||||
|
||||
public async Task FlushBackendMessagesAndCommitAsync(IBackendManager backendManager)
|
||||
{
|
||||
await FlushPendingBackendMessagesAsync(backendManager).ConfigureAwait(false);
|
||||
await CommitTransactionAsync("FlushBackendMessagesAndCommitAsync").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Task FlushPendingBackendMessagesAsync(IBackendManager backendManager)
|
||||
=> RunOnMain(() => backendManager.FlushPendingMessagesAsync(m_db, GetTransaction(), CancellationToken.None).ConfigureAwait(false));
|
||||
|
||||
public Task CommitTransactionAsync(string message, bool restart = true)
|
||||
{
|
||||
return RunOnMain(() =>
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Duplicati.Library.Main.Operation
|
||||
|
||||
if (newvol.Filesize > (m_options.VolumeSize - m_options.Blocksize))
|
||||
{
|
||||
await FinishVolumeAndUpload(db, backendManager, newvol, newvolindex, uploadedVolumes);
|
||||
await FinishVolumeAndUpload(db, backendManager, newvol, newvolindex, uploadedVolumes, rtr);
|
||||
|
||||
newvol = new BlockVolumeWriter(m_options);
|
||||
newvol.VolumeID = db.RegisterRemoteVolume(newvol.RemoteFilename, RemoteVolumeType.Blocks, RemoteVolumeState.Temporary, rtr.Transaction);
|
||||
@@ -215,7 +215,7 @@ namespace Duplicati.Library.Main.Operation
|
||||
|
||||
if (blocksInVolume > 0)
|
||||
{
|
||||
await FinishVolumeAndUpload(db, backendManager, newvol, newvolindex, uploadedVolumes).ConfigureAwait(false);
|
||||
await FinishVolumeAndUpload(db, backendManager, newvol, newvolindex, uploadedVolumes, rtr).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -323,7 +323,7 @@ namespace Duplicati.Library.Main.Operation
|
||||
yield return d;
|
||||
}
|
||||
|
||||
private async Task FinishVolumeAndUpload(LocalDeleteDatabase db, IBackendManager backendManager, BlockVolumeWriter newvol, IndexVolumeWriter newvolindex, List<KeyValuePair<string, long>> uploadedVolumes)
|
||||
private async Task FinishVolumeAndUpload(LocalDeleteDatabase db, IBackendManager backendManager, BlockVolumeWriter newvol, IndexVolumeWriter newvolindex, List<KeyValuePair<string, long>> uploadedVolumes, ReusableTransaction rtr)
|
||||
{
|
||||
Action indexVolumeFinished = () =>
|
||||
{
|
||||
@@ -339,8 +339,16 @@ namespace Duplicati.Library.Main.Operation
|
||||
uploadedVolumes.Add(new KeyValuePair<string, long>(newvol.RemoteFilename, newvol.Filesize));
|
||||
if (newvolindex != null)
|
||||
uploadedVolumes.Add(new KeyValuePair<string, long>(newvolindex.RemoteFilename, newvolindex.Filesize));
|
||||
|
||||
// TODO: The upload here does not flush the database messages,
|
||||
// and this can leave the database in a state where it does not know of the remote file
|
||||
// To fix it, we need thread-safe access to the daabase and transaction
|
||||
// Once fixed, we can perhaps let he backend manager simply call the database directly
|
||||
if (!m_options.Dryrun)
|
||||
await backendManager.PutAsync(newvol, newvolindex, indexVolumeFinished, false, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
{
|
||||
rtr.Commit("CommitUpload");
|
||||
await backendManager.PutAsync(newvol, newvolindex, indexVolumeFinished, false, null, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
Logging.Log.WriteDryrunMessage(LOGTAG, "WouldUploadGeneratedBlockset", "Would upload generated blockset of size {0}", Library.Utility.Utility.FormatSizeString(newvol.Filesize));
|
||||
}
|
||||
|
||||
@@ -213,7 +213,11 @@ namespace Duplicati.Library.Main.Operation
|
||||
db.UpdateRemoteVolume(f.Key, RemoteVolumeState.Deleting, f.Value, null, tr);
|
||||
|
||||
tr.Commit();
|
||||
await backendManager.PutAsync(vol, null, null, true, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
|
||||
await backendManager.PutAsync(vol, null, null, true, async () =>
|
||||
{
|
||||
await backendManager.FlushPendingMessagesAsync(db, null, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
}, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
await backendManager.DeleteAsync(prevfilename, -1, true, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
await backendManager.WaitForEmptyAsync(db, null, m_result.TaskControl.ProgressToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -350,7 +350,8 @@ namespace Duplicati.Library.Main.Operation
|
||||
|
||||
fsw.VolumeID = db.RegisterRemoteVolume(fsw.RemoteFilename, RemoteVolumeType.Files, -1, RemoteVolumeState.Temporary);
|
||||
db.LinkFilesetToVolume(filesetId, fsw.VolumeID, null);
|
||||
await backendManager.PutAsync(fsw, null, null, false, cancellationToken).ConfigureAwait(false);
|
||||
// TODO: Rewrite to use transactions and flush db messages as needed
|
||||
await backendManager.PutAsync(fsw, null, null, false, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (anyDlistUploads)
|
||||
@@ -415,7 +416,8 @@ namespace Duplicati.Library.Main.Operation
|
||||
else
|
||||
{
|
||||
db.UpdateRemoteVolume(volumeWriter.RemoteFilename, RemoteVolumeState.Uploading, -1, null, null);
|
||||
await backendManager.PutAsync(volumeWriter, null, null, false, cancellationToken).ConfigureAwait(false);
|
||||
// TODO: Rewrite to use transactions and flush db messages as needed
|
||||
await backendManager.PutAsync(volumeWriter, null, null, false, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (n.Type == RemoteVolumeType.Index)
|
||||
@@ -456,7 +458,8 @@ namespace Duplicati.Library.Main.Operation
|
||||
else
|
||||
{
|
||||
db.UpdateRemoteVolume(w.RemoteFilename, RemoteVolumeState.Uploading, -1, null, null);
|
||||
await backendManager.PutAsync(w, null, null, false, cancellationToken).ConfigureAwait(false);
|
||||
// TODO: Rewrite to use transactions and flush db messages as needed
|
||||
await backendManager.PutAsync(w, null, null, false, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (n.Type == RemoteVolumeType.Blocks)
|
||||
@@ -552,7 +555,8 @@ namespace Duplicati.Library.Main.Operation
|
||||
else
|
||||
{
|
||||
db.UpdateRemoteVolume(w.RemoteFilename, RemoteVolumeState.Uploading, -1, null, null);
|
||||
await backendManager.PutAsync(w, null, null, false, cancellationToken).ConfigureAwait(false);
|
||||
// TODO: Rewrite to use transactions and flush db messages as needed
|
||||
await backendManager.PutAsync(w, null, null, false, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,12 +898,15 @@ namespace Duplicati.UnitTest
|
||||
using (var c = new Controller("file://" + TARGETFOLDER, testopts.Expand(new { full_remote_verification = true }), null))
|
||||
TestUtils.AssertResults(c.Test(long.MaxValue));
|
||||
|
||||
// If we fail on the dlist after the upload, it is promoted to a regular backup
|
||||
var expectedFilesets = before || failOnLastDblock ? 2 : 1;
|
||||
|
||||
// Make a new backup, should continue as normal
|
||||
using (var c = new Controller("file://" + TARGETFOLDER, testopts, null))
|
||||
{
|
||||
var backupResults = c.Backup(new string[] { DATAFOLDER });
|
||||
TestUtils.AssertResults(backupResults);
|
||||
Assert.AreEqual(2, c.List().Filesets.Count());
|
||||
Assert.AreEqual(expectedFilesets, c.List().Filesets.Count());
|
||||
}
|
||||
|
||||
// Test that we can recreate
|
||||
@@ -916,12 +919,12 @@ namespace Duplicati.UnitTest
|
||||
using (var c = new Controller("file://" + TARGETFOLDER, testopts, null))
|
||||
TestUtils.AssertResults(c.Repair());
|
||||
|
||||
// Check that we have 2 versions
|
||||
// Check that we have the correct versions
|
||||
using (var c = new Controller("file://" + TARGETFOLDER, testopts, null))
|
||||
{
|
||||
var listResults = c.List();
|
||||
TestUtils.AssertResults(listResults);
|
||||
Assert.AreEqual(2, listResults.Filesets.Count());
|
||||
Assert.AreEqual(expectedFilesets, listResults.Filesets.Count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,12 +987,15 @@ namespace Duplicati.UnitTest
|
||||
// Make spacing for the next backup
|
||||
Thread.Sleep(3000);
|
||||
|
||||
// If we fail on the dlist after the upload, it is promoted to a regular backup
|
||||
var expectedFilesets = before || failOnLastDblock ? 2 : 1;
|
||||
|
||||
// Make a new backup, should continue as normal
|
||||
using (var c = new Controller("file://" + TARGETFOLDER, testopts, null))
|
||||
{
|
||||
IBackupResults backupResults = c.Backup(new string[] { DATAFOLDER });
|
||||
TestUtils.AssertResults(backupResults);
|
||||
Assert.AreEqual(2, c.List().Filesets.Count());
|
||||
Assert.AreEqual(expectedFilesets, c.List().Filesets.Count());
|
||||
}
|
||||
|
||||
// Verify that all is in order
|
||||
@@ -1011,7 +1017,7 @@ namespace Duplicati.UnitTest
|
||||
{
|
||||
var listResults = c.List();
|
||||
TestUtils.AssertResults(listResults);
|
||||
Assert.AreEqual(2, listResults.Filesets.Count());
|
||||
Assert.AreEqual(expectedFilesets, listResults.Filesets.Count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,12 +1072,39 @@ namespace Duplicati.UnitTest
|
||||
[TestCase(false, true, 3, false)]
|
||||
[TestCase(true, true, 3, true)]
|
||||
[TestCase(false, true, 3, true)]
|
||||
[TestCase(false, false, 3, true)]
|
||||
[TestCase(true, false, 3, true)]
|
||||
public void TestMultiExceptionOnDlist(bool before, bool modifyInBetween, int runs, bool withBase)
|
||||
{
|
||||
var testopts = TestOptions;
|
||||
testopts["number-of-retries"] = "0";
|
||||
testopts["dblock-size"] = "10mb";
|
||||
var expectedFilesets = withBase ? 3 : 2;
|
||||
// We always have at least 1 backup at the end
|
||||
var expectedFilesets = 1;
|
||||
// If the files are modified, this will create multiple versions
|
||||
if (modifyInBetween)
|
||||
{
|
||||
// If we prevent the file from being uploaded,
|
||||
// it will be a temporary file, and a new synthetic file
|
||||
// will be attempted, but that fails as well
|
||||
// If it is after, then the upload completes,
|
||||
// and it will be promoted to a regular uploaded backup
|
||||
expectedFilesets += before ? 1 : runs;
|
||||
|
||||
// If we modify with a base, then that creates a version as well
|
||||
if (withBase)
|
||||
expectedFilesets += 1;
|
||||
}
|
||||
else if (before && !withBase)
|
||||
{
|
||||
// Because an incomplete fileset is treated as a partial fileset,
|
||||
// the final non-interrupted backup will see the syntheticly
|
||||
// created fileset as a partial fileset, and create a new non-partial
|
||||
// fileset, even though there are no changes between the two
|
||||
// This does not happen with a base, because it simply does not
|
||||
// attempt to upload the failing filesets
|
||||
expectedFilesets += 1;
|
||||
}
|
||||
|
||||
if (withBase)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user