Skip to content

fix(tsa): gate on the clock's post-correction error, not the raw offset - #369

Merged
Bugs5382 merged 1 commit into
mainfrom
fix/364-tsa-clock-gate-after-step
Oct 14, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
fix/364-tsa-clock-gate-after-step

Conversation

@Bugs5382

Copy link
Copy Markdown
Contributor

What and why

Refs #364

A boot-time clock step larger than pki.tsa.max_clock_error_ms left every TSA
request refused with timeNotAvailable until the engine's next periodic poll
(up to MinPoll, 64s later), even though the step had already corrected the
clock. Engine.succeed() recorded LastOffset as the offset measured
before correction, and tsa.ClockGate / EstimatedClockError treated that
value as the clock's current error, conflating "how far off the clock was
found to be" with "how far off it still is" after the correction was applied.

Design (as decided before coding, per the issue): LastOffset keeps its
existing meaning (the pre-correction measurement; TestBootSyncStepsALargeOffset
is unchanged). A new field, last_sync_error on TimeSyncStatus, is set by
succeed():

  • for a step, the residual actually passed to Clock.MarkSynced (root
    distance plus half the round-trip delay);
  • for a slew, the full offset measured, same as before - a slew has not
    caught up to its offset yet when the round finishes, so there is no
    conflation to fix on that path.

tsa.ClockGate and EstimatedClockError now read last_sync_error (plus
drift since the sync) instead of last_offset. No extra settle poll was
added; the fix only changes which figure the gate trusts.

This intentionally changes what tsa.ClockGate's unit tests feed it:
internal/tsa/clock_test.go's goodSync helper now sets last_sync_error
(the field the gate actually reads) alongside last_offset, and
internal/init/tsa_test.go's fixture sets both fields too. Two new table
cases cover a large raw offset with a small post-correction residual
(granted) and a small raw offset with a genuinely large post-correction
residual (still refused).

Verification

  • Lint clean
  • Tests pass
  • Build succeeds
  • Documentation updated (if behavior or API changed)

How this was verified

  • gofmt -l cmd internal - clean
  • go vet ./... - clean
  • golangci-lint run ./... - 0 issues
  • buf lint / buf format -d --exit-code - clean
  • task generate:verify - gen/ matches the updated proto
  • go test ./... (under the systemd-run memory cap) - all packages pass
  • New engine tests (TestBootSyncStepLeavesASmallPostStepSyncError,
    TestBootSyncStepRecordsSyncErrorFromRootDistance,
    TestBootSyncSlewSyncErrorIsTheFullOffset) and two new tsa.TestClockGate
    cases were confirmed to fail for the right reason against the old code
    (reverted locally, re-ran, then restored) before being left green against
    the fix.
  • Updated docs/tsa.md's clock-error explanation to describe the
    post-correction residual instead of the raw offset.

Engine.succeed() stored LastOffset as the offset measured before any
correction, and the TSA clock gate treated that as the clock's current
error. A step corrects the offset down to roughly its root distance plus
half the round-trip delay, but the gate kept judging it by the size of
the correction that was just applied, so a boot step larger than
max_clock_error_ms left every TSA request refused with
timeNotAvailable until the next poll, even though the clock was
already correct.

Add last_sync_error to TimeSyncStatus: for a step, the residual
actually passed to Clock.MarkSynced; for a slew, the full offset
measured, since a slew has not caught up to it yet. ClockGate and
EstimatedClockError now read that field instead of last_offset.
last_offset keeps its old meaning (the pre-correction measurement) for
display.

Refs #364

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382
Bugs5382 marked this pull request as ready for review October 14, 2026 00:13
@github-actions github-actions Bot added the fix Bug fix (fix). Patch. label Oct 14, 2026
@Bugs5382
Bugs5382 merged commit 1c81c8b into main Oct 14, 2026
27 checks passed
@Bugs5382
Bugs5382 deleted the fix/364-tsa-clock-gate-after-step branch October 14, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Bug fix (fix). Patch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant