Improved stop handling during backend retries

This commit is contained in:
Kenneth Skovhede
2025-05-28 21:39:31 +02:00
parent d00264639f
commit ff141d9456
2 changed files with 51 additions and 25 deletions
@@ -424,11 +424,11 @@ partial class BackendManager
// Refresh DNS name if we fail to connect in order to prevent issues with incorrect DNS entries
var dnsFailure = Library.Utility.ExceptionExtensions.FlattenException(ex)
.Any(x =>
(x is System.Net.WebException wex && wex.Status == System.Net.WebExceptionStatus.NameResolutionFailure)
||
(x is System.Net.Sockets.SocketException sockEx && sockEx.SocketErrorCode == System.Net.Sockets.SocketError.HostNotFound)
);
.Any(x =>
(x is System.Net.WebException wex && wex.Status == System.Net.WebExceptionStatus.NameResolutionFailure)
||
(x is System.Net.Sockets.SocketException sockEx && sockEx.SocketErrorCode == System.Net.Sockets.SocketError.HostNotFound)
);
if (dnsFailure)
{
try
@@ -459,10 +459,17 @@ partial class BackendManager
if (!recovered && retries <= maxRetries && retryDelay.Ticks != 0)
{
var delay = Library.Utility.Utility.GetRetryDelay(retryDelay, retries, retryWithExponentialBackoff);
await Task.Delay(delay, context.TaskReader.ProgressToken).ConfigureAwait(false);
using var ct = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, context.TaskReader.ProgressToken, context.TaskReader.TransferToken, context.TaskReader.StopToken);
await Task.Delay(delay, ct.Token).ConfigureAwait(false);
}
}
if (!await context.TaskReader.ProgressRendevouz())
{
op.SetCancelled();
return;
}
} while (retries <= maxRetries);
// If we have a last exception, we failed
@@ -1,22 +1,22 @@
// Copyright (C) 2025, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// Copyright (C) 2025, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
@@ -52,6 +52,11 @@ namespace Duplicati.Library.Main.Operation.Common
/// <returns><c>true</c> if the transfer should continue, <c>false</c> if it should stop</returns>
Task<bool> TransferRendevouz();
/// <summary>
/// A cancellation token that can be used to monitor stop requests
/// </summary>
CancellationToken StopToken { get; }
#if DEBUG
/// <summary>
/// Callback for testing the task control
@@ -142,6 +147,11 @@ namespace Duplicati.Library.Main.Operation.Common
/// </summary>
private readonly CancellationTokenSource m_transferTcs = new();
/// <summary>
/// Cancellation token source that is cancelled if the operation is stopped
/// </summary>
private readonly CancellationTokenSource m_stopTcs = new CancellationTokenSource();
/// <summary>
/// The control lock instance
/// </summary>
@@ -167,6 +177,11 @@ namespace Duplicati.Library.Main.Operation.Common
/// </summary>
public CancellationToken TransferToken => m_transferTcs.Token;
/// <summary>
/// A cancellation token that is cancelled if the operation is stopped
/// </summary>
public CancellationToken StopToken => m_stopTcs.Token;
/// <summary>
/// Initializes a new instance of the <see cref="Duplicati.Library.Main.Operation.Common.TaskControl"/> class.
/// </summary>
@@ -269,6 +284,8 @@ namespace Duplicati.Library.Main.Operation.Common
m_transfer.SetResult(false);
m_transferstate = State.Stopped;
}
m_stopTcs.Cancel();
}
}
@@ -298,6 +315,8 @@ namespace Duplicati.Library.Main.Operation.Common
m_transferstate = State.Terminated;
m_transferTcs.Cancel();
}
m_stopTcs.Cancel();
}
}