Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various time related fixes #178

Merged
merged 7 commits into from
Jan 26, 2025
Merged

Various time related fixes #178

merged 7 commits into from
Jan 26, 2025

Conversation

nulltoken
Copy link
Collaborator

@nulltoken nulltoken commented Jan 25, 2025

Pull request description

  • Mostly fix TimeProvider isn't used everywhere #169
  • Additional freebies
    • Cron expression helper for tests (built from most used expressions)
    • Favor UTC over local date when logging
    • Make JobRun.RunAt immutable

PR meta checklist

  • Pull request is targeted at main branch for code
  • Pull request is linked to all related issues, if any.

Code PR specific checklist

  • My code follows the code style of this project and AspNetCore coding guidelines.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • I have updated the appropriate sub section in the CHANGELOG.md.
  • I have added, updated or removed tests to according to my changes.
    • All tests passed.

@nulltoken nulltoken force-pushed the ntk/timeprovider branch 2 times, most recently from 21ceaad to eabe5ce Compare January 25, 2025 12:59
Copy link

codecov bot commented Jan 25, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Files with missing lines Coverage Δ
src/NCronJob/Execution/StartupJobManager.cs 94.11% <100.00%> (+0.17%) ⬆️
src/NCronJob/Observer/ExecutionProgress.cs 93.33% <100.00%> (+0.74%) ⬆️
.../NCronJob/Observer/JobExecutionProgressObserver.cs 96.07% <100.00%> (+0.33%) ⬆️
src/NCronJob/Registry/IInstantJobRegistry.cs 92.59% <100.00%> (+0.13%) ⬆️
src/NCronJob/Registry/JobRun.cs 98.71% <100.00%> (+0.08%) ⬆️
src/NCronJob/Registry/JobState.cs 77.77% <100.00%> (ø)
src/NCronJob/Scheduler/JobProcessor.cs 100.00% <100.00%> (ø)
src/NCronJob/Scheduler/JobQueue.cs 100.00% <100.00%> (+11.11%) ⬆️
src/NCronJob/Scheduler/JobQueueManager.cs 92.06% <100.00%> (-0.13%) ⬇️
src/NCronJob/Scheduler/JobWorker.cs 94.11% <100.00%> (+0.68%) ⬆️

@nulltoken nulltoken requested a review from linkdotnet January 25, 2025 13:06
Copy link
Member

@linkdotnet linkdotnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really cool work @nulltoken !

src/NCronJob/Registry/JobRun.cs Show resolved Hide resolved
src/NCronJob/Registry/JobState.cs Outdated Show resolved Hide resolved
src/NCronJob/Scheduler/JobWorker.cs Show resolved Hide resolved
tests/NCronJob.Tests/Cron.cs Show resolved Hide resolved
@linkdotnet
Copy link
Member

Interesting:WhileAwaitingJobTriggeringInstantJobShouldAnywayTriggerCronJob failed now twice in a row on the ubuntu runner (non-arm)

@nulltoken nulltoken force-pushed the ntk/timeprovider branch 3 times, most recently from 8cd09d5 to 88ff3ed Compare January 25, 2025 14:24
@nulltoken
Copy link
Collaborator Author

Interesting:WhileAwaitingJobTriggeringInstantJobShouldAnywayTriggerCronJob failed now twice in a row on the ubuntu runner (non-arm)

Seen this. I'm going to try and dig deeper in this test to understand where this comes from.

@nulltoken nulltoken force-pushed the ntk/timeprovider branch 3 times, most recently from 089a055 to 0592032 Compare January 25, 2025 15:19
@@ -317,6 +319,50 @@ public async Task WhileAwaitingJobTriggeringInstantJobShouldAnywayTriggerCronJob
Assert.Equal(ExecutionState.OrchestrationCompleted, instantOrchestrationEvents[6].State);
Assert.Equal(7, instantOrchestrationEvents.Count);

/*
Copy link
Collaborator Author

@nulltoken nulltoken Jan 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@linkdotnet Below the captured timestamps of a CI failing build.

I'm slightly puzzled by the 24.04 arm result (first below). And the tests don't fail consistently.

Just for comparison, the latest passing 24.04 arm passing build output:

 s 0 2000-01-01T00:00:00.0000000+00:00
 i 0 2000-01-01T00:00:01.0000000+00:00
 s 3 2000-01-01T00:00:01.0000000+00:00
 i 2 2000-01-01T00:00:01.0499600+00:00
 s 4 2000-01-01T00:00:01.0000000+00:00
 i 6 2000-01-01T00:00:01.2258700+00:00

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compared to the failing one

24.04 arm
s 0 2000-01-01T00:00:00.0000000+00:00
i 0 2000-01-01T00:00:01.0000000+00:00
s 3 2000-01-01T00:00:01.0000000+00:00
i 2 2000-01-01T00:00:01.0000000+00:00   <-----
s 4 2000-01-01T00:00:01.0000000+00:00
i 6 2000-01-01T00:00:01.1924400+00:00

Given i 6 entries values aren't that far one from from another, I can't explain why i 2 hasn't moved forward even a little bit in that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about potential concurrency related issues with the FakeTimeProvider, but that seems out of context now that we're running xUnit v3 (https://github.com/dotnet/extensions/blob/main/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/README.md#xunit-v3)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can give NCronJob some time here?

 FakeTimer.Advance(TimeSpan.FromSeconds(1));

// Added here not in the source code:
// To enable processing time
await Task.Delay(100);

 Guid instantOrchestrationId = provider.GetRequiredService<IInstantJobRegistry>().RunInstantJob<SimpleJob>(token: CancellationToken);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@linkdotnet Ok. I've found a workaround.

Below the latest timings from 24.04 arm

s 0 2000-01-01T00:00:00.0020000+00:00
i 0 2000-01-01T00:00:00.0080000+00:00
s 3 2000-01-01T00:00:00.0100000+00:00
i 2 2000-01-01T00:00:00.0603800+00:00
s 4 2000-01-01T00:00:00.0100000+00:00
i 6 2000-01-01T00:00:00.2286500+00:00

I've tweaked the setup of FakeTimer to benefit from AutoAdvanceAmount. This more or less simulates time passing between clock access.

@nulltoken nulltoken force-pushed the ntk/timeprovider branch 3 times, most recently from a36b29a to c441361 Compare January 25, 2025 16:04
@nulltoken nulltoken force-pushed the ntk/timeprovider branch 3 times, most recently from 7b84054 to 36c0ea3 Compare January 25, 2025 16:33
@nulltoken
Copy link
Collaborator Author

@linkdotnet Everything seem back to green, now.

Does the tweak to FakeTimer fit you?

@linkdotnet
Copy link
Member

@linkdotnet Everything seem back to green, now.

Does the tweak to FakeTimer fit you?

Interesting. I am not sure why advancing 1ms will make it seemingly stable.

the example you showed also has entries in the sub millisecond precision. Where is this coming from?

@nulltoken
Copy link
Collaborator Author

@linkdotnet Everything seem back to green, now.
Does the tweak to FakeTimer fit you?

Interesting. I am not sure why advancing 1ms will make it seemingly stable.

It makes sense as this test is only interested in the order in which events are triggered, not how much time they really took.

In the real life, the compute done by the lib takes some time to be done. In this test context, time is frozen and controlled by the test through Advance().

As every call to GetUtcNow() performs a slight nudge to the time, this emulates behind the scene what happens in a deployed environment and we don't end up with identical timestamps (except when they make sense (Orchestration Started/Completed relatively to initial and final events of an orchestration)).

the example you showed also has entries in the sub millisecond precision. Where is this coming from?

Not everything depends on the FakeTimer.

Every call to WaitForOrchestrationCompletion()

  • spends some time copying over the new events
  • awaits for some time (Task.Delay being also dependent to the precision of the underlying system clock)

@linkdotnet
Copy link
Member

I totally get where you are driving at - I am just "surprised" by those times. So if we use the TimeProvider everywhere (doesn't matter how often called) I'd expect only increments of milliseconds and higher.

Anyhow - feel free to merge. My approval still stands :D

@nulltoken nulltoken merged commit aa7b584 into main Jan 26, 2025
5 checks passed
@nulltoken nulltoken deleted the ntk/timeprovider branch January 26, 2025 18:10
@nulltoken
Copy link
Collaborator Author

So if we use the TimeProvider everywhere (doesn't matter how often called) I'd expect only increments of milliseconds and higher.

Unless it's moved further through Advance()

https://github.com/dotnet/extensions/blob/main/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs#L127-L137

@linkdotnet
Copy link
Member

Fair - not sure where this is called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TimeProvider isn't used everywhere
2 participants