Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
5531d97
refactor: add handler registry and decorator for request messages
JH-A-Kim Jun 5, 2026
f8d1de1
refactor: register all shared handlers to all handlers with handles d…
JH-A-Kim Jun 5, 2026
3f22f84
refactor: resolve handle_mask_secret to return none with empty set
JH-A-Kim Jun 5, 2026
89c3ed1
fix: resolve return type error on handle_mask_secret
JH-A-Kim Jun 5, 2026
6698321
refactor: move client up to WatchedSubProcess base class and replace …
JH-A-Kim Jun 5, 2026
4de5af1
refactor: remove redundant elif chains
JH-A-Kim Jun 6, 2026
302ef8c
refactor: fix _RecordingSupervisor construction in schema integration…
JH-A-Kim Jun 6, 2026
115000e
refactor: add client to input filed in MaskSecret
JH-A-Kim Jun 6, 2026
3d7acdd
refactor: added empty returns to handle_request to follow supervisor …
JH-A-Kim Jun 6, 2026
f9df3ea
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 6, 2026
623f64e
refactor: add empty message send on handle_request to meet message st…
JH-A-Kim Jun 6, 2026
1a9ca05
refactor: move isinstance cases to handler registry
JH-A-Kim Jun 7, 2026
196439d
refactor: remove redundant isinstance cases
JH-A-Kim Jun 7, 2026
e392c90
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 7, 2026
3a047ab
refactor: updated test case to handle unified handler
JH-A-Kim Jun 7, 2026
d3bac47
fix: resolve None client caused by attrs/cached_property conflict
JH-A-Kim Jun 7, 2026
10e8db6
fix: rename client field to _client to allow property override
JH-A-Kim Jun 7, 2026
935527d
fix: resolve lazy initialization for client in tests
JH-A-Kim Jun 8, 2026
d4ca9bd
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 8, 2026
650b8bf
refactor: removed redundant commented out code
JH-A-Kim Jun 8, 2026
732e367
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 9, 2026
9c8672a
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 9, 2026
137bc02
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 9, 2026
83ecb58
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 9, 2026
6fd216b
chore: run prek
JH-A-Kim Jun 9, 2026
4c5751a
add comment to handle_request for explicit close of factory
JH-A-Kim Jun 9, 2026
51d7ef5
fix: revert test to original, update mask_secret patch path
JH-A-Kim Jun 10, 2026
b303d9e
add: add debug logging for _handle_request
JH-A-Kim Jun 10, 2026
83b5e03
refactor: removed unnecessary isinstance in getAssetsByAlias
JH-A-Kim Jun 10, 2026
345af5c
refactor: add make_client as classmethod for subclasses to override
JH-A-Kim Jun 10, 2026
da6d474
chore: run prek
JH-A-Kim Jun 10, 2026
cc349e0
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
068e89a
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
0f68dda
chore: prek run
JH-A-Kim Jun 10, 2026
cdcc7cb
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
9632656
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
46d1262
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
96870a1
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
f562754
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 10, 2026
30db227
refactor: remove redundant client comment
JH-A-Kim Jun 11, 2026
ce63a0d
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 11, 2026
237462f
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 17, 2026
bf958d3
refactor: rename comms functions to follow comms.py definitions
JH-A-Kim Jun 17, 2026
5eeb88f
chore: lint check
JH-A-Kim Jun 17, 2026
df344c2
refactor: add frozen set to resolve toSupervisor type alias problem
JH-A-Kim Jun 17, 2026
51a28fd
refactor: add frozen set to resolve toSupervisor type alias problem
JH-A-Kim Jun 17, 2026
8fe733d
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 17, 2026
cfceffd
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 18, 2026
9e240c6
refactor: add unioned type checking
JH-A-Kim Jun 18, 2026
865e0f2
refactor: add self.send_msg to triggerstatechange
JH-A-Kim Jun 18, 2026
64d7153
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 18, 2026
5502655
chore: remove redundant variable assignment
JH-A-Kim Jun 18, 2026
fbed797
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 18, 2026
b446145
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 22, 2026
253b8a8
resolve broken imports
JH-A-Kim Jun 23, 2026
27d21e2
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 23, 2026
7abcf31
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 23, 2026
1f99b93
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 23, 2026
1ab71cd
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 23, 2026
68ad65d
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 23, 2026
3be2611
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 23, 2026
3173a1a
fix task-sdk failing test case
JH-A-Kim Jun 23, 2026
1c106a3
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jun 25, 2026
e1066c5
resolve test failures due to misaligned test contracts with request h…
JH-A-Kim Jun 25, 2026
c62a4b4
resolve undefined names in test cases
JH-A-Kim Jun 26, 2026
3d23ebf
align the input output contract in the test cases for request handlers
JH-A-Kim Jun 26, 2026
b163822
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 26, 2026
07f83f0
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jun 26, 2026
ec2ce98
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jul 4, 2026
2aa18b6
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 4, 2026
e32ff46
fix breaking merge conflicts with triggerer and broken imports
JH-A-Kim Jul 4, 2026
3268e88
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 4, 2026
1cfe827
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 4, 2026
0d58359
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 5, 2026
cf9626f
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jul 10, 2026
d2906da
resolve failing test case by realigning output contracts
JH-A-Kim Jul 11, 2026
518e8a8
remove unused import
JH-A-Kim Jul 11, 2026
6057a00
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 11, 2026
93d1c61
Merge branch 'main' into refactor-handle_request-with-handler-registry
JH-A-Kim Jul 21, 2026
adb7b3f
resolve failing test cases by updating output to include partition key
JH-A-Kim Jul 21, 2026
33e9e96
Merge branch 'apache:main' into refactor-handle_request-with-handler-…
JH-A-Kim Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 4 additions & 86 deletions airflow-core/src/airflow/dag_processing/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import traceback
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Annotated, BinaryIO, ClassVar, Literal
from typing import TYPE_CHECKING, Annotated, Any, BinaryIO, ClassVar, Literal

import attrs
from pydantic import BaseModel, Field, TypeAdapter
Expand Down Expand Up @@ -71,24 +71,8 @@
XComSequenceIndexResult,
XComSequenceSliceResult,
)
from airflow.sdk.execution_time.request_handlers import (
handle_delete_variable,
handle_get_prev_successful_dag_run,
handle_get_previous_dag_run,
handle_get_previous_ti,
handle_get_task_states,
handle_get_ti_count,
handle_get_variable_keys,
handle_get_xcom,
handle_get_xcom_count,
handle_get_xcom_sequence_item,
handle_get_xcom_sequence_slice,
handle_mask_secret,
handle_put_variable,
)
from airflow.sdk.execution_time.supervisor import WatchedSubprocess
from airflow.sdk.execution_time.task_runner import RuntimeTaskInstance, _send_error_email_notification
from airflow.sdk.log import mask_secret
from airflow.serialization.serialized_objects import DagSerialization, LazyDeserializedDAG
from airflow.utils.dag_version_inflation_checker import check_dag_file_stability
from airflow.utils.file import iter_airflow_imports
Expand Down Expand Up @@ -562,14 +546,12 @@ class DagFileProcessorProcess(WatchedSubprocess, LoggingMixin):
in core Airflow.
"""

_msg_union: ClassVar[Any] = ToManager
logger_filehandle: BinaryIO
parsing_result: DagFileParsingResult | None = None
decoder: ClassVar[TypeAdapter[ToManager]] = TypeAdapter[ToManager](ToManager)
had_callbacks: bool = False # Track if this process was started with callbacks to prevent stale DAG detection false positives

client: Client
"""The HTTP client to use for communication with the API server."""

bundle_name: str
dag_file_rel_path: str

Expand Down Expand Up @@ -651,75 +633,11 @@ def _create_log_forwarder(
)

def _handle_request(self, msg: ToManager, log: FilteringBoundLogger, req_id: int) -> None:
from airflow.sdk.api.datamodels._generated import (
ConnectionResponse,
VariableResponse,
)

resp: BaseModel | None = None
dump_opts: dict[str, bool] = {}
if isinstance(msg, DagFileParsingResult):
self.parsing_result = msg
elif isinstance(msg, GetConnection):
conn = self.client.connections.get(msg.conn_id)
if isinstance(conn, ConnectionResponse):
if conn.password:
mask_secret(conn.password)
if conn.extra:
mask_secret(conn.extra)
conn_result = ConnectionResult.from_conn_response(conn)
resp = conn_result
dump_opts = {"exclude_unset": True, "by_alias": True}
else:
resp = conn
elif isinstance(msg, GetVariable):
var = self.client.variables.get(msg.key)
if isinstance(var, VariableResponse):
if var.value:
mask_secret(var.value, var.key)
var_result = VariableResult.from_variable_response(var)
resp = var_result
dump_opts = {"exclude_unset": True}
else:
resp = var
elif isinstance(msg, GetVariableKeys):
resp, dump_opts = handle_get_variable_keys(self.client, msg)
elif isinstance(msg, PutVariable):
resp, dump_opts = handle_put_variable(self.client, msg)
elif isinstance(msg, DeleteVariable):
resp, dump_opts = handle_delete_variable(self.client, msg)
elif isinstance(msg, GetPreviousDagRun):
resp, dump_opts = handle_get_previous_dag_run(self.client, msg)
elif isinstance(msg, GetPrevSuccessfulDagRun):
resp, dump_opts = handle_get_prev_successful_dag_run(self.client, self.id)
elif isinstance(msg, GetXCom):
resp, dump_opts = handle_get_xcom(self.client, msg)
elif isinstance(msg, GetXComCount):
resp, dump_opts = handle_get_xcom_count(self.client, msg)
elif isinstance(msg, GetXComSequenceItem):
resp, dump_opts = handle_get_xcom_sequence_item(self.client, msg)
elif isinstance(msg, GetXComSequenceSlice):
resp, dump_opts = handle_get_xcom_sequence_slice(self.client, msg)
elif isinstance(msg, MaskSecret):
handle_mask_secret(msg)
elif isinstance(msg, GetTICount):
resp, dump_opts = handle_get_ti_count(self.client, msg)
elif isinstance(msg, GetTaskStates):
resp, dump_opts = handle_get_task_states(self.client, msg)
elif isinstance(msg, GetPreviousTI):
resp, dump_opts = handle_get_previous_ti(self.client, msg)
else:
log.error("Unhandled request", msg=msg)
self.send_msg(
None,
request_id=req_id,
error=ErrorResponse(
detail={"status_code": 400, "message": "Unhandled request"},
),
)
self.send_msg(None, request_id=req_id, error=None)
return

self.send_msg(resp, request_id=req_id, error=None, **dump_opts)
super()._handle_request(msg, log, req_id)
Comment thread
JH-A-Kim marked this conversation as resolved.

@property
def is_ready(self) -> bool:
Expand Down
115 changes: 19 additions & 96 deletions airflow-core/src/airflow/jobs/triggerer_job_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from __future__ import annotations

import asyncio
import functools
import logging
import math
import os
Expand Down Expand Up @@ -101,30 +100,6 @@
_RequestFrame,
)
from airflow.sdk.execution_time.context import AssetStateStoreAccessors
from airflow.sdk.execution_time.request_handlers import (
handle_clear_asset_state_store_by_name,
handle_clear_asset_state_store_by_uri,
handle_delete_asset_state_store_by_name,
handle_delete_asset_state_store_by_uri,
handle_delete_variable,
handle_delete_xcom,
handle_get_asset_state_store_by_name,
handle_get_asset_state_store_by_uri,
handle_get_connection,
handle_get_dag_run_state,
handle_get_dr_count,
handle_get_previous_ti,
handle_get_task_states,
handle_get_ti_count,
handle_get_variable,
handle_get_variable_keys,
handle_get_xcom,
handle_mask_secret,
handle_put_variable,
handle_set_asset_state_store_by_name,
handle_set_asset_state_store_by_uri,
handle_set_xcom,
)
from airflow.sdk.execution_time.supervisor import WatchedSubprocess, make_buffered_socket_reader
from airflow.sdk.execution_time.task_runner import RuntimeTaskInstance
from airflow.serialization.serialized_objects import DagSerialization
Expand Down Expand Up @@ -484,6 +459,7 @@ class TriggerRunnerSupervisor(WatchedSubprocess):
rather than silently zombieing the supervisor.
"""

_msg_union: ClassVar[Any] = ToTriggerSupervisor
job: Job | None = None
capacity: int
queues: set[str] | None = None
Expand Down Expand Up @@ -547,6 +523,7 @@ def start( # type: ignore[override]
proc = super().start(
id=proc_id,
job=job,
client=cls.make_client(),
target=cls.run_in_process,
logger=logger,
use_exec=supervisor._should_use_exec(),
Expand All @@ -558,11 +535,8 @@ def start( # type: ignore[override]
proc.send_msg(msg, request_id=0)
return proc

@functools.cached_property
def client(self) -> Client:
return self.make_client()

def make_client(self) -> Client:
@classmethod
def make_client(cls) -> Client:
"""
Build the API client used to talk to the API server.

Expand All @@ -580,9 +554,6 @@ def make_client(self) -> Client:
return client

def _handle_request(self, msg: ToTriggerSupervisor, log: FilteringBoundLogger, req_id: int) -> None:

resp: BaseModel | None = None
dump_opts: dict[str, bool] = {}
self._last_runner_comms = time.monotonic()

if isinstance(msg, messages.TriggerStateChanges):
Expand All @@ -608,85 +579,37 @@ def _handle_request(self, msg: ToTriggerSupervisor, log: FilteringBoundLogger, r
while self.persisted_event_seqs:
events_persisted.append(self.persisted_event_seqs.popleft())

response = messages.TriggerStateSync(
sync = messages.TriggerStateSync(
to_create=[],
to_cancel=self.cancelling_triggers,
to_cancel=self.cancelling_triggers.copy(),
events_persisted=events_persisted or None,
)

# Pull out of these dequeues in a thread-safe manner
while self.creating_triggers:
workload = self.creating_triggers.popleft()
response.to_create.append(workload)
self.running_triggers.update(m.id for m in response.to_create)
resp = response

elif isinstance(msg, GetConnection):
resp, dump_opts = handle_get_connection(self.client, msg)
elif isinstance(msg, DeleteVariable):
resp, dump_opts = handle_delete_variable(self.client, msg)
elif isinstance(msg, GetVariable):
resp, dump_opts = handle_get_variable(self.client, msg)
elif isinstance(msg, GetVariableKeys):
resp, dump_opts = handle_get_variable_keys(self.client, msg)
elif isinstance(msg, PutVariable):
resp, dump_opts = handle_put_variable(self.client, msg)
elif isinstance(msg, DeleteXCom):
resp, dump_opts = handle_delete_xcom(self.client, msg)
elif isinstance(msg, GetXCom):
resp, dump_opts = handle_get_xcom(self.client, msg)
elif isinstance(msg, SetXCom):
resp, dump_opts = handle_set_xcom(self.client, msg)
elif isinstance(msg, GetDRCount):
resp, dump_opts = handle_get_dr_count(self.client, msg)
elif isinstance(msg, GetDagRunState):
resp, dump_opts = handle_get_dag_run_state(self.client, msg)

elif isinstance(msg, GetTICount):
resp, dump_opts = handle_get_ti_count(self.client, msg)

elif isinstance(msg, GetTaskStates):
resp, dump_opts = handle_get_task_states(self.client, msg)
elif isinstance(msg, GetPreviousTI):
resp, dump_opts = handle_get_previous_ti(self.client, msg)
elif isinstance(msg, UpdateHITLDetail):
sync.to_create.append(workload)
self.running_triggers.update(m.id for m in sync.to_create)
self.send_msg(sync, request_id=req_id, error=None)
return

if isinstance(msg, UpdateHITLDetail):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like ToSupervisor has UpdateHITLDetail and GetHITLDetailResponse listed, but no handler? @ashb - what do you think? That looks like a bug. Do we add the handler (combining that one and this one into the registry) or remove them from ToSupervisor? It looks like righ tnow we're accepting thm but not actually handling them.

api_resp = self.client.hitl.update_response(
ti_id=msg.ti_id,
chosen_options=msg.chosen_options,
params_input=msg.params_input,
)
resp = HITLDetailResponseResult.from_api_response(response=api_resp)
elif isinstance(msg, GetHITLDetailResponse):
self.send_msg(resp, request_id=req_id, error=None)
return

if isinstance(msg, GetHITLDetailResponse):
api_resp = self.client.hitl.get_detail_response(ti_id=msg.ti_id)
resp = HITLDetailResponseResult.from_api_response(response=api_resp)
elif isinstance(msg, MaskSecret):
handle_mask_secret(msg)
elif isinstance(msg, ClearAssetStateStoreByName):
handle_clear_asset_state_store_by_name(self.client, msg)
resp = OKResponse(ok=True)
elif isinstance(msg, ClearAssetStateStoreByUri):
handle_clear_asset_state_store_by_uri(self.client, msg)
resp = OKResponse(ok=True)
elif isinstance(msg, DeleteAssetStateStoreByName):
handle_delete_asset_state_store_by_name(self.client, msg)
resp = OKResponse(ok=True)
elif isinstance(msg, DeleteAssetStateStoreByUri):
handle_delete_asset_state_store_by_uri(self.client, msg)
resp = OKResponse(ok=True)
elif isinstance(msg, GetAssetStateStoreByName):
resp, dump_opts = handle_get_asset_state_store_by_name(self.client, msg)
elif isinstance(msg, GetAssetStateStoreByUri):
resp, dump_opts = handle_get_asset_state_store_by_uri(self.client, msg)
elif isinstance(msg, SetAssetStateStoreByName):
handle_set_asset_state_store_by_name(self.client, msg)
resp = OKResponse(ok=True)
elif isinstance(msg, SetAssetStateStoreByUri):
handle_set_asset_state_store_by_uri(self.client, msg)
resp = OKResponse(ok=True)
else:
raise ValueError(f"Unknown message type {type(msg)}")
self.send_msg(resp, request_id=req_id, error=None)
return

self.send_msg(resp, request_id=req_id, error=None, **dump_opts)
super()._handle_request(msg, log, req_id)

def run(self) -> None:
"""Run synchronously and handle all database reads/writes."""
Expand Down
4 changes: 2 additions & 2 deletions airflow-core/tests/unit/dag_processing/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2214,7 +2214,7 @@ def test_handle_request_get_connection_masks_password_and_extra(self, proc):
)

with (
patch("airflow.dag_processing.processor.mask_secret") as mock_mask_secret,
patch("airflow.sdk.execution_time.request_handlers.mask_secret") as mock_mask_secret,
patch.object(DagFileProcessorProcess, "send_msg", autospec=True) as mock_send_msg,
):
proc._handle_request(
Expand Down Expand Up @@ -2251,7 +2251,7 @@ def test_handle_request_get_variable_masks_value_with_key(self, proc):
)

with (
patch("airflow.dag_processing.processor.mask_secret") as mock_mask_secret,
patch("airflow.sdk.execution_time.request_handlers.mask_secret") as mock_mask_secret,
patch.object(DagFileProcessorProcess, "send_msg", autospec=True) as mock_send_msg,
):
proc._handle_request(
Expand Down
Loading