From 6ff807d3aca7450f9e6678e3fbce80de56f3b49a Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Tue, 28 Jul 2026 00:19:37 +0100 Subject: [PATCH] fix(data-imports): stop duckgres enablement crashing on non-UUID control-plane org ids The duckgres batch sink's per-poll enablement refresh filtered DuckgresServer rows by the managed-warehouse control plane's raw organization_id strings, which aren't guaranteed to be valid UUIDs (the control plane has rows keyed by human-readable slugs, e.g. for internal test/dev servers). Passing one of those straight into a Django ORM filter against a UUID foreign key raised a ValidationError and aborted the whole refresh for every team, not just the mismatched row. Filter by the app DB's own resolved organization ids instead, which are already guaranteed to be valid UUIDs. Branch: posthog-code/fix-duckgres-enablement-uuid-crash Generated-By: PostHog Code Task-Id: 94dc4a2d-02ac-452b-89cf-41e90a414c50 --- .../pipeline_v3/duckgres/enablement.py | 6 ++++- .../pipeline_v3/duckgres/test_enablement.py | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/enablement.py b/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/enablement.py index ddec8f161f37..fa733fe1ad9a 100644 --- a/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/enablement.py +++ b/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/enablement.py @@ -86,10 +86,14 @@ def duckgres_sink_enablement() -> SinkEnablement | None: "id", "uuid", "organization_id" ) } + # Filtered by the app DB's own org ids, never by the control plane's row.organization_id: + # that value is an external, unvalidated string (the CP has test/dev rows keyed by + # human-readable slugs, not UUIDs), and passing it straight into a UUID FK lookup + # raises ValidationError before the org_id match-up below ever runs. budgets = { str(org_id): sink_max_concurrency for org_id, sink_max_concurrency in DuckgresServer.objects.filter( - organization_id__in={row.organization_id for row in rows} + organization_id__in={org_id for _, org_id in team_info.values()} ).values_list("organization_id", "sink_max_concurrency") } diff --git a/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/test_enablement.py b/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/test_enablement.py index eac84be3128c..c9fad2a14267 100644 --- a/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/test_enablement.py +++ b/products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/duckgres/test_enablement.py @@ -110,6 +110,32 @@ def test_duckgres_sink_enablement_uses_memberships_and_carries_org_budgets( assert mock_feature_enabled.call_count == 2 +@pytest.mark.django_db +@patch.object(enablement, "is_dev_mode", return_value=False) +@patch.object(enablement.posthoganalytics, "feature_enabled") +def test_duckgres_sink_enablement_ignores_non_uuid_control_plane_org_ids( + mock_feature_enabled: MagicMock, _mock_dev: MagicMock +) -> None: + """The control plane can report extra rows for a team keyed by a non-UUID + organization_id (e.g. dev/test rows using human-readable slugs instead of a real + org id). Those must not crash the whole refresh via a Django UUID lookup — they + should just fail the org match-up and be skipped, like any other mismatched row.""" + org = Organization.objects.create(name="Org") + team = Team.objects.create(organization=org) + DuckgresServer.objects.create(organization=org, host="h", username="root", password="x") + mock_feature_enabled.return_value = True + + mismatched_row = _cp_row(team) + mismatched_row["org_id"] = "not-a-uuid-slug" + + with _patch_all_rows([_cp_row(team), mismatched_row]): + result = enablement.duckgres_sink_enablement() + + assert result is not None + assert result.team_ids == [team.id] + mock_feature_enabled.assert_called_once() + + @pytest.mark.django_db @patch.object(enablement, "is_dev_mode", return_value=False) @patch.object(enablement.posthoganalytics, "feature_enabled")