Repository navigation
Java SDK: Serialize native Dags to DagSerialization v3 - #71190
Merged
jason810496 merged 13 commits intoOct 8, 2026
Merged
Conversation
This was referenced Aug 5, 2026
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
3 times, most recently
from
August 14, 2026 06:46
10a0553 to
3a0d86d
Compare
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
from
August 21, 2026 07:30
3a0d86d to
83e98da
Compare
1 task done
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
7 times, most recently
from
September 23, 2026 07:20
c0280df to
654271f
Compare
1 task done
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
2 times, most recently
from
October 6, 2026 06:03
efba9d2 to
c95e521
Compare
1 task done
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
2 times, most recently
from
October 7, 2026 07:44
2fbc57b to
50f810b
Compare
kaxil
reviewed
Oct 7, 2026
henry3260
approved these changes
Oct 7, 2026
henry3260
left a comment
Contributor
There was a problem hiding this comment.
Just want to help unblock the technical side here, not a formal review.
jason810496
force-pushed
the
feature/java-sdk-dag-serialization
branch
from
October 7, 2026 17:22
50f810b to
b8938f8
Compare
jason810496
requested review from
ephraimbuddy and
jedcunningham
as code owners
October 7, 2026 17:22
jason810496
commented
Oct 7, 2026
jason810496
left a comment
Member
Author
There was a problem hiding this comment.
Thanks for the review.
A Java-authored Dag could not reach the scheduler on its own: the runtime answered task-execution requests only, so a Python stub file still had to exist purely to describe the Dag's structure. With dependency edges and schema-keyed configuration now recorded on the Java Dag model, the runtime has everything it needs to answer the coordinator's DagFileParseRequest the same way the Go SDK does, and the nativedag examples become real schedulable Dags with no Python counterpart. Native Java tasks deliberately emit no _arg_bindings: the execution API delivers bindings only for Python _StubOperator tasks, and a Java task always runs inside the JVM bundle that already holds its wired inputs, so the runtime resolves them locally. Cron schedules map to CronTriggerTimetable only, mirroring the Go SDK until the supervisor forwards the [scheduler] timetable flags over the coordinator protocol (see the TODO at the timetable serializer).
…steers it `Bundle.register` expands the group edges, `Refs` decides which group each task lands in, and `sdk/build.gradle.kts` generates the schema fields the serializer writes from. All three change the serialized output, so add them to the hook's trigger set. Report a failed classpath build with the Gradle output instead of an `IndexError` on an empty line list or a bare `CalledProcessError`.
Expand a cron preset before writing it, so `@daily` serializes as `0 0 * * *` and the Dag hashes the same as the Python one. Reject a schedule that is neither a preset nor a cron expression where the Dag is written, rather than letting it reach a scheduler that cannot build a timetable from it. Take the timezone from the Dag's start date, as Python's `DAG.timezone` does, instead of always writing UTC: a Dag started at a non-UTC offset now fires at that offset rather than at the same wall clock in UTC.
A native Dag's serialized form recorded its task edges but nothing about what each task was called with, so changing a literal argument left the Dag byte-identical: no new version, and nothing in the version diff. Write the same `is_stub` flag and `_arg_bindings` spec the TypeScript SDK writes, as ADR-0007 decision G says a natively authored Dag should. The wiring view now passes its parameter names along with the arguments, so each binding can name the parameter it feeds. A Java task still resolves its arguments from the Dag in its own bundle rather than reading the spec back: the bundle that runs the task also holds the call that wired it, so the values never have to travel, and a `TaskInput` keeps binding as one whole input. The spec is what Airflow records and shows. Since the spec travels as JSON, a literal with no JSON form is now rejected where the Dag is parsed instead of reaching a task that cannot receive it.
…arse A Dag that could not be serialized threw out of the parse response, so every other Dag in the same JAR vanished with it and `import_errors` was never filled. Report the failure under the bundle-relative path the way the TypeScript SDK does, naming the Dag, and serialize the rest.
`native-dag-authoring`, `task-args`, `taskflow-dependencies` and `task-group` are new in this release, not in 3.3, which is what the matrix's "supported since" column says.
…uts first The cron shape check now accepts comma lists of names, month and weekday names, the `#`, `L` and `W` qualifiers and the `@midnight` and `@annually` aliases, which Python stores unexpanded. Each of these previously turned the Dag into an import error. The schema-default comment records why explicit default values are dropped, and the timezone TODO and the Java docs note that a cron Dag with no start date is scheduled in UTC. A wired input wins over a runtime binding in ArgValues. The Java docs and ADR 0007 now say so, and a test pins it. The conformance run now passes `--supports literal_inputs` and the Java serializer wires each task's upstream handles and literals as call arguments, so the run exercises `_arg_bindings`. The hook's file filter covers `internal/Fields.kt`. Selective checks skip the hook unless a java-sdk file, Airflow's serializer or schema, or the shared harness changed, through a new file group because serializer changes do not force full tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Make the runtime can answer the coordinator's
DagFileParseRequestitself.How
parseDagsserializes every Dag registered on theBundleto DagSerialization v3.Server.dispatchTaskgains aDagFileParseRequestbranch alongsideStartupDetails, so a bundle process serves either a task run or a parse request.[core]config, such asmax_active_tasksandcatchup. Airflow fills those in from its own config when it receives the Dag.task_groupobject Python writes, with each group carrying its own edges.is_stub, and one the Dag called with arguments also carries the_arg_bindingsspec ADR-0007 defines, so the serialized Dag records what each task is called with.Example
load(transform(extracted, lit(1.5)))in a wiring class serializes as:The spec travels as JSON, so
lit(...)takes a string, number, boolean, list or map; anything else is rejected when the Dag is parsed.Cross-language validation
check-java-sdk-serialization-conformancebuilds the shared test Dags ofscripts/ci/lang_sdk_serialization/test_dags.yamlwith both this SDK and Airflow's own serializer, loads the Java output throughDagSerialization.validate_schemaandfrom_dict, and compares the two field by field.Known limitation
Cron schedules map to
CronTriggerTimetableonly, as the TypeScript SDK does, until the supervisor forwards the[scheduler]timetable flags over the coordinator protocol. There is aTODOat the timetable serializer.Was generative AI tooling used to co-author this PR?