Skip to content

Index devices.site_id and coalesce the outage refresh on status flips (large-fleet performance) - #47

Open
coreyhauer wants to merge 2 commits into
AthenaNetworks:mainfrom
coreyhauer:pr/sites-index-outages-throttle
Open

coreyhauer wants to merge 2 commits into
AthenaNetworks:mainfrom
coreyhauer:pr/sites-index-outages-throttle

Conversation

@coreyhauer

Copy link
Copy Markdown
Contributor

Two small fixes from running My Mate against a ~25k-device / 2.7k-site fleet. Both are independent of each other.

1. Index devices (site_id, status)

devices.site_id (2026_07_23_000002) was added as a constrained foreign key but never indexed, and Postgres does not index FK columns automatically. SiteController@index counts devices and down devices per site with two correlated subqueries, so each GET /api/sites call was a full scan of devices per site.

Measured on our box (25,423 devices, 2,722 sites): 61 s per call, tripping the reverse proxy's 120 s timeout about two thirds of the time. The geo map's site markers wait on this call, so the map sat empty after login. With the composite index the same call takes 0.2 s.

The migration is guarded with Schema::hasIndex so an operator who already built the index live (CREATE INDEX CONCURRENTLY, same name) is not failed by it.

2. Coalesce the outage-timeline refresh on status flips

ca330ee invalidates the outages query on every real up/down flip so the timeline lands ahead of its 15 s poll. On a large fleet that is not a rare event: we see several hundred flips per minute during weather, and one invalidateQueries per flip turned every open browser tab into a ~100 req/s client of /api/outages (about 850k requests per hour from two tabs), starving php-fpm for the requests that actually draw the map.

This keeps the intent (fresh within seconds of a flip) but coalesces to at most one refresh per 5 s window, trailing-edge so the last flip in a burst is still reflected. The timer is cleared on unmount.

tsc is clean; no behaviour change for small fleets beyond the outage list refreshing at most every 5 s instead of instantly.

…ll devices scan per site

devices.site_id was added as a constrained foreign key but never indexed, and
Postgres does not index FK columns automatically. SiteController@index counts
devices (and down devices) per site with two correlated subqueries, so on a
25k-device / 2.7k-site fleet each call seq-scanned devices 2,722 times: ~60 s,
and it tripped the 120 s proxy timeout about two thirds of the time. The geo
map's site markers wait on this call, so the map sat empty after login.

With the composite index the same call takes 0.2 s. Guarded with hasIndex so a
live CREATE INDEX CONCURRENTLY under the same name does not fail the migration.
ca330ee refreshes the outage timeline on every real up<->down flip so it lands
ahead of the 15 s poll. On a large fleet that is not a rare event: hundreds of
devices can flap per minute, and one invalidateQueries per flip made every open
tab fetch /api/outages ~100 times a second, starving php-fpm for the requests
that actually draw the map.

Keep the intent (fresh within seconds of a flip) but coalesce: at most one
refresh per 5 s window, trailing-edge so the last flip in a burst still shows.
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