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

time: mockable time support #8869

Open
jmhodges opened this Issue Oct 5, 2014 · 7 comments

Comments

Projects
None yet
7 participants
@jmhodges
Copy link
Contributor

jmhodges commented Oct 5, 2014

With https://github.com/jmhodges/clock and the broader https://github.com/benbjohnson/clock API,
there's a desire for the timing systems in Go to be fakeable[1]. Using a fake clock
instead of time.Now directly is useful when testing code that stores timestamps, caches
data, etc.

The benbjohnson clock package attempts to make Ticker and Timer calls (as well as their
related AfterFunc and Sleep methods) work against a fake time that can be set and
updated in tests. However, it relies on some micro-sleeps and runtime.Gosched calls that
are obviously going to be flaky. But there is a desire to able to test code that uses
Tickers and Timers, not by adjusting the durations they work for (which can induce flaky
testing), but by adjusting when they think they need to wake up.

To do that, we seem to need more runtime magic to help developers out. In fact, it might
be best if a clock package like these lived in the stdlib so that it could be tied more
carefully and thoughtfully to the scheduler.

 [1] or "mockable", whatever language you prefer. The summary of this issue is to distinguish it from issue #5356.
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Oct 5, 2014

Comment 1:

I'm not entirely sure this is a good idea.  I'm also not sure what it would look like. 
If the goal is simply to provide Ticker and Timer calls for testing, I think that could
be done entirely independently of the time package, along the lines of the old
playground code.  The testing package would keep its own queue of events, and step
forward to the next event without actually waiting.

Labels changed: added repo-main, release-none.

@dvyukov

This comment has been minimized.

Copy link
Member

dvyukov commented Oct 5, 2014

Comment 2:

I am not sure playground approach will work in large apps. In playground we advice time
when the process in completely out of other work. But what if we have a goroutine in a
syscall/cgo, should we advice time? Or wait for it to return from syscall/cgo first?
@sougou

This comment has been minimized.

Copy link
Contributor

sougou commented Oct 5, 2014

Comment 3:

The main challenge we faced was related to the original code importing the standard time
package, which is not mockable. This forces the tests to also use actual clock time.
We've mostly managed to work around these. So, it's not a big issue now. But it's still
a nice-to-have.
@sougou

This comment has been minimized.

Copy link
Contributor

sougou commented Oct 30, 2014

Comment 4:

What if the time package accelerated the clock (just for the process) by an order of
magnitude? We can consider restricting this ability to the testing package.
There are still some downsides: it won't work for integration tests, but it may satisfy
the needs of simple in-process tests.

@jmhodges jmhodges added new labels Oct 30, 2014

@bradfitz bradfitz removed the new label Dec 18, 2014

@rsc rsc added this to the Unplanned milestone Apr 10, 2015

@rsc rsc removed release-none labels Apr 10, 2015

@jmhodges

This comment has been minimized.

Copy link
Contributor Author

jmhodges commented Sep 2, 2015

Any chance of something happening for this?

@jmhodges

This comment has been minimized.

Copy link
Contributor Author

jmhodges commented Sep 2, 2015

(I'm okay with hearing no! It's just an open cycle in my head I'm trying to clear out.)

@adg

This comment has been minimized.

Copy link
Contributor

adg commented Sep 2, 2015

It's unlikely to happen any time soon.

Dieterbe added a commit to grafana/metrictank that referenced this issue Oct 13, 2016

fix those damn sporadically false positive usage tests
after lots of experimentation I figured out the mock clock sometimes
simply doesn't properly trigger, so that in Usage.Report() sometimes
nothing is received on the tick channel, despite advancing the fake
clock by more than strictly nessecary (i tried with an extra ms),
despite calling runtime.Goshed() ourselves, and despite sleeping
20 ms with the real clock.

The author of the clock package confirms that due to the way the
runtime schedules goroutines, there's no way around the fake clock
sometimes not working. See
https://gophers.slack.com/archives/general/p1462238960008162

Furthermore, in discussion with the golang developers at
golang/go#8869 it becomes clear that it's
unlikely that we'll have a fakeable clock anytime soon.

Ben Johnson (clock author) suggests in the above mentioned gophers
thread that we could mock out the tick function and pass in a different
function in tests.  However, that changes so much of the time logic
that it becomes pointless to do any time-based testing in this design.

We could also switch to simply test the basics, not time based.
Since the timing code is pretty easy.

However before we go that route, I wanted to try working with the real
clock.  Basically run the usage reporting in real time, but scaled down
to millisecond level instead of second level, to make it finish fairly
quickly.

So now some semantics are changing a bit:
* we allow up to <period> ms for the usage report to be in the state we
need it
* so we now works with steps, which don't happen at exact predictable
  timestamps, rather they have to happen within a timeframe
* checking timestamp would have gotten more complicated, so I just
removed it.  It's easy to reason that if the updates come within the
alotted times, then the timestamps should also be set correctly.
* there's no serious need to explicitly pass around interval settings
  anymore, we just use 1 everywhere.

If it turns out that this approach also triggers false positives
(for example due to circleCI machines being maxed out of CPU and the
reporting unable to happen within the needed time) then we can address
as needed and still switch to the simpler approach.
But that seems very unlikely.  This should work.

Dieterbe added a commit to grafana/metrictank that referenced this issue Oct 13, 2016

fix those damn sporadically false positive usage tests
after lots of experimentation I figured out the mock clock sometimes
simply doesn't properly trigger, so that in Usage.Report() sometimes
nothing is received on the tick channel, despite advancing the fake
clock by more than strictly nessecary (i tried with an extra ms),
despite calling runtime.Goshed() ourselves, and despite sleeping
20 ms with the real clock.

The author of the clock package confirms that due to the way the
runtime schedules goroutines, there's no way around the fake clock
sometimes not working. See
https://gophers.slack.com/archives/general/p1462238960008162

Furthermore, in discussion with the golang developers at
golang/go#8869 it becomes clear that it's
unlikely that we'll have a fakeable clock anytime soon.

Ben Johnson (clock author) suggests in the above mentioned gophers
thread that we could mock out the tick function and pass in a different
function in tests.  However, that changes so much of the time logic
that it becomes pointless to do any time-based testing in this design.

We could also switch to simply test the basics, not time based.
Since the timing code is pretty easy.

However before we go that route, I wanted to try working with the real
clock.  Basically run the usage reporting in real time, but scaled down
to millisecond level instead of second level, to make it finish fairly
quickly.

So now some semantics are changing a bit:
* we allow up to <period> ms for the usage report to be in the state we
need it
* so we now works with steps, which don't happen at exact predictable
  timestamps, rather they have to happen within a timeframe
* checking timestamp would have gotten more complicated, so I just
removed it.  It's easy to reason that if the updates come within the
alotted times, then the timestamps should also be set correctly.
* there's no serious need to explicitly pass around interval settings
  anymore, we just use 1 everywhere.

If it turns out that this approach also triggers false positives
(for example due to circleCI machines being maxed out of CPU and the
reporting unable to happen within the needed time) then we can address
as needed and still switch to the simpler approach.
But that seems very unlikely.  This should work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.