Repository navigation
Anchor service metadata patterns so foreach paths do not prefix-match - #3357
Conversation
…r-select
Client.parent_tasks/child_tasks build a filter pattern from a task's
foreach-execution-path and query filter_tasks_by_metadata. The local provider
matched it with regex.match, which only anchors the start, so an exact path
like "middle:1" also matched "middle:10" and "middle:11" once a foreach had
10+ splits, and parent_tasks/child_tasks returned the wrong tasks.
Use regex.fullmatch in the local provider, and anchor the pattern the service
provider forwards to the metadata service as ^(?:pattern)$ so it matches in
full there too (the existing ".*" and "{path},.*" patterns already require
the service to treat this as a regex). The ".*" match-all short-circuit and
the "{path},.*" descendant form are unchanged.
Fixes Netflix#3341
Greptile SummaryThe PR anchors metadata-service task-filter patterns so exact foreach execution paths no longer select tasks whose indices merely share a prefix.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| metaflow/plugins/metadata_providers/service.py | Anchors forwarded task-metadata regex patterns while preserving the match-all short circuit. |
| test/unit/test_service_metadata_provider.py | Verifies exact and descendant patterns are anchored and match-all remains unfiltered. |
Reviews (2): Last reviewed commit: "Scope fix to service metadata provider" | Re-trigger Greptile
|
Thanks for the credit and for laying out the overlap so clearly, that was a considerate way to handle it. The split you proposed works well from my side: #3342 has been open since Aug 16 with the local provider fix ( @talsperre happy to go with whatever ordering you prefer. |
|
Thanks @nileshpatil6, likewise, your #3342 nailed the local-provider side. Hoping we can get both landed. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3357 +/- ##
=========================================
Coverage ? 31.02%
=========================================
Files ? 382
Lines ? 52717
Branches ? 9303
=========================================
Hits ? 16353
Misses ? 35153
Partials ? 1211 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks for the review @talsperre 🙏 |
…#3342) Addresses the local-provider side of #3341. The service-provider side is handled by #3357. `LocalMetadataProvider.filter_tasks_by_metadata` matched metadata values with `regex.match`, which anchors only at the start. `Task.parent_tasks` / `child_tasks` build their pattern from `foreach-execution-path`, so once a foreach has 11 or more items the pattern `middle:1` also matched the values `middle:10` and `middle:11`, and the client resolved the wrong tasks. With `spin` this turns into a hard failure ("not a join step but gets multiple inputs"). The fix switches that one call to `regex.fullmatch`, so a pattern has to match the whole value. The reason `fullmatch` is safe here is that the patterns this function receives are already written to cover the part of the path they care about. From `metaflow/client/core.py`: - an exact ancestor path, e.g. `middle:1` — should match only that task, which is what this fixes - a descendant pattern built as `f"{current_path},.*"` — the trailing `.*` still consumes the rest of the value, so nested foreach lookups are unaffected - `".*"` for the match-all case — unaffected, and it is short-circuited before the regex anyway I checked the values that a 12 item foreach produces: | pattern | `re.match` (before) | `re.fullmatch` (after) | |---|---|---| | `middle:1` | `middle:1`, `middle:10`, `middle:11`, `middle:1,inner:0` | `middle:1` | | `middle:1,.*` | `middle:1,inner:0` | `middle:1,inner:0` | | `.*` | all | all | Only the over-matching case changes. The service metadata provider passes the pattern to the backend instead of matching locally, so this is the only client-side matcher affected. Added `test_filter_tasks_by_metadata_does_not_match_prefixes` in `test/unit/test_local_metadata_provider.py`, covering the exact path, the prefixed sibling, a nested descendant pattern and match-all. Verified it fails before the change (the `middle:1` lookup returns three extra tasks) and passes after; the existing test in that file still passes. --------- Co-authored-by: Shashank Srikanth <[email protected]>
Documents that the fix is complete and provides verification instructions. The core fixes were already merged in PRs Netflix#3342 and Netflix#3357. This commit adds comprehensive documentation showing: - The fix is complete and verified - How to verify the fix works - Technical details of before/after behavior - Links to the merged PRs Issue Netflix#3341 can now be closed. Co-authored-by: Shriprasad R Patil <[email protected]>
This commit adds a comprehensive verification script that demonstrates the fix for issue Netflix#3341 is working correctly. Issue: Netflix#3341 The issue occurred when Task.parent_tasks/child_tasks resolved wrong tasks because foreach indices like 'middle:1' would match 'middle:10' and 'middle:11' due to prefix collision in regex matching. Fix applied upstream in PRs Netflix#3342 and Netflix#3357: - LocalMetadataProvider: Changed from regex.match to regex.fullmatch - ServiceMetadataProvider: Anchored patterns with ^(?:pattern)$ The verification script tests: 1. Local provider uses fullmatch to prevent prefix collisions 2. Service provider anchors patterns correctly 3. Both providers produce consistent results 4. Descendant patterns (e.g., 'middle:1,.*') still work 5. Wildcard patterns (e.g., '.*') still match all paths All tests pass, confirming the fix works as expected.
Documents that the fix is complete and provides verification instructions. The core fixes were already merged in PRs Netflix#3342 and Netflix#3357. This commit adds comprehensive documentation showing: - The fix is complete and verified - How to verify the fix works - Technical details of before/after behavior - Links to the merged PRs Issue Netflix#3341 can now be closed.
PR Type
Summary
Handles the OSS service-provider side of #3341.
Task.parent_tasksandTask.child_taskspass foreach execution-path patterns to the metadataservice, whose regex matching is unanchored. An exact path such as
middle:1could therefore also select
middle:10andmiddle:11.The local-provider side is handled separately by #3342.
Root Cause
ServiceMetadataProvider.filter_tasks_by_metadataforwarded the raw patternto the metadata service:
The service applies that pattern as an unanchored regex search.
Fix
Wrap the forwarded pattern in full-string anchors:
This makes
middle:1exact while preserving the descendant patternmiddle:1,.*. The existing.*match-all case remains short-circuited andsends no pattern.
Tests
test/unit/test_service_metadata_provider.pycovers:Run with:
Non-Goals
OSS client boundary.
AI Tool Usage
Tool disclosed by the author: Claude Code, used to help implement the original
change and tests. The service-only rescope retains that implementation.