Conversation
Previously some gems masked a bug in Rails v6 and v7 where `logger` was not required, plugging the hole by requiring it themselves. Because this app is pinned to Rails v6.1, we must require logger manually. In order to not be reliant on other libraries to fix the bug in Rails for us, we do it too. See: https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror
- explicit proxy for determining if running in parallel
- `created_at` / `timestamp` is for wall clock time (for humans) - `started_at` is for monotonic / elapsed time (for computers)
sferik
left a comment
There was a problem hiding this comment.
Thanks for the PR.
I added some minor concerns that I left comments about but I have a bigger concern with the core approach: monotonic time doesn’t work across processes.
Monotonic clock values are per-process. They represent something like the process’s view of system uptime and have no shared epoch between processes. The started_at value gets serialized to .resultset.json and then read back in time_since_result_creation by a different process (the one doing the merge). When that process computes current_monotonic - stored_started_at, the two values come from different processes with different monotonic origins, so the result is meaningless. This could cause valid results to be incorrectly expired or stale results to be kept.
The cross-process merge path is actually one place where wall clock time is the right choice, since it’s a clock with a shared epoch. If the concern is Time.now being stubbed by Timecop, switching created_at to use Process.clock_gettime(Process::CLOCK_REALTIME), solves that without needing monotonic time.
Would you consider a version of this PR scoped to just that change?
| class Timer | ||
| # Monotonic clock: Process::CLOCK_MONOTONIC | ||
| # Wall clock: Process::CLOCK_REALTIME | ||
| attr_accessor :start_time, :clock |
There was a problem hiding this comment.
These should be attr_reader, since there’s no reason to mutate them after construction.
| # and for scenarios where the normal ENV variables may not get set, | ||
| # use an explicit trigger defined internally: SIMPLECOV_PARALLEL | ||
| return true if ENV.fetch("SIMPLECOV_PARALLEL", nil) | ||
|
|
There was a problem hiding this comment.
This environment variable is introduced but not documented, tested, and doesn’t guard against "false" being truthy.
|
|
||
| BUNDLED WITH | ||
| 2.7.0.dev | ||
| 2.6.5 |
There was a problem hiding this comment.
This looks like a local artifact that shouldn’t be committed.
There was a problem hiding this comment.
Probably should be updated to not be a pre-release version of bundler!
There was a problem hiding this comment.
It has been update to 4.0.9 in the main branch. Feel free to rebase.
I just looked into this a bit deeper, because it surprised me to hear it. What you say is true of some programming languages, including notably Python. It is not generally true of Ruby, nor of system level OS calls in POSIX systems.
Exceptions:
I don't know if this changes your view. If it doesn't, then I'm happy to switch it back to wall clock - though I expect it could reintroduce the race condition I built this to work around. Perhaps another option would be to allow the user to configure the timer either way. Then cross-server setups have meaningful timings. Worth noting that if a suite is run across servers it almost certainly implies long running suites. On the other hand, if a suite runs on a single server in different processes there is a decent chance of them executing extremely fast, increasing the liklihood of running into the classic issues with wall clock time math. What do you think of supporting both? |
|
Found this interesting documentation:
|
|
I stand corrected on monotonic time. I’m not sure why I had a mistaken understanding of how it worked. Thank you for bringing me up-to-speed. Despite being right about that, I still feel like this pull request is over-engineered for the actual problem. If the main goal is to avoid Timecop interference, Note, I don’t believe this change would resolve #559 or #815. I believe those issues are caused by a process synchronization race condition in the
|
|
I'll submit a new PR with the simpler approach! |
Important background: Monotonic time is the "correct" way to calculate elapsed time in Ruby:
https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
started_atbased on the monotonic clock which is guaranteed to always move forward (processor uptime)created_at/timestampbased onProcess.clock_gettime(Process::CLOCK_REALTIME), to avoidTime.nowstubbing & Timecop issuesI do not understand the rubocop failure in CI, because when I run locally there are no violations:
Same result when run with
bundle exec rake rubocopas it is in CI.CC @ilyazub