Repository navigation
feat(hogql): opt flagged users into the native events cluster - #119468
vdekrijger wants to merge 56 commits into
Conversation
…uster A user in the native-events-cluster-queries flag gets useNewEventsCluster and useNewEventsSchema on their query API requests, for eligible projects and supported query types, unless the query sets either modifier itself. The opt-in reaches the runner as constructor modifiers, before the cache key is computed, so native results keep their own cache entries.
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 40bc8e0: 3 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
🤖 CI report
|
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
posthog/api/services/query.py:597 |
posthog/api/services/query.py:621 |
22 | 187 |
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:1 |
products/warehouse_sources/backend/temporal/data_imports/sources/kalshi/source.py:1 |
22 | 176 |
posthog/api/services/query.py:365 |
posthog/api/services/query.py:389 |
21 | 169 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_ads/source.py:68 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_selling_partner/source.py:175 |
39 | 159 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_selling_partner/source.py:21 |
products/warehouse_sources/backend/temporal/data_imports/sources/appstack/source.py:18 |
19 | 146 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_budgets/source.py:17 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cost_and_usage_report/source.py:22 |
17 | 146 |
products/warehouse_sources/backend/temporal/data_imports/sources/adyen/source.py:19 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:25 |
17 | 143 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/dynamodb/source.py:81 |
39 | 141 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_selling_partner/source.py:27 |
products/warehouse_sources/backend/temporal/data_imports/sources/greenhouse/source.py:17 |
14 | 138 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_athena/source.py:102 |
38 | 136 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_athena/athena.py:62 |
products/warehouse_sources/backend/temporal/data_imports/sources/trino/trino.py:41 |
14 | 128 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_iam_access_analyzer/source.py:135 |
33 | 122 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/amazon_cloudwatch.py:361 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_athena/athena.py:176 |
14 | 115 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cost_anomaly_detection/source.py:155 |
31 | 111 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_organizations/source.py:161 |
31 | 111 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_cost_management/azure_cost_management.py:314 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_log_analytics/azure_log_analytics.py:188 |
16 | 111 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cost_and_usage_report/source.py:166 |
31 | 110 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cost_explorer/source.py:153 |
30 | 106 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_sagemaker/source.py:153 |
28 | 101 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_config/source.py:143 |
27 | 100 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_glue_data_catalog/source.py:135 |
27 | 98 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_guardduty/source.py:126 |
27 | 98 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_inspector/source.py:146 |
27 | 98 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cloudtrail/source.py:154 |
26 | 96 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_cost_management/source.py:85 |
products/warehouse_sources/backend/temporal/data_imports/sources/azure_log_analytics/source.py:43 |
27 | 95 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_budgets/source.py:150 |
26 | 94 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_macie/source.py:131 |
26 | 94 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/amazon_cloudwatch.py:333 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_athena/athena.py:202 |
15 | 93 |
products/warehouse_sources/backend/temporal/data_imports/sources/gcp_cloud_billing/gcp_cloud_billing.py:8 |
products/warehouse_sources/backend/temporal/data_imports/sources/kalshi/kalshi.py:11 |
11 | 91 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/amazon_cloudwatch.py:158 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_cost_and_usage_report/aws_cost_and_usage_report.py:116 |
13 | 90 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_batch/source.py:122 |
23 | 80 |
products/warehouse_sources/backend/temporal/data_imports/sources/amazon_cloudwatch/source.py:195 |
products/warehouse_sources/backend/temporal/data_imports/sources/aws_ses/source.py:167 |
22 | 76 |
products/warehouse_sources/backend/temporal/data_imports/sources/cloudflare_d1/source.py:68 |
products/warehouse_sources/backend/temporal/data_imports/sources/drip/source.py:110 |
20 | 71 |
⚠️ Duplication (TypeScript) — 10 new duplicated blocks (worst 1387 tokens)
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.
| First copy | Second copy | Lines | Tokens |
|---|---|---|---|
frontend/src/products.tsx:394 |
products/ai_observability/manifest.tsx:219 |
121 | 1387 |
frontend/src/products.tsx:429 |
products/ai_observability/manifest.tsx:257 |
87 | 1029 |
frontend/src/products.tsx:1332 |
products/ai_observability/manifest.tsx:355 |
83 | 630 |
frontend/src/products.tsx:75 |
products/ai_observability/manifest.tsx:180 |
38 | 237 |
products/signals/frontend/inbox/signalSourcesLogic.ts:688 |
products/signals/frontend/inbox/signalSourcesLogic.ts:753 |
64 | 140 |
frontend/src/products.tsx:2700 |
products/ai_observability/manifest.tsx:486 |
21 | 132 |
products/signals/frontend/inbox/signalSourcesLogic.ts:614 |
products/signals/frontend/inbox/signalSourcesLogic.ts:688 |
63 | 130 |
frontend/src/scenes/hog-functions/email-templater/EmailTemplater.tsx:240 |
frontend/src/scenes/hog-functions/email-templater/EmailTemplater.tsx:795 |
24 | 84 |
products/ai_observability/frontend/datasets/AIObservabilityDatasetsScene.tsx:76 |
products/ai_observability/frontend/datasets/DatasetItems.tsx:134 |
15 | 75 |
products/signals/frontend/inbox/signalSourcesLogic.ts:644 |
products/signals/frontend/inbox/signalSourcesLogic.ts:783 |
35 | 74 |
✅ MCP agent API — no agent API changes
This PR no longer changes the tools, params, scopes or annotations agents see.
⚠️ MCP snapshots — 4 updated (4 modified, 0 added, 0 deleted)
Snapshots: MCP unit test snapshots updated
Changes: 4 snapshots (4 modified, 0 added, 0 deleted)
What this means:
- Snapshots have been automatically updated to match current output
Next steps:
- Review the changes to ensure they're intentional
- If unexpected, investigate what caused the output to change
The rollout opt-in now starts from the query's effective modifiers, including nested sources, so settings such as personsOnEventsMode survive and a nested explicit opt-out keeps its route. HogQL queries are no longer opted in automatically, because their workload depends on the tables they read and is only known once they are printed.
…cle' into vasco/native-events-user-flag
…nto vasco/native-events-user-flag
…events-user-flag # Conflicts: # posthog/api/services/query.py
…nd dashboard tiles The insights API now passes the user's native cluster opt-in into calculate_for_query_based_insight, and the opt-in looks through wrapper nodes such as InsightVizNode to the query they run. Shared views and background callers keep the legacy route.
…cle' into pr119468 # Conflicts: # posthog/hogql/test/test_new_events_schema_routing.py
…cle' into pr119468
# Conflicts: # posthog/hogql_queries/query_runner.py
…users The user flag unwrapped every source, so runnable actor wrappers such as FunnelsActorsQuery were opted in and then refused by their runner. It now unwraps only the UI wrappers process_query_model unwraps, and asks native_route_can_run whether the execution mode can use the native route.
…m the request user Both entrypoints pass the request user instead of computing modifiers, so a saved insight's query is parsed once. The flag reads the request's access method itself, so personal API key traffic stays legacy on both the query and insights paths. The flag uses posthog_feature_flag_enabled like other server-side gates.
With the async gate off, cache-only reads took the native cache entry while the async fills they waited on wrote the legacy one, so a flagged user's dashboard tiles could come back empty. The flag now waits for CLICKHOUSE_NATIVE_EVENTS_ASYNC, so every mode agrees on one route. Native stores skip the warming freshness index, which only tracks legacy entries. Entrypoints pass interactive_request instead of the user twice, and the opt-in resolves next to the runner, cheap checks first.
|
@PostHog review |
|
@vdekrijger PostHog Review is reviewing this pull request. |
|
Superseded by #122489, which merged on Oct 26 and routes events_json reads to the events cluster with one rule instead of the per-query opt-in this PR carried. Closing; the branch stays. |
Problem
Changes
native-events-cluster-queriesfeature flag now runs their app queries on the native events cluster and the native JSON events table.CLICKHOUSE_NATIVE_EVENTS_TEAMSand the native host is set;useNewEventsClusteroruseNewEventsSchema;useNewEventsSchemadefault.personsOnEventsMode.CLICKHOUSE_NATIVE_EVENTS_ASYNCis on. Dashboard tiles fill through an async run and read back from the cache, so both steps must agree on one route; with the gate off they would not.process_query_modelresolves the opt-in from theuserthey already pass, before the runner computes the cache key. Native and legacy results therefore keep separate cache entries, and a saved insight's query is parsed once.InsightVizNodeandDataTableNode, which saved insights store, and checks the query that runs. Runnable wrappers such asFunnelsActorsQueryare checked as themselves, so the flag never opts in a query its runner would refuse./querytake the flag like any other request.posthog_feature_flag_enabledlike other server-side gates.Warning
To roll back, turn the flag off first. Only remove the project from
CLICKHOUSE_NATIVE_EVENTS_TEAMSor clear the native host after queued queries drain. Workers recheck eligibility and fail a queued native query rather than silently moving it to another cluster.Note
The flag does not exist yet. Create
native-events-cluster-queriesin PostHog, targeted by email, before this has any effect.How did you test this code?
Test rationale:
TestNewEventsClusterPilotruns one query with the flag off and on. It catches the opt-in not reaching the runner, and native results sharing the legacy cache key. It fails when the query API stops marking its requests interactive.TestNewEventsClusterRoutingcatches the flag dropping a query's other modifiers, or forcing raw HogQL, unsupported query types, explicit schema choices or nested opt-outs onto the native route.FunnelsActorsQuerythat the flag must leave alone.Release status
Docs update
None. No doc covers the native cluster opt-in yet.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Opus 5.5 (
claude-opus-5-5)create_default_modifiers_for_user, was dropped after reading the cache path (see Changes).🤖 Generated with Claude Code