Browser notices: every open tab collects once a minute; let one tab collect for all of them #1155
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1155
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists today
static/js/app.js:7751-7798:readyNoticeCollection()runs once per page load when<body>carriesdata-browser-notices(templates/base.html:74, set bynotifications/context_processors.py:8-29when the person has a browser notifier on and has granted permission). It callscollectNotices()at once, every 60 s withsetInterval, and on each return to visibility. Each call is aPOSTtonotifications:waiting(notifications/views.py:40), which runsnotifications/inbox.py:75-100collect(): anexpire()DELETE on every call, a SELECT, and an UPDATE when there is something to hand over. The view stays inside the request transaction on purpose (A9 explains why).Every tab does this independently, hidden ones included (a hidden tab's timers are throttled to about once a minute, which is this interval, so nothing is saved). Correctness is fine: notices are marked shown by id, so two tabs never show the same one. The cost is the requests and write transactions multiplied by the number of tabs.
Skipping hidden tabs is not a fix: a browser notification exists to reach somebody who is not looking at Postulo, so at least one tab, visible or not, must keep collecting.
What would change
Elect one collector with the Web Locks API (
navigator.locks, in every browser Postulo supports; the Chromium extension already uses it, per the "already good" list): each tab callsnavigator.locks.request("postulo-notices", () => new Promise(() => { start the interval here })). The lock holder collects; the others wait in the lock's queue and the next one takes over when that tab closes or navigates away. The visibility-change collection stays in every tab, so a tab brought to the front still collects at once. Wherenavigator.locksis missing, behave as today.No library needed; it is a platform API.
What it saves or protects
small-medium: with five Postulo tabs open, 300 signed-in POSTs an hour become 60, each one a DELETE of expired notices and, under SQLite, a write transaction (A9/R1); only for people with a browser notifier switched on
Accessibility
none: notifications are the operating system's; the page's own content is unchanged
Decisions
expire()DELETE on every collection? Recommended: yes for now; the scheduler'sforget_oldis not guaranteed to run (an instance without a scheduler), and with one collector the DELETE runs once a minute per person, not per tab.Found by the efficiency and reliability audit of 2026-10-30 (finding A10; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).