Skip to content

feat: store session usage per app and family in template usage stats - #29109

Open
EhabY wants to merge 2 commits into
mainfrom
feat/session-family-usage-rollup
Open

EhabY wants to merge 2 commits into
mainfrom
feat/session-family-usage-rollup

Conversation

@EhabY

@EhabY EhabY commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Store per-app session minutes in template_usage_stats_session_apps, replacing the fixed family-minute columns. A new app no longer requires a rollup schema change, and the API, Prometheus, and telemetry outputs are unchanged.

Reads group app names into families through the registry in codersdk, so the registry never reaches SQL and attribution applies to all history rather than only the buckets the rollup still revisits. GetTemplateInsightsByTemplate, which feeds Prometheus, groups by app for the same reason, so both surfaces report a family identically. A digest with length-prefixed names skips child-row writes for unchanged buckets without confusing names that contain delimiters.

Semantic change: a family total is the sum of its apps, not the distinct minutes any of them was active, so a minute two apps of one family share counts in both. Reported numbers are unchanged today, because every agent reports one canonical app name per family. The difference appears once #28338 lands the producers and clients send names such as cursor or zed. The apps report already overlaps across rows, and the dashboard builds its percentage denominator from the sum of the rows, so a family row stays consistent with the per-app breakdown it will sit above.

Migration 000596: backfill the family totals the fixed columns recorded, sftp included, under the family name as the app name, which the registry maps back to itself. Workspace web-app usage stays separate. The migration alters template_usage_stats; the exclusive locks the ALTERs acquire last until the migration transaction commits. Downgrading folds app names back into the five fixed columns and discards the per-app detail. Already-merged migration 000590 is unchanged.

The second commit is the response to review and reads on its own; the first is the rest of the work squashed.

Follows merged #28337 and #29134; #28338 enables the producers. Covers the storage portion of #27413. Dynamic external projections (#27411 and the remaining #27413 work) and the connection-log migration (#27412) remain separate.

Design decisions and performance context
  • A child table rather than a family table: the family grouping is a read-time concern, and the per-app rows are what the dashboard breakdown needs anyway. Folding in Go costs nothing on reads, because the query aggregates straight to its output rows instead of adding an intermediate per-bucket aggregate that spills at work_mem=8MB.
  • A child table rather than a jsonb column on template_usage_stats: jsonb is cheaper to write and store but roughly 2.3x the read, and it widens the main row by 22% for every query that scans it.
  • Recomputed buckets and old buckets are attributed the same way, because attribution happens at read time.
  • Synthetic fixtures on PostgreSQL 17, ~308k buckets and 539k app rows over a 30 day window, median of 11 runs. GetTemplateInsights 429 ms with the family table against 444 ms folding in Go, 777 ms folding in SQL, and 1034 ms from a jsonb column. Cold rollup over 422k agent stats 3386 ms against 2687 ms. Storage 221 MB against 151 MB. These are fixture measurements, not production guarantees.

Description updated by Coder Agents for @EhabY.

🤖 Generated with Claude Code

@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from 83aa8d3 to cd9543f Compare September 9, 2026 14:33
@EhabY
EhabY removed this pull request from stack #29110 September 9, 2026 14:34
@EhabY
EhabY changed the base branch from feat/session-count-family-attribution to feat/session-count-generic-queries September 9, 2026 14:34
@EhabY
EhabY added this pull request to stack #29135 September 9, 2026 14:35
@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from cd9543f to bdac30a Compare September 10, 2026 11:27
@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from bdac30a to d34d4b6 Compare September 10, 2026 11:54
Base automatically changed from feat/session-count-generic-queries to main September 10, 2026 12:07
@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from d34d4b6 to 32b28f7 Compare September 10, 2026 12:07
@EhabY
EhabY marked this pull request as ready for review September 10, 2026 12:12
@EhabY
EhabY requested a review from code-asher September 10, 2026 16:31
@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from a16831d to d17e5b7 Compare September 14, 2026 18:10
-- caps a user's minutes per half hour across templates, can look up one user's
-- rows through that prefix. The upsert's conflict target names the same
-- columns in the parent's order, satisfied by the unique index.
CREATE TABLE template_usage_stats_session_families (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this table? My instinct is that in the database we should only need to record per app rather than family. The family grouping seems more like a display-time thing to me that we do in code. We need the individual counts anyway to provide the breakdown in the dashboard, right? So eventually this table and associated queries would go unused.

I think the reduction in query complexity would be worth it, unless summing families is prohibitively expensive or something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped it. Folding in Go is within noise of the family table on reads, and the rollup gets ~20% faster with ~30% less storage. Folding in SQL is the slow path, that's the cost worth avoiding. Can share numbers.

The registry is out of SQL now (@app_families and its dbauthz validation are gone) and attribution applies to all history instead of freezing at rollup time. I also grouped GetTemplateInsightsByTemplate by app so Prometheus and the API agree.

Semantic change: a family total is the sum of its apps, not distinct minutes, so VS Code + Cursor in the same minute counts twice. No-op today since agents report one name per family, starts mattering with #28338. The report's rows already overlap and the UI divides by the row sum, so this keeps families consistent with the per-app breakdown.


COMMENT ON COLUMN template_usage_stats_session_families.usage_mins IS 'Total minutes the user has been using the family. Minutes shared by two apps of the family count once.';

CREATE TABLE template_usage_stats_session_apps (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget what we landed on in the RFC, but maybe this should be a jsonb column just to be consistent with the web app minutes? Might need to rename the existing column web_app_usage_mins or something though

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my first experiment but the reads are x2.4 slower compared to a separate table, I can provide the benchmark results for this. It would've let us drop the digest, the FK and the delete/insert pair, but not worth 2.4x on the insights read.

@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch 2 times, most recently from 066a75f to 44025d6 Compare September 16, 2026 12:19
Replace the fixed family-minute columns with per-app and per-family
child tables, and skip the child writes for buckets whose session usage
digest is unchanged.
Drop template_usage_stats_session_families and fold app names into
families in Go, so the registry no longer reaches SQL and attribution
applies to every bucket rather than only the ones the rollup revisits.
@EhabY
EhabY force-pushed the feat/session-family-usage-rollup branch from 44025d6 to cea8c2f Compare September 16, 2026 12:42
@EhabY
EhabY requested a review from code-asher September 16, 2026 12:44
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.

2 participants