feat(providers/amazon): allow disabling hook-level lineage in S3Hook …#63499
feat(providers/amazon): allow disabling hook-level lineage in S3Hook …#63499shnhdan wants to merge 19 commits into
Conversation
|
cc @kacpermuda can you clarify what is the desired behavior? I think this apply also for google storage and other cloud vendors. I'd rather we won't have different customizations for each provider. |
|
@shnhdan I believe the PR now contains only test, with no implementation on the actual hook. Maybe some faulty merge/rebase happened? |
|
Responded on the linked issue, not sure how, but we should make sure this arg is named consistently across providers. |
|
@kacpermuda Fixed the rebase error and restored the implementation.All 154 S3 tests are passing locally. I’m following the naming discussion and am open to renaming the parameter to align with Airflow's consistency standards. |
9bb9c06 to
0b3dec6
Compare
Co-authored-by: Vincent <97131062+vincbeck@users.noreply.github.com>
|
Thanks for working on this - the use case makes sense, and having a way to limit hook-level lineage emission can definitely be useful. One thing I’d like to raise before we settle on the exact shape of the solution is that hook-level lineage is fundamentally a core feature, but the places where we will likely want to control it live in multiple providers (S3, GCS, etc.). Because of that, it would be good to think a bit about how to keep it consistent across providers without introducing a dependency on newer core versions. In particular:
Because of that, it might be worth having a short discussion (either here or on devlist) about the intended approach. Maybe we end up doing exactly what this PR proposes, but it would be good to confirm that this is the direction we want and that we apply it consistently across providers. Curious what others think. cc @eladkal @potiuk @mobuchowski |
|
Honestly the name of this isn't right - "enable_hook_level_lineage" is what it does right now, but the intent is "no auto create assets". Not about disabling lineage entirely... Also this sounds like it is too broad -- we might want to disable the auto creation of assets, but keep the metadata that "hook X accessed asset Y". Additionally back to the original ask, maybe you need this at a per call level too -- you might want to disable it for the individual parts when uploaded, but still keep it when the single "upload complete" file is uploaded. As for into basehook or not: yes perhaps, but I think that also depends on answers to ^^ |
|
I agree that standardising this in BaseHook is the better path for long-term consistency. Even if this shift moves the work toward the Core team, I'd like to remain involved and work alongside the maintainers to implement the final version. Following this conversation for the final direction. |
|
@shnhdan There is no "core team" (or there is, but we don't have a monopoly on making changes to task-sdk etc.) PRs welcome in other words. Feel free to update this PR in place to become change to core. |
|
@ashb Thanks for the clarification ! I'm happy to take this on. I'll work on moving the implementation to Core/Task SDK and will update this PR once the refactor is ready for review. |
There was a problem hiding this comment.
Pull request overview
This PR adds a configuration switch to S3Hook intended to let users disable hook-level lineage emission (motivated by large numbers of per-object assets when uploading many files).
Changes:
- Adds an
enable_hook_level_lineageflag onS3Hookand gates someget_hook_lineage_collector()usage behind it. - Adds unit tests covering default/explicit values and
load_string()lineage behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
providers/amazon/src/airflow/providers/amazon/aws/hooks/s3.py |
Introduces the enable_hook_level_lineage flag and conditionally skips some lineage emission calls. |
providers/amazon/tests/unit/amazon/aws/hooks/test_s3.py |
Adds tests for the new flag and for lineage behavior in load_string(). |
| def __init__( | ||
| self, | ||
| aws_conn_id: str | None | ArgNotSet = AwsBaseHook.default_conn_name, | ||
| transfer_config_args: dict | None = None, | ||
| extra_args: dict | None = None, | ||
| *args, | ||
| **kwargs, | ||
| ) -> None: | ||
| kwargs["client_type"] = "s3" | ||
| kwargs["aws_conn_id"] = aws_conn_id | ||
| self._requester_pays = kwargs.pop("requester_pays", False) | ||
| self.enable_hook_level_lineage = kwargs.pop("enable_hook_level_lineage", True) | ||
|
|
| get_hook_lineage_collector().add_input_asset( | ||
| context=self, scheme="file", asset_kwargs={"path": filename} | ||
| ) | ||
| get_hook_lineage_collector().add_output_asset( | ||
| context=self, scheme="s3", asset_kwargs={"bucket": bucket_name, "key": key} | ||
| ) | ||
| if self.enable_hook_level_lineage: | ||
| get_hook_lineage_collector().add_output_asset( | ||
| context=self, scheme="s3", asset_kwargs={"bucket": bucket_name, "key": key} | ||
| ) |
| class TestS3HookLineageConfig: | ||
| def test_hook_lineage_enabled_by_default(self): | ||
| from airflow.providers.amazon.aws.hooks.s3 import S3Hook | ||
|
|
||
| hook = S3Hook() | ||
| assert hook.enable_hook_level_lineage is True | ||
|
|
||
| def test_hook_lineage_disabled_when_flag_false(self): | ||
| from airflow.providers.amazon.aws.hooks.s3 import S3Hook | ||
|
|
||
| hook = S3Hook(enable_hook_level_lineage=False) | ||
| assert hook.enable_hook_level_lineage is False | ||
|
|
||
| from unittest import mock | ||
|
|
||
| @mock.patch("airflow.providers.amazon.aws.hooks.s3.get_hook_lineage_collector") | ||
| @mock.patch("airflow.providers.amazon.aws.hooks.s3.S3Hook.get_conn") | ||
| def test_load_string_skips_lineage_when_disabled(self, mock_conn, mock_collector): | ||
| from airflow.providers.amazon.aws.hooks.s3 import S3Hook |
|
Hey @shnhdan, any updates? Are you still planning to continue working on this or would you like me to take over? |
|
Just FYI, I'm working on a more generic solution on the OL provider level, that would allow controlling event emission and content in more details, for a more detailed scope. Draft PR is #66992. This might solve the above problem as well. We might think about adding a hook scope to the hook_lineage control there, to cover all occurrences of hook across operators, but even the current approach might be enough. |
|
No response from OP. Closing |
…ineage Previously load_file (input asset), copy_object (both assets), and download_file (both assets) were ungated — firing even when enable_hook_level_lineage=False. All get_hook_lineage_collector() calls in S3Hook are now consistently gated behind the flag. Also normalized getattr() guard to direct attribute access.
|
Thanks @shnhdan, I think we can work without overlap here, those two PRs have the same goal, but you're focusing on producing side and I'm taking care of consuming side, which makes sense. For this PR, I think we can go with the solution you proposed, but maybe we can add a new config in lineage section of config, something very similar to default_deferrable, and then we can use this value as default for |
Following the default_deferrable pattern, add a new [lineage] section to airflow.cfg with default_hook_lineage boolean (default: True). S3Hook.__init__ now reads this as the default for enable_hook_level_lineage instead of hardcoding True. Allows global disabling via config while permitting per-instance override. Usage: [lineage] default_hook_lineage = False # airflow.cfg S3Hook(enable_hook_level_lineage=False) # per-instance As suggested by @kacpermuda in review. Pattern can be extended to other hooks in future PRs. Related: apache#66992
57e6158 to
b1c7886
Compare
|
@kacpermuda implemented as suggested — following the
Test results: 158 S3 tests passing, 988 lineage tests passing across amazon/openlineage/common.compat providers. |
|
|
||
| [lineage] | ||
|
|
||
| # When enabled, hooks will collect lineage data by default. | ||
| # Individual hooks can still override this via enable_hook_level_lineage kwarg. | ||
| # Set to False to disable hook-level lineage collection globally (e.g. to suppress | ||
| # intermediate asset spam during multipart uploads). | ||
| default_hook_lineage = True | ||
|
|
There was a problem hiding this comment.
There already is a lineage section in airflow config, you can just add a new key there
|
Note 🗑️ Closing stale draft · by This draft has been inactive for ~10 days. Closing to keep the queue tidy — no judgment on the change itself. Please reopen (or open a fresh PR) whenever you pick it back up, and a maintainer will be glad to take a look. Automated triage — may be imperfect. |
Description
When set to
False, allget_hook_lineage_collector()calls in the hook areskipped, preventing asset spam during operations like multipart uploads.
Default behavior is unchanged.
Changes:
hooks/s3.py— new param; all lineage calls acrossload_file,load_string,load_bytes,load_file_obj,copy_object, anddownload_fileconsistently gatedtests/test_s3.py— tests for default, disabled, and explicit enabled behaviorNote: Complementary to #66992 — that PR controls whether the OL listener
reads from
HookLineageCollectorat task end; this PR prevents the calls fromfiring at the source in
s3.pyentirely.Closes #63371
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Important
🛠️ Maintainer triage note for @shnhdan · by
@potiuk· 2026-06-22 06:31 UTCHelpful heads-up from the maintainers — please address before this PR can be reviewed (see the Pull Request quality criteria):
The ball is in your court — you've been assigned to this PR. Fix the above, then mark it Ready for review.
Automated triage — may be imperfect; a maintainer takes the next look.