-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: Fix bugs in applying stream feature view and retrieving online features #2754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -372,6 +372,40 @@ def _get_feature_view( | |
| feature_view.entities = [] | ||
| return feature_view | ||
|
|
||
| @log_exceptions_and_usage | ||
| def get_stream_feature_view( | ||
| self, name: str, allow_registry_cache: bool = False | ||
| ) -> StreamFeatureView: | ||
| """ | ||
| Retrieves a stream feature view. | ||
|
|
||
| Args: | ||
| name: Name of stream feature view. | ||
| allow_registry_cache: (Optional) Whether to allow returning this entity from a cached registry | ||
|
|
||
| Returns: | ||
| The specified stream feature view. | ||
|
|
||
| Raises: | ||
| FeatureViewNotFoundException: The feature view could not be found. | ||
| """ | ||
| return self._get_stream_feature_view( | ||
| name, allow_registry_cache=allow_registry_cache | ||
| ) | ||
|
|
||
| def _get_stream_feature_view( | ||
| self, | ||
| name: str, | ||
| hide_dummy_entity: bool = True, | ||
| allow_registry_cache: bool = False, | ||
| ) -> StreamFeatureView: | ||
| stream_feature_view = self._registry.get_stream_feature_view( | ||
| name, self.project, allow_cache=allow_registry_cache | ||
| ) | ||
| if hide_dummy_entity and stream_feature_view.entities[0] == DUMMY_ENTITY_NAME: | ||
| stream_feature_view.entities = [] | ||
| return stream_feature_view | ||
|
|
||
| @log_exceptions_and_usage | ||
| def get_on_demand_feature_view(self, name: str) -> OnDemandFeatureView: | ||
| """ | ||
|
|
@@ -935,7 +969,6 @@ def get_historical_features( | |
| all_feature_views, | ||
| all_request_feature_views, | ||
| all_on_demand_feature_views, | ||
| all_stream_feature_views, | ||
| ) = self._get_feature_views_to_use(features) | ||
|
|
||
| if all_request_feature_views: | ||
|
|
@@ -1321,9 +1354,14 @@ def write_to_online_store( | |
| ingests data directly into the Online store | ||
| """ | ||
| # TODO: restrict this to work with online StreamFeatureViews and validate the FeatureView type | ||
| feature_view = self.get_feature_view( | ||
| feature_view_name, allow_registry_cache=allow_registry_cache | ||
| ) | ||
| try: | ||
| feature_view = self.get_stream_feature_view( | ||
| feature_view_name, allow_registry_cache=allow_registry_cache | ||
| ) | ||
| except FeatureViewNotFoundException: | ||
| feature_view = self.get_feature_view( | ||
| feature_view_name, allow_registry_cache=allow_registry_cache | ||
| ) | ||
|
Comment on lines
+1357
to
+1364
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO we should try to build on top of #2733 to migrate these kinds of stray feature views to the right location.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm I see, I'll add a todo for this. |
||
| entities = [] | ||
| for entity_name in feature_view.entities: | ||
| entities.append( | ||
|
|
@@ -1456,7 +1494,6 @@ def _get_online_features( | |
| requested_feature_views, | ||
| requested_request_feature_views, | ||
| requested_on_demand_feature_views, | ||
| request_stream_feature_views, | ||
| ) = self._get_feature_views_to_use( | ||
| features=features, allow_cache=True, hide_dummy_entity=False | ||
| ) | ||
|
|
@@ -1994,15 +2031,17 @@ def _get_feature_views_to_use( | |
| allow_cache=False, | ||
| hide_dummy_entity: bool = True, | ||
| ) -> Tuple[ | ||
| List[FeatureView], | ||
| List[RequestFeatureView], | ||
| List[OnDemandFeatureView], | ||
| List[StreamFeatureView], | ||
| List[FeatureView], List[RequestFeatureView], List[OnDemandFeatureView], | ||
| ]: | ||
|
|
||
| fvs = { | ||
| fv.name: fv | ||
| for fv in self._list_feature_views(allow_cache, hide_dummy_entity) | ||
| for fv in [ | ||
| *self._list_feature_views(allow_cache, hide_dummy_entity), | ||
| *self._registry.list_stream_feature_views( | ||
| project=self.project, allow_cache=allow_cache | ||
| ), | ||
| ] | ||
| } | ||
|
|
||
| request_fvs = { | ||
|
|
@@ -2019,15 +2058,8 @@ def _get_feature_views_to_use( | |
| ) | ||
| } | ||
|
|
||
| sfvs = { | ||
| fv.name: fv | ||
| for fv in self._registry.list_stream_feature_views( | ||
| project=self.project, allow_cache=allow_cache | ||
| ) | ||
| } | ||
|
|
||
| if isinstance(features, FeatureService): | ||
| fvs_to_use, request_fvs_to_use, od_fvs_to_use, sfvs_to_use = [], [], [], [] | ||
| fvs_to_use, request_fvs_to_use, od_fvs_to_use = [], [], [] | ||
| for fv_name, projection in [ | ||
| (projection.name, projection) | ||
| for projection in features.feature_view_projections | ||
|
|
@@ -2048,23 +2080,18 @@ def _get_feature_views_to_use( | |
| fv = fvs[projection.name].with_projection(copy.copy(projection)) | ||
| if fv not in fvs_to_use: | ||
| fvs_to_use.append(fv) | ||
| elif fv_name in sfvs: | ||
| sfvs_to_use.append( | ||
| sfvs[fv_name].with_projection(copy.copy(projection)) | ||
| ) | ||
| else: | ||
| raise ValueError( | ||
| f"The provided feature service {features.name} contains a reference to a feature view" | ||
| f"{fv_name} which doesn't exist. Please make sure that you have created the feature view" | ||
| f'{fv_name} and that you have registered it by running "apply".' | ||
| ) | ||
| views_to_use = (fvs_to_use, request_fvs_to_use, od_fvs_to_use, sfvs_to_use) | ||
| views_to_use = (fvs_to_use, request_fvs_to_use, od_fvs_to_use) | ||
| else: | ||
| views_to_use = ( | ||
| [*fvs.values()], | ||
| [*request_fvs.values()], | ||
| [*od_fvs.values()], | ||
| [*sfvs.values()], | ||
| ) | ||
|
|
||
| return views_to_use | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,7 @@ class FeastObjectType(Enum): | |
| FEATURE_VIEW = "feature view" | ||
| ON_DEMAND_FEATURE_VIEW = "on demand feature view" | ||
| REQUEST_FEATURE_VIEW = "request feature view" | ||
| STREAM_FEATURE_VIEW = "stream feature view" | ||
| FEATURE_SERVICE = "feature service" | ||
|
|
||
| @staticmethod | ||
|
|
@@ -93,6 +94,9 @@ def get_objects_from_registry( | |
| FeastObjectType.REQUEST_FEATURE_VIEW: registry.list_request_feature_views( | ||
| project=project | ||
| ), | ||
| FeastObjectType.STREAM_FEATURE_VIEW: registry.list_stream_feature_views( | ||
| project=project, | ||
| ), | ||
| FeastObjectType.FEATURE_SERVICE: registry.list_feature_services( | ||
| project=project | ||
| ), | ||
|
|
@@ -108,6 +112,7 @@ def get_objects_from_repo_contents( | |
| FeastObjectType.FEATURE_VIEW: repo_contents.feature_views, | ||
| FeastObjectType.ON_DEMAND_FEATURE_VIEW: repo_contents.on_demand_feature_views, | ||
| FeastObjectType.REQUEST_FEATURE_VIEW: repo_contents.request_feature_views, | ||
| FeastObjectType.STREAM_FEATURE_VIEW: repo_contents.stream_feature_views, | ||
| FeastObjectType.FEATURE_SERVICE: repo_contents.feature_services, | ||
| } | ||
|
|
||
|
|
@@ -717,6 +722,30 @@ def get_feature_view( | |
| return FeatureView.from_proto(feature_view_proto) | ||
| raise FeatureViewNotFoundException(name, project) | ||
|
|
||
| def get_stream_feature_view( | ||
| self, name: str, project: str, allow_cache: bool = False | ||
| ) -> StreamFeatureView: | ||
| """ | ||
| Retrieves a stream feature view. | ||
|
|
||
| Args: | ||
| name: Name of stream feature view | ||
| project: Feast project that this stream feature view belongs to | ||
| allow_cache: Allow returning feature view from the cached registry | ||
|
|
||
| Returns: | ||
| Returns either the specified feature view, or raises an exception if | ||
| none is found | ||
| """ | ||
| registry_proto = self._get_registry_proto(allow_cache=allow_cache) | ||
| for feature_view_proto in registry_proto.stream_feature_views: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we moved this into a different field in the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so, the proto for stream feature view and feature view are different. |
||
| if ( | ||
| feature_view_proto.spec.name == name | ||
| and feature_view_proto.spec.project == project | ||
| ): | ||
| return StreamFeatureView.from_proto(feature_view_proto) | ||
| raise FeatureViewNotFoundException(name, project) | ||
|
|
||
| def delete_feature_service(self, name: str, project: str, commit: bool = True): | ||
| """ | ||
| Deletes a feature service or raises an exception if not found. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.