Skip to content

feat(web-analytics): cache warmer demand selection to widen the lookback window - #73493

Merged
lricoy merged 6 commits into
masterfrom
lricoy/web-analytics-warmer-window
Jul 24, 2026
Merged

feat(web-analytics): cache warmer demand selection to widen the lookback window#73493
lricoy merged 6 commits into
masterfrom
lricoy/web-analytics-warmer-window

Conversation

@lricoy

@lricoy lricoy commented Jul 24, 2026

Copy link
Copy Markdown
Member

Problem

The web analytics cache warmer picks "hot" query shapes to keep precomputed by scanning system.query_log over a lookback window. That window was 2 days, which only catches teams that use web analytics daily. Demand analysis shows most teams return every few days to weekly: widening the window to 14 days nearly triples the teams with a warmable shape (≈8.3k → ≈24k), and team acquisition is still climbing at the 14-day edge.

The window couldn't just be bumped. The selection scans terabytes of log_comment fleet-wide and runs hourly, so a 14-day window would multiply that cost ~7× and exceed the default read cap (the query would fail, not just cost more).

Changes

  • Decouple selection from warming. The demand list is cached in Redis, keyed by the selection parameters, with a TTL (WEB_ANALYTICS_WARMING_SELECTION_TTL_SECONDS, default 6h). The hourly warmer replays the cached list; the expensive scan only re-runs when the cache expires — roughly 4×/day instead of 24×. Fails open on Redis errors (degrades to a fresh scan).
  • Widen the window. WEB_ANALYTICS_WARMING_DAYS default 214. The scan's read cap is lifted (max_bytes_to_read=0, bounded by max_execution_time) since 14 days exceeds the 15 TiB default.
  • Make the selection query cheaper — measured 2.2× faster (18.3s → 8.2s) on a 2-day window with identical results:
    • Stop computing normalizedQueryHash(query), which read the entire query SQL-text column (~550 GiB compressed fleet-wide) purely for a logging id. The id is now derived from the JSON already read out of log_comment. This also removes a per-row SQL-normalization CPU cost across ~600M rows — most of the speedup.
    • PREWHERE the cheap native columns (type, is_initial_query, query_kind = 'Select') so the big log_comment string is read only for surviving rows. is_initial_query alone prunes ~9/10 of the window; query_kind = 'Select' also excludes the warmer's own INSERT replays without a JSON parse.

Realizing the broader coverage also needs WEB_ANALYTICS_WARMING_MAX_SHAPES (currently 20,000) raised, since selection is LIMIT max_shapes ordered by demand and weekly shapes rank below the cut. That's a monitored runtime ramp, deliberately not part of this PR.

How did you test this code?

Automated (products/web_analytics/dags/tests/test_cache_warming.py, 35 passing):

  • test_second_run_reuses_cached_selection — guards the whole point of the cache: a second warming run must not re-run the terabyte scan. Fails if the cache read regresses.
  • test_redis_failure_falls_back_to_scan — Redis being down degrades to a fresh scan instead of breaking warming.
  • Updated test_op_reads_instance_settings to force a cache miss so it stays deterministic regardless of Redis state.
  • Existing test_selection_sql_survives_driver_percent_formatting still guards %-escaping (it caught a literal % in a new comment during development).

Also benchmarked the new vs old selection query directly against prod query_log (2-day window): 2.2× faster wall time, −24% peak memory, identical 2,000-shape result. No manual UI testing.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Built with Claude Code. Skills invoked: /writing-tests.

The decoupling was implemented as a cache-with-TTL inside the selection op rather than a second Dagster schedule, so it self-heals: a cache miss just recomputes, with no ordering dependency between a selection job and the warming job. The query-cost work was driven by measuring in prod — query_log column sizes (query is the largest column after log_comment) and prefilter selectivity (is_initial_query prunes ~90% of rows) — which is what pointed at dropping the query-column read plus a PREWHERE as the win.

@lricoy lricoy self-assigned this Jul 24, 2026
@lricoy
lricoy marked this pull request as ready for review July 24, 2026 13:01
@lricoy
lricoy requested a review from a team July 24, 2026 13:01
@pr-assigner-resolver-posthog

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-devex (.github/owners.yaml)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). The locator after each owner is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
products/web_analytics/dags/cache_warming.py:372-380
**Keep payload validation fail-open**

A decodable cache object with an unexpected structure or field type raises during the unchecked field access or timestamp arithmetic, so the selection op fails instead of falling back to a fresh scan and the hourly warming run is skipped.

Reviews (1): Last reviewed commit: "chore(ci): cover posthog/storage in dags..." | Re-trigger Greptile

Comment thread products/web_analytics/dags/cache_warming.py Outdated

@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: 91221a6a29

ℹ️ 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 products/web_analytics/dags/cache_warming.py
Comment thread products/web_analytics/dags/cache_warming.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🤖 CI report

Playwright — all passed

All tests passed.

View test results →

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

stamphog Bot commented Jul 24, 2026

Copy link
Copy Markdown

Note

🤖 stamphog reviewed 9060d6dd4339295d91a4b222c70665025f0e621e — verdict: REFUSED

Gates denied this PR (CI workflow path edit hit the infra/CI deny-list, and size/cross-team scope classified it T2-never), which routes to human review regardless of my own read; the change also alters fleet-wide ClickHouse scan cost/behavior and constance settings, which is risky territory best confirmed by a human even though prior agent review comments were addressed.

  • 👍 on the PR from greptile-apps[bot], hex-security-app[bot].
  • Gate verdict DENIED: matched infra_cicd deny-list (edit to .github/workflows/ci-dagster.yml) and classified T2-never on size/cross-team scope.
  • Change widens a fleet-wide ClickHouse query_log scan and lifts its byte-read cap — production cost/behavior change that warrants human sign-off even though Greptile/Codex review comments (alias shadowing, kill-switch bypass, fail-open caching) were addressed in commit 9060d6d with new tests.
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list matches: infra_cicd
size 145L, 3F substantive, 210L/4F incl. docs/generated/snapshots — within ceiling
tier classified as T2-never: T2-never (210L, 4F, cross-cutting, feat)
stamphog 2.0.0b3 .stamphog/policy.yml @ 0029527 · reviewed head 9060d6d

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 24, 2026
@lricoy
lricoy enabled auto-merge (squash) July 24, 2026 13:56
@lricoy
lricoy merged commit 62ae082 into master Jul 24, 2026
421 of 452 checks passed
@lricoy
lricoy deleted the lricoy/web-analytics-warmer-window branch July 24, 2026 14:07
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-24 14:42 UTC Run
prod-us ✅ Deployed 2026-07-24 14:58 UTC Run
prod-eu ✅ Deployed 2026-07-24 15:01 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.

3 participants