Skip to content

Keep device runtime state out of PostgreSQL - #7

Merged
yellowman merged 10 commits into
mainfrom
openai/inventory-only-device-db
Sep 19, 2026
Merged

yellowman merged 10 commits into
mainfrom
openai/inventory-only-device-db

Conversation

@yellowman

@yellowman yellowman commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Makes the devices table inventory/configuration-only and keeps poll-derived runtime state in memory.

Runtime state

  • Removes poller writes of device status/status_reason for Wave, LTU/AirMAX, MAC mismatch, child propagation, association loss, and firmware upgrade runtime state.
  • API no longer reads devices.status/status_reason. With no live stats after restart, status is intentionally unknown until a fresh poll.
  • Reports derive status from the in-memory stats store rather than persisted device rows.
  • MAC replacement relies on the durable device_identity_mismatches record rather than status_reason.

Last available

  • Keeps last_seen as the one polling-derived durable marker.
  • Persists it on an hourly wall-clock ticker, not per poll cycle.
  • Uses one batch UPDATE with each device's actual in-memory LastSeen timestamp; rows are written at most roughly hourly and only when the in-memory timestamp is newer.
  • Live API responses still expose the newer in-memory LastSeen while the process is running.

STA write amplification

  • Child status propagation and association loss are memory/WebSocket-only.
  • Stable existing STAs no longer receive a DB UPDATE on every AP peer poll.
  • The hot existing-STA update is guarded by IS DISTINCT FROM checks so only real inventory changes write.
  • New STA creation records inventory and initial last_seen, without persisted runtime status.

Other write reduction

  • AirMAX GPS coordinates are treated as inventory: fill them if missing rather than rewriting on small GPS jitter.
  • Explicit Add Device may stamp last_seen as part of the inventory transaction, but no longer persists status.

Legacy status/status_reason columns remain for schema compatibility but are documented as unused by the poller/API. Job, alert, audit, and mismatch records remain durable by design.


Note

Overview
Treats devices as durable inventory only and stops using PostgreSQL for poll-derived runtime state (online/offline, reasons, upgrading). After a restart, APIs and bulk ops report unknown until the in-memory stats store is populated—no fallback to stale devices.status.

Poller and children: Removes per-poll (and per-transition) UPDATE devices for status/reason across Wave, AirMAX/LTU, MAC mismatch, parent/child propagation, and missing STA association. Child updates SELECT identity from DB and push state via the stats store + WebSocket only. markMissingSTAsOffline uses SetStatusByMACChanged so WebSocket fires only when status/reason actually change.

last_seen: Replaces ~10-minute batch status/last_seen sync with an hourly syncLastSeenToDB that batch-updates each MAC’s in-memory timestamp (with skip logic for recent DB writes). Add/upsert paths no longer set status on insert/update; STA hot paths add IS DISTINCT FROM guards to avoid write amplification.

API/reports/firmware: List/get devices overlay status, reason, and fresher last_seen from live stats; reports use reportLiveStatus. Firmware jobs no longer set devices.status = 'upgrading'. Learn MAC keys off device_identity_mismatches instead of status_reason. AirMAX GPS is fill-once inventory (COALESCE); SPEC documents the pattern.

Reviewed by Cursor Bugbot for commit f2b5383. Bugbot is set up for automated code reviews on this repo. Configure here.

Follow-up review fixes

  • Missing/disconnected STAs now broadcast only when live status or status reason actually changes; repeated AP polls do not emit identical offline updates.
  • Bulk upgrade dry-run no longer reads or falls back to devices.status; a device with no live stats is ineligible until freshly polled.
  • Hourly last_seen persistence is chunked at 2,000 rows (4,000 bind parameters) per statement and binds Go timestamps as timestamptz.
  • Removed the now-unused OnlineStatusBatch helper.
  • Added a unit test covering repeated status/reason suppression and reason-only changes.
  • SPEC documents AirMAX poller GPS as fill-once durable inventory with live in-memory overlay.

Validation

CI run 35412981298 is green on head f2b5383d43dea1faa0446cb2b089065b2c6deb0e:

  • Go tests: passed
  • Server build: passed
  • JavaScript syntax: passed

@cursor

cursor Bot commented Sep 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_24d7273f-5853-4bf0-8c22-a8d7527088c3)

@cursor

cursor Bot commented Sep 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ca4a0a37-d2a8-4bfa-b53b-7013f32c691e)

@cursor

cursor Bot commented Sep 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_59aff440-7150-4df1-b04f-dd1c4f745928)

@cursor

cursor Bot commented Sep 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_dd399add-753b-4674-99fb-e112cbfd386a)

@yellowman
yellowman merged commit 40cc9b6 into main Sep 19, 2026
2 checks passed
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