diff --git a/Tools/RemoteSynchronization/LightWeightBackendManager.cs b/Tools/RemoteSynchronization/LightWeightBackendManager.cs
index f3fffe94e..617b7c142 100644
--- a/Tools/RemoteSynchronization/LightWeightBackendManager.cs
+++ b/Tools/RemoteSynchronization/LightWeightBackendManager.cs
@@ -9,6 +9,21 @@ using Duplicati.Library.Utility;
namespace RemoteSynchronization
{
+ ///
+ /// A lightweight backend manager that handles remote synchronization operations.
+ /// This class is designed to manage backend operations such as Get, Put, Delete, Rename, and List asynchronously.
+ /// It supports retrying operations with a specified delay and can automatically create folders if they do not exist.
+ /// The class implements IDisposable to ensure proper resource management.
+ /// It uses a streaming backend for efficient file operations and handles exceptions gracefully to allow for retries and recovery.
+ /// A retry incurs a new instantiation of the backend.
+ /// It is designed to mimic the behavior of Duplicati.Library.Main.Backend.BackendManager.
+ ///
+ /// The backend URL string.
+ /// A dictionary of options to pass to the backend.
+ /// The maximum number of retries for failed operations.
+ /// The delay between retries, in milliseconds.
+ /// Whether to automatically create folders if they do not exist.
+ /// Whether to use exponential backoff for retries.
public class LightWeightBackendManager(string backendUrl, Dictionary options, int maxRetries = 3, int retryDelay = 1000, bool autoCreateFolders = false, bool retryWithExponentialBackoff = false) : IDisposable
{
private static readonly string LOGTAG = Duplicati.Library.Logging.Log.LogTagFromType();
@@ -24,9 +39,12 @@ namespace RemoteSynchronization
private int _retryDelay = retryDelay;
private IStreamingBackend? _streamingBackend = null;
- //TODO Du skal have lavet docstrings
- //TODO Du skal tjekke at alle parametrene er korrekte i det originale program
-
+ ///
+ /// Deletes a file from the remote backend.
+ ///
+ /// The name of the remote file to delete.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous delete operation.
public Task DeleteAsync(string remotename, CancellationToken token)
{
return RetryWithDelay(
@@ -38,6 +56,11 @@ namespace RemoteSynchronization
);
}
+ ///
+ /// Gets the display name of the backend.
+ /// This property initializes the backend if it has not been instantiated yet.
+ ///
+ /// The display name of the backend.
public string DisplayName
{
get
@@ -48,6 +71,9 @@ namespace RemoteSynchronization
}
}
+ ///
+ /// Disposes of the backend and streaming backend resources.
+ ///
public void Dispose()
{
try
@@ -63,6 +89,14 @@ namespace RemoteSynchronization
}
}
+ ///
+ /// Gets a file from the remote backend and writes it to the specified stream.
+ /// This method retries the operation with a delay if it fails.
+ ///
+ /// The name of the remote file to get.
+ /// The stream to write the file to.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous get operation.
public Task GetAsync(string remotename, Stream stream, CancellationToken token)
{
return RetryWithDelay(
@@ -78,6 +112,13 @@ namespace RemoteSynchronization
);
}
+ ///
+ /// Instantiates the backend if it has not been instantiated yet.
+ /// If the backend is already instantiated, it simply returns.
+ /// If the maximum number of retries has been reached, it throws an InvalidOperationException.
+ /// This method is called internally to ensure that the backend is ready for operations.
+ ///
+ /// If the maximum number of instantiations has been reached.
private void Instantiate()
{
if (_backend != null)
@@ -98,6 +139,11 @@ namespace RemoteSynchronization
}
}
+ ///
+ /// Lists the files in the remote backend asynchronously.
+ ///
+ /// A cancellation token to cancel the operation.
+ /// An asynchronous enumerable of file entries.
public IAsyncEnumerable ListAsync(CancellationToken token)
{
while (true)
@@ -119,6 +165,13 @@ namespace RemoteSynchronization
}
+ ///
+ /// Puts a file to the remote backend from the specified stream.
+ ///
+ /// The name of the remote file to put.
+ /// The stream containing the file data to put.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous put operation.
public Task PutAsync(string remotename, Stream stream, CancellationToken token)
{
return RetryWithDelay(
@@ -134,6 +187,15 @@ namespace RemoteSynchronization
);
}
+ ///
+ /// Renames a file in the remote backend.
+ /// If the backend supports renaming, it uses the RenameAsync method.
+ /// If the backend does not support renaming, it downloads the file, renames it, and deletes the old one.
+ ///
+ /// The current name of the remote file.
+ /// The new name for the remote file.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous rename operation.
public Task RenameAsync(string oldname, string newname, CancellationToken token)
{
Instantiate();
@@ -174,6 +236,20 @@ namespace RemoteSynchronization
};
}
+ ///
+ /// Retries an operation with a delay if it fails.
+ /// This method will instantiate the backend if it has not been instantiated yet.
+ /// If the operation fails, it will log the error, dispose of the current backend and streaming backend,
+ /// reset the stream if specified, and attempt to recover from the exception.
+ /// If recovery is not possible, it will wait for the specified retry delay before retrying the operation.
+ /// The retry delay can be increased exponentially if specified.
+ ///
+ /// The name of the operation being retried, used for logging.
+ /// The action to perform.
+ /// The stream to use for the operation. Used when resetting the stream during recovery.
+ /// Whether to reset the stream if the operation fails.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous operation.
private async Task RetryWithDelay(string operationName, Func action, Stream? stream, bool resetStream, CancellationToken token)
{
while (true)
@@ -203,6 +279,11 @@ namespace RemoteSynchronization
}
}
+ ///
+ /// Attempts to create a folder in the remote backend.
+ ///
+ /// A cancellation token to cancel the operation.
+ /// A task that represents the asynchronous operation. The task result is true if the folder was created successfully, false otherwise.
private async Task TryCreateFolder(CancellationToken token)
{
try
@@ -224,6 +305,17 @@ namespace RemoteSynchronization
}
}
+ ///
+ /// Attempts to recover from an exception that occurred during a backend operation.
+ /// This method checks for specific types of exceptions, such as DNS resolution failures or folder missing exceptions.
+ /// If a DNS failure is detected, it attempts to refresh the DNS name by re-instantiating the backend and resolving DNS names.
+ /// If the exception is a folder missing exception and auto-creation of folders is enabled, it attempts to create the folder.
+ /// If recovery is not possible, it waits for the specified retry delay.
+ /// The retry delay will be doubled if exponential backoff is enabled.
+ ///
+ /// The exception that occurred during the operation.
+ /// A cancellation token to cancel the operation.
+ /// A task representing the asynchronous recovery operation.
private async Task TryRecoverFromException(Exception ex, CancellationToken token)
{
// Copied from Duplicati.Library.Main.Backend.BackendManager.Handler.
diff --git a/Tools/RemoteSynchronization/Program.cs b/Tools/RemoteSynchronization/Program.cs
index 907fb8e9b..9af910aca 100644
--- a/Tools/RemoteSynchronization/Program.cs
+++ b/Tools/RemoteSynchronization/Program.cs
@@ -552,8 +552,8 @@ destination will be verified before being overwritten (if they seemingly match).
/// The files to delete are the files that are found in the destination but not found in the source.
/// The files to verify are the files that are found in both the source and the destination, and that have the same size and modification date.
///
- /// The source backend.
- /// The destination backend.
+ /// The source lightweight backend manager.
+ /// The destination lightweight backend manager.
/// The parsed configuration for the tool.
/// A tuple of Lists each holding the files to copy, delete and verify.
private static async Task<(IEnumerable, IEnumerable, IEnumerable)> PrepareFileLists(LightWeightBackendManager b_src, LightWeightBackendManager b_dst, Config config, CancellationToken cancelToken)
@@ -638,7 +638,7 @@ destination will be verified before being overwritten (if they seemingly match).
/// Renames the files in a backend.
/// The renaming is done by deleting the file and re-uploading it with a new name.
///
- /// The backend to rename the files in.
+ /// The lightweight backend manager to issue rename operations to.
/// The files to rename.
/// The parsed configuration for the tool.
/// The number of successful renames.
@@ -704,8 +704,8 @@ destination will be verified before being overwritten (if they seemingly match).
/// Verifies the files in the destination backend.
/// The verification is done by downloading the files from the destination backend and comparing them to the source files.
///
- /// The source backend.
- /// The destination backend.
+ /// The source lightweight backend manager.
+ /// The destination lightweight backend manager.
/// The files to verify.
/// The parsed configuration for the tool.
/// A list of the files that failed verification.