Add partition_key and partition_key_pattern filters for asset events#64610
Add partition_key and partition_key_pattern filters for asset events#64610hussein-awala wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a regex-based partition key filter for asset events end-to-end (public REST API, execution API, Task SDK/supervisor), plus a supporting DB index/migration and documentation updates.
Changes:
- Introduces
partition_key_pattern(REST) /partition_key(execution API + SDK) regex filtering and wires it through API ↔ supervisor ↔ Task SDK accessor. - Adds
(asset_id, partition_key)composite index onasset_eventwith Alembic migration + updates migration head mapping/docs. - Regenerates OpenAPI artifacts (YAML + UI TS clients) and adds tests/docs for the new filter.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| task-sdk/tests/task_sdk/execution_time/test_supervisor.py | Supervisor request forwarding test updates + new partition_key cases |
| task-sdk/tests/task_sdk/api/test_client.py | SDK client query param forwarding tests for partition_key |
| task-sdk/src/airflow/sdk/execution_time/supervisor.py | Forwards partition_key from supervisor messages to API client |
| task-sdk/src/airflow/sdk/execution_time/context.py | Adds .partition_key(pattern) chaining to InletEventsAccessor |
| task-sdk/src/airflow/sdk/execution_time/comms.py | Adds partition_key to supervisor request message models |
| task-sdk/src/airflow/sdk/api/client.py | Adds partition_key query param forwarding in asset_events.get() |
| airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_asset_events.py | Adds execution API regex filter tests (postgres-only) |
| airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py | Adds REST API partition_key_pattern filtering tests (postgres-only) |
| airflow-core/src/airflow/utils/db.py | Updates migration head mapping for 3.3.0 |
| airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts | Regenerates UI request types incl. partitionKeyPattern |
| airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts | Regenerates request service to pass partition_key_pattern |
| airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts | Regenerates request schemas (incl. ValidationError schema changes) |
| airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts | Regenerates suspense query hook with partitionKeyPattern |
| airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts | Regenerates query hook with partitionKeyPattern |
| airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts | Regenerates prefetch helper with partitionKeyPattern |
| airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts | Regenerates ensureQueryData helper with partitionKeyPattern |
| airflow-core/src/airflow/ui/openapi-gen/queries/common.ts | Regenerates query key fn to include partitionKeyPattern |
| airflow-core/src/airflow/models/asset.py | Adds ORM index idx_asset_event_asset_id_partition_key |
| airflow-core/src/airflow/migrations/versions/0111_3_3_0_add_idx_asset_event_partition_key.py | New migration to create/drop the composite index |
| airflow-core/src/airflow/api_fastapi/execution_api/routes/asset_events.py | Adds execution API regex filter param + query clause |
| airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py | Adds REST partition_key_pattern filter dependency to /assets/events |
| airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml | Regenerates REST OpenAPI (adds param + changes ValidationError schema) |
| airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml | Regenerates private UI OpenAPI (ValidationError schema changes) |
| airflow-core/src/airflow/api_fastapi/common/parameters.py | Adds reusable _RegexParam + regex_param_factory + query alias type |
| airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/openapi-gen/requests/types.gen.ts | Regenerates simple auth manager UI request types |
| airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/openapi-gen/requests/schemas.gen.ts | Regenerates simple auth manager UI request schemas |
| airflow-core/src/airflow/api_fastapi/auth/managers/simple/openapi/v2-simple-auth-manager-generated.yaml | Regenerates simple auth manager OpenAPI (ValidationError schema changes) |
| airflow-core/docs/migrations-ref.rst | Updates auto-generated migration reference table (new head) |
| airflow-core/docs/authoring-and-scheduling/assets.rst | Documents Task SDK accessor + REST query usage for partition key regex |
Comments suppressed due to low confidence (1)
airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py:341
- If
partition_key_patterncontains an invalid regex, the underlying DB will raise during query execution (e.g. PostgreSQL error), which currently propagates as a 500. Consider catching the relevant SQLAlchemy/DBAPI exception around the query execution and returning a 400 with a clear message that the regex pattern is invalid for the current backend.
assets_event_select, total_entries = paginated_select(
statement=base_statement,
filters=[
asset_id,
source_dag_id,
source_task_id,
source_run_id,
source_map_index,
partition_key_pattern,
name_pattern,
timestamp_range,
],
order_by=order_by,
offset=offset,
limit=limit,
session=session,
)
6304aa9 to
0626b0b
Compare
7ddc996 to
6cf39d4
Compare
371aac1 to
5dbfe96
Compare
f509f70 to
5afedc4
Compare
165ec20 to
7ec7341
Compare
pierrejeambrun
left a comment
There was a problem hiding this comment.
Is regexp required here? We have a lot of pattern and prefix_pattern filters doing both substring match and prefix match. (supporting logical OR, some special character too "~" etc....)
That introduce an inconsistency for that specific pattern param as well as introducing complexity handling regexp.
Would it be possible to implement that search similarly to what we already have on other APIs and discuss on a separate issue/PR specifically the introduction of Regexp matching.
|
Sorry for the late reply @pierrejeambrun, and thanks for the review. You raise a fair point about consistency, and I agree the regex introduction deserves a dedicated discussion. Let me explain the concrete gap so we can decide together. The existing 1. The Both filters treat
2. Suffix / "ends with" matching Find all partitions for a given date regardless of the region prefix:
3. Character-class / format constraints Match only well-formed date partitions for a region:
4. Structured mid-string alternation Match either region, but only for a specific month:
|
Add two new query parameters to the asset events API endpoints: - partition_key: exact-match filter leveraging the new composite B-tree index on (asset_id, partition_key) - partition_key_pattern: regex-based filter using database-native regexp_match for flexible pattern matching Includes Core API, Execution API, Task SDK (InletEventsAccessor), client, documentation, migration for the composite index, and tests. Made-with: Cursor
da1abd9 to
02b4b83
Compare
…-regex-filter # Conflicts: # airflow-core/docs/authoring-and-scheduling/assets.rst # airflow-core/docs/migrations-ref.rst # airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts # airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts # airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts # airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts # airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts # airflow-core/src/airflow/utils/db.py
The pattern is evaluated by the database's regex engine, so a pathological pattern could consume DB CPU (ReDoS). Mitigations: - Cap the pattern length (MAX_REGEX_PATTERN_LENGTH) in both the Core and Execution API validators, returning 400 for over-long patterns. - Bound the regex query with a transaction-local statement_timeout on PostgreSQL (apply_regex_query_timeout); no-op elsewhere. MySQL bounds regex evaluation via its built-in regexp_time_limit; SQLite has no server-side regex and MariaDB is unsupported. - Document the safeguards in assets.rst.
Summary
Add two query parameters for filtering asset events by partition key across the full stack:
partition_key— exact-match filter using==equality, leverages the B-treecomposite index
(asset_id, partition_key)for fast lookups.partition_key_pattern— regex filter using SQLAlchemy'sregexp_match, which mapsto database-native regex on all backends (PostgreSQL
~, MySQLREGEXP, SQLitere.match).The two parameters are mutually exclusive — providing both returns a 400 error.
Also includes:
(asset_id, partition_key)onasset_eventtable with Alembic migration.assets.rstwith usage examples for both parameters.InletEventsAccessorexposes.partition_key(value)for exact match and.partition_key_pattern(pattern)for regex within tasks.Details
Exact match (
partition_key)Uses a simple
WHERE partition_key = ?equality check. This is the preferred optionwhen you know the full partition key — it leverages the B-tree index directly with
zero regex overhead.
Regex filter (
partition_key_pattern)Uses
regexp_matchwhich maps to database-native regex on all supported backends:PostgreSQL (
~operator), MySQL (REGEXP), and SQLite (via Pythonre.match).Note that SQLite's
re.matchis anchored at the start of the string, so patternsshould use
^-anchored regex for consistent cross-backend behavior.Invalid regex patterns are validated with
re.compile()before hitting the database,returning a 400 error with a descriptive message.
A
_RegexParamclass andregex_param_factorywere added to the common FastAPIparameters module for reuse (the factory accepts a customizable
description).Performance note
Currently,
partition_key_patternperforms a full table scan (filtered by any otherWHERE clauses) since standard B-tree indexes cannot accelerate regex matching. For
exact matches, the composite B-tree index
(asset_id, partition_key)is used directly.For high-volume deployments where regex filtering on
partition_keybecomes a bottleneck,a trigram (n-gram) index can significantly improve performance on PostgreSQL. This
requires the
pg_trgmextension and can be created manually by a database administratorwithout any application-level changes:
PostgreSQL's query planner will automatically use the trigram index for
~(regex)queries emitted by
regexp_match— no Airflow configuration changes needed.When it helps:
.*sales$,.*\|2024-.*,us.*westWhen it doesn't help (and what to use instead):
^us\|2024-— for these, usepartition_key(exact match)which leverages the B-tree index directly with zero overhead
update than B-tree, adding write-time cost on every
INSERTintoasset_eventShipping this as an Alembic migration would require
CREATE EXTENSIONprivileges(typically superuser) and a cross-backend strategy, since MySQL and SQLite have no
equivalent. This can be discussed separately if there is demand.
Files changed
Core API layer:
parameters.py— new_RegexParam,regex_param_factory,QueryAssetEventPartitionKeyFilter(exact match),
QueryAssetEventPartitionKeyRegex(regex)assets.py— accept bothpartition_keyandpartition_key_patternfilters inGET /api/v2/assets/eventswith mutual-exclusion validationExecution API layer:
asset_events.py— accept both params in/by-assetand/by-asset-aliasendpoints,with mutual-exclusion and regex validation
Task SDK:
client.py— sendpartition_keyandpartition_key_patternas separate query params,with client-side
ValueErrorif both are providedcomms.py— add both fields to request modelssupervisor.py— forward both fields to API clientcontext.py—InletEventsAccessorexposes.partition_key()and.partition_key_pattern()(mutually exclusive — setting one clears the other)
Database:
asset.py— composite indexidx_asset_event_asset_id_partition_key0112_3_3_0_add_idx_asset_event_partition_key.pyAuto-generated:
openapi-gen/queries/*,openapi-gen/requests/*)Tests:
Docs:
assets.rst— usage examples for both parameters via REST API andInletEventsAccessorTest plan
ValueErrorwhen both params are providedInletEventsAccessorclears the other field when setting one