Made the LocalPurgeDatabase methods accept a cancellation token
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.Library.Utility;
|
||||
using Microsoft.Data.Sqlite;
|
||||
@@ -40,13 +42,14 @@ namespace Duplicati.Library.Main.Database
|
||||
/// <param name="path">The path to the database file.</param>
|
||||
/// <param name="pagecachesize">The size of the page cache.</param>
|
||||
/// <param name="dbnew">The optional existing database instance to use. Used to mimic constructor chaining.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains a new instance of <see cref="LocalPurgeDatabase"/>.</returns>
|
||||
public static async Task<LocalPurgeDatabase> CreateAsync(string path, long pagecachesize, LocalPurgeDatabase? dbnew = null)
|
||||
public static async Task<LocalPurgeDatabase> CreateAsync(string path, long pagecachesize, LocalPurgeDatabase? dbnew, CancellationToken token)
|
||||
{
|
||||
dbnew ??= new LocalPurgeDatabase();
|
||||
|
||||
dbnew = (LocalPurgeDatabase)
|
||||
await LocalDeleteDatabase.CreateAsync(path, "Purge", pagecachesize, dbnew)
|
||||
await LocalDeleteDatabase.CreateAsync(path, "Purge", pagecachesize, dbnew, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
dbnew.ShouldCloseConnection = true;
|
||||
@@ -59,13 +62,14 @@ namespace Duplicati.Library.Main.Database
|
||||
/// </summary>
|
||||
/// <param name="dbparent">The parent database to use.</param>
|
||||
/// <param name="dbnew">An optional existing database instance to use. Used to mimic constructor chaining.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains a new instance of <see cref="LocalPurgeDatabase"/>.</returns>
|
||||
public static async Task<LocalPurgeDatabase> CreateAsync(LocalDatabase dbparent, LocalPurgeDatabase? dbnew = null)
|
||||
public static async Task<LocalPurgeDatabase> CreateAsync(LocalDatabase dbparent, LocalPurgeDatabase? dbnew, CancellationToken token)
|
||||
{
|
||||
dbnew ??= new LocalPurgeDatabase();
|
||||
|
||||
dbnew = (LocalPurgeDatabase)
|
||||
await LocalDeleteDatabase.CreateAsync(dbparent, dbnew)
|
||||
await LocalDeleteDatabase.CreateAsync(dbparent, dbnew, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return dbnew;
|
||||
@@ -75,10 +79,11 @@ namespace Duplicati.Library.Main.Database
|
||||
/// Creates a new instance of the <see cref="ITemporaryFileset"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parentid">The ID of the parent fileset.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains a new instance of <see cref="ITemporaryFileset"/>.</returns>
|
||||
public async Task<ITemporaryFileset> CreateTemporaryFileset(long parentid)
|
||||
public async Task<ITemporaryFileset> CreateTemporaryFileset(long parentid, CancellationToken token)
|
||||
{
|
||||
return await TemporaryFileset.CreateAsync(parentid, this)
|
||||
return await TemporaryFileset.CreateAsync(parentid, this, token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -86,8 +91,9 @@ namespace Duplicati.Library.Main.Database
|
||||
/// Gets the remote volume name for a fileset with the specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the fileset.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains the name of the remote volume.</returns>
|
||||
public async Task<string> GetRemoteVolumeNameForFileset(long id)
|
||||
public async Task<string> GetRemoteVolumeNameForFileset(long id, CancellationToken token)
|
||||
{
|
||||
await using var cmd = m_connection.CreateCommand(@"
|
||||
SELECT
|
||||
@@ -101,8 +107,8 @@ namespace Duplicati.Library.Main.Database
|
||||
")
|
||||
.SetParameterValue("@FilesetId", id);
|
||||
|
||||
await using var rd = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
|
||||
if (!await rd.ReadAsync().ConfigureAwait(false))
|
||||
await using var rd = await cmd.ExecuteReaderAsync(token).ConfigureAwait(false);
|
||||
if (!await rd.ReadAsync(token).ConfigureAwait(false))
|
||||
throw new Exception($"No remote volume found for fileset with id {id}");
|
||||
else
|
||||
return rd.ConvertValueToString(0) ?? throw new Exception($"Remote volume name for fileset with id {id} is null");
|
||||
@@ -111,8 +117,9 @@ namespace Duplicati.Library.Main.Database
|
||||
/// <summary>
|
||||
/// Counts the number of orphan files in the database.
|
||||
/// </summary>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains the count of orphan files.</returns>
|
||||
internal async Task<long> CountOrphanFiles()
|
||||
internal async Task<long> CountOrphanFiles(CancellationToken token)
|
||||
{
|
||||
await using var cmd = m_connection.CreateCommand(m_rtr);
|
||||
cmd.SetCommandAndParameters(@"
|
||||
@@ -123,8 +130,8 @@ namespace Duplicati.Library.Main.Database
|
||||
FROM ""FilesetEntry""
|
||||
)
|
||||
");
|
||||
await using var rd = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
|
||||
if (await rd.ReadAsync().ConfigureAwait(false))
|
||||
await using var rd = await cmd.ExecuteReaderAsync(token).ConfigureAwait(false);
|
||||
if (await rd.ReadAsync(token).ConfigureAwait(false))
|
||||
return rd.ConvertValueToInt64(0, 0);
|
||||
else
|
||||
return 0;
|
||||
@@ -156,27 +163,31 @@ namespace Duplicati.Library.Main.Database
|
||||
/// Applies a filter to the temporary fileset.
|
||||
/// </summary>
|
||||
/// <param name="filter">The filter to apply.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that completes when the filter has been applied.</returns>
|
||||
Task ApplyFilter(Library.Utility.IFilter filter);
|
||||
Task ApplyFilter(Library.Utility.IFilter filter, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Applies a filter using a custom command.
|
||||
/// </summary>
|
||||
/// <param name="filtercommand">The command to execute for filtering.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that completes when the filter has been applied.</returns>
|
||||
Task ApplyFilter(Func<SqliteCommand, long, string, Task<int>> filtercommand);
|
||||
Task ApplyFilter(Func<SqliteCommand, long, string, Task<int>> filtercommand, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Converts the temporary fileset to a permanent fileset.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new fileset.</param>
|
||||
/// <param name="timestamp">The timestamp for the new fileset.</param>
|
||||
/// <param name="isFullBackup">Indicates if this is a full backup.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains a tuple with the remote volume ID and the new fileset ID.</returns>
|
||||
Task<Tuple<long, long>> ConvertToPermanentFileset(string name, DateTime timestamp, bool isFullBackup);
|
||||
Task<Tuple<long, long>> ConvertToPermanentFileset(string name, DateTime timestamp, bool isFullBackup, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Lists all deleted files in the temporary fileset.
|
||||
/// </summary>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>An asynchronous enumerable of key-value pairs where the key is the file path and the value is the file size.</returns>
|
||||
IAsyncEnumerable<KeyValuePair<string, long>> ListAllDeletedFiles();
|
||||
IAsyncEnumerable<KeyValuePair<string, long>> ListAllDeletedFiles(CancellationToken token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -217,8 +228,9 @@ namespace Duplicati.Library.Main.Database
|
||||
/// </summary>
|
||||
/// <param name="parentid">The ID of the parent fileset.</param>
|
||||
/// <param name="db">The database instance to use.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that when awaited contains a new instance of <see cref="TemporaryFileset"/>.</returns>
|
||||
public static async Task<TemporaryFileset> CreateAsync(long parentid, LocalDatabase db)
|
||||
public static async Task<TemporaryFileset> CreateAsync(long parentid, LocalDatabase db, CancellationToken token)
|
||||
{
|
||||
var tempf = new TemporaryFileset()
|
||||
{
|
||||
@@ -232,7 +244,7 @@ namespace Duplicati.Library.Main.Database
|
||||
CREATE TEMPORARY TABLE ""{tempf.m_tablename}"" (
|
||||
""FileID"" INTEGER PRIMARY KEY
|
||||
)
|
||||
")
|
||||
", token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return tempf;
|
||||
@@ -242,23 +254,25 @@ namespace Duplicati.Library.Main.Database
|
||||
/// Applies a filter to the temporary fileset using a custom command.
|
||||
/// </summary>
|
||||
/// <param name="filtercommand">The command to execute for filtering.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that completes when the filter has been applied.</returns>
|
||||
public async Task ApplyFilter(Func<SqliteCommand, long, string, Task<int>> filtercommand)
|
||||
public async Task ApplyFilter(Func<SqliteCommand, long, string, Task<int>> filtercommand, CancellationToken token)
|
||||
{
|
||||
int updated;
|
||||
await using (var cmd = m_db.Connection.CreateCommand())
|
||||
updated = await filtercommand(cmd, ParentID, m_tablename)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await PostFilterChecks(updated).ConfigureAwait(false);
|
||||
await PostFilterChecks(updated, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a filter to the temporary fileset.
|
||||
/// </summary>
|
||||
/// <param name="filter">The filter to apply.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that completes when the filter has been applied.</returns>
|
||||
public async Task ApplyFilter(Library.Utility.IFilter filter)
|
||||
public async Task ApplyFilter(Library.Utility.IFilter filter, CancellationToken token)
|
||||
{
|
||||
if (Library.Utility.Utility.IsFSCaseSensitive && filter is FilterExpression expression && expression.Type == Duplicati.Library.Utility.FilterType.Simple)
|
||||
{
|
||||
@@ -272,19 +286,19 @@ namespace Duplicati.Library.Main.Database
|
||||
CREATE TEMPORARY TABLE ""{filenamestable}"" (
|
||||
""Path"" TEXT NOT NULL
|
||||
)
|
||||
")
|
||||
", token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await cmd.SetCommandAndParameters($@"
|
||||
INSERT INTO ""{filenamestable}"" (""Path"")
|
||||
VALUES (@Path)
|
||||
")
|
||||
.PrepareAsync()
|
||||
.PrepareAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var s in p)
|
||||
await cmd.SetParameterValue("@Path", s)
|
||||
.ExecuteNonQueryAsync()
|
||||
.ExecuteNonQueryAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await cmd.SetCommandAndParameters($@"
|
||||
@@ -299,11 +313,11 @@ namespace Duplicati.Library.Main.Database
|
||||
AND ""B"".""Path"" IN ""{filenamestable}""
|
||||
")
|
||||
.SetParameterValue("@FilesetId", ParentID)
|
||||
.ExecuteNonQueryAsync()
|
||||
.ExecuteNonQueryAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await cmd
|
||||
.ExecuteNonQueryAsync($@"DROP TABLE IF EXISTS ""{filenamestable}"" ")
|
||||
.ExecuteNonQueryAsync($@"DROP TABLE IF EXISTS ""{filenamestable}"" ", token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
@@ -316,7 +330,7 @@ namespace Duplicati.Library.Main.Database
|
||||
INSERT INTO ""{m_tablename}"" (""FileID"")
|
||||
VALUES (@FileId)
|
||||
")
|
||||
.PrepareAsync()
|
||||
.PrepareAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
cmd.SetCommandAndParameters(@"
|
||||
@@ -332,8 +346,8 @@ namespace Duplicati.Library.Main.Database
|
||||
")
|
||||
.SetParameterValue("@FilesetId", ParentID);
|
||||
|
||||
await using var rd = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
|
||||
while (await rd.ReadAsync().ConfigureAwait(false))
|
||||
await using var rd = await cmd.ExecuteReaderAsync(token).ConfigureAwait(false);
|
||||
while (await rd.ReadAsync(token).ConfigureAwait(false))
|
||||
{
|
||||
rd.GetValues(values);
|
||||
var path = values[0] as string;
|
||||
@@ -341,21 +355,22 @@ namespace Duplicati.Library.Main.Database
|
||||
{
|
||||
await cmd2
|
||||
.SetParameterValue("@FileId", values[1])
|
||||
.ExecuteNonQueryAsync()
|
||||
.ExecuteNonQueryAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await PostFilterChecks(0).ConfigureAwait(false);
|
||||
await PostFilterChecks(0, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the filter checks after filtering.
|
||||
/// </summary>
|
||||
/// <param name="updated">The number of files updated by the filter.</param>
|
||||
/// <param name="token">A cancellation token to cancel the operation.</param>
|
||||
/// <returns>A task that completes when the checks have been applied.</returns>
|
||||
private async Task PostFilterChecks(int updated)
|
||||
private async Task PostFilterChecks(int updated, CancellationToken token)
|
||||
{
|
||||
await using var cmd = m_db.Connection.CreateCommand();
|
||||
UpdatedFileCount = updated;
|
||||
@@ -363,7 +378,7 @@ namespace Duplicati.Library.Main.Database
|
||||
RemovedFileCount = await cmd.ExecuteScalarInt64Async($@"
|
||||
SELECT COUNT(*)
|
||||
FROM ""{m_tablename}""
|
||||
", 0)
|
||||
", 0, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
RemovedFileSize = await cmd.ExecuteScalarInt64Async($@"
|
||||
@@ -382,7 +397,7 @@ namespace Duplicati.Library.Main.Database
|
||||
AND ""D"".""BlocksetID"" = ""C"".""ID""
|
||||
)
|
||||
)
|
||||
", 0)
|
||||
", 0, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var filesetcount = await cmd.SetCommandAndParameters($@"
|
||||
@@ -391,23 +406,27 @@ namespace Duplicati.Library.Main.Database
|
||||
WHERE ""FilesetID"" = @ParentID
|
||||
")
|
||||
.SetParameterValue("@ParentID", ParentID)
|
||||
.ExecuteScalarInt64Async(0)
|
||||
.ExecuteScalarInt64Async(0, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (filesetcount == RemovedFileCount)
|
||||
throw new Interface.UserInformationException($"Refusing to purge {RemovedFileCount} files from fileset with ID {ParentID}, as that would remove the entire fileset.\nTo delete a fileset, use the \"delete\" command.", "PurgeWouldRemoveEntireFileset");
|
||||
}
|
||||
|
||||
public async Task<Tuple<long, long>> ConvertToPermanentFileset(string name, DateTime timestamp, bool isFullBackup)
|
||||
/// <inheritdoc/>
|
||||
public async Task<Tuple<long, long>> ConvertToPermanentFileset(string name, DateTime timestamp, bool isFullBackup, CancellationToken token)
|
||||
{
|
||||
var remotevolid =
|
||||
await m_db.RegisterRemoteVolume(name, RemoteVolumeType.Files, RemoteVolumeState.Temporary)
|
||||
await m_db
|
||||
.RegisterRemoteVolume(name, RemoteVolumeType.Files, RemoteVolumeState.Temporary, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var filesetid = await m_db.CreateFileset(remotevolid, timestamp)
|
||||
var filesetid = await m_db
|
||||
.CreateFileset(remotevolid, timestamp, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await m_db.UpdateFullBackupStateInFileset(filesetid, isFullBackup)
|
||||
await m_db
|
||||
.UpdateFullBackupStateInFileset(filesetid, isFullBackup, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await using (var cmd = m_db.Connection.CreateCommand(m_db.Transaction))
|
||||
@@ -428,13 +447,14 @@ namespace Duplicati.Library.Main.Database
|
||||
")
|
||||
.SetParameterValue("@TargetFilesetId", filesetid)
|
||||
.SetParameterValue("@SourceFilesetId", ParentID)
|
||||
.ExecuteNonQueryAsync()
|
||||
.ExecuteNonQueryAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new Tuple<long, long>(remotevolid, filesetid);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<KeyValuePair<string, long>> ListAllDeletedFiles()
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<KeyValuePair<string, long>> ListAllDeletedFiles([EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
await using var cmd = m_db.Connection.CreateCommand();
|
||||
await using var rd = await cmd.ExecuteReaderAsync($@"
|
||||
@@ -448,8 +468,10 @@ namespace Duplicati.Library.Main.Database
|
||||
WHERE
|
||||
""A"".""FileID"" = ""B"".""ID""
|
||||
AND ""B"".""BlocksetID"" = ""C"".""ID""
|
||||
").ConfigureAwait(false);
|
||||
while (await rd.ReadAsync().ConfigureAwait(false))
|
||||
", token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
while (await rd.ReadAsync(token).ConfigureAwait(false))
|
||||
yield return new KeyValuePair<string, long>(
|
||||
rd.ConvertValueToString(0) ?? "",
|
||||
rd.ConvertValueToInt64(1)
|
||||
@@ -467,7 +489,7 @@ namespace Duplicati.Library.Main.Database
|
||||
{
|
||||
await using var cmd = m_db.Connection.CreateCommand();
|
||||
await cmd
|
||||
.ExecuteNonQueryAsync($@"DROP TABLE IF EXISTS ""{m_tablename}""")
|
||||
.ExecuteNonQueryAsync($@"DROP TABLE IF EXISTS ""{m_tablename}""", default)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
|
||||
Reference in New Issue
Block a user