Skip to content

Add the manifest format for manifest-backed versioned Dag bundles - #72476

Open
ephraimbuddy wants to merge 5 commits into
apache:mainfrom
astronomer:add-manifest-bundle-primitives
Open

ephraimbuddy wants to merge 5 commits into
apache:mainfrom
astronomer:add-manifest-bundle-primitives

Conversation

@ephraimbuddy

Copy link
Copy Markdown
Contributor

This is the first of several changes that add manifest-backed Dag bundles. A manifest lists every file in a bundle with its checksum, and the bundle version is a hash of that list.

Bundles get published and read on different machines, so both sides have to agree on how the list is written and on what makes a version or a file path valid. Keeping those rules in one place lets the storage backends that come next share them instead of each keeping its own copy.

POC: https://github.com/ephraimbuddy/airflow-manifest-bundle


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Opus 5 following the guidelines


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@ephraimbuddy ephraimbuddy changed the title Add the manifest format for versioned Dag bundles Add the manifest format for manifest-backed versioned Dag bundles Sep 10, 2026
@ephraimbuddy
ephraimbuddy force-pushed the add-manifest-bundle-primitives branch from 310fb7b to c93a7f7 Compare September 10, 2026 19:02
Comment thread airflow-core/src/airflow/dag_processing/bundles/manifest.py Outdated
Comment thread airflow-core/src/airflow/dag_processing/bundles/manifest.py Outdated
Comment thread airflow-core/src/airflow/dag_processing/bundles/manifest.py Outdated
@ephraimbuddy
ephraimbuddy force-pushed the add-manifest-bundle-primitives branch from 4ab9e24 to bc0b815 Compare October 7, 2026 10:19

@ferruzzi ferruzzi left a comment

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.

Couple non-blocking thoughts, otherwise thanks for the version_id addition.

I like the error kind split that you added as well, that's a clean solution

Comment thread airflow-core/src/airflow/dag_processing/bundles/manifest.py Outdated
not check the manifest against its own contents; a caller that did not choose
``expected_sha256`` itself must additionally confirm that ``manifest["version"]``
equals ``compute_bundle_version(manifest["files"])``.
"""

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.

Non-blocking: Is there a reason to only verify the sha and nott he contents? Is that a security thing? IIRC your PoC had a validator that compared more thoroughly?

One reason I ask if because more thorough checking here cold confirm the structure and make the KeyError less likely (maybe impossible, but users will always find some new way to break things) as an alternative to my comment above about changing the error type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You are right. The digest check alone wasn't enough, so I've brought the PoC's validator back with stricter checks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You might want to take a look again

This is the first of several changes that add manifest-backed Dag bundles.
A manifest lists every file in a bundle with its checksum, and the bundle
version is a hash of that list.

Bundles get published and read on different machines, so both sides have to
agree on how the list is written and on what makes a version or a file path
valid. Keeping those rules in one place lets the storage backends that come
next share them instead of each keeping its own copy.
Reading a Dag bundle's source tree can fail for two different reasons. A file
that vanished mid-walk means someone edited the Dag folder while we were
looking at it, and a publisher should simply try again on its next pass. A
permission problem or a stale network handle is a standing fault that an
operator has to see and fix.

Only the first was being caught, so the second escaped as a raw OSError
instead of a bundle error, and folding both into the "source changed" error
would leave a misconfigured Dag folder pinned to a stale version with nothing
but a debug log to show for it.
An object store can keep several versions of the same key, so the key a release
was built from is not necessarily the one a consumer fetches later. Carrying the
backend's own identifier for each stored object, such as an S3 VersionId, leaves
room to pin a fetch to the exact object instead of trusting whatever currently
sits at the key.

Nothing populates it yet; it is optional so the local backend, which has no such
identifier, is unaffected. It is deliberately excluded from the bundle version:
republishing identical files to a versioned bucket mints new object versions, and
that must still resolve to one bundle version.
Every other read of a bundle source now reports trouble as a manifest error, but
a missing or unreadable root still escaped as a bare OSError, so a caller that
guards a publish with the module's own error type would not catch it. Unlike a
file vanishing mid-walk, a root we never saw is a configured path rather than a
concurrent edit, so none of these say the source changed: a typo in the
configuration must not look like something worth quietly retrying.

Backend object versions are also checked before the hashing pass rather than
during it, so a bad identifier no longer costs a full read of the tree first.
A digest only establishes that a manifest is the document it was taken from, and a
consumer reading a manifest had no way to check the file entries against anything.
Reading a malformed entry also failed as a stray KeyError rather than a manifest error.

Re-deriving the version from the entries binds the two together, which is an
integrity guarantee only against a version the publisher did not supply, such as an
operator's pin or one Airflow already recorded; checked against nothing but itself, a
manifest is merely self-consistent. Making the caller state which case applies keeps
the weaker one from being the default, and fixing the entry order keeps one set of
files from being publishable under several versions.
@ephraimbuddy
ephraimbuddy force-pushed the add-manifest-bundle-primitives branch from fa1d563 to a30fcde Compare October 8, 2026 13:54

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants