Dashboard: the counters widget runs four COUNTs where one aggregate would do #1114

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

What exists today

src/postulo/applications/widgets.py:38-51 _counters() runs four count()s on the person's applications: open, interviewing (three statuses), offer, applied in the last RECENT_DAYS. On the second GET of core:home for a fresh user under the test settings (probe audit/m_scripts/test_probe_queries.py) the page made 20 queries, 5 of them on applications_application.

What changed from the candidate:

  • "Quiet widget evaluates twice" is not a double evaluation. _gone_quiet() (:54-59) runs sources.quiet[:10] and sources.quiet.count(), two different queries: the first ten to show and the total for the line that says how many. Both are needed as long as the widget says the total. Note that #1099 (gone-quiet days per status and channel) will rewrite quiet(); any change here belongs with it.
  • "Fingerprint ~9 aggregates per dashboard view" (applications/analytics.py:339, used by insights_for() at :422) is deliberate (#231, #396): a handful of indexed aggregates in place of building every figure from the whole history on every view, with a cache hit after it. Folding them into one statement means subqueries over seven tables, no cheaper on either database. Leave it.

What would change

Replace the four counts with one aggregate() using filtered Counts, as analytics.fingerprint() already does for reminders:

applications.aggregate(open=Count("pk", filter=<open Q>), interviewing=Count("pk", filter=Q(status__in=[...])), offer=Count("pk", filter=Q(status=Status.OFFER)), recent=Count("pk", filter=Q(applied_at__gte=...)))

open() is a queryset method; its condition has to be available as a Q (or the method rewritten to use one) so the two do not drift. The widget's context keys stay the same.

What it saves or protects

small: three queries per dashboard view; the other two parts of the candidate are not worth changing

Accessibility

none

Decisions

  • File the quiet and fingerprint parts? Recommended: no; list the fingerprint under "already good" in the index.

Found by the efficiency and reliability audit of 2026-10-30 (finding Q3; index postulo/postulo#1108). Checked against main at daee30b15 (2026-10-30).

## What exists today `src/postulo/applications/widgets.py:38-51` `_counters()` runs four `count()`s on the person's applications: open, interviewing (three statuses), offer, applied in the last `RECENT_DAYS`. On the second GET of `core:home` for a fresh user under the test settings (probe `audit/m_scripts/test_probe_queries.py`) the page made 20 queries, 5 of them on `applications_application`. What changed from the candidate: - **"Quiet widget evaluates twice"** is not a double evaluation. `_gone_quiet()` (`:54-59`) runs `sources.quiet[:10]` and `sources.quiet.count()`, two different queries: the first ten to show and the total for the line that says how many. Both are needed as long as the widget says the total. Note that #1099 (gone-quiet days per status and channel) will rewrite `quiet()`; any change here belongs with it. - **"Fingerprint ~9 aggregates per dashboard view"** (`applications/analytics.py:339`, used by `insights_for()` at `:422`) is deliberate (#231, #396): a handful of indexed aggregates in place of building every figure from the whole history on every view, with a cache hit after it. Folding them into one statement means subqueries over seven tables, no cheaper on either database. Leave it. ## What would change Replace the four counts with one `aggregate()` using filtered `Count`s, as `analytics.fingerprint()` already does for reminders: `applications.aggregate(open=Count("pk", filter=<open Q>), interviewing=Count("pk", filter=Q(status__in=[...])), offer=Count("pk", filter=Q(status=Status.OFFER)), recent=Count("pk", filter=Q(applied_at__gte=...)))` `open()` is a queryset method; its condition has to be available as a `Q` (or the method rewritten to use one) so the two do not drift. The widget's context keys stay the same. ## What it saves or protects small: three queries per dashboard view; the other two parts of the candidate are not worth changing ## Accessibility none ## Decisions - **File the quiet and fingerprint parts?** Recommended: no; list the fingerprint under "already good" in the index. --- Found by the efficiency and reliability audit of 2026-10-30 (finding Q3; 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:06 +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#1114
No description provided.