Skip to content

Backfill: back off retries for a strap with a future-dated clock (#160)#11

Closed
digitalerdude wants to merge 2 commits into
mainfrom
backfill-clock-untrusted-throttle
Closed

Backfill: back off retries for a strap with a future-dated clock (#160)#11
digitalerdude wants to merge 2 commits into
mainfrom
backfill-clock-untrusted-throttle

Conversation

@digitalerdude

Copy link
Copy Markdown
Owner

Summary

  • Addresses HR not displaying while band is active ryanbr/noop#160 ("HR not displaying while band is active").
  • The reporter's own connection log export showed the actual mechanism: throughout a 9-minute live session, Toggle Realtime HR cycled off/on every 10-40s, each cycle paired with a fresh Backfill: session started. Live REALTIME_DATA/REALTIME_RAW_DATA frames only appeared in the last ~10s of the whole capture before being cut off again. The strap's own RTC was reporting a time 7868h-28445h ahead of the wall clock throughout — the known #928 future-dated-clock fault.
  • Traced the mechanism: beginBackfill() itself never touches realtime HR. The toggle-off/toggle-on pairing comes from the once-per-BLE-connection handshake, which only replays on an actual disconnect — so its ~10-40s repetition points at the link itself reconnecting that often, most plausibly the existing #80 marginal-radio "arm → die → rescan → arm → die" loop (its documented ~20s window matches the observed cadence), aggravated by realtime streaming and historical-offload traffic competing for the same link with nothing today keeping them apart.
  • #1012 already caps a future-dated strap's auto-continue chain to one pass per connection, but BackfillPolicy (the baseline .periodic/.strap/.connect rate limiter) has no clock-health awareness at all — and because a future-dated strap still banks real (if mis-dated) rows each pass, it never trips the existing empty-streak backoff either. So it gets the exact same retry cadence as a perfectly healthy strap, generating extra BLE traffic on every reconnect that plausibly feeds back into the radio instability.
  • This PR's scope: fold the already-tracked future-dated signal (BackfillContinuation.isFutureDatedNewest, existing #1012 machinery) into BackfillPolicy.shouldRun's existing empty-streak backoff shape, so .strap/.periodic retries back off immediately for a known-bogus-clock strap. .connect/.foreground/.manual/.autoContinue stay un-floored, matching how emptyStreak already treats them.
  • Explicitly out of scope: the deeper BLE reconnect/realtime-vs-offload arbitration behavior. This is a partial, safe mitigation (less redundant backfill traffic competing for the link), not a full fix for the reconnect churn itself — that needs real-hardware verification against the WHOOP 4.0 strap this was reported on, which I can't do in this environment.

Test plan

  • Pure logic (BackfillPolicy.shouldRun) verified against the exact production file content (workaround for a pre-existing, unrelated StrandTests TEST_HOST/PRODUCT_NAME mismatch in this environment that blocks running the Xcode test target directly): all new clockUntrusted cases pass, plus regression checks that every existing case is unchanged.
  • Added StrandTests/BackfillPolicyTests.swift coverage mirroring the existing emptyStreak tests: backs off .strap/.periodic with no empty streak, doesn't stack past the existing 4x cap, and never delays .connect/.foreground/.manual/.autoContinue.
  • macOS (Strand) and iOS (NOOPiOS) targets both build clean via xcodebuild.
  • Not tested on real hardware. Per docs/CONTRIBUTING.md's own checklist, BLE-path changes need real-strap verification beyond "builds and unit tests pass" — I don't have a WHOOP strap in this environment. This should be confirmed on the reporter's WHOOP 4.0 (or another future-dated-clock strap) before merge.

🤖 Generated with Claude Code

…nbr#160)

A strap whose RTC reads future-dated (#928) still banks REAL rows on
every backfill pass, so it never trips the existing empty-streak
backoff — but #1012 already refuses to trust that range past one pass
per connection, so the automatic retries get near-zero value while
still costing BLE airtime. Reported as ryanbr#160 ("HR not displaying while
band is active"): the user's own connection log showed backfill
sessions kicking off every 10-40s on a strap logging exactly this
future-dated-clock warning, with live REALTIME_DATA frames only
appearing in the last ~10s of a 9-minute capture.

Folds the already-tracked future-dated signal into BackfillPolicy's
existing empty-streak backoff shape (maxed immediately, since there's
no "streak" to build for a clock fault) so .strap/.periodic retries on
a known-bogus-clock strap stop competing with realtime HR streaming
for the link as often. .connect/.foreground/.manual/.autoContinue stay
un-floored, matching how emptyStreak already treats them.

This mitigates one contributing factor; it does not touch the deeper
BLE reconnect/radio-arbitration behavior (a ryanbr#80-style marginal-radio
loop is the likely proximate cause of the reconnect churn itself),
which needs real-hardware verification I can't do here.
@digitalerdude

Copy link
Copy Markdown
Owner Author

Closing this fork-internal PR to declutter — the real submission lives at ryanbr/noop (upstream). See that repo's PR list for the current, actionable version of this work.

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.

1 participant