Skip to content

feat: Version-pinning for FeatureService (online + offline) - #6718

Open
HSarwat wants to merge 4 commits into
feast-dev:masterfrom
HSarwat:feat/feature-service-version-pinning
Open

feat: Version-pinning for FeatureService (online + offline)#6718
HSarwat wants to merge 4 commits into
feast-dev:masterfrom
HSarwat:feat/feature-service-version-pinning

Conversation

@HSarwat

@HSarwat HSarwat commented Aug 9, 2026

Copy link
Copy Markdown

Closes #6717.
Related to #6389 (feature versioning in offline retrieval).

What

Lets a FeatureService pin a specific historical FeatureView / OnDemandFeatureView version, honored by both get_online_features and get_historical_features. Feast's docs currently list this as a known limitation ("Feature services always resolve to the active (promoted) version"); this closes that gap.

Commits

  1. fix: Pin FeatureView version in FeatureService projections — stamp projection.version_tag from the source view's version in FeatureService.__init__; read projection.version_tag (instead of None) in utils._get_feature_views_to_use. version="latest" is unaffected.
  2. feat: Support string feature references in FeatureServicefeatures accepts "<fv>[@<version>][:<feature>]" strings, resolved once in FeatureStore.apply via FeatureService.resolve_pending_refs. Factors _parse_feature_or_view_ref out of _parse_feature_ref (optional :feature).
  3. fix: Resolve version-pinned OnDemandFeatureViews in offline retrieval — shared utils._get_requested_on_demand_feature_views used by all three unversioned ODFV call sites; pinned ODFV refs now resolve to the pinned snapshot offline. This is the offline-retrieval versioning gap tracked in Proposal: Version-qualified feature refs in get_historical_features (offline store versioning) #6389.
  4. docs: Document FeatureService version pinning — alpha versioning page + enable_online_feature_view_versioning docstring clarified to note it gates offline too; Known Limitations updated.

Problem

Building a service from a version-pinned FeatureView silently falls back to the promoted version:

fv  = FeatureView(name="driver_stats", version="v2", ...)
svc = FeatureService(name="model_v1", features=[fv[["conv_rate"]]])
svc.feature_view_projections[0].version_tag    # -> None   (should be 2)
svc.feature_view_projections[0].name_to_use()  # -> "driver_stats"  (should be "driver_stats@v2")

Two bugs cause this: FeatureService.__init__ never translates the view's version string into projection.version_tag, and utils._get_feature_views_to_use hard-codes the version as None for the FeatureService branch. A related pre-existing bug (#6389) breaks offline retrieval of version-pinned OnDemandFeatureViews: offline stores re-fetch ODFVs via an unversioned registry.list_on_demand_feature_views, so a pinned ODFV ref raises ValueError: Could not find feature view from reference ... or silently drops features across every offline backend.

Design notes for reviewers

  • Priority ordering (commit 2): a pinned string ref always resolves from registry.get_feature_view_by_version, never from fvs_to_update (the apply batch only ever holds "latest" objects and would substitute the wrong schema). Covered by test_pinned_ref_ignores_fvs_to_update.
  • Backward compatible: unversioned refs / version="latest" behave exactly as before; every existing (unversioned) FeatureService is unaffected.
  • Flag name: enable_online_feature_view_versioning is misleadingly scoped to "online" — it gates both paths. Clarified in the docstring/docs here rather than renamed (rename would be a breaking config change; happy to follow up if maintainers prefer).

Tests

New unit tests: test_feature_service_versioning.py (object + string-ref pinning, proto round-trip, backward compat, error guards, priority ordering) and test_odfv_offline_versioning.py (version-aware ODFV resolution, v0 fallback, non-ODFV skip, dedup). All pass; ruff (0.16.0) + mypy clean on changed files.

HSarwat added 4 commits August 9, 2026 23:21
A FeatureService built from a version-pinned FeatureView (e.g.
FeatureView(version="v2")) silently served the promoted version instead
of the pinned one, defeating the guarantee a FeatureService is meant to
provide. Two bugs caused this:

1. FeatureService.__init__ appended the source view's projection without
   translating its `version` string into `projection.version_tag` (the
   field name_to_use() checks to render "fv@v2").
2. utils._get_feature_views_to_use hard-coded the version as None for the
   FeatureService branch, so retrieval always fell through to the
   promoted snapshot.

Stamp version_tag from the source view's version in __init__, and read
projection.version_tag (instead of None) during retrieval. The default
version ("latest") leaves version_tag as None, so every existing
unversioned FeatureService is unaffected. Covers both online and offline
retrieval, which share _get_feature_views_to_use.

Signed-off-by: h-sarwat <hussein_sarwat@yahoo.com>
FeatureService.features now accepts string refs using the same
'<feature_view>[@<version>][:<feature>]' syntax as
get_historical_features/get_online_features, e.g. "driver_stats@v2" or
"driver_stats@v2:trips_today". This lets a service pin a historical
FeatureView version without importing or reconstructing the underlying
object.

String entries are stashed at construction (no registry is available
then) and resolved once, in FeatureStore.apply/plan, via a new
FeatureService.resolve_pending_refs. A pinned ref always resolves from
the registry snapshot for that version; an unversioned ref prefers the
apply batch, then the promoted version. utils._parse_feature_ref is
refactored onto a new _parse_feature_or_view_ref that makes the
':<feature>' suffix optional (whole-view refs); _parse_feature_ref is
unchanged for existing callers.

Signed-off-by: h-sarwat <hussein_sarwat@yahoo.com>
Offline stores re-fetch OnDemandFeatureViews from the registry
independently of the version-aware online path, using an unversioned
registry.list_on_demand_feature_views. A version-pinned ODFV (e.g. from
a FeatureService pinning a specific ODFV version) therefore either raised
"Could not find feature view from reference odfv@v1:feat" or silently
served the promoted version during get_historical_features.

Add a shared utils._get_requested_on_demand_feature_views that resolves
each ref version-aware — pinned refs via get_feature_view_by_version
(stamping projection.version_tag so name_to_use() matches downstream),
unversioned refs via the promoted list as before. Route the three
unversioned call sites through it: OnDemandFeatureView.get_requested_odfvs,
offline_utils.get_feature_view_query_context, and dask.py's inline
duplicate. This covers every offline backend, which all funnel through
one of those. Unversioned refs are unchanged.

Signed-off-by: h-sarwat <hussein_sarwat@yahoo.com>
Document pinning a historical feature view version inside a
FeatureService, via both string feature refs
("driver_stats@v2:trips_today") and version-pinned FeatureView objects,
on the alpha feature-view-versioning page. Clarify that
enable_online_feature_view_versioning gates both online and offline
versioned resolution (they share one code path), both in the page and in
the RegistryConfig field docstring. Update Known Limitations: offline
version-qualified retrieval and version-pinned feature services are now
supported.

Signed-off-by: h-sarwat <hussein_sarwat@yahoo.com>
@HSarwat
HSarwat requested review from a team as code owners August 9, 2026 20:51
@HSarwat
HSarwat requested review from ejscribner, robhowley and tokoko and removed request for a team August 9, 2026 20:51
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.

FeatureService cannot pin a specific FeatureView/OnDemandFeatureView version

1 participant