Measure request duration on the injectable clock - #54
Merged
Conversation
Handler took its start time from nowFn but closed the measurement with time.Since, so the duration always came from the wall clock and no test could control it. TestBenchmarks_Handler therefore slept 50ms per request and asserted the result within a 10ms band, which failed roughly one run in ten on a loaded machine. Both ends read nowFn now, which is time.Now in production and carries the same monotonic reading time.Since uses, so the measurement is unchanged there. The test drives a fake clock instead of sleeping: the numbers are exact rather than banded, and the package tests run about five seconds faster.
Coverage Report for CI Build 32197939154Coverage decreased (-0.04%) to 97.454%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
umputun
approved these changes
Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TestBenchmarks_Handlerfails roughly one run in ten on a loaded machine, onassert.InDelta(t, 50000, res.MaxRespTime, 10000). It reproduces on clean master and has nothing to do with what it appears to test: the handler sleeps 50ms and the assertion requires that to land within 60ms of wall time, which a busy CI box does not guarantee.The cause is a half-wired clock seam.
Handlertakes its start time from the injectablenowFn, then closes the measurement withtime.Since(st):So the bucketing follows
nowFnand can be faked, while the measured duration always comes from the wall clock and cannot. A test can control which second a request lands in but not how long it took.Both ends read
nowFnnow. In productionnowFnistime.Now, and two values from it carry the monotonic readings thatSubprefers, exactly astime.Sincedoes, so the measurement is unchanged there.With the seam complete the test drives a fake clock instead of sleeping. The handler advances fake time by 50ms per request, so the response-time assertions become exact equality rather than a 10ms band, and the request rate is deterministic too since bucketing already went through
nowFn.Two side effects worth having: the test no longer sleeps 5 seconds, so the package suite drops from about 11s to 5.5s, and the numbers it asserts are now derived rather than approximated.
Verified with 20 consecutive
-raceruns of the test and repeated full-suite runs, including under parallel load, with no failures.Does this change what the middleware measures?
No. In production
nowFnistime.Now, sob.nowFn().Sub(st)istime.Now().Sub(st), which is whattime.Sinceis defined as. Measured rather than assumed, on this branch against master:time.Since)nowFn().Sub)The difference is run-to-run noise. The residual error is
time.Sleepovershoot plus per-request handler overhead, roughly a constant ~1ms in both, which is why it shrinks as a share of a longer request.Comparing the two expressions directly over 5000 samples,
time.Since(st)exceedsnowFn().Sub(st)by a mean of 22-29ns and never less, becauseSincesamples the clock one statement later. Both keep the monotonic reading, so neither is exposed to wall-clock adjustment.