@startuml state-poller !theme plain title sofarr — Poller State Diagram [*] --> CheckConfig : startPoller() state CheckConfig <> CheckConfig --> Disabled : POLL_INTERVAL = 0\nor 'off' / 'false' CheckConfig --> Idle : POLL_INTERVAL > 0 state Disabled { state "On-demand mode\nNo background timer" as od od : Data fetched only when\na dashboard request\nfinds empty cache } Disabled --> Polling : pollAllServices()\n(triggered by dashboard request) Polling --> Disabled : Poll complete\n(return to on-demand) state Idle { state "Waiting for\nnext interval" as waiting } Idle --> Polling : setInterval fires\nor immediate first poll state Polling { state "polling = true" as lock state "Fetching all services\n(Promise.all)" as fetching state "Storing results\nin cache" as storing state "Recording timings" as timing [*] --> lock lock --> fetching fetching --> storing : All promises resolved fetching --> ErrorState : Any individual service\nerror (caught per-service) storing --> timing timing --> [*] : polling = false } state ErrorState as "Handle Error" { state "Log error\npolling = false" as err } ErrorState --> Idle : Next interval Polling --> Idle : Poll complete\n(back to waiting) state "Concurrent Poll\nAttempt" as skip { state "polling === true\n→ skip" as sk } Idle --> skip : Interval fires while\nprevious still running skip --> Idle : Log "still running,\nskipping" note right of Polling **Cache TTL**: POLL_INTERVAL Ɨ 3 Ensures data survives between polls even if one cycle is slow. end note note right of Disabled **Cache TTL**: 30000ms (30s) After expiry, next dashboard request triggers a fresh poll. end note @enduml