Skip to content

feat(toolbar): load the full clickmap automatically after first paint - #68063

Merged
pauldambra merged 2 commits into
masterfrom
posthog-code/toolbar-clickmap-auto-load
Jul 3, 2026
Merged

feat(toolbar): load the full clickmap automatically after first paint#68063
pauldambra merged 2 commits into
masterfrom
posthog-code/toolbar-clickmap-auto-load

Conversation

@pauldambra

@pauldambra pauldambra commented Jul 2, 2026

Copy link
Copy Markdown
Member

Problem

The clickmap now loads a single 5,000-row page (#67549/#67755) — fast, but for a 90-day range on a busy page that's nowhere near the full picture, and recovering it means clicking "Load more" repeatedly. Each click is also expensive server-side: the element stats query is LIMIT/OFFSET over a live GROUP BY, so ClickHouse re-runs the full aggregation for every page (measured 3-7s per scan on heavy teams) — and rows that shift position between scans can fall through page boundaries entirely (offset pagination misses them; the hash dedupe only catches duplicates).

Changes

  • Keep the 5,000-row first page for fast first paint, then automatically fetch up to 50,000 rows (the old server default) in one background request from offset 0, replacing the first page. One extra scan total, no boundary-gap row loss, and the single-flight guard is structural: only an initial-triggered result starts the refetch, so an auto-load result can never re-trigger itself.
  • "Load more" (offset pagination + hash dedupe) only appears beyond the 50,000 budget.
  • New per-row match cache keyed on the server's type:chain_hash identity: the auto-load refetch reuses the first page's 5,000 match results instead of redoing them, and definitive misses are cached too. Cleared whenever the page-elements cache invalidates (DOM mutation, href change, refresh, toggle-off, unmount) and when "Match links by their target URL" is toggled, since that changes matching semantics.
  • The truncation hint now reads "…loading the rest automatically…" while the background fetch is in flight.
  • Telemetry: trigger gains auto-load, and a new match_cache_hit_count distinguishes cached resolutions from fresh index/fallback matches (documented in TELEMETRY.md).

No backend changes — the limit parameter and offset-0 replace semantics already existed.

How did you test this code?

  • All 430 toolbar jest tests pass; frontend typecheck clean; kea typegen regenerated.
  • No new jest tests: the refetch loop-guard is structural (trigger-derived, not counter-based) and the logic has no existing kea test harness — the observable behavior lands in the toolbar clickmap processed telemetry (trigger=auto-load, match_cache_hit_count), which is how the rollout will be verified, mirroring how the serialization win in feat(toolbar): serialize element stats without models and trim attributes #67755 was verified from production spans.
  • Server-side capacity for the 50,000-row background request was validated in production by feat(toolbar): serialize element stats without models and trim attributes #67755's trace data: 50,000 rows serialized in ~7.5s on the old code path's worst trace would now be ~2-4s on the shipped dict path, in a background request after the user already has the top-5,000 heatmap.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Built with Claude Code, following the user's direction after presenting options measured from production (per-page ClickHouse scan cost from query_log, singleton row distribution, serialize cost per row from APM spans). A min-count/HAVING filter was explicitly rejected by the author (empty-looking results would confuse teams evaluating the product).
  • Skills invoked: /writing-tests, /writing-kea-logics conventions via frontend rules.
  • Decisions: refetch-from-zero instead of offset pagination for the auto-load (equal server cost, eliminates boundary-gap row loss); 50,000 budget = the pre-fix(toolbar): make clickmap element matching linear and position-aware #67549 server default, now affordable in the background; match cache keyed on the chain_hash introduced in feat(toolbar): serialize element stats without models and trim attributes #67755 and skipped for legacy null-hash rows.

Created with PostHog Code

Ninety days of data on a busy page needs far more than one 5,000-row
page, and clicking load more repeatedly re-runs the full server-side
aggregation per click since LIMIT/OFFSET over a live GROUP BY cannot
reuse work — while also missing rows that shift across page boundaries
between scans.

Keep the 5,000-row first page for a fast first paint, then fetch up to
50,000 rows in one background request from offset zero, replacing the
first page rather than merging with it: one extra scan, no boundary
gaps, and load more only appears past that budget. A per-row match
cache keyed on the server's chain hash means the refetch reuses the
first page's matching work instead of redoing it, and clears whenever
the page-elements cache invalidates or href matching is toggled. The
run's trigger and cache reuse are visible in the clickmap telemetry.

Generated-By: PostHog Code
Task-Id: 2d3886bf-73f2-40a4-8644-f3a1eb44ff27

pauldambra commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 2, 2026 22:20
@pauldambra pauldambra added the stamphog Request AI approval (no full review) label Jul 2, 2026
@pauldambra
pauldambra enabled auto-merge (squash) July 2, 2026 22:20

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Three reviewers (Codex, hex-security-app, greptile-apps) have 👀 reactions indicating in-progress reviews on this PR, and none has yet posted a completed review — auto-approval must not preempt an active review.

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 2, 2026
@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(toolbar): load the full clickmap au..." | Re-trigger Greptile

Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fc1c587ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Bundle size — 🔺 +2.0 KiB (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 81.55 MiB · 🔺 +2.0 KiB (+0.0%)

File Size Δ vs base
toolbar/src/toolbar/toolbar.js 12.98 MiB 🔺 +1.5 KiB (+0.0%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
3.76 MiB · 695 files 🔺 +68 B (+0.0%) █████████░ 87.7% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
11.52 MiB · 2,937 files 🔺 +458 B (+0.0%) █████████░ 89.5% of 12.87 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
275.2 KiB ../node_modules/.pnpm/posthog-js@1.396.5/node_modules/posthog-js/dist/rrweb.js
266.9 KiB ../node_modules/.pnpm/@posthog+icons@0.37.4_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
224.5 KiB src/taxonomy/core-filter-definitions-by-group.json
211.3 KiB ../node_modules/.pnpm/posthog-js@1.396.5/node_modules/posthog-js/dist/module.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.9 KiB src/lib/api.ts
69.7 KiB src/products.tsx
61.7 KiB src/lib/lemon-ui/icons/icons.tsx
57.2 KiB src/lib/utils/eventUsageLogic.ts
39.6 KiB src/lib/KeaDevTools.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
458.3 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/driving-hogzilla.mjs
302.9 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/coffee-run.mjs
275.2 KiB ../node_modules/.pnpm/posthog-js@1.396.5/node_modules/posthog-js/dist/rrweb.js
266.9 KiB ../node_modules/.pnpm/@posthog+icons@0.37.4_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
253.2 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/chart-hog.mjs
239.2 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/x-ray.mjs
224.5 KiB src/taxonomy/core-filter-definitions-by-group.json
211.6 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/money.mjs
211.3 KiB ../node_modules/.pnpm/posthog-js@1.396.5/node_modules/posthog-js/dist/module.js
185.9 KiB ../node_modules/.pnpm/@posthog+brand@0.3.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/hogpatch.mjs

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Dist folder size — 🟢 -365.98 MiB (-20.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1464.56 MiB · 🟢 -365.98 MiB (-20.0%)

@pauldambra pauldambra added the stamphog Request AI approval (no full review) label Jul 2, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Two independent bot reviewers (Greptile and Codex) both flagged plausible race conditions on the current head — one where the auto-load fetch's success can supersede/abort the still-processing initial processElements run via the shared breakpoint, and one where trigger classification could read a stale lastElementStatsRequest — and neither concern has been addressed or resolved in the diff. Combined with the author not being on the owning team for this behavioral logic change, this needs human (or team) review before merge.

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 2, 2026
@pauldambra pauldambra added the stamphog Request AI approval (no full review) label Jul 3, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is a behavioral change to toolbar clickmap loading logic owned by team-web-analytics, authored by someone outside that team, and two independent automated reviewers (Greptile, Codex) both flagged unresolved race-condition concerns in the new async auto-load/processElements interaction that the author hasn't addressed or replied to.

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 3, 2026

@pauldambra pauldambra left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note

🤖 Automated comment by QA Swarm — not written by a human

QA Swarm review complete. See inline comments.

One general finding with no line anchor — [qa-team/generalist-a] 🟡 MEDIUM: the auto-load dispatch, trigger derivation, loop guard, and match-cache invalidation ship with zero test changes in this PR; the mounted-logic tests that cover this behaviour exist only in the stacked child PR #68079. Tests belong with the behaviour they cover.

Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
Comment thread frontend/src/toolbar/elements/heatmapToolbarMenuLogic.ts
Comment thread frontend/src/toolbar/stats/HeatmapToolbarMenu.tsx
@pauldambra

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: qa-team (specialists + generalists), paul-reviewer, xp-reviewer, security-audit

Verdict: 💬 APPROVE WITH NITS (round 1 @ 7fc1c58)

The design is sound and deliberately reasoned (fetch-bigger-from-zero with the rationale documented, structural loop guard, order-independent match-cache identity, thorough invalidation). The substantive findings — a failed background auto-load silently disabling the heatmap, missing heatmapEnabled gating, no post-breakpoint gate re-check — are already fixed in the stacked child PR #68079, which this stack merges with; the remaining items are accepted follow-ups.

Key findings

  • 🟠 HIGH — background auto-load failure flips heatmapEnabled false, hiding the overlay mid-view (fixed in feat(toolbar): enable clickmaps by default in the heatmap menu #68079: the failure entry is removed and "Load more" doubles as retry)
  • 🟡 MEDIUM — auto-load gate lacks heatmapEnabled; loader never re-checks gates post-breakpoint; background 403 blanks the painted page (gates + re-check + preserved-results return all land in feat(toolbar): enable clickmaps by default in the heatmap menu #68079)
  • 🟡 MEDIUM — 50k response parse cost inside customer pages; watch the shipped result-count telemetry before trusting the budget
  • 🟡 MEDIUM — behaviour ships untested here; the mounted-logic tests live one PR up the stack
  • 🟢 LOW — trigger-by-sentinel-limit, copy truthfulness, and OnceAndOnlyOnce hygiene (shared chain identity helper, cache-reset clump)

Reviewer summaries

Reviewer Assessment
🔍 qa-team Design sound, loop guard structurally safe; fix the background-failure path before merge (done, one PR up).
👤 paul High trust, stamp with notes — likes the documented why and the observability; wants explicit trigger recording over magic-number inference.
📐 xp Clean, well-reasoned; loop guard is correct-by-construction; only OnceAndOnlyOnce nudges.
🛡 security-audit No findings — loop-safe by construction, no new sinks, team scoping unchanged.

Automated by QA Swarm — not a human review

Co-authored-by: paul <paul@pauls-MacBook-Air.local>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🎭 Playwright report · View test results →

⚠️ 1 flaky test:

  • Retention calculations, period, breakdown, and chart (chromium)

These issues are not necessarily caused by your changes.
Annoyed by this comment? Help fix flakies and failures and it'll disappear!

@DanielVisca
DanielVisca self-requested a review July 3, 2026 00:43
@pauldambra
pauldambra merged commit 32c34f7 into master Jul 3, 2026
198 checks passed
@pauldambra
pauldambra deleted the posthog-code/toolbar-clickmap-auto-load branch July 3, 2026 00:50
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 3, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-03 01:14 UTC Run
prod-us ✅ Deployed 2026-07-03 01:27 UTC Run
prod-eu ✅ Deployed 2026-07-03 01:33 UTC Run

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