Skip to content

Commit 72d0cce

Browse files
authored
fix(reminders): observe completed retries before timeout (#10916)
1 parent 137d9ac commit 72d0cce

2 files changed

Lines changed: 48 additions & 12 deletions

File tree

src/Orleans.Reminders.TestKit/ReminderTableRetryPolicy.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,23 @@ internal static async Task<T> ExecuteUntilAsync<T>(
147147
try
148148
{
149149
var attempt = operation();
150-
remaining = timeout - stopwatch.Elapsed;
151-
if (remaining <= TimeSpan.Zero)
150+
T value;
151+
if (attempt.IsCompleted)
152152
{
153-
lastException = new TimeoutException("The retry timeout elapsed while starting the operation.");
154-
ThrowTimeout();
153+
value = await attempt;
154+
}
155+
else
156+
{
157+
remaining = timeout - stopwatch.Elapsed;
158+
if (remaining <= TimeSpan.Zero)
159+
{
160+
lastException = new TimeoutException("The retry timeout elapsed while starting the operation.");
161+
ThrowTimeout();
162+
}
163+
164+
value = await attempt.WaitAsync(remaining, cancellationToken);
155165
}
156166

157-
var value = await attempt.WaitAsync(remaining, cancellationToken);
158167
lastException = null;
159168
lastObservation = describe(value);
160169
if (succeeded(value))

test/Orleans.Reminders.TestKit.Tests/ReminderTableRetryPolicyTests.cs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,34 @@ public async Task UniformPolicy_HardBoundsTheFirstAttempt()
153153
}
154154

155155
[Fact]
156-
public async Task UniformPolicy_EnforcesDeadlineAfterStartingFirstAttempt()
156+
public async Task UniformPolicy_ObservesCompletedFirstAttemptAfterDeadline()
157+
{
158+
var invocations = 0;
159+
160+
var result = await ReminderTableRetryPolicy.ExecuteUntilAsync(
161+
() =>
162+
{
163+
invocations++;
164+
Thread.Sleep(TimeSpan.FromMilliseconds(20));
165+
return Task.FromResult("late-success");
166+
},
167+
_ => true,
168+
"DelayedStart",
169+
"ReadGuarantee",
170+
"ReadRow",
171+
"a completed result",
172+
value => value,
173+
"read convergence",
174+
TimeSpan.FromMilliseconds(1),
175+
TimeSpan.FromMilliseconds(5),
176+
TestContext.Current.CancellationToken);
177+
178+
Assert.Equal("late-success", result);
179+
Assert.Equal(1, invocations);
180+
}
181+
182+
[Fact]
183+
public async Task UniformPolicy_ObservesCompletedFaultAfterDeadline()
157184
{
158185
var invocations = 0;
159186

@@ -163,23 +190,23 @@ public async Task UniformPolicy_EnforcesDeadlineAfterStartingFirstAttempt()
163190
{
164191
invocations++;
165192
Thread.Sleep(TimeSpan.FromMilliseconds(20));
166-
return Task.FromResult("late-success");
193+
return Task.FromException<string>(new InvalidOperationException("late-contention"));
167194
},
168195
_ => true,
169196
"DelayedStart",
170-
"ReadGuarantee",
171-
"ReadRow",
172-
"a result within the deadline",
197+
"MutationGuarantee",
198+
"UpsertRow",
199+
"a completed mutation",
173200
value => value,
174-
"read convergence",
201+
"mutation retry",
175202
TimeSpan.FromMilliseconds(1),
176203
TimeSpan.FromMilliseconds(5),
177204
TestContext.Current.CancellationToken));
178205

179206
Assert.Equal(1, invocations);
180207
Assert.Contains("attempts=1", exception.Message, StringComparison.Ordinal);
181208
Assert.Contains("Last observation: <no completed attempt>", exception.Message, StringComparison.Ordinal);
182-
Assert.Contains("Last exception: System.TimeoutException", exception.Message, StringComparison.Ordinal);
209+
Assert.Contains("Last exception: System.InvalidOperationException: late-contention", exception.Message, StringComparison.Ordinal);
183210
}
184211

185212
private sealed class TestRunner(IReminderTable table, string providerName)

0 commit comments

Comments
 (0)