Skip to content

Native-path seek recovery waits 12-24 s on a target the producer is already serving — the reconcile budget is sized for a slow source, not for the island it can measure #408

Description

@rrgomes

Reporting the cost side of the slow-source seek recovery (a4e049b4, fix(seek): recover slow-source VOD seeks without reverting the clock). The recovery itself is correct and does land the seek. What I would like to raise is when it decides to act: on a LAN origin with the target already cache-resident, the wedge is detectable within a second or so, and the engine waits 12 s — or 24 s with extensions — before re-anchoring.

What it looks like

Apple TV 4K (3rd gen), tvOS 26.6, wired GbE, engine 6.34.1. Origin is a Plex server on the same subnet. I checked the reconcile-budget path against 6.37.0 before filing: the three constants, seekIsWedged and the re-anchor branch are unchanged, and the 6.36.0 notes say the wedge re-anchor was left deliberately untouched, so I have not re-measured on the newer tags. Asset is 1080p H.264, ~35 Mbit/s, direct play on the loopback-HLS native path. Backward skip presses, 166-251 ms apart, each -10 s.

seek slow but producer serving target
  (island=7.30s at target, rendered=456.13s buffered=456.13s); extending budget 1/4

seek did not land within budget
  (1 extension; starved, island=7.30s at target, rendered=456.13s buffered=456.13s);
  holding clock at target 466.10s and re-anchored producer at 466.10s, re-seeking

The re-anchor then lands it immediately. Four instances in one ten-minute session across two devices; every one carried a non-trivial island at the target and rendered == bufferedEnd.

The wait is the two constants, not a measurement

nativeSeekReconcileBudgetSeconds = 8.0
nativeSeekExtensionBudgetSeconds = 4.0
nativeSeekMaxDeadlineExtensions  = 4

Observed landings cluster exactly on that arithmetic: 12.07, 12.10, 12.11, 12.13, 12.29 s (8 + 4, one extension), and ~26.5-27.0 s at the top of the extension ladder. Median landing for the same asset and device when this path is NOT hit is 166 ms (127 samples).

So the figure a viewer waits is not derived from anything the engine observed about this seek. It is the same 12 s whether the target is a fresh network fetch or, as here, content the producer is already serving.

Why the island seems like the signal

seekIsWedged already establishes the diagnosis — rendered == bufferedEnd, AVPlayer parked with nothing ahead of it — and the log line already reports island=Ns at target. Both are available at the first deadline check. When the island at the target is comfortably above nativeSeekProgressIslandFloorSeconds and the rendered position has not moved, waiting appears to buy nothing: there is no fetch in flight that more time would complete.

I want to be careful about the other direction, because the budget is clearly there for a reason. On a genuinely slow origin, re-anchoring the producer early would thrash, and the current figures are presumably tuned for that. So this is not a request to lower them — it is a question of whether the wait could be conditioned on what is already measured, so a session with the content in hand does not pay a slow-origin budget.

Failing that, exposing the three as configurable would let a host on a known-local origin choose, the way AVIOReader.connStallTimeout now can be.

Where this bites hardest

In a synchronised viewing session the cost is multiplied rather than shared. The peer that stalls reports itself buffering, the group gate pauses everyone, and every participant watches a spinner for the full 12 s — for a seek one device could have completed in 166 ms. Repeated skip presses are ordinary during such a session, so this is reached often.

Two things that shift the odds, in case they narrow it

Embedded subtitles enabled make it markedly more likely. Same asset, same device, same kind of press runs:

subtitles landed seeks median over 1 s
on 63 252 ms 7 (11%)
off 127 166 ms 2 (1.6%)

With subtitles on, the two worst cases (~27 s, the full extension ladder) occur; with them off the residue tops out at ~12 s. Every slow instance in both columns is a BACKWARD seek into content the producer reports as resident.

I have the full engine logs for each instance and can send whichever are useful, including the subtitle-event bursts that accompany the subtitles-on cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions