Skip to content

fix: Keep entity columns out of StreamFeatureView proto features - #7033

Open
vidigoat wants to merge 1 commit into
feast-dev:masterfrom
vidigoat:fix/stream-feature-view-entity-columns
Open

vidigoat wants to merge 1 commit into
feast-dev:masterfrom
vidigoat:fix/stream-feature-view-entity-columns

Conversation

@vidigoat

@vidigoat vidigoat commented Oct 18, 2026 •

Copy link
Copy Markdown

What this PR does / why we need it:

StreamFeatureView.to_proto() serializes spec.features from self.schema, and schema includes the entity (join key) columns. FeatureView.to_proto_spec() uses self.features, so every other feature view type keeps join keys only in spec.entity_columns. Because of this mismatch, a stream feature view loaded back from the registry lists its join key as a feature.

Repro with the local provider (file offline store, SQLite online store):

driver = Entity(name="driver", join_keys=["driver_id"], value_type=ValueType.INT64)
src = FileSource(name="src", path=path, timestamp_field="event_timestamp")
push = PushSource(name="push", batch_source=src)
sfv = StreamFeatureView(
    name="driver_sfv", entities=[driver], ttl=timedelta(days=1),
    schema=[Field(name="driver_id", dtype=Int64), Field(name="conv_rate", dtype=Float32)],
    source=push, timestamp_field="event_timestamp",
)
store.apply([driver, src, push, sfv])

[f.name for f in sfv.features]                                         # ['conv_rate']
[f.name for f in store.get_stream_feature_view("driver_sfv").features]  # ['conv_rate', 'driver_id']

svc = FeatureService(name="svc", features=[store.get_stream_feature_view("driver_sfv")])
store.apply([svc])
store.get_historical_features(entity_df=entity_df, features=svc)
# ValueError: The column label 'driver_id' is not unique.

The same spec.features field is read by the Go feature server (NewFeatureViewFromStreamFeatureViewProto) and the UI, so they also see the join key as a feature.

This PR serializes self.features instead of self.schema, the same as FeatureView.to_proto_spec(). from_proto already restores entity_columns from spec.entity_columns, so nothing is lost. With the change, the registry copy above has features ['conv_rate'] and the historical retrieval returns the expected frame.

A registry written by an earlier version keeps the extra column until the next feast apply. That apply reports the stream feature view as updated and rewrites spec.features. I checked this locally: apply on master, then re-apply with this change. If you would rather also filter entity columns out on the read side for registries that are never re-applied, I can add that here.

Merge order: please merge #7034 first (or together with this). Today the extra join key in the registry copy happens to make StreamFeatureView.__eq__ return False; once this PR removes it, the existing __eq__ short-circuit for UDF-less views makes the SQL registry skip writes when only aggregations change. #7034 fixes that equality.

Which issue(s) this PR fixes:

No existing issue. I found this while checking feature view proto round-trips, and searched open and closed issues and PRs for a duplicate before opening this.

Checks

  • I've made sure the tests are passing.
  • My commits are signed off (git commit -s)
  • My PR title follows conventional commits format

Testing Strategy

  • Unit tests
  • Integration tests
  • Manual tests
  • Testing is not required for this change

New test test_stream_feature_view_serialization_excludes_entity_columns_from_features in sdk/python/tests/unit/test_stream_feature_view.py defines the join key in schema. It checks that spec.features and spec.entity_columns are disjoint and that from_proto(to_proto(sfv)) == sfv. It fails on master (['dummy_field', 'driver_id'] == ['dummy_field']) and passes with this change. Manual test: the repro above, end to end, with the local provider.

Misc

Developed with Claude Code (AI) under my direction; the new unit test fails on master and passes with this change.

StreamFeatureView.to_proto() built spec.features from self.schema, which
includes the entity (join key) columns, while FeatureView.to_proto_spec()
uses self.features. A stream feature view read back from the registry
therefore listed its join key as a feature, and a FeatureService built
from it failed historical retrieval with a duplicate join key column.

Serialize self.features, matching FeatureView. Entity columns are still
stored in spec.entity_columns and restored by from_proto.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Vidit Patankar <vidit.patankar16@gmail.com>
@codecov-commenter

codecov-commenter commented Oct 18, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 50.98%. Comparing base (c9b9d72) to head (05a2956).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #7033   +/-   ##
=======================================
  Coverage   50.98%   50.98%           
=======================================
  Files         455      455           
  Lines       56964    56964           
  Branches     8313     8313           
=======================================
  Hits        29044    29044           
  Misses      25935    25935           
  Partials     1985     1985           
Flag Coverage Δ
go-feature-server 30.58% <ø> (ø)
python-unit 52.44% <ø> (ø)
Files with missing lines Coverage Δ
sdk/python/feast/stream_feature_view.py 70.35% <ø> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c9b9d72...05a2956. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants