Skip to content

Commit aa95145

Browse files
authored
Merge pull request #1128 from eventflow/handfire-bug
Fix: Scheduling bug in Hangfire
2 parents af2c71c + 6392e04 commit aa95145

2 files changed

Lines changed: 24 additions & 8 deletions

File tree

Source/EventFlow.Hangfire.Tests/Integration/HangfireJobSchedulerTests.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
using System;
2424
using System.Collections.Concurrent;
25+
using System.Diagnostics;
2526
using System.Linq;
2627
using System.Threading;
2728
using System.Threading.Tasks;
@@ -124,40 +125,55 @@ public async Task ScheduleNow()
124125
[Test]
125126
public async Task ScheduleAsyncWithDateTime()
126127
{
127-
await ValidateScheduleHappens((j, s) => s.ScheduleAsync(j, DateTimeOffset.Now.AddSeconds(1), CancellationToken.None)).ConfigureAwait(false);
128+
var minimumDelay = TimeSpan.FromSeconds(0.75);
129+
var runAt = DateTimeOffset.UtcNow.AddSeconds(1);
130+
await ValidateScheduleHappens(
131+
(j, s) => s.ScheduleAsync(j, runAt, CancellationToken.None),
132+
minimumDelay).ConfigureAwait(false);
128133
}
129134

130135
[Test]
131136
public async Task ScheduleAsyncWithTimeSpan()
132137
{
133-
await ValidateScheduleHappens((j, s) => s.ScheduleAsync(j, TimeSpan.FromSeconds(1), CancellationToken.None)).ConfigureAwait(false);
138+
var minimumDelay = TimeSpan.FromSeconds(0.75);
139+
await ValidateScheduleHappens(
140+
(j, s) => s.ScheduleAsync(j, TimeSpan.FromSeconds(1), CancellationToken.None),
141+
minimumDelay).ConfigureAwait(false);
134142
}
135143

136-
private async Task ValidateScheduleHappens(Func<IJob, IJobScheduler, Task<IJobId>> schedule)
144+
private async Task ValidateScheduleHappens(Func<IJob, IJobScheduler, Task<IJobId>> schedule, TimeSpan? minimumElapsed = null)
137145
{
138146
// Arrange
139147
var testId = ThingyId.New;
140148
var pingId = PingId.New;
141149
var executeCommandJob = PublishCommandJob.Create(new ThingyPingCommand(testId, pingId), ServiceProvider);
142150

143151
// Act
152+
// Use a monotonic timer to avoid false negatives from wall-clock drift in CI.
153+
var stopwatch = Stopwatch.StartNew();
144154
var jobId = await schedule(executeCommandJob, _jobScheduler).ConfigureAwait(false);
145155

146156
// Assert
147-
var start = DateTimeOffset.Now;
148-
while (DateTimeOffset.Now < start + TimeSpan.FromSeconds(10))
157+
while (stopwatch.Elapsed < TimeSpan.FromSeconds(10))
149158
{
150159
var testAggregate = await AggregateStore.LoadAsync<ThingyAggregate, ThingyId>(testId, CancellationToken.None).ConfigureAwait(false);
151160
if (!testAggregate.IsNew)
152161
{
162+
var elapsed = stopwatch.Elapsed;
163+
stopwatch.Stop();
153164
await AssertJobIsSuccessfullyAsync(jobId).ConfigureAwait(false);
165+
if (minimumElapsed.HasValue)
166+
{
167+
elapsed.ShouldBeGreaterThanOrEqualTo(minimumElapsed.Value);
168+
}
154169
Assert.Contains(pingId, testAggregate.PingsReceived.ToList());
155-
Assert.Pass();
170+
return;
156171
}
157172

158173
await Task.Delay(TimeSpan.FromSeconds(0.2)).ConfigureAwait(false);
159174
}
160175

176+
stopwatch.Stop();
161177
Assert.Fail("Aggregate did not receive the command as expected");
162178
}
163179

Source/EventFlow.Hangfire/Integration/HangfireJobScheduler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public Task<IJobId> ScheduleAsync(IJob job, DateTimeOffset runAt, CancellationTo
7171
cancellationToken,
7272
(jobDefinition, json) =>
7373
_queueName == null
74-
? _backgroundJobClient.Enqueue(ExecuteMethodCallExpression(jobDefinition, json))
74+
? _backgroundJobClient.Schedule(ExecuteMethodCallExpression(jobDefinition, json), runAt)
7575
: _backgroundJobClient.Schedule(_queueName, ExecuteMethodCallExpression(jobDefinition, json), runAt));
7676
}
7777

@@ -82,7 +82,7 @@ public Task<IJobId> ScheduleAsync(IJob job, TimeSpan delay, CancellationToken ca
8282
cancellationToken,
8383
(jobDefinition, json) =>
8484
_queueName == null
85-
? _backgroundJobClient.Enqueue(ExecuteMethodCallExpression(jobDefinition, json))
85+
? _backgroundJobClient.Schedule(ExecuteMethodCallExpression(jobDefinition, json), delay)
8686
: _backgroundJobClient.Schedule(_queueName, ExecuteMethodCallExpression(jobDefinition, json), delay));
8787
}
8888

0 commit comments

Comments
 (0)