fix: report each flock reissue, and scale the gap to the retry budget

Two non-blocking review observations.

A 45s retry is indistinguishable from a hang with nothing on the wire, so
GuestLockRetry.Execute takes an onRetry hook and InvokeGuestTask reports
each reissue through WriteVerbose.

The gap between attempts now scales with the budget, capped at the 2s
production value. A caller passing a short window wants a fast answer
rather than one long sleep, which also takes the retrying unit tests off
a real 2s sleep each: the xUnit run drops from 8s to 4s. PveHttpClient's
window becomes a field so those tests can shorten it too.
This commit is contained in:
goodolclint-claude[bot]
2026-09-01 21:49:00 -05:00
parent db416a3f03
commit 1e94d4188c
5 changed files with 64 additions and 14 deletions
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using PSProxmoxVE.Core.Exceptions;
@@ -15,6 +16,10 @@ namespace PSProxmoxVE.Core.Tests.Utilities
private const string LxcLockError =
"can't lock file '/run/lock/lxc/pve-config-100.lock' - got timeout";
// The gap between attempts scales with the budget, so a short window keeps the
// retrying tests off a 2s production sleep.
private static readonly TimeSpan ShortWindow = TimeSpan.FromMilliseconds(400);
private static PveApiException ApiError(string message) =>
new PveApiException(HttpStatusCode.InternalServerError, message, "nodes/pve9a/qemu/100/config", "PUT");
@@ -73,6 +78,23 @@ namespace PSProxmoxVE.Core.Tests.Utilities
Assert.False(GuestLockRetry.IsLockTimeout(new InvalidOperationException(VmLockError)));
}
[Fact]
public void Execute_ReportsEachReissueToTheOnRetryHook()
{
var attempts = 0;
var reported = new List<Exception>();
GuestLockRetry.Execute(() =>
{
attempts++;
if (attempts < 3) throw TaskError(VmLockError);
return 0;
}, ShortWindow, onRetry: reported.Add);
Assert.Equal(2, reported.Count);
Assert.All(reported, e => Assert.IsType<PveTaskFailedException>(e));
}
[Fact]
public void Execute_ReturnsWithoutRetryingWhenTheOperationSucceeds()
{
@@ -94,7 +116,7 @@ namespace PSProxmoxVE.Core.Tests.Utilities
attempts++;
if (attempts < 2) throw TaskError(VmLockError);
return "cloned";
});
}, ShortWindow);
Assert.Equal("cloned", result);
Assert.Equal(2, attempts);
@@ -124,7 +146,7 @@ namespace PSProxmoxVE.Core.Tests.Utilities
attempts++;
if (attempts < 2) throw ApiError(VmLockError);
return Task.FromResult("written");
});
}, ShortWindow);
Assert.Equal("written", result);
Assert.Equal(2, attempts);