LIVE_TTL_SECONDS is defined and never read; every page load hits upstream FPL #10

Closed
opened 2026-08-27 20:57:39 +02:00 by solvreven · 2 comments
Owner

LIVE_TTL_SECONDS = 900 appears exactly once in the repository — its
own definition at backend/web/main.py. Nothing reads it.

$ grep -rn "LIVE_TTL_SECONDS" --include=*.py --include=*.js .
./backend/web/main.py:1461:LIVE_TTL_SECONDS = 900

It sits directly above /api/refresh-live, carrying a four-line docstring
explaining why prices and availability need a shorter refresh cycle than the
~170s simulation — and the endpoint beneath it never consults it. Every call
unconditionally runs FplClient().get_bootstrap().

frontend/app.js calls refreshLiveFast(null, true) once, on startup. So
every page load fetches upstream FPL, with the freshness policy written down
three lines above the endpoint and never connected.

Suggested fix. Honour the TTL server-side: if the last successful bootstrap
is younger than LIVE_TTL_SECONDS, return the cached fetch time and a flag
saying no upstream call was made, rather than silently skipping (a caller that
cannot tell a refresh from a no-op will read stale flags as current — the exact
condition the endpoint's own docstring says it exists to end).

`LIVE_TTL_SECONDS = 900` appears **exactly once in the repository** — its own definition at `backend/web/main.py`. Nothing reads it. ``` $ grep -rn "LIVE_TTL_SECONDS" --include=*.py --include=*.js . ./backend/web/main.py:1461:LIVE_TTL_SECONDS = 900 ``` It sits directly above `/api/refresh-live`, carrying a four-line docstring explaining why prices and availability need a shorter refresh cycle than the ~170s simulation — and the endpoint beneath it never consults it. Every call unconditionally runs `FplClient().get_bootstrap()`. `frontend/app.js` calls `refreshLiveFast(null, true)` once, on startup. So **every page load fetches upstream FPL**, with the freshness policy written down three lines above the endpoint and never connected. **Suggested fix.** Honour the TTL server-side: if the last successful bootstrap is younger than `LIVE_TTL_SECONDS`, return the cached fetch time and a flag saying no upstream call was made, rather than silently skipping (a caller that cannot tell a refresh from a no-op will read stale flags as current — the exact condition the endpoint's own docstring says it exists to end).
Author
Owner

Retired on fix/gw2-window-and-provenance (46d6f47, pushed just now) — removed rather than wired, because the model it belonged to is gone.

A TTL is a pull design: serve stale data until someone asks and the clock says it expired. autorefresh.Refresher (9d9bd4d) is a push design — it re-fetches bootstrap and fixtures together every 300s, atomically, and publishes the age of what it holds. "How old is this" is answered by measurement instead of by an assumption about how long an answer stays good, so there is no remaining code path that would consult a bootstrap age against a limit.

/api/status now carries live.age_seconds (None until the first success — never zero, which would read as "just refreshed"), plus last_error and consecutive_failures.

Deleted rather than commented in place: unlike a CSS fallback there is no context in which the value would apply. The comment left behind records what stood there and why it went.

This issue also noted the reload-on-every-request cost; that is addressed by the same change, since the refresher owns the cadence rather than each request re-deciding.

**Retired on `fix/gw2-window-and-provenance` (`46d6f47`, pushed just now)** — removed rather than wired, because the model it belonged to is gone. A TTL is a **pull** design: serve stale data until someone asks and the clock says it expired. `autorefresh.Refresher` (`9d9bd4d`) is a **push** design — it re-fetches bootstrap *and* fixtures together every 300s, atomically, and publishes the age of what it holds. "How old is this" is answered by measurement instead of by an assumption about how long an answer stays good, so there is no remaining code path that would consult a bootstrap age against a limit. `/api/status` now carries `live.age_seconds` (None until the first success — never zero, which would read as "just refreshed"), plus `last_error` and `consecutive_failures`. Deleted rather than commented in place: unlike a CSS fallback there is no context in which the value would apply. The comment left behind records what stood there and why it went. This issue also noted the reload-on-every-request cost; that is addressed by the same change, since the refresher owns the cadence rather than each request re-deciding.
Collaborator

Closed after PR #19 merged as 6e888a5. The implementation described in the latest issue comment is now on main.

Closed after PR #19 merged as 6e888a5. The implementation described in the latest issue comment is now on main.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
solvreven/FPL#10
No description provided.