Skip to content

Latest commit

 

History

History
104 lines (82 loc) · 6.07 KB

File metadata and controls

104 lines (82 loc) · 6.07 KB

Task execution architecture

This page is developer-facing background on what happens inside a worker when a task actually runs — how the Task SDK, the Supervisor and Coordinator processes, and the language runtimes work together: which processes are involved, and the classes and protocols they use to communicate. It complements Adding a new language SDK, which covers how to implement the coordinator side of a new runtime.

End users do not need any of this: they only configure a queue and write tasks. The user-facing guides live under Non-Python Task SDKs.

When a worker actually runs a task, it does not run the user's code directly. Instead it starts a lightweight Supervisor that runs in its own native operating-system process and forks a second native process in which the Task SDK runtime (task_runner) executes the user code. The two processes talk over a socket, and the Supervisor is the only side that ever holds the short-lived task JWT or talks to the Execution API — the user's code never sees the token and never touches the database.

The same runtime can also run in-process (a single Python process, no fork, no sockets, no HTTP) for dag.test() and local runs. The diagram below contrasts the two paths and marks where each Python process lives:

images/diagram_task_sdk_execution_architecture.png

The message flow of a supervised run — startup, running the user code, proxied Connection/Variable/XCom lookups, heartbeats, and reporting the final state — is shown below as a sequence diagram, with each process on its own lifeline. The Supervisor sits in the middle, so the Task ↔ Supervisor request/response round-trip (the task asks for a Connection/Variable/XCom and gets the answer back) reads as arrows going back and forth between neighboring lifelines. Each arrow is numbered, colored by its sender, and labeled with the message class or protocol used:

images/diagram_task_sdk_execution_sequence.png

The Task Execution Interface (TEI) introduced in AIP-72 is language-agnostic, so a task can also be written in a compiled, non-Python language. A Python Dag still declares the task with @task.stub(queue=...) (so Python and non-Python tasks can be mixed in one Dag), but the actual work is delegated to the matching runtime. The first-class integration is the Coordinator layer — the Python Supervisor drives the language runtime as a subprocess and proxies every Execution-API call for it — and it is the shared direction for the Java, Go, and upcoming language SDKs.

Coordinator (Java and Go). Both the Java SDK and the Go SDK reuse the existing Python Supervisor through the Coordinator layer. CoordinatorManager resolves the task's queue to a BaseCoordinator — a SubprocessCoordinator (JavaCoordinator for the java queue, ExecutableCoordinator for the Go bundle queue, e.g. golang), or the built-in _PythonCoordinator otherwise. The coordinator opens two loopback-TCP servers, spawns the language bundle subprocess (java -classpath ... <main class> for Java, or the self-contained packed bundle binary for Go) with --comm / --logs appended, and drives it with _PopenActivitySubprocess (a subclass of the shared ActivitySubprocess). The subprocess connects back over TCP and speaks the same msgpack protocol as a Python task, so the Python side heartbeats, manages state, and proxies every Execution-API call — meaning the language task, like a Python task, never holds the task JWT itself. Because the mature Python Supervisor handles the Airflow-facing concerns, this mode inherits its capabilities — remote task logs (S3/GCS, etc.), the full range of task states, and alternate XCom backends:

images/diagram_coordinator_execution_architecture.png

The end-to-end workflow of a coordinator task — from @task.stub through the coordinator, the language subprocess, the proxied Connection/Variable/XCom lookups, and reporting the final state — is shown below as a sequence diagram. The Supervisor is the central lifeline, so the subprocess ↔ Supervisor round-trip over loopback TCP is drawn as arrows going back and forth to its neighbours:

images/diagram_coordinator_execution_sequence.png

Note

Both the Go and Java SDKs are experimental and under active development. See the Non-Python Task SDKs guide — the Go SDK and Java SDK pages — for current status, quick-starts, and known limitations.


To implement the coordinator side of a new runtime, continue to Adding a new language SDK.