Skip to content

[Python] Refactor MatchContinuously onto the Watch transform - #39461

Open
Eliaaazzz wants to merge 4 commits into
apache:masterfrom
Eliaaazzz:matchcontinuously-on-watch
Open

[Python] Refactor MatchContinuously onto the Watch transform#39461
Eliaaazzz wants to merge 4 commits into
apache:masterfrom
Eliaaazzz:matchcontinuously-on-watch

Conversation

@Eliaaazzz

@Eliaaazzz Eliaaazzz commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Routes fileio.MatchContinuously through the Watch transform when deduplication is enabled.

Built on the Watch transform, which merged in #39023.

What changes

The polling loop and the set of already-matched file ids move into the splittable DoFn restriction. The per-key state DoFns _RemoveDuplicates and _RemoveOldDuplicates are removed, since Watch performs the deduplication.

has_deduplication=False keeps the previous PeriodicImpulse behaviour, so that path is unchanged.

Behaviour change worth calling out

Because the matched ids are part of the restriction, a runner with checkpointing enabled restores them after a restart and does not reprocess files. The class docstring previously stated the opposite, that already processed files are reprocessed on restart, which was accurate for the earlier memory-only implementation. The docstring is updated in this PR.

Validation

Fault tolerance on Flink 1.20 with checkpointing enabled: two files present at start, two added while running, then the TaskManager was killed mid stream. The JobManager restored the job from checkpoint 3 and every file was still emitted exactly once, with no reprocessing.

Completed checkpoint 3 for job 15502720... (56814 bytes)
Job beam-watch-matchcontinuously switched from state RUNNING to RESTARTING
Job beam-watch-matchcontinuously switched from state RESTARTING to RUNNING
Restoring job 15502720... from Checkpoint 3

Also exercised on Dataflow Runner v2 reading a real GCS prefix: files present at startup and files added to the bucket mid run were each emitted exactly once.

Unit tests: watch_test.py 28 passed, fileio_test.py MatchContinuously tests 8 passed. Formatted with yapf 0.43.0 and isort 7.0.0.


Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:

  • Choose reviewer(s) and mention them in a comment (R: @username).
  • Mention the appropriate issue in your description (for example: addresses #123), if applicable. This will automatically add a link to the pull request in the issue. If you would like the issue to automatically close on merging the pull request, comment fixes #<ISSUE NUMBER> instead.
  • Update CHANGES.md with noteworthy changes.
  • If this contribution is large, please file an Apache Individual Contributor License Agreement.

See the Contributor Guide for more tips on how to make review process smoother.

To check the build health, please visit https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @tvalentyn for label python.

Note: If you would like to opt out of this review, comment assign to next reviewer.

Available commands:

  • stop reviewer notifications - opt out of the automated review tooling
  • remind me after tests pass - tag the comment author after tests pass
  • waiting on author - shift the attention set back to the author (any comment or push by the author will return the attention set to the reviewers)

The PR bot will only process comments in the main thread (not review comments).

@tvalentyn

Copy link
Copy Markdown
Contributor

R: @Abacn

@github-actions

Copy link
Copy Markdown
Contributor

Stopping reviewer notifications for this pull request: review requested by someone other than the bot, ceding control. If you'd like to restart, comment assign set of reviewers

Route MatchContinuously through Watch when deduplication is enabled. The
polling loop and the set of already-matched file ids now live in the
splittable DoFn restriction, replacing the per-key state DoFns.

Because the matched ids are part of the restriction, a runner with
checkpointing enabled restores them after a restart and does not reprocess
files. The docstring is updated accordingly. Verified on Flink 1.20: after
killing the TaskManager mid stream the job restored from a checkpoint and
every file was still emitted exactly once.

has_deduplication=False keeps the previous PeriodicImpulse behaviour.
Comment thread sdks/python/apache_beam/io/fileio.py Outdated
termination = _WatchWindowTermination(clock, start_ts.micros, max_polls)
if self.match_upd:
output_key_fn = _file_path_and_mtime_key
output_key_coder = TupleCoder([StrUtf8Coder(), FloatCoder()])

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.

Please clean up the PR against the merged version of watch transform. In particular, please check #39023 (comment)

These explicit coder settings shouldn't be needed, otherwise it suggests the coder inference in #39023 still has some gaps

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, there was a real gap: registry.get_coder does not convert typing or native generic annotations, so tuple[str, float] silently fell back to pickling, and the explicit coder here was masking exactly that. Watch now converts hints with convert_to_beam_type before the registry lookup, the explicit settings are gone, and the key functions infer the same StrUtf8Coder and TupleCoder as before, with a new watch_test case covering the native generic annotation. Also trimmed the docstrings this PR adds and updated the stale stacked PR note in the description. This round is one follow up commit on top of the reviewed one, commit 4a804a4.

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.

One more follow up commit, 4587416, from the same self review pass: the poll return type is annotated PollResult[FileMetadata], typing.Tuple key inference is covered next to the native form, the third party test imports are ordered, and the remaining Java references in test comments are gone.

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.

I see: https://github.com/apache/beam/pull/39461/changes#diff-85cec812142c922bcbd7cee2c200ba285d780eb1aec2595ccbbd035691971b6dR649

#39527 just changed relavent typehints to native tuple[] ones. cc: @jrmccluskey would this suggest a inconsistency between PEP 585 types and old style typing.Tuple ones?

@Eliaaazzz Eliaaazzz Jul 29, 2026

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.

Split the inference fix and its tests into #39547 as requested, and commit b8b96ae returns watch.py and watch_test.py to master here, so this PR is fileio only again.

registry.get_coder receives typing and native generic annotations such
as tuple[str, float] unconverted and falls back to pickling. Watch now
converts hints with convert_to_beam_type before the registry lookup, so
MatchContinuously's annotated key functions infer the same StrUtf8Coder
and TupleCoder the explicit settings supplied. Also trims the docstrings
and comments this PR adds.
…ports

Annotates _MatchContinuouslyPollFn with PollResult[FileMetadata], covers
typing.Tuple key inference alongside the native form, reorders the
third-party test imports, and drops the remaining Java references from
test comments.
restarted, already processed files will be reprocessed. Consider an alternate
technique, such as Pub/Sub Notifications
(https://cloud.google.com/storage/docs/pubsub-notifications)
file ids for every file the pattern has matched. With ``has_deduplication``

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.

Just a side note, with #39090 in we should be able to eliminate this comment

Matching continuously scales poorly, as it is stateful, and requires storing

watch.py and watch_test.py return to master; the inference fix lands
separately so it can make the release cut. The annotated key functions
meanwhile fall back to the deterministic FastPrimitivesCoder form, which
keeps dedup correct.
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