Title: Wire ComputeEngine.get_historical_features() into the standard retrieval path to replace per-store BFV transformation duplication · Issue #6359 · feast-dev/feast · GitHub
Open Graph Title: Wire ComputeEngine.get_historical_features() into the standard retrieval path to replace per-store BFV transformation duplication · Issue #6359 · feast-dev/feast
X Title: Wire ComputeEngine.get_historical_features() into the standard retrieval path to replace per-store BFV transformation duplication · Issue #6359 · feast-dev/feast
Description: Background Feast currently has two separate mechanisms for applying BatchFeatureView (BFV) transformations during historical feature retrieval: Compute Engine DAG path (SparkComputeEngine.get_historical_features(), RayComputeEngine.get_h...
Open Graph Description: Background Feast currently has two separate mechanisms for applying BatchFeatureView (BFV) transformations during historical feature retrieval: Compute Engine DAG path (SparkComputeEngine.get_histo...
X Description: Background Feast currently has two separate mechanisms for applying BatchFeatureView (BFV) transformations during historical feature retrieval: Compute Engine DAG path (SparkComputeEngine.get_histo...
Opengraph URL: https://github.com/feast-dev/feast/issues/6359
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Wire ComputeEngine.get_historical_features() into the standard retrieval path to replace per-store BFV transformation duplication","articleBody":"**Background**\n\nFeast currently has two separate mechanisms for applying BatchFeatureView (BFV) transformations during historical feature retrieval:\n\n1. **Compute Engine DAG path** (SparkComputeEngine.get_historical_features(), RayComputeEngine.get_historical_features()) — uses FeatureBuilder + ExecutionPlan with proper node sequencing: source → transform → filter → dedup. This path already handles BFV transformations correctly for both materialization and retrieval.\n\n2. **Offline Store path** (passthrough_provider.get_historical_features() → offline_store.get_historical_features()) — the standard path invoked by FeatureStore.get_historical_features(). This path bypasses the compute engine entirely.\n\nBecause of this split, each offline store that wants BFV compute-on-read during historical retrieval must re-implement transformation detection and application logic independently:\n\n- RayOfflineStore.get_historical_features() implements it via resolve_feature_view_source_with_fallback() + map_batches()\n- [PR #6357](https://github.com/feast-dev/feast/pull/6357) adds a parallel implementation for SparkOfflineStore\n\nThis means transformation logic is duplicated across stores rather than living in the shared compute engine layer.\n\n**Root Cause**\n\npassthrough_provider.get_historical_features() always delegates to self.offline_store, never to self.batch_engine:\n\n```\n# sdk/python/feast/infra/passthrough_provider.py\ndef get_historical_features(self, config, feature_views, ...):\n job = self.offline_store.get_historical_features(...) # batch_engine never called\n return job\n```\n\nNote the contrast with materialization, which correctly routes through the engine:\n\n`jobs = self.batch_engine.materialize(registry, task)`\n\n**Blocking Interface Mismatch**\n\nThe compute engine cannot be directly substituted today because of a fundamental impedance mismatch:\n\n- passthrough_provider.get_historical_features() receives feature_views: List[FeatureView] — multiple views for a single multi-view PIT join\n- HistoricalRetrievalTask (the compute engine's input) has feature_view: Union[BatchFeatureView, StreamFeatureView] — a single view\n\n```\n# sdk/python/feast/infra/common/retrieval_task.py\n@dataclass\nclass HistoricalRetrievalTask:\n feature_view: Union[BatchFeatureView, StreamFeatureView] # singular\n entity_df: Union[pd.DataFrame, str]\n ...\n```\n\nThe compute engine is designed for per-view DAG execution. The PIT join logic that combines multiple views into one entity-aligned training dataset lives in each offline store's SQL template, not in the compute engine.\n\n**Proposed Solution**\n\nThis is a multi-step refactor:\n\n**Step 1** — Extend HistoricalRetrievalTask to support multiple feature views:\n\n```\n@dataclass\nclass HistoricalRetrievalTask:\n feature_views: List[Union[BatchFeatureView, FeatureView]] # plural\n entity_df: Union[pd.DataFrame, str]\n feature_refs: List[str]\n```\n\n**Step 2** — Move PIT join coordination into ComputeEngine.get_historical_features():\n\nThe base ComputeEngine should orchestrate per-view DAG execution across all requested feature views and join the results back to entity_df — making the PIT join logic engine-owned rather than SQL-template-owned.\n\n**Step 3** — Wire the provider to call batch_engine for historical retrieval:\n\n```\n# passthrough_provider.py\ndef get_historical_features(self, config, feature_views, feature_refs, entity_df, ...):\n task = HistoricalRetrievalTask(\n feature_views=feature_views,\n feature_refs=feature_refs,\n entity_df=entity_df,\n ...\n )\n return self.batch_engine.get_historical_features(registry, task)\n```\n\n**Step 4** — Retire per-store transformation logic:\n\nOnce the compute engine owns this path, the custom BFV transformation code in RayOfflineStore and SparkOfflineStore can be removed. All stores get transformation support for free through the engine's FeatureBuilder.\n","author":{"url":"https://github.com/ntkathole","@type":"Person","name":"ntkathole"},"datePublished":"2026-05-02T06:09:24.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/6359/feast/issues/6359"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:3b8d9cb6-3c5e-951e-9aed-95e1f211738d |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 8E68:12F35E:1AEE372:26901A6:6A4F7AE0 |
| html-safe-nonce | 1b2bccbbafeefde393b558ed11886211bd9ed9d77cf9eae7a21da06102957d43 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4RTY4OjEyRjM1RToxQUVFMzcyOjI2OTAxQTY6NkE0RjdBRTAiLCJ2aXNpdG9yX2lkIjoiMjQ4MjEzNTM1OTYxNTM2NzkwNCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 49d63935b293a1a846621cc16274a0373878a03ffb2888436b6649111c27f087 |
| hovercard-subject-tag | issue:4368138826 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/feast-dev/feast/6359/issue_layout |
| twitter:image | https://opengraph.githubassets.com/3a580eeae620dfbe7b265111c33a87e88ce77286102c1ba9106040ebf6c65471/feast-dev/feast/issues/6359 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/3a580eeae620dfbe7b265111c33a87e88ce77286102c1ba9106040ebf6c65471/feast-dev/feast/issues/6359 |
| og:image:alt | Background Feast currently has two separate mechanisms for applying BatchFeatureView (BFV) transformations during historical feature retrieval: Compute Engine DAG path (SparkComputeEngine.get_histo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | ntkathole |
| hostname | github.com |
| expected-hostname | github.com |
| None | b92d11c0aa4a77d54ef4af1078b6a15fb5a70a215b30c4ecf28889d5a8e656d9 |
| turbo-cache-control | no-preview |
| go-import | github.com/feast-dev/feast git https://github.com/feast-dev/feast.git |
| octolytics-dimension-user_id | 57027613 |
| octolytics-dimension-user_login | feast-dev |
| octolytics-dimension-repository_id | 161133770 |
| octolytics-dimension-repository_nwo | feast-dev/feast |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 161133770 |
| octolytics-dimension-repository_network_root_nwo | feast-dev/feast |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 4b249b445842943ed31549e027f57a8ade9881ed |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width