Repository navigation
feat(sessions): ship the sessions v3 schema with more ad click ids - #104566
Conversation
|
😎 Stack merged successfully - details. |
|
The PR is not yet safe to merge because long entry URLs can silently lose advertising attribution. Reviews (3) · Last reviewed commit: "fix(sessions): unblock migration replay ..." |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. ℹ️ Recent review info
Walkthrough
Priority: Merge Risk:
|
🤖 CI report
|
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
posthog/hogql/database/schema/test/test_sessions_v2.py:343 |
posthog/hogql/database/schema/test/test_sessions_v3.py:433 |
82 | 344 |
posthog/hogql/database/schema/test/test_sessions_v2.py:158 |
posthog/hogql/database/schema/test/test_sessions_v3.py:281 |
95 | 343 |
posthog/hogql/database/schema/test/test_sessions_v2.py:192 |
posthog/hogql/database/schema/test/test_sessions_v3.py:289 |
62 | 315 |
posthog/hogql/database/schema/test/test_sessions_v2.py:67 |
posthog/hogql/database/schema/test/test_sessions_v3.py:64 |
69 | 282 |
posthog/hogql/database/schema/test/test_sessions_v2.py:301 |
posthog/hogql/database/schema/test/test_sessions_v3.py:391 |
43 | 199 |
posthog/hogql/database/schema/test/test_sessions_v2.py:267 |
posthog/hogql/database/schema/test/test_sessions_v3.py:357 |
35 | 169 |
✅ Duplication (TypeScript) — clean
New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.
⚠️ Bundle size — 🔺 +379.1 KiB (+0.5%)
Uncompressed size of every built .js bundle, compared against the base branch.
Total: 78.86 MiB · 🔺 +379.1 KiB (+0.5%)
| File | Size | Δ vs base |
|---|---|---|
render-query/src/render-query/render-query.js |
25.04 MiB | 🔺 +165.3 KiB (+0.6%) |
exporter/_parent/products/signals/frontend/inbox/logics/reportGithubAccessLogic.js |
130.3 KiB | 🔺 +130.3 KiB (new) |
posthog-app/_parent/products/ai_observability/frontend/AIObservabilityTraceScene.js |
227.6 KiB | 🔺 +31.9 KiB (+16.3%) |
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js |
483.5 KiB | 🟢 -31.7 KiB (-6.1%) |
posthog-app/src/scenes/web-analytics/WebAnalyticsScene.js |
317.1 KiB | 🔺 +30.7 KiB (+10.7%) |
exporter/src/exporter/scenes/ExporterNotebookScene.js |
3.91 MiB | 🔺 +8.4 KiB (+0.2%) |
posthog-app/_parent/products/workflows/frontend/WorkflowsScene.js |
71.0 KiB | 🔺 +8.0 KiB (+12.8%) |
posthog-app/src/scenes/marketing-analytics/MarketingAnalyticsScene.js |
198.0 KiB | 🔺 +6.0 KiB (+3.1%) |
exporter/src/queries/schema.js |
1.27 MiB | 🔺 +3.9 KiB (+0.3%) |
posthog-app/src/queries/schema.js |
1.27 MiB | 🔺 +3.9 KiB (+0.3%) |
posthog-app/_parent/products/data_modeling/frontend/ModelsScene.js |
89.9 KiB | 🟢 -3.8 KiB (-4.0%) |
posthog-app/_parent/products/business_intelligence/frontend/BusinessIntelligenceScene.js |
179.9 KiB | 🔺 +3.7 KiB (+2.1%) |
posthog-app/_parent/products/workflows/frontend/Transactional/TransactionalScene.js |
36.4 KiB | 🔺 +3.5 KiB (+10.6%) |
posthog-app/_parent/products/business_intelligence/frontend/BIWorksheetsScene.js |
18.0 KiB | 🔺 +3.4 KiB (+23.5%) |
posthog-app/src/scenes/hog-functions/HogFunctionScene.js |
70.5 KiB | 🔺 +3.3 KiB (+4.9%) |
posthog-app/src/scenes/AuthenticatedShell.js |
274.6 KiB | 🔺 +2.3 KiB (+0.9%) |
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene.js |
92.3 KiB | 🔺 +2.3 KiB (+2.5%) |
exporter/_parent/products/workflows/frontend/Workflows/Workflow.js |
229.2 KiB | 🔺 +2.2 KiB (+1.0%) |
posthog-app/_parent/products/signals/frontend/inbox/logics/reportGithubAccessLogic.js |
2.2 KiB | 🔺 +2.2 KiB (new) |
posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene.js |
121.4 KiB | 🔺 +1.7 KiB (+1.4%) |
posthog-app/_parent/products/tracing/frontend/TracingScene.js |
163.6 KiB | 🔺 +1.6 KiB (+1.0%) |
posthog-app/_parent/products/endpoints/frontend/EndpointScene.js |
64.8 KiB | 🔺 +1.4 KiB (+2.3%) |
posthog-app/_parent/products/data_modeling/frontend/nodeDetail/NodeDetailScene.js |
46.4 KiB | 🔺 +1009 B (+2.2%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
⚠️ Eager graph — 1 over 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 |
1.03 MiB · 25 files | 🔺 +3.5 KiB (+0.3%) | █████████░ 90.3% of 1.14 MiB |
logged-out boot: index + App + bootApp (preloaded by every page, including /login)src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts |
3.00 MiB · 623 files | 🔺 +9.7 KiB (+0.3%) | ██████████ 99.0% of 3.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.49 MiB · 2,544 files | 🔺 +7.6 KiB (+0.1%) | ██████████ 100.0% of 7.50 MiB |
dashboard scenesrc/scenes/dashboard/Dashboard.tsx |
9.90 MiB · 3,708 files | 🔺 +25.9 KiB (+0.3%) | ██████████ 98.9% of 10.01 MiB |
today home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx |
7.67 MiB · 2,636 files | 🔺 +8.0 KiB (+0.1%) | ██████████ 99.1% of 7.74 MiB |
🟡 AI-first home pathsrc/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx |
7.92 MiB · 2,630 files | 🔺 +8.4 KiB (+0.1%) | ██████████ 100.2% of 7.91 MiB |
events scenesrc/scenes/activity/explore/EventsScene.tsx |
9.34 MiB · 3,484 files | 🔺 +22.6 KiB (+0.2%) | ██████████ 97.4% of 9.59 MiB |
web analytics scenesrc/scenes/web-analytics/WebAnalyticsScene.tsx |
10.30 MiB · 3,751 files | 🔺 +53.4 KiB (+0.5%) | ██████████ 95.1% of 10.83 MiB |
replay detail scenesrc/scenes/session-recordings/detail/SessionRecordingDetail.tsx |
8.35 MiB · 3,073 files | 🔺 +11.2 KiB (+0.1%) | █████████░ 88.1% of 9.47 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/wizard/frontend/runs/WizardRunDetailsDrawer.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayShell.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayContextPanelFloating.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodaySidePanel.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/TodayTopBar.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/layout/today/todayRailItems.tsx stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/queries/validators.js stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 packages/quill/packages/charts/ stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/VisualizationWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/components/tool/widgets/RecordingsWidget.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 products/posthog_ai/frontend/api/primitives.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/notebooks/Notebook/notebookLogic.ts stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/queries/validators.js stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/LiveMetricsDashboard/LiveWebAnalyticsMetrics.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 products/product_analytics/frontend/insights/trends/WorldMap/countryVectors.tsx stays out of src/scenes/web-analytics/WebAnalyticsScene.tsx
🟢 src/scenes/settings/SettingsMap.tsx stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
🟢 @jspm/core/nodelibs/browser/fs stays out of src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 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 |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.7 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 4.0 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.7 KiB | src/index.tsx |
| 1.7 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.7 KiB | src/lib/utils/bootTiming.ts |
| 1.2 KiB | src/RootErrorBoundary.tsx |
| 949 B | src/scenes/ChunkLoadErrorBoundary.tsx |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.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 |
| 102.6 KiB | src/products.tsx |
| 86.7 KiB | src/lib/api.ts |
| 67.9 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 41.9 KiB | ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js |
| 36.4 KiB | ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js |
| 27.0 KiB | ../node_modules/.pnpm/tailwind-merge@2.2.2/node_modules/tailwind-merge/dist/bundle-mjs.mjs |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 86.7 KiB | src/lib/api.ts |
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 144.1 KiB | ../packages/quill/packages/primitives/dist/index.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 86.7 KiB | src/lib/api.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 144.1 KiB | ../packages/quill/packages/primitives/dist/index.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 144.1 KiB | ../packages/quill/packages/primitives/dist/index.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Largest files eagerly shipped from src/scenes/web-analytics/WebAnalyticsScene.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 187.9 KiB | src/queries/validators.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 144.1 KiB | ../packages/quill/packages/primitives/dist/index.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
| Size | File |
|---|---|
| 323.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 316.1 KiB | ../node_modules/.pnpm/posthog-js@1.439.0_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js |
| 286.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 239.9 KiB | ../packages/quill/packages/quill/dist/index.js |
| 217.5 KiB | ../node_modules/.pnpm/@posthog+icons@0.39.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 153.8 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.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 |
| 102.6 KiB | src/products.tsx |
| 94.5 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
| 92.5 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.33 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.33 MiB · 18 files | 🔺 +6.0 KiB (+0.3%) | ████░░░░░░ 40.8% of 5.72 MiB |
| Deferred (lazy) | 2.11 MiB · 40 files | 🔺 +10 B (+0.0%) | n/a — loads on demand |
Loader dist/toolbar.js |
1.2 KiB | no change | █░░░░░░░░░ 6.0% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 940.2 KiB | dist/toolbar/toolbar-app-XCB2SKCB.css |
| 697.2 KiB | dist/toolbar/chunk-chunk-GLH7EOTZ.js |
| 261.2 KiB | dist/toolbar/chunk-chunk-IIW2OHDL.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 130.4 KiB | dist/toolbar/chunk-chunk-WCFJXEB6.js |
| 75.7 KiB | dist/toolbar/toolbar-app-X37MDLC2.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-DXUHSQKN.js |
| 21.7 KiB | dist/toolbar/chunk-chunk-2SQRISWU.js |
| 6.8 KiB | dist/toolbar/chunk-chunk-DV7IWQNF.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +3.82 MiB (+0.4%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 983.42 MiB · 🔺 +3.82 MiB (+0.4%)
⚠️ Backend snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Query snapshots: Backend query snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Query snapshots have been automatically updated to match current output
- These changes reflect modifications to database queries or schema
Next steps:
- Review the query changes to ensure they're intentional
- If unexpected, investigate what caused the query to change
⚠️ Backend coverage — 96.0% of changed backend lines covered — 2 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 96.0% (51 / 53)
| File | Patch | Uncovered changed lines |
|---|---|---|
posthog/conftest.py |
50.0% | 517 |
posthog/raw_sessions_v3_ad_ids.py |
85.7% | 58 |
🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 574542922937897 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
platform_features |
██░░░░░░░░░░░░░░░░░░ 12.1% |
7 / 58 |
warehouse_sources_queue |
██████░░░░░░░░░░░░░░ 29.1% |
92 / 316 |
demo |
████████████░░░░░░░░ 57.8% |
1,545 / 2,673 |
data_tools |
████████████░░░░░░░░ 61.2% |
90 / 147 |
aeo |
██████████████░░░░░░ 70.5% |
467 / 662 |
ai_gateway |
███████████████░░░░░ 75.0% |
9 / 12 |
batch_exports |
████████████████░░░░ 81.0% |
21,250 / 26,225 |
apm |
█████████████████░░░ 84.1% |
1,306 / 1,553 |
ml_inference |
█████████████████░░░ 86.7% |
450 / 519 |
cdp |
██████████████████░░ 88.2% |
4,527 / 5,133 |
signals |
██████████████████░░ 88.7% |
51,721 / 58,335 |
product_tours |
██████████████████░░ 89.3% |
1,331 / 1,491 |
dashboards |
██████████████████░░ 89.8% |
7,234 / 8,056 |
data_warehouse |
██████████████████░░ 89.9% |
13,738 / 15,285 |
cohorts |
██████████████████░░ 90.2% |
8,238 / 9,138 |
notebooks |
██████████████████░░ 90.2% |
15,287 / 16,945 |
mcp_analytics |
██████████████████░░ 90.6% |
4,696 / 5,181 |
streamlit_apps |
██████████████████░░ 90.7% |
2,625 / 2,895 |
managed_warehouse |
██████████████████░░ 90.9% |
10,215 / 11,234 |
tasks |
██████████████████░░ 91.1% |
73,305 / 80,461 |
engineering_analytics |
██████████████████░░ 91.3% |
9,999 / 10,946 |
data_modeling |
██████████████████░░ 91.4% |
10,490 / 11,479 |
business_knowledge |
██████████████████░░ 91.6% |
6,899 / 7,528 |
exports |
██████████████████░░ 91.8% |
9,685 / 10,555 |
ai_training |
██████████████████░░ 92.2% |
356 / 386 |
conversations |
███████████████████░ 92.5% |
28,705 / 31,025 |
early_access_features |
███████████████████░ 92.6% |
1,341 / 1,448 |
managed_migrations |
███████████████████░ 92.7% |
1,581 / 1,705 |
visual_review |
███████████████████░ 92.8% |
9,244 / 9,966 |
canvas |
███████████████████░ 92.8% |
6,877 / 7,409 |
error_tracking |
███████████████████░ 93.0% |
15,699 / 16,882 |
approvals |
███████████████████░ 93.0% |
3,919 / 4,214 |
mcp_registry |
███████████████████░ 93.1% |
1,670 / 1,794 |
notifications |
███████████████████░ 93.2% |
1,145 / 1,229 |
slack_app |
███████████████████░ 93.2% |
13,528 / 14,519 |
surveys |
███████████████████░ 93.3% |
6,571 / 7,040 |
stamphog |
███████████████████░ 93.4% |
7,716 / 8,262 |
context_layer |
███████████████████░ 93.8% |
3,373 / 3,595 |
web_analytics |
███████████████████░ 93.9% |
21,653 / 23,051 |
ai_observability |
███████████████████░ 94.0% |
20,565 / 21,869 |
alerts |
███████████████████░ 94.1% |
8,553 / 9,094 |
billing_alerts |
███████████████████░ 94.1% |
2,094 / 2,226 |
mcp_store |
███████████████████░ 94.4% |
8,940 / 9,472 |
wizard |
███████████████████░ 94.4% |
5,791 / 6,134 |
reminders |
███████████████████░ 94.8% |
760 / 802 |
workflows |
███████████████████░ 94.8% |
13,379 / 14,111 |
review_hog |
███████████████████░ 94.9% |
11,490 / 12,109 |
customer_analytics |
███████████████████░ 95.1% |
24,457 / 25,717 |
annotations |
███████████████████░ 95.1% |
817 / 859 |
endpoints |
███████████████████░ 95.1% |
9,211 / 9,681 |
marketing_analytics |
███████████████████░ 95.2% |
18,893 / 19,846 |
legal_documents |
███████████████████░ 95.2% |
2,311 / 2,427 |
tracing |
███████████████████░ 95.3% |
3,330 / 3,495 |
logs |
███████████████████░ 95.4% |
15,235 / 15,967 |
growth |
███████████████████░ 95.4% |
9,812 / 10,282 |
actions |
███████████████████░ 95.5% |
756 / 792 |
posthog_ai |
███████████████████░ 95.5% |
2,438 / 2,553 |
messaging |
███████████████████░ 95.9% |
3,766 / 3,927 |
skills |
███████████████████░ 95.9% |
6,649 / 6,932 |
replay_vision |
███████████████████░ 95.9% |
26,697 / 27,825 |
autoresearch |
███████████████████░ 96.0% |
7,716 / 8,037 |
product_analytics |
███████████████████░ 96.2% |
28,496 / 29,618 |
access_control |
███████████████████░ 96.2% |
6,845 / 7,113 |
revenue_analytics |
███████████████████░ 96.4% |
1,876 / 1,946 |
user_interviews |
███████████████████░ 96.5% |
2,859 / 2,963 |
feature_flags |
███████████████████░ 96.5% |
24,761 / 25,661 |
experiments |
███████████████████░ 96.7% |
33,943 / 35,108 |
warehouse_sources |
███████████████████░ 97.2% |
442,080 / 454,800 |
data_quality |
████████████████████ 97.7% |
7,592 / 7,774 |
links |
████████████████████ 97.9% |
234 / 239 |
security |
████████████████████ 98.1% |
1,258 / 1,283 |
metrics |
████████████████████ 98.1% |
4,085 / 4,166 |
analytics_platform |
████████████████████ 98.3% |
2,778 / 2,827 |
data_catalog |
████████████████████ 98.3% |
3,932 / 3,999 |
pulse |
████████████████████ 98.5% |
2,043 / 2,075 |
live_debugger |
████████████████████ 99.2% |
626 / 631 |
field_notes |
████████████████████ 99.4% |
172 / 173 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
ℹ️ ClickHouse migration SQL — 2 migration(s)
ClickHouse migration SQL per cloud environment
- unset
- all
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE sharded_raw_sessions_v3 ADD INDEX IF NOT EXISTS flag_key_values_bloom_filter flag_key_values TYPE bloom_filter() GRANULARITY 1
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS emails SimpleAggregateFunction(groupUniqArrayArray(10), Array(String)) AFTER hosts
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS emails SimpleAggregateFunction(groupUniqArrayArray(10), Array(String)) AFTER hosts
ALTER TABLE raw_sessions_v3 ADD COLUMN IF NOT EXISTS emails SimpleAggregateFunction(groupUniqArrayArray(10), Array(String)) AFTER hosts
ALTER TABLE sharded_raw_sessions_v3 ADD INDEX IF NOT EXISTS hosts_bloom_filter hosts TYPE bloom_filter() GRANULARITY 1
ALTER TABLE sharded_raw_sessions_v3 ADD INDEX IF NOT EXISTS emails_bloom_filter emails TYPE bloom_filter() GRANULARITY 1
ALTER TABLE sharded_raw_sessions_v3 DROP INDEX IF EXISTS flag_keys_bloom_filter
ALTER TABLE sharded_raw_sessions_v3 DROP INDEX IF EXISTS flag_key_values_bloom_filter
ALTER TABLE sharded_raw_sessions_v3 DROP COLUMN IF EXISTS flag_keys
ALTER TABLE writable_raw_sessions_v3 DROP COLUMN IF EXISTS flag_keys
ALTER TABLE raw_sessions_v3 DROP COLUMN IF EXISTS flag_keys
CREATE MATERIALIZED VIEW IF NOT EXISTS raw_sessions_v3_mv TO posthog_test.writable_raw_sessions_v3 AS WITH JSONExtract(properties, 'Tuple( `$current_url` Nullable(String), `$external_click_url` Nullable(String), `$browser` Nullable(String), `$browser_version` Nullable(String), `$os` Nullable(String), `$os_version` Nullable(String), `$device_type` Nullable(String), `$viewport_width` Nullable(Int64), `$viewport_height` Nullable(Int64), `$geoip_country_code` Nullable(String), `$geoip_subdivision_1_code` Nullable(String), `$geoip_subdivision_1_name` Nullable(String), `$geoip_city_name` Nullable(String), `$geoip_time_zone` Nullable(String), `$referring_domain` Nullable(String), `utm_source` Nullable(String), `utm_campaign` Nullable(String), `utm_medium` Nullable(String), `utm_term` Nullable(String), `utm_content` Nullable(String), `gclid` Nullable(String), `gad_source` Nullable(String), `fbclid` Nullable(String), `$host` Nullable(String), `gclsrc` Nullable(String), `dclid` Nullable(String), `gbraid` Nullable(String), `wbraid` Nullable(String), `gad_campaignid` Nullable(String), `srsltid` Nullable(String), `msclkid` Nullable(String), `twclid` Nullable(String), `li_fat_id` Nullable(String), `mc_cid` Nullable(String), `igshid` Nullable(String), `ttclid` Nullable(String), `rdt_cid` Nullable(String), `epik` Nullable(String), `qclid` Nullable(String), `sccid` Nullable(String), `_kx` Nullable(String), `irclid` Nullable(String), `irclickid` Nullable(String), `utm_id` Nullable(String), `utm_source_platform` Nullable(String), `utm_creative_format` Nullable(String), `utm_marketing_tactic` Nullable(String), `yclid` Nullable(String), `tblci` Nullable(String), `dicbo` Nullable(String), `ndclid` Nullable(String), `cjevent` Nullable(String), `awc` Nullable(String), `syclid` Nullable(String), `_bhlid` Nullable(String), `rtid` Nullable(String), `oppref` Nullable(String), `bfcid` Nullable(String) )') as p, JSONExtractString(person_properties, 'email') as _person_email, tupleElement(p, '$current_url') as _current_url, if(event = '$pageview' OR event = '$screen', left(coalesce(_current_url, ''), 16384), '') as _attribution_url, tupleElement(p, '$external_click_url') as _external_click_url, tupleElement(p, '$browser') as _browser, tupleElement(p, '$browser_version') as _browser_version, tupleElement(p, '$os') as _os, tupleElement(p, '$os_version') as _os_version, tupleElement(p, '$device_type') as _device_type, tupleElement(p, '$viewport_width') as _viewport_width, tupleElement(p, '$viewport_height') as _viewport_height, tupleElement(p, '$geoip_country_code') as _geoip_country_code, tupleElement(p, '$geoip_subdivision_1_code') as _geoip_subdivision_1_code, tupleElement(p, '$geoip_subdivision_1_name') as _geoip_subdivision_1_name, tupleElement(p, '$geoip_city_name') as _geoip_subdivision_city_name, tupleElement(p, '$geoip_time_zone') as _geoip_time_zone, tupleElement(p, '$referring_domain') as _referring_domain, tupleElement(p, 'utm_source') as _utm_source, tupleElement(p, 'utm_campaign') as _utm_campaign, tupleElement(p, 'utm_medium') as _utm_medium, tupleElement(p, 'utm_term') as _utm_term, tupleElement(p, 'utm_content') as _utm_content, tupleElement(p, 'gclid') as _gclid, tupleElement(p, 'gad_source') as _gad_source, tupleElement(p, 'fbclid') as _fbclid, coalesce(nullIf(tupleElement(p, 'gclsrc'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'gclsrc')), 2048)), '')) as gclsrc, coalesce(nullIf(tupleElement(p, 'dclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'dclid')), 2048)), '')) as dclid, coalesce(nullIf(tupleElement(p, 'gbraid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'gbraid')), 2048)), '')) as gbraid, coalesce(nullIf(tupleElement(p, 'wbraid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'wbraid')), 2048)), '')) as wbraid, coalesce(nullIf(tupleElement(p, 'gad_campaignid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'gad_campaignid')), 2048)), '')) as gad_campaignid, coalesce(nullIf(tupleElement(p, 'srsltid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'srsltid')), 2048)), '')) as srsltid, coalesce(nullIf(tupleElement(p, 'msclkid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'msclkid')), 2048)), '')) as msclkid, coalesce(nullIf(tupleElement(p, 'twclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'twclid')), 2048)), '')) as twclid, coalesce(nullIf(tupleElement(p, 'li_fat_id'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'li_fat_id')), 2048)), '')) as li_fat_id, coalesce(nullIf(tupleElement(p, 'mc_cid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'mc_cid')), 2048)), '')) as mc_cid, coalesce(nullIf(tupleElement(p, 'igshid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'igshid')), 2048)), '')) as igshid, coalesce(nullIf(tupleElement(p, 'ttclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'ttclid')), 2048)), '')) as ttclid, coalesce(nullIf(tupleElement(p, 'rdt_cid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'rdt_cid')), 2048)), '')) as rdt_cid, coalesce(nullIf(tupleElement(p, 'epik'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'epik')), 2048)), '')) as epik, coalesce(nullIf(tupleElement(p, 'qclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'qclid')), 2048)), '')) as qclid, coalesce(nullIf(tupleElement(p, 'sccid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'sccid')), 2048)), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'ScCid')), 2048)), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'SsCid')), 2048)), '')) as sccid, coalesce(nullIf(tupleElement(p, '_kx'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, '_kx')), 2048)), '')) as _kx, coalesce(nullIf(tupleElement(p, 'irclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'irclid')), 2048)), '')) as irclid, coalesce(nullIf(tupleElement(p, 'irclickid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'irclickid')), 2048)), '')) as irclickid, coalesce(nullIf(tupleElement(p, 'utm_id'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'utm_id')), 2048)), '')) as utm_id, coalesce(nullIf(tupleElement(p, 'utm_source_platform'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'utm_source_platform')), 2048)), '')) as utm_source_platform, coalesce(nullIf(tupleElement(p, 'utm_creative_format'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'utm_creative_format')), 2048)), '')) as utm_creative_format, coalesce(nullIf(tupleElement(p, 'utm_marketing_tactic'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'utm_marketing_tactic')), 2048)), '')) as utm_marketing_tactic, coalesce(nullIf(tupleElement(p, 'yclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'yclid')), 2048)), '')) as yclid, coalesce(nullIf(tupleElement(p, 'tblci'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'tblci')), 2048)), '')) as tblci, coalesce(nullIf(tupleElement(p, 'dicbo'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'dicbo')), 2048)), '')) as dicbo, coalesce(nullIf(tupleElement(p, 'ndclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'ndclid')), 2048)), '')) as ndclid, coalesce(nullIf(tupleElement(p, 'cjevent'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'cjevent')), 2048)), '')) as cjevent, coalesce(nullIf(tupleElement(p, 'awc'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'awc')), 2048)), '')) as awc, coalesce(nullIf(tupleElement(p, 'syclid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'syclid')), 2048)), '')) as syclid, coalesce(nullIf(tupleElement(p, '_bhlid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, '_bhlid')), 2048)), '')) as _bhlid, coalesce(nullIf(tupleElement(p, 'rtid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'rtid')), 2048)), '')) as rtid, coalesce(nullIf(tupleElement(p, 'oppref'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'oppref')), 2048)), '')) as oppref, coalesce(nullIf(tupleElement(p, 'bfcid'), ''), nullIf(toValidUTF8(left(decodeURLFormComponent(extractURLParameter(_attribution_url, 'bfcid')), 2048)), '')) as bfcid, CAST(arrayFilter(x -> x IS NOT NULL, [ if(gclsrc IS NOT NULL, concat('gclsrc=', gclsrc), NULL), if(dclid IS NOT NULL, concat('dclid=', dclid), NULL), if(gbraid IS NOT NULL, concat('gbraid=', gbraid), NULL), if(wbraid IS NOT NULL, concat('wbraid=', wbraid), NULL), if(gad_campaignid IS NOT NULL, concat('gad_campaignid=', gad_campaignid), NULL), if(srsltid IS NOT NULL, concat('srsltid=', srsltid), NULL), if(msclkid IS NOT NULL, concat('msclkid=', msclkid), NULL), if(twclid IS NOT NULL, concat('twclid=', twclid), NULL), if(li_fat_id IS NOT NULL, concat('li_fat_id=', li_fat_id), NULL), if(mc_cid IS NOT NULL, concat('mc_cid=', mc_cid), NULL), if(igshid IS NOT NULL, concat('igshid=', igshid), NULL), if(ttclid IS NOT NULL, concat('ttclid=', ttclid), NULL), if(rdt_cid IS NOT NULL, concat('rdt_cid=', rdt_cid), NULL), if(epik IS NOT NULL, concat('epik=', epik), NULL), if(qclid IS NOT NULL, concat('qclid=', qclid), NULL), if(sccid IS NOT NULL, concat('sccid=', sccid), NULL), if(_kx IS NOT NULL, concat('_kx=', _kx), NULL), if(irclid IS NOT NULL, concat('irclid=', irclid), NULL), if(irclickid IS NOT NULL, concat('irclickid=', irclickid), NULL), if(utm_id IS NOT NULL, concat('utm_id=', utm_id), NULL), if(utm_source_platform IS NOT NULL, concat('utm_source_platform=', utm_source_platform), NULL), if(utm_creative_format IS NOT NULL, concat('utm_creative_format=', utm_creative_format), NULL), if(utm_marketing_tactic IS NOT NULL, concat('utm_marketing_tactic=', utm_marketing_tactic), NULL), if(yclid IS NOT NULL, concat('yclid=', yclid), NULL), if(tblci IS NOT NULL, concat('tblci=', tblci), NULL), if(dicbo IS NOT NULL, concat('dicbo=', dicbo), NULL), if(ndclid IS NOT NULL, concat('ndclid=', ndclid), NULL), if(cjevent IS NOT NULL, concat('cjevent=', cjevent), NULL), if(awc IS NOT NULL, concat('awc=', awc), NULL), if(syclid IS NOT NULL, concat('syclid=', syclid), NULL), if(_bhlid IS NOT NULL, concat('_bhlid=', _bhlid), NULL), if(rtid IS NOT NULL, concat('rtid=', rtid), NULL), if(oppref IS NOT NULL, concat('oppref=', oppref), NULL), if(bfcid IS NOT NULL, concat('bfcid=', bfcid), NULL) ]) AS Array(String)) as ad_ids_set, tupleElement(p, '$host') as _host, -- attribution properties from non-pageview/screen events should be deprioritized, so make the timestamp +/- 1 year so they sort last if (event = '$pageview' OR event = '$screen', timestamp, timestamp + toIntervalYear(1)) as pageview_prio_timestamp_min, if (event = '$pageview' OR event = '$screen', timestamp, timestamp - toIntervalYear(1)) as pageview_prio_timestamp_max SELECT team_id, `$session_id_uuid` AS session_id_v7, initializeAggregation('argMaxState', source_table.distinct_id, timestamp) as distinct_id, initializeAggregation('groupUniqArrayState', source_table.distinct_id) as distinct_ids, timestamp AS min_timestamp, timestamp AS max_timestamp, inserted_at AS max_inserted_at, -- urls - only update if the event is a pageview or screen if(_current_url IS NOT NULL AND (event = '$pageview' OR event = '$screen'), [_current_url], []) AS urls, initializeAggregation('argMinState', _current_url, pageview_prio_timestamp_min) as entry_url, initializeAggregation('argMaxState', _current_url, pageview_prio_timestamp_max) as end_url, initializeAggregation('argMaxState', _external_click_url, timestamp) as last_external_click_url, -- device initializeAggregation('argMinState', _browser, timestamp) as browser, initializeAggregation('argMinState', _browser_version, timestamp) as browser_version, initializeAggregation('argMinState', _os, timestamp) as os, initializeAggregation('argMinState', _os_version, timestamp) as os_version, initializeAggregation('argMinState', _device_type, timestamp) as device_type, initializeAggregation('argMinState', _viewport_width, timestamp) as viewport_width, initializeAggregation('argMinState', _viewport_height, timestamp) as viewport_height, -- geo ip initializeAggregation('argMinState', _geoip_country_code, timestamp) as geoip_country_code, initializeAggregation('argMinState', _geoip_subdivision_1_code, timestamp) as geoip_subdivision_1_code, initializeAggregation('argMinState', _geoip_subdivision_1_name, timestamp) as geoip_subdivision_1_name, initializeAggregation('argMinState', _geoip_subdivision_city_name, timestamp) as geoip_subdivision_city_name, initializeAggregation('argMinState', _geoip_time_zone, timestamp) as geoip_time_zone, -- attribution initializeAggregation('argMinState', _referring_domain, pageview_prio_timestamp_min) as entry_referring_domain, initializeAggregation('argMinState', _utm_source, pageview_prio_timestamp_min) as entry_utm_source, initializeAggregation('argMinState', _utm_campaign, pageview_prio_timestamp_min) as entry_utm_campaign, initializeAggregation('argMinState', _utm_medium, pageview_prio_timestamp_min) as entry_utm_medium, initializeAggregation('argMinState', _utm_term, pageview_prio_timestamp_min) as entry_utm_term, initializeAggregation('argMinState', _utm_content, pageview_prio_timestamp_min) as entry_utm_content, initializeAggregation('argMinState', _gclid, pageview_prio_timestamp_min) as entry_gclid, initializeAggregation('argMinState', _gad_source, pageview_prio_timestamp_min) as entry_gad_source, initializeAggregation('argMinState', _fbclid, pageview_prio_timestamp_min) as entry_fbclid, -- has gclid/fbclid for reading fewer bytes when calculating channel type -- clients send "gclid": "" and that is not a click id, so an empty value must not count as present initializeAggregation('argMinState', nullIf(_gclid, '') IS NOT NULL, pageview_prio_timestamp_min) as entry_has_gclid, initializeAggregation('argMinState', nullIf(_fbclid, '') IS NOT NULL, pageview_prio_timestamp_min) as entry_has_fbclid, -- other ad ids initializeAggregation('argMinState', ad_ids_set, pageview_prio_timestamp_min) as entry_ad_ids_set, -- channel type initializeAggregation('argMinState', tuple(_utm_source, _utm_medium, _utm_campaign, _referring_domain, nullIf(_gclid, '') IS NOT NULL, nullIf(_fbclid, '') IS NOT NULL, _gad_source), pageview_prio_timestamp_min) as entry_channel_type_properties, -- counts initializeAggregation('uniqState', if(event='$pageview', uuid, NULL)) as pageview_uniq, initializeAggregation('uniqState', if(event='$autocapture', uuid, NULL)) as autocapture_uniq, initializeAggregation('uniqState', if(event='$screen', uuid, NULL)) as screen_uniq, -- perf initializeAggregation('uniqUpToState(1)', if(event='$pageview' OR event='$screen', uuid, NULL)) as page_screen_uniq_up_to, event = '$autocapture' as has_autocapture, -- flags arrayMap((k, v) -> concat(k, '=', v), mapKeys(properties_group_feature_flags), mapValues(properties_group_feature_flags)) as flag_key_values, -- event names [event] as event_names, -- hosts if(_host IS NOT NULL AND _host != '', [_host], []) AS hosts, -- emails if(_person_email IS NOT NULL AND _person_email != '', [_person_email], []) AS emails, false as has_replay_events FROM posthog_test.sharded_events AS source_table WHERE bitAnd(bitShiftRight(toUInt128(accurateCastOrNull(`$session_id`, 'UUID')), 76), 0xF) == 7 -- has a session id and is valid uuidv7 -- Clients can send a UUIDv7 session id with a garbage timestamp (1970, year 6339). Each one puts its session in its -- own monthly partition, and its events stretch the session duration across months. Keep only events within 31 days -- of the session id's timestamp. The bound is symmetric because the client mints the session id with its own clock -- while the server corrects event timestamps, so a client clock that runs days ahead is still a real session. AND fromUnixTimestamp64Milli(toUInt64(bitShiftRight(`$session_id_uuid`, 80))) BETWEEN timestamp - INTERVAL 31 DAY AND timestamp + INTERVAL 31 DAY AND TRUE
CREATE MATERIALIZED VIEW IF NOT EXISTS raw_sessions_v3_recordings_mv TO posthog_test.writable_raw_sessions_v3 AS WITH min_first_timestamp as timestamp, CAST(fromUnixTimestamp64Milli(9223372036854775), 'DateTime64(6)') as max_ts_64, -- max positive Int64 / 1000 CAST(fromUnixTimestamp64Milli(-9223372036854775), 'DateTime64(6)') as min_ts_64, -- max negative Int64 / 1000 CAST(NULL, 'Nullable(String)') as null_s, CAST(NULL, 'Nullable(Int64)') as null_i64, CAST(NULL, 'Nullable(UUID)') as null_uuid SELECT team_id, toUInt128(accurateCast(session_id, 'UUID')) AS session_id_v7, fromUnixTimestamp64Milli(toUInt64(bitShiftRight(session_id_v7, 80))) AS session_timestamp, initializeAggregation('argMaxState', source_table.distinct_id, min_ts_64) as distinct_id, initializeAggregation('groupUniqArrayState', source_table.distinct_id) as distinct_ids, timestamp AS min_timestamp, timestamp AS max_timestamp, fromUnixTimestamp(0) AS max_inserted_at, -- urls - only update if the event is a pageview or screen CAST([], 'Array(String)') AS urls, initializeAggregation('argMinState', null_s, max_ts_64) as entry_url, initializeAggregation('argMaxState', null_s, min_ts_64) as end_url, initializeAggregation('argMaxState', null_s, min_ts_64) as last_external_click_url, -- device initializeAggregation('argMinState', null_s, max_ts_64) as browser, initializeAggregation('argMinState', null_s, max_ts_64) as browser_version, initializeAggregation('argMinState', null_s, max_ts_64) as os, initializeAggregation('argMinState', null_s, max_ts_64) as os_version, initializeAggregation('argMinState', null_s, max_ts_64) as device_type, initializeAggregation('argMinState', null_i64, max_ts_64) as viewport_width, initializeAggregation('argMinState', null_i64, max_ts_64) as viewport_height, -- geo ip initializeAggregation('argMinState', null_s, max_ts_64) as geoip_country_code, initializeAggregation('argMinState', null_s, max_ts_64) as geoip_subdivision_1_code, initializeAggregation('argMinState', null_s, max_ts_64) as geoip_subdivision_1_name, initializeAggregation('argMinState', null_s, max_ts_64) as geoip_subdivision_city_name, initializeAggregation('argMinState', null_s, max_ts_64) as geoip_time_zone, -- attribution initializeAggregation('argMinState', null_s, max_ts_64) as entry_referring_domain, initializeAggregation('argMinState', null_s, max_ts_64) as entry_utm_source, initializeAggregation('argMinState', null_s, max_ts_64) as entry_utm_campaign, initializeAggregation('argMinState', null_s, max_ts_64) as entry_utm_medium, initializeAggregation('argMinState', null_s, max_ts_64) as entry_utm_term, initializeAggregation('argMinState', null_s, max_ts_64) as entry_utm_content, initializeAggregation('argMinState', null_s, max_ts_64) as entry_gclid, initializeAggregation('argMinState', null_s, max_ts_64) as entry_gad_source, initializeAggregation('argMinState', null_s, max_ts_64) as entry_fbclid, -- has gclid/fbclid for reading fewer bytes when calculating channel type initializeAggregation('argMinState', false, max_ts_64) as entry_has_gclid, initializeAggregation('argMinState', false, max_ts_64) as entry_has_fbclid, -- other ad ids initializeAggregation('argMinState', CAST([], 'Array(String)'), max_ts_64) as entry_ad_ids_set, -- channel type initializeAggregation('argMinState', tuple(null_s, null_s, null_s, null_s, false, false, null_s), max_ts_64) as entry_channel_type_properties, -- counts initializeAggregation('uniqState', null_uuid) as pageview_uniq, initializeAggregation('uniqState', null_uuid) as autocapture_uniq, initializeAggregation('uniqState', null_uuid) as screen_uniq, -- perf initializeAggregation('uniqUpToState(1)', null_uuid) as page_screen_uniq_up_to, false as has_autocapture, -- flags CAST([], 'Array(String)') as flag_key_values, -- event names CAST([], 'Array(String)') as event_names, -- hosts CAST([], 'Array(String)') as hosts, -- emails CAST([], 'Array(String)') as emails, -- replay true as has_replay_events FROM posthog_test.sharded_session_replay_events AS source_table WHERE bitAnd(bitShiftRight(toUInt128(accurateCastOrNull(session_id, 'UUID')), 76), 0xF) == 7 -- has a session id and is valid uuidv7 -- same session timestamp guard as the events SELECT. accurateCastOrNull because accurateCast throws on a malformed -- session_id, and ClickHouse can evaluate this condition before the uuidv7 check above filters those rows out AND fromUnixTimestamp64Milli(toUInt64(bitShiftRight(toUInt128(accurateCastOrNull(session_id, 'UUID')), 80))) BETWEEN min_first_timestamp - INTERVAL 31 DAY AND min_first_timestamp + INTERVAL 31 DAY AND TRUE
- all
- US, EU, DEV
0364_sessions_v3_more_ad_ids— operations list is empty under this environment- data
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE raw_sessions_v3 ADD COLUMN IF NOT EXISTS flag_key_values SimpleAggregateFunction(groupUniqArrayArray(10000), Array(String)) AFTER has_autocapture
ALTER TABLE sharded_raw_sessions_v3 ADD INDEX IF NOT EXISTS flag_key_values_bloom_filter flag_key_values TYPE bloom_filter() GRANULARITY 1
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE raw_sessions_v3 ADD COLUMN IF NOT EXISTS hosts SimpleAggregateFunction(groupUniqArrayArray(100), Array(String)) AFTER event_names
ALTER TABLE sharded_raw_sessions_v3 ADD COLUMN IF NOT EXISTS emails SimpleAggregateFunction(groupUniqArrayArray(10), Array(String)) AFTER hosts
ALTER TABLE writable_raw_sessions_v3 ADD COLUMN IF NOT EXISTS emails SimpleAggregateFunction(groupUniqArrayArray(10), Array(String)) AFTER hosts
ALTER TABLE raw_sessions_v3 ADD COL
…truncated. See the full SQL in the workflow logs.
✅ Hogbox preview — ready, open the preview
▶ Open the preview
| 🔑 Login | test@posthog.com / 12345678 (demo data) |
| 🧩 Running | this PR's backend on the PostHog :master base (frontend unchanged by this PR) |
| 🔗 Link | stable across rebuilds: a re-push swaps the box underneath, the URL stays |
| 🔒 Access | tailnet only (PostHog VPN) |
| 🛠️ Admin | inspect & debug state in hogland |
| 💤 Idle | sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen |
commit 57e49e1 · box box-33be5b008eac · ready in 1282s (push → usable) · build log · rebuilds on every push, torn down on close
|
Comments Outside DiffThese findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.
|
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
dba2e5c to
7614d6d
Compare
CI status: rebased and green except one pre-existing master failureI rebased this branch onto Failing check: Why it is not this PR's:
What the underlying bug looks like: the failures come from the autouse
I've deliberately not fixed the signals test isolation here, since it belongs to another product and this PR carries a ClickHouse migration, which the repo guidance wants kept narrow. Happy to spin that fix out separately if you'd like — just say the word. 🦉 via talyn.dev |
CI is green — the signals shard failure was a shared test-fixture leakThe It is not a signals bug. That is why the shard passed or failed depending on how Fixed in da97f7e by setting the data up on the instance instead, which is what the Verified locally on the full
The 5 remaining local failures are Also merged Status now: every check passes (173 success, 98 skipped, 0 failing) and the branch is mergeable and not behind. One test, Two things for a human:
🦉 via talyn.dev |
|
Re the outside-diff note from @greptile-apps ("Migration includes application changes" — asking that the ClickHouse migration ship on its own, ahead of the HogQL and ad-ID changes): not splitting this one. The migration recreates The narrow-migration guidance is about not bundling unrelated work with a migration, and there is none here — the diff is the new columns, the code that generates them, the code that reads them, and their tests. 🦉 via talyn.dev |
|
That context is fair, and I agree this is a single coordinated schema change rather than unrelated application work. The migration’s DDL, raw-session model/catalog, and HogQL reader must describe the same |
0f26fe9 to
4fcb56c
Compare
Conflict cleared — ClickHouse migration renumbered
|
There was a problem hiding this comment.
Not approved yet — waiting on the conditions below.
Re-add the stamphog label to request another review once you have addressed this.
stamphog declined to auto-review this pull request because three gates failed. The deny-list gate matched the migrations path, because the PR adds posthog/clickhouse/migrations/0360_sessions_v3_more_ad_ids.py and edits 0325_sessions_v3_flag_key_values.py. The size gate found 2941 substantive lines against an 800-line ceiling (4453 lines across 39 files including docs, generated files and snapshots). The tier gate classified it as T2-never because it spans two areas and is a feature.
A human reviewer needs to look at this one. To make review easier, you could split it into smaller pull requests, for example the migration and table-shape changes separately from the new ad click ID tracking and the HogQL read changes.
- 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✗ | matches: migrations |
| size | ✗ | too large for auto-review (2941L substantive in global — ceiling is 800L; 2941L, 31F total, 4453L/39F incl. docs/generated/snapshots) |
| tier | ✗ | classified as T2-never: T2-never (4453L, 39F, two-areas, feat) |
| stamphog 2.6.1 | .stamphog/policy.yml @ unknown · reviewed head 8debe15 |
8debe15 to
9ace3aa
Compare
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 9ace3aa: 1 Should fix. This pull request is large, so the review ran in 4 parts with less depth than usual.
This is review round 4 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.
Conflict cleared — ClickHouse migration renumbered
|
9ace3aa to
50eba1a
Compare
|
Rebased this branch onto current The only conflict was Still waiting on the code-owner review (ClickHouse team) before this can be enqueued. 🦉 via talyn.dev |
50eba1a to
f43722a
Compare
Track lower-tier ad click ids in raw_sessions_v3 as a flat 'key=value' set with a URL query-param fallback, exposing each as an $entry_<id> and $entry_has_<id> session property. Rework the v3 pipeline: drop entry_ad_ids_map and flag_keys, switch the uniqExact states to uniq, and replace the flag key-values bloom filter with a tokenized text index.
The v3 sessions MV read $geoip_subdivision_city_name, which ingestion never sets (geoip enrichment writes $geoip_city_name), so the geoip_subdivision_city_name column was NULL on every row. Read $geoip_city_name instead; the column name is kept to match sessions v2.
The URL fallback could persist bytes that are not valid UTF-8. decodeURLComponent turns '%FF' into a raw 0xFF byte, and left() counts bytes, so it can also cut a multi-byte character in half. Once such a value lands in entry_ad_ids_set, every reader that expects text fails on that session: the /sessions/values picker decodes the value with json.loads, which raises UnicodeDecodeError rather than the JSONDecodeError it handles. Wrap the truncation in toValidUTF8 so the stored value is always text, and regenerate the checked-in HCL, golden SQL and schema snapshots. Generated-By: PostHog Desktop Task-Id: c0a67d0e-1f50-4281-be1a-26a2f1f64baa
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The URL fallback used decodeURLComponent, which leaves a query-string '+' alone. The SDK decodes the same parameter with '+' as a space before promoting it to an event property, so a value like utm_id=spring+sale was stored as 'spring+sale' from the URL and 'spring sale' from the property, splitting one campaign across two attribution values. decodeURLFormComponent follows rfc-1866 and decodes '+' as a space, so both paths now agree. Regenerates the HCL goldens and schema snapshots, and adds a regression case covering a plus-encoded space in a URL ad id. Generated-By: PostHog Desktop Task-Id: c0a67d0e-1f50-4281-be1a-26a2f1f64baa
Migration 0325 ended by recreating raw_sessions_v3_v from the shared view builder. That builder now merges uniq states instead of uniqExact, so on an install whose last applied ClickHouse migration sits between 0158 and 0325 the statement runs against a table that still holds uniqExact states and aborts the upgrade: Code: 43. Illegal type AggregateFunction(uniqExact, Nullable(UUID)) of argument for aggregate function with Merge suffix Freezing a historical copy of the view SQL does not work, because a fresh install builds the table from today's SQL at 0158 and would then need the opposite merge function at 0325. The recreation is redundant on every path that still reaches it: 0158 already creates the view from the same source a fresh install uses, and 0354 recreates it after rebuilding the tables. Dropping the operation leaves the final schema unchanged and lets the older install reach 0354. Generated-By: PostHog Desktop Task-Id: c0a67d0e-1f50-4281-be1a-26a2f1f64baa
f43722a to
e5cf4fa
Compare
|
/depot retry Retrying the Depot jobs that failed on an infrastructure timeout ( 🦉 via talyn.dev |
Migrations 0180 and 0325 replay on a fresh cluster and add flag_keys plus the flag_keys and flag_key_values bloom filter indexes on top of today's base SQL, which no longer declares any of them. A fresh install therefore ended up with a raw_sessions_v3 the declared HCL does not describe, and the multinode migration smoke reported the drift. Drop all three in the non-cloud branch of 0364, before the materialized views are created. Cloud is unaffected: that branch is still empty, because the ClickHouse team applies the rebuild by hand. Generated-By: PostHog Desktop Task-Id: c0a67d0e-1f50-4281-be1a-26a2f1f64baa
Problem
raw_sessions_v3has a new shape, and HogQL still reads the old columns (entry_ad_ids_map,uniqExactMerge).utm_id,gad_campaignid,srsltid) are the most common untracked params in production entry URLs.ScCidorSsCid, and the parser reads onlysccid(Snapchat Click ID parameter broken #37368).irclickid; the list carries onlyirclid.rtidwas requested in Feature Request: Support Rokt's click ID (rtid) #54665.Changes
sessionTableVersion: "v3"in their HogQL modifiers. The default stays v2, so nothing changes for anyone else.entry_ad_ids_setholds flatkey=valuepairs and replacesentry_ad_ids_map.uniqstates instead ofuniqExact.flag_key_values, with a text index tokenized on=.flag_keysand the always-emptyflag_valuesare gone.raw_sessions_v3attributes 19 new entry params:utm_id,utm_source_platform,utm_creative_format,utm_marketing_tacticgad_campaignidsrsltidrdt_cidirclickidsyclid_bhlidawctblciyclidndcliddicbocjeventrtidopprefbfcid$pageviewand$screenevents.ScCid,SsCidandsccidmap to onesccidkey. An import-time guard keeps an unsafe identifier out of the generated SQL.gclidorfbclidproperty no longer counts as a click ID, which matches v2. Channel type read"gclid": ""as a paid click.mkt_tok,_hsenc,mc_eid), HubSpot'shsa_*family, Triple Whale template params, and genericclickid.Note
The ClickHouse team applied the cloud side by hand: the sessions-cluster table, the October backfill, the ingestion pipeline, and the
Distributedtables on the main and events clusters (thread). As of 2026-10-28 prod-us is done; dev and EU are still to do.CREATE … IF NOT EXISTS. Migration 0178 dropped them, and this is the only migration that brings them back.flag_valuesis gone, and prod-usraw_sessions_v3now points at thesessionscluster, as prod-eu already did.entry_ad_ids_setgets no bloom filter index, because ClickHouse rejects one on anAggregateFunction(argMin, ...)state column.golden/andsql/, snapshots, and the HogQL$entry_*field lists.Warning
Two schema items are still declared but outside this PR's reach:
sharded_raw_sessions_v3, which no longer exists there. It comes from the sharedroles/data/commonlayer, so removing it for one env belongs in posthog-cloud-infra'soverrides/.flag_valuescolumn until the ClickHouse team drops it. No migration drops it in cloud, because the ingestionDistributedtable must lose it first.How did you test this code?
Test rationale:
test_raw_sessions_v3_model.py:"gclid": ""counts as a paid click again.test_sessions_v3.pycovers channel type for 10 support-ticket shapes, entry UTMs across events, and replay rows that start a session or exist alone.hcl/check.sh.Release status
Docs update
frontend/src/scenes/web-analytics/contributing.mdexplains how v3 picks entry and exit values, the empty click ID rule, the session timestamp guard, and the accepted gap between$entry_utm_*and$channel_type.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Fable 5 (initial ad ID work) and Claude Opus 5.5 (schema realignment,
bfcid, rollout fixes and this description).Skills invoked:
/querying-production-databases-via-metabase,/clickhouse-migrations,/writing-tests,/writing-code-comments,/reviewing-with-coderabbit,/writing-pr-descriptions.