Skip to content

fix(rescore): pace a backgrounded pass in 10-second quanta so it isn't suspended after every night - #2318

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
Iskrata:fix/rescore-pace-in-quanta
Sep 18, 2026
Merged

ryanbr merged 1 commit into
ryanbr:mainfrom
Iskrata:fix/rescore-pace-in-quanta

Conversation

@Iskrata

@Iskrata Iskrata commented Sep 18, 2026

Copy link
Copy Markdown

What this PR does

Follow-up to #2296 (the pacing from #2280). A backgrounded pass now does 10 seconds of work between rests (backgroundWorkQuantumSeconds) instead of resting after every night. It rests as long as it worked, still capped at 30 s, so it holds the same ~50% ceiling under iOS's 80%-over-60 s limit.

It also logs, once per running pass, when a forced re-score is queued behind it:

re-score: queued behind a 4000-day pass running for 812 s

Why

A rest is a Task.sleep, and a backgrounded process that is only sleeping is exactly what iOS suspends, until the next bluetooth wake about ten minutes later. Each night of a pass is milliseconds to a few seconds of CPU. So resting after every night meant a backgrounded pass advanced about one night per wake, and only the CPU limit was being respected.

On one phone (Release build, #2293's cache fixes applied):

  • A one-time full-history pass (runEffortRescoreIfNeeded, maxDays 4000) took the lock at a 21:34 relaunch and still held it at 11:05 the next morning.
  • Every post-offload pass in between returned at guard !computing without a word: 26 Backfill: refreshing dashboard cache lines and no re-score: line at all.
  • As a result, that morning's night was never scored: no daily row, no session.

The earlier 21-night passes of 2 h 27 min and 39 min reported on #2280 have the same shape.

The second change is why this took a database pull to find. The log said nothing between "sync done" and "no score". The queued line names the pass that is holding the lock and how long it has held it.

Type of change

  • Bug fix

How it was tested

  • RescoreBackgroundPolicyTests: work under a quantum rests zero, a quantum rests its own length, the cap still holds, and the foreground never rests. RescoreBackgroundSchedulerTests pass (34 tests across the two suites).
  • doc_comment_lint.py is clean.
  • parity_ratchet.py --base upstream/main --offline reports one error, base authority cannot be reproduced with the current derivation. It reports the same on an unmodified upstream/main checkout, so it isn't from this change.

Android: nothing to port. Android has no equivalent background suspension, and there is no Kotlin twin of the pacing.

Checklist

  • No new build warnings introduced
  • Follows the conventions in docs/CONTRIBUTING.md
  • I did not commit generated output (Strand.xcodeproj/) or any secrets/keystores

@ryanbr
ryanbr merged commit 35ebf64 into ryanbr:main Sep 18, 2026
4 checks passed
@ryanbr ryanbr mentioned this pull request Sep 18, 2026
5 tasks
ryanbr pushed a commit that referenced this pull request Sep 19, 2026
A backgrounded offload that finds a re-score already owed, with nothing running,
now defers only while the last pass STARTED less than 30 minutes ago. After that
it runs, paced as usual.

The owed rule dates from #1538, when a backgrounded pass was killed by the CPU
limit every time and retrying on each offload was the livelock. Pacing (#2296,
#2318) keeps a background pass under that limit now, but the rule still deferred
on any unfinished debt with no end, until a BGProcessingTask or the next
foreground. On one phone that left a pass unfinished at 11:25 deferring every
offload until the app was opened at 09:26 the next morning: 19 hours, and the
night in between was never scored.

The start time is recorded only by a pass that is about to work. The deferral
path re-marks the debt without touching it, so repeated deferrals cannot keep an
old unfinished pass looking recent.

Nothing to port: Android has no equivalent deferral or background suspension.

By @Iskrata.
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.

2 participants