Browser notices: every open tab collects once a minute; let one tab collect for all of them #1155

Closed
opened 2026-10-31 03:04:23 +00:00 by tiagoagueda · 0 comments
Owner

What exists today

static/js/app.js:7751-7798: readyNoticeCollection() runs once per page load when <body> carries data-browser-notices (templates/base.html:74, set by notifications/context_processors.py:8-29 when the person has a browser notifier on and has granted permission). It calls collectNotices() at once, every 60 s with setInterval, and on each return to visibility. Each call is a POST to notifications:waiting (notifications/views.py:40), which runs notifications/inbox.py:75-100 collect(): an expire() 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 calls navigator.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. Where navigator.locks is 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

  • Keep the expire() DELETE on every collection? Recommended: yes for now; the scheduler's forget_old is 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 main at daee30b15 (2026-10-30).

## What exists today `static/js/app.js:7751-7798`: `readyNoticeCollection()` runs once per page load when `<body>` carries `data-browser-notices` (`templates/base.html:74`, set by `notifications/context_processors.py:8-29` when the person has a browser notifier on and has granted permission). It calls `collectNotices()` at once, every 60 s with `setInterval`, and on each return to visibility. Each call is a `POST` to `notifications:waiting` (`notifications/views.py:40`), which runs `notifications/inbox.py:75-100` `collect()`: an `expire()` 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 calls `navigator.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. Where `navigator.locks` is 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 - **Keep the `expire()` DELETE on every collection?** Recommended: yes for now; the scheduler's `forget_old` is 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 `main` at `daee30b15` (2026-10-30).
tiagoagueda added this to the 0.6.0 milestone 2026-10-31 03:04:23 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1155
No description provided.