167 lines
5.5 KiB
C#
167 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Duplicati.Library.Interface;
|
|
using Duplicati.Library.Utility;
|
|
|
|
namespace RemoteSynchronization
|
|
{
|
|
public class LightWeightBackendManager(string backendUrl, Dictionary<string, string> options, int maxRetries = 3, int retryDelay = 1000) : IDisposable
|
|
{
|
|
private static readonly string LOGTAG = Duplicati.Library.Logging.Log.LogTagFromType<Program>();
|
|
|
|
public IBackend? _backend = null;
|
|
private readonly string _backendUrl = backendUrl;
|
|
private int _instantiations = 0;
|
|
private readonly int _maxRetries = maxRetries;
|
|
private Dictionary<string, string> _options = options;
|
|
private readonly int _retryDelay = retryDelay;
|
|
private IStreamingBackend? _streamingBackend = null;
|
|
|
|
public Task DeleteAsync(string remotename, CancellationToken token)
|
|
{
|
|
return RetryWithDelay(
|
|
$"Delete {remotename}",
|
|
() => _streamingBackend.DeleteAsync(remotename, token)
|
|
);
|
|
}
|
|
|
|
public string DisplayName
|
|
{
|
|
get
|
|
{
|
|
if (_streamingBackend == null)
|
|
{
|
|
Instantiate();
|
|
}
|
|
|
|
return _streamingBackend.DisplayName;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
_streamingBackend?.Dispose();
|
|
_streamingBackend = null;
|
|
_backend?.Dispose();
|
|
_backend = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Duplicati.Library.Logging.Log.WriteErrorMessage(LOGTAG, "rsync", ex, "Error during Dispose", null);
|
|
}
|
|
}
|
|
|
|
public Task GetAsync(string remotename, Stream stream, CancellationToken token)
|
|
{
|
|
return RetryWithDelay(
|
|
$"Get {remotename}",
|
|
() => _streamingBackend.GetAsync(remotename, stream, token)
|
|
);
|
|
}
|
|
|
|
private void Instantiate()
|
|
{
|
|
if (_instantiations < _maxRetries)
|
|
{
|
|
_backend = Duplicati.Library.DynamicLoader.BackendLoader.GetBackend(_backendUrl, _options);
|
|
_streamingBackend = _backend as IStreamingBackend ?? throw new InvalidOperationException("Backend does not support streaming operations.");
|
|
_instantiations++;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("Maximum number of backend instantiations reached.");
|
|
}
|
|
}
|
|
|
|
public IAsyncEnumerable<IFileEntry> ListAsync(CancellationToken token)
|
|
{
|
|
while (true)
|
|
{
|
|
if (_streamingBackend == null)
|
|
{
|
|
Instantiate();
|
|
}
|
|
|
|
try
|
|
{
|
|
return _streamingBackend.ListAsync(token);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Duplicati.Library.Logging.Log.WriteErrorMessage(LOGTAG, "rsync", ex, "Error during operation: List", null);
|
|
Dispose(); // Dispose current backend and streaming backend
|
|
Task.Delay(_retryDelay, token).Await();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public Task PutAsync(string remotename, Stream stream, CancellationToken token)
|
|
{
|
|
return RetryWithDelay(
|
|
$"Put {remotename}",
|
|
() => _streamingBackend.PutAsync(remotename, stream, token)
|
|
);
|
|
}
|
|
|
|
public Task RenameAsync(string oldname, string newname, CancellationToken token)
|
|
{
|
|
if (_streamingBackend == null)
|
|
{
|
|
Instantiate();
|
|
}
|
|
|
|
return _backend switch
|
|
{
|
|
IStreamingBackend sb =>
|
|
RetryWithDelay(
|
|
$"Rename {oldname} to {newname}",
|
|
async () =>
|
|
{
|
|
// Download the file, rename it, and delete the old one
|
|
using var downloaded = new MemoryStream();
|
|
await sb.GetAsync(oldname, downloaded, token);
|
|
await sb.PutAsync(newname, downloaded, token);
|
|
await sb.DeleteAsync(oldname, token);
|
|
}
|
|
),
|
|
IRenameEnabledBackend ireb =>
|
|
RetryWithDelay(
|
|
$"Rename {oldname} to {newname}",
|
|
() => ireb.RenameAsync(oldname, newname, token)
|
|
),
|
|
_ => throw new InvalidOperationException("Backend does not support renaming."),
|
|
};
|
|
}
|
|
|
|
private async Task RetryWithDelay(string operationName, Func<Task> action)
|
|
{
|
|
while (true)
|
|
{
|
|
if (_streamingBackend == null)
|
|
{
|
|
Instantiate();
|
|
}
|
|
|
|
try
|
|
{
|
|
await action();
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Duplicati.Library.Logging.Log.WriteErrorMessage(LOGTAG, "rsync", ex, "Error during operation: {0}", operationName);
|
|
Dispose(); // Dispose current backend and streaming backend
|
|
await Task.Delay(_retryDelay);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|