Skip to content

fix(signage): keep display updates, cron and metrics reliable - #526

Merged
MrYuion merged 2 commits into
developfrom
fix/signage-player-scheduling
Oct 2, 2026
Merged

MrYuion merged 2 commits into
developfrom
fix/signage-player-scheduling

Conversation

@MrYuion

@MrYuion MrYuion commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

The player could miss display updates, run cron takeovers at the wrong time around DST, and lose or misreport data over long uptime:

  • Validators: ETag and Last-Modified were saved before the payload was applied. A failed apply was then answered with 304 and never fetched again. A full localStorage made the same happen.
  • Cron: in the DST fall-back hour, cron searches started one hour in the past.
  • Timers: each setDisplay could start another 15 s tick chain that could not be stopped.
  • Diagnostics: poll.last_success advanced while the backend was down.
  • Metrics: a failed metrics post rejected unhandled, and counts recorded during the post were lost.
  • Memory: completed schedule keys grew without limit.
  • Log noise: a release after a cache eviction logged "Unable to release cached media … not found".

Changes

All in signage.service.ts and cron-helpers.ts:

  • Validators are saved together with the display signature, after the apply. localStorage reads and writes are best effort.
  • Cron searches floor and step in UTC and match local fields. A repeated wall-clock time runs once. Searches are capped at 366 days.
  • One tick timer handle, cleared on destroy.
  • last_success is set only when the backend answers (a 304 counts).
  • Metrics are posted from a fresh object; failed counts are merged back.
  • Completed keys are pruned once their window has passed.
  • Release only the files the sync kept.

Testing

  • Unit tests for each fix, pinned to New York and Sydney time for DST. Each fails without its fix.
  • nx test signage (427) and nx build signage pass.
  • Local PlaceOS stack: 9/9 checks pass. These covered validators after a failed apply, full storage, backend down, tick timers across navigation, metrics while blocked, single pass, Sydney DST, and regression.

Notes

  • US-SIG-029 says metrics posting is delayed by up to 60 s, but the code uses up to 60 ms. Not changed here; it needs a decision.

Independent of the other signage PRs from this review. Based on develop.


Changes made by Claude Opus 5.5 (1M context) in Claude Code, running in T3 Code.

🤖 Generated with Claude Code

- Save ETag and Last-Modified only after a payload is applied, so a
  failed apply is fetched again. localStorage reads and writes are best
  effort, so a full store no longer blocks new content.
- Cron searches step in UTC and match local fields. A repeated
  wall-clock time in the DST fall-back hour runs once. Searches are
  capped at 366 days.
- Keep one schedule tick timer and clear it on destroy.
- Record poll.last_success only when the backend answers.
- Post metrics from a fresh object, catch errors and keep failed counts.
- Prune completed schedule keys once their window has passed.
- Do not release media that the cache sync already evicted.
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
frontend-templates Ignored Ignored Oct 2, 2026 3:14am UTC

@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Fixes cron scheduling and metrics reliability in signage display app.

The PR is not yet safe to merge because a permitted long-running cron takeover can still stop early.

Findings

  1. P1 Long cron takeovers stop early ▶

Summary

The signage player now keeps display refreshes, cron schedules, timers, and metrics moving reliably through storage trouble, clock changes, and long uptime. The PR also bounds completed takeover records and avoids releasing media files the cache has already removed.

  • Applies display response validators only after the display update finishes and treats local storage as best effort.
  • Searches local-time cron schedules without stepping backward during daylight saving changes.
  • Keeps schedule ticks to one timer and preserves metrics recorded during or after a failed post.
  • Forgets completed takeover keys after their schedule window and checks cache contents before releasing files.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Check takeover schedules] --> B[Find active run keys]
  B --> C{Key completed?}
  C -->|No| D[Play takeover]
  D --> E[Record keys and window end]
  E --> F[Takeover finishes]
  F --> G[Keep keys until window ends]
  G --> A
Loading

Reviews (2) · Last reviewed commit: "fix(signage): find long cron runs and ke..."


/** Search limit in milliseconds, capped to `MAX_SEARCH_LIMIT_SECONDS` */
function searchLimitMs(search_limit_in_seconds: number) {
return Math.min(search_limit_in_seconds, MAX_SEARCH_LIMIT_SECONDS) * 1000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Long cron takeovers stop early

If a cron takeover has a play_period longer than 366 days, the new search cap can hide its start while the run is still active. For example, a February 29 run with a 368-day period is still active on March 2, 2025, but the player cannot find it and stops showing it. The search needs to cover the configured period.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 209e804. The search limit is now the requested period, capped at 10 years. To keep long searches cheap, both searches skip local days the cron cannot match (one check per day), and crons that can never run at any time of day return at once. Measured: a 10-year search for 0 0 31 2 * takes about 5 ms. Test: "finds a run that started more than a year ago" (0 0 29 2 *, 368-day period, checked on 2 Mar 2025).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remaining case is a deliberate limit, not an oversight. The search now covers the requested period, with a 10-year ceiling. Without a ceiling, a corrupt or extreme play_period (the manager sets no maximum) would make each schedule tick walk an unbounded number of days on low-power display hardware. A run is now missed only if its period exceeds 10 years and it started more than 10 years ago. For signage, a takeover that long is effectively permanent, and a background schedule is the right tool for it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That addresses the concern. Using the requested play period preserves long-running takeovers, while the 10-year ceiling is a reasonable defensive bound against an unbounded walk on display hardware. Given the stated behavior for periods beyond the ceiling and the fallback to background schedules for effectively permanent content, I’m withdrawing this finding.

Comment thread apps/signage/src/app/signage.service.ts Outdated
- Cron searches cover the requested period, capped at 10 years, so a
  run longer than a year is still found. They skip local days the cron
  cannot match, so a long search costs one check per day.
- Completed takeover runs are remembered until their window ends, not
  only while active. A playlist that briefly leaves the display no
  longer replays a finished takeover.
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

Deployment failed for project frontend-templates with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/placeos?upgradeToPro=build-rate-limit

@MrYuion
MrYuion merged commit 8c50f14 into develop Oct 2, 2026
4 of 5 checks passed
@MrYuion
MrYuion deleted the fix/signage-player-scheduling branch October 2, 2026 04:04
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