Skip to content

Add Kinesis Data Streams trigger - #71135

Open
aaron-y-chen wants to merge 2 commits into
apache:mainfrom
aaron-y-chen:add-kinesis-trigger
Open

Add Kinesis Data Streams trigger#71135
aaron-y-chen wants to merge 2 commits into
apache:mainfrom
aaron-y-chen:add-kinesis-trigger

Conversation

@aaron-y-chen

@aaron-y-chen aaron-y-chen commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

related: #52712

This is the first PR for Kinesis part of #52712, I will integrate the trigger with the common-messaging interface and add a system/e2e test in the next PR.

Summary

Add an asynchronous Kinesis Data Streams trigger for consuming records without occupying worker slots.

The trigger polls all shards, emits JSON-serializable record batches, handles iterator expiry, throttling, pagination, and in-run resharding, and checkpoints per-shard sequence numbers when an asset state store is available. New shards first discovered after a restart honor the configured initial position, so LATEST may skip records written during the downtime.

This is the provider-side foundation for the common-messaging integration tracked in #52712.


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

Generated-by: [GPT 5.6-sol] following the guidelines

@boring-cyborg boring-cyborg Bot added area:providers provider:amazon AWS/Amazon - related issues labels Aug 5, 2026
@aaron-y-chen

Copy link
Copy Markdown
Contributor Author

Those failures seem to be caused by the same issue:

flit build: error: unrecognized arguments: --no-setup-py

There is already a PR to fix it: #71118

@aaron-y-chen
aaron-y-chen marked this pull request as ready for review August 5, 2026 09:18
@aaron-y-chen
aaron-y-chen requested a review from o-nikolas as a code owner August 5, 2026 09:18
@aaron-y-chen

Copy link
Copy Markdown
Contributor Author

Waiting for another PR #71145 to be merged.

@aaron-y-chen

Copy link
Copy Markdown
Contributor Author

Okay, all CI failures have been fixed.

@eladkal
eladkal requested a review from vincbeck August 6, 2026 05:49
Comment thread providers/amazon/tests/unit/amazon/aws/triggers/test_kinesis.py
@aaron-y-chen

Copy link
Copy Markdown
Contributor Author

Hi @jason810496, I'd appreciate it if you could take a look when you have time. :)

@eladkal
eladkal requested a review from vincbeck August 17, 2026 20:09
@vincbeck

Copy link
Copy Markdown
Contributor

@seanghaeli @ramitkataria

Comment thread providers/amazon/src/airflow/providers/amazon/aws/triggers/kinesis.py Outdated
Comment thread providers/amazon/src/airflow/providers/amazon/aws/triggers/kinesis.py Outdated
@aaron-y-chen

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews, I believe this version is more concrete than before :)

@jason810496 jason810496 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding the new trigger to compelete #52712.

Comment on lines +173 to +174
def _save_checkpoint(self, sequence_numbers: dict[str, str]) -> None:
store = getattr(self, "asset_state_store", None)

@jason810496 jason810496 Aug 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IIUC, this means we're calling a blocking call in the trigger run coroutine.
We should follow the same pattern (asyncio.to_thread) in #71387 to workaround.

Or wait until #72127 get merged and invoke the async native method from the accessor.

Comment on lines +181 to +183
try:
store.set(self._build_checkpoint_key(), dict(sequence_numbers))
except ValueError:

@jason810496 jason810496 Aug 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the second PR that touched the Task Store within the trigger. #71387 is the first one.

cc @amoghrajesh to double check regarding the usage.
Just in case using Task Store within trigger isn't an expected pattern.

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's a good pattern to use (but slight correction: its asset state store not task state store), he Triggerer itself injects it at: https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/jobs/triggerer_job_runner.py#L1405-L1408, so a BaseEventTrigger attached to a watched asset is designed to read and write asset state. Watermarking a stream cursor is one of the use case.

Comment on lines +120 to +121
@property
def hook(self) -> KinesisHook:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure do we need to define as cached property or not? We might need to check the lifecycle of the Kinesis client. IIRC, this might be related to the https://lists.apache.org/thread/owp7869xmr5cyks9gx3lp66x53w1owyg discussion ( #71701 PR).

Just in case Kinesis client invoke eager external calls during construction.

config=self.botocore_config,
)

def _build_checkpoint_key(self) -> str:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be better to make this as a cache property? (e.g. asset_store_checkpoint_key

@amoghrajesh amoghrajesh 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.

Blocking due to sync access to asset state store

Comment on lines +227 to +250
async def _get_records(
self,
client: BaseAwsConnection,
shard_id: str,
shard_iterator: str,
after_sequence_number: str | None,
fallback_iterator_type: str,
) -> dict[str, Any]:
try:
return await client.get_records(
ShardIterator=shard_iterator,
Limit=self.batch_size,
)
except client.exceptions.ExpiredIteratorException:
shard_iterator = await self._get_shard_iterator(
client,
shard_id,
after_sequence_number,
fallback_iterator_type,
)
return await client.get_records(
ShardIterator=shard_iterator,
Limit=self.batch_size,
)

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.

A second ExpiredIteratorException will propagate through and kill the trigger.

Comment on lines +58 to +59
:param shard_iterator_type: Position used when a shard has no checkpoint. ``LATEST`` only sees records
that arrive after the watcher starts; ``TRIM_HORIZON`` starts from the oldest retained record.

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.

AT_TIMESTAMP and AT_SEQUENCE_NUMBER are rejected?

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

Labels

area:providers provider:amazon AWS/Amazon - related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants