Skip to content

Commit ed4a4f2

Browse files
feat: Add version tracking to FeatureView (#6101)
* feat: Add version tracking to FeatureView, StreamFeatureView, and OnDemandFeatureView Every `feast apply` now creates a version snapshot. Users can pin a feature view to a specific historical version declaratively via `version="v2"`. By default, the latest version is always served. - New proto: FeatureViewVersion.proto with version record/history - Added `version` field to FeatureViewSpec, StreamFeatureViewSpec, OnDemandFeatureViewSpec and version metadata to their Meta messages - New version_utils module for parsing/normalizing version strings - Version-aware apply_feature_view in both SQL and file registries - New `list_feature_view_versions` API on FeatureStore and registries - CLI: `feast feature-views versions <name>` subcommand - Updated all 14 templates with explicit `version="latest"` - Unit tests (28) and integration tests (7) for versioning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address PR review feedback from Devin - Fix current_version_number=0 being silently dropped during proto deserialization in FeatureView, OnDemandFeatureView (proto3 int32 default 0 is falsy in Python); use spec.version to disambiguate - Add current_version_number restoration in StreamFeatureView.from_proto (was missing entirely) - Use timezone-aware UTC datetime in SqlRegistry.list_feature_view_versions for consistency with the rest of the codebase - Add test for v0 proto roundtrip Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: Add feature view versioning documentation - Add Versioning section to feature-view.md concept page covering automatic snapshots, version pinning, version string formats, CLI usage, and Python SDK API - Add `feast feature-views versions` command to CLI reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address second round of PR review feedback from Devin - Fix current_version_number roundtrip bug: version="latest" (always truthy) caused None to become 0 after proto roundtrip; now check that spec.version is not "latest" before treating 0 as intentional - Use write_engine (not read_engine) for pre/post apply reads in SqlRegistry to avoid read replica lag causing missed version snapshots - Remove redundant version check in StreamFeatureView.__eq__ (parent FeatureView.__eq__ already checks it) - Add else clause to StreamFeatureView.from_proto for consistency - Add test for latest/None roundtrip preservation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Clean up version history on delete and use write_engine consistently - delete_feature_view now also deletes version history records, preventing IntegrityError when re-creating a previously deleted FV - _get_next_version_number uses write_engine instead of read_engine to avoid stale version numbers with read replicas Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: Clarify versioning auto-increment behavior and pin/revert flow - Add step-by-step walkthrough showing how versions auto-increment on changes and skip on identical re-applies - Add CLI example showing the apply/change/apply cycle - Clarify that pinning ignores constructor params and uses the snapshot - Explain how to return to auto-incrementing after a pin/revert Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Add pin conflict detection to both file and SQL registries Raises FeatureViewPinConflict when a user pins to an older version while also modifying the feature view definition (schema, source, etc.). Fixes FeatureView.__copy__() to include description and owner fields, which was causing false positive conflict detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address Devin review feedback on versioning - Add version parameter to BatchFeatureView constructor for consistency with FeatureView, StreamFeatureView, and OnDemandFeatureView - Clean up version history records in file registry delete_feature_view to prevent orphaned records on re-creation - Fix current_version_number proto roundtrip: preserve 0 when version="latest" (after first apply) instead of incorrectly returning None Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: Document concurrent multi-version serving limitations Clarify that versioning provides definition management and rollback, not concurrent multi-version serving. Document recommended approaches (separate projects or distinct FV names) for A/B testing scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Implement version-qualified feature references (@v<N>) Extends feature view versioning with support for reading features from specific versions at query time using the syntax: "driver_stats@v2:trips_today" Core changes: - Add _parse_feature_ref() to parse version-qualified feature references - Update all feature reference parsing to use _parse_feature_ref() - Add get_feature_view_by_version() to BaseRegistry and all implementations - Add FeatureViewProjection.version_tag for multi-version query support - Add version-aware _table_id() in SQLite online store (v0→unversioned, v1+→_v{N}) - Add VersionedOnlineReadNotSupported error for unsupported stores Features: - "driver_stats:trips" = "driver_stats@latest:trips" (backward compatible) - "driver_stats@v2:trips" reads from v2 snapshot using _v2 table suffix - Multiple versions in same query: ["driver@v1:trips", "driver@v2:daily"] - Version parameter added to all decorator functions for consistency Backward compatibility: - Unversioned table serves as v0, only v1+ get _v{N} suffix - All existing queries work unchanged - SQLite-only for now, other stores raise clear error Documentation: - Updated feature-view.md with @Version syntax examples - Updated feature-retrieval.md reference format - Added version examples to how-to guides Tests: 47 unit + 11 integration tests pass, no regressions Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix: Resolve mypy type errors in proto_registry_utils.py - Fix type inference issues in get_feature_view_by_version() - Use distinct variable names for different proto types - Ensure proper type annotations for BaseFeatureView subclasses * feat: Add version metadata to clean @v2 syntax from feature names Implement optional feature view version metadata in API responses to address the issue where internal @v2 version syntax was leaking into client responses. Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix: Update provider implementations with version metadata parameter Add missing include_feature_view_version_metadata parameter to: - EmbeddedGoOnlineFeaturesService.get_online_features() - FooProvider.get_online_features() and get_online_features_async() - FooProvider.retrieve_online_documents() and retrieve_online_documents_v2() This resolves CI failures where provider implementations were not updated with the new parameter from the abstract Provider interface. Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com> * fix: Add version metadata parameter to all online store implementations Add missing include_feature_view_version_metadata parameter to: - SqliteOnlineStore.retrieve_online_documents/v2 - FaissOnlineStore.retrieve_online_documents - QdrantOnlineStore.retrieve_online_documents - MilvusOnlineStore.retrieve_online_documents_v2 - RemoteOnlineStore.retrieve_online_documents/v2 - PostgresOnlineStore.retrieve_online_documents/v2 - ElasticsearchOnlineStore.retrieve_online_documents/v2 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Resolve mypy type errors in versioning code - Fix _schema_or_udf_changed overrides to accept BaseFeatureView parameter type, satisfying Liskov substitution principle. Each subclass narrows via isinstance check in the method body. - Use getattr for __code__ access on UDF in StreamFeatureView to handle MethodType correctly. - Change _update_metadata_fields proto parameter from Message to Any since the method accesses .spec and .meta attributes. - Guard BaseFeatureView attribute access (online, offline, etc.) with hasattr checks in _update_metadata_fields. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address Devin review feedback on versioning - Fix version-qualified features dropped with full_feature_names=True: use _parse_feature_ref to build clean requested_result_row_names - Fix retrieve_online_documents breaking with @vn refs: use _parse_feature_ref instead of split(":") for FV name extraction - Fix metadata-only updates not committed: add self.commit() after _update_metadata_fields in file registry - Fix ODFV transforms broken by version-qualified refs: use _parse_feature_ref in _augment_response_with_on_demand_transforms - Fix _update_metadata_fields not updating spec.version: add version field update so pinned-to-latest transitions persist - Fix _resolve_feature_counts inflating FV count: strip @vn from feature view names in metrics - Fix version snapshots storing stale current_version_number: set version number before serializing snapshot in both file and SQL registries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address additional Devin review feedback - Set spec.project on snapshot protos in SqlRegistry before serializing, so version snapshots include the correct project field - Fix _check_versioned_read_support to check projection.version_tag instead of current_version_number, so explicitly version-qualified reads (@v0) are correctly rejected on non-SQLite stores Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Make feature view versioning opt-in via registry config Versioning was always active on every `feast apply`. This adds an `enable_feature_view_versioning` boolean (default False) to RegistryConfig so version history, version pins, and version-qualified refs are only available when explicitly enabled. Existing behaviour is fully preserved when the flag is set to true. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address Devin review feedback on versioning issues - Fix SQLite _table_id to prefer projection.version_tag over current_version_number so @v2 refs read from the correct table - Detect feature name collisions for multi-version queries with full_feature_names=True (e.g. fv@v1:feat vs fv@v2:feat) - Remove unused include_feature_view_version_metadata parameter from retrieve_online_documents (v1) across all providers and online stores - Remove redundant name check from _schema_or_udf_changed since callers always match by name first Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Preserve version tag in response column names for multi-version queries When version-qualified refs (e.g. fv@v1:feat, fv@v2:feat) are used, include the version tag in full_feature_names output so multi-version queries produce distinct columns (fv@v1__feat vs fv@v2__feat). Also fix proto roundtrip test to match -1 sentinel behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Handle version race conditions gracefully with retry and forward declaration Auto-increment path (version="latest") now retries up to 3 times on IntegrityError in the SQL registry when concurrent applies race on the same version number. Explicit version path (version="v<N>") now creates the version if it doesn't exist (forward declaration) instead of raising FeatureViewVersionNotFound, with ConcurrentVersionConflict on race. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Gate feature services that reference versioned feature views Fail fast at apply time and retrieval time when a feature service references a versioned FV (current_version_number > 0) and enable_online_feature_view_versioning is off. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Resolve mypy errors and rename config field for clarity Rename enable_feature_view_versioning -> enable_online_feature_view_versioning to clarify that it controls online reads, not version history tracking. Fix mypy type narrowing issues with current_version_number (int | None) and variable redefinition in feature_store.py. Remove -1 sentinel from proto serialization in favor of treating proto default 0 without spec.version as None. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Enable feature service serving for versioned feature views When enable_online_feature_view_versioning is on and a FeatureService references a versioned FV, set version_tag on the projection so that online reads resolve to the correct versioned table. Previously the FeatureService path never set version_tag, causing reads from the wrong (unversioned) online store table. Changes: - _get_features(): version-qualify feature refs for FeatureService projections - _get_feature_views_to_use(): capture current_version_number before with_projection() discards it, then set version_tag on the projection - feature_store.py: fix mypy type narrowing for the gate check - Add integration tests for both code paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: Update RFC for feature service support and rename CLI command Update the Feature Services section in the RFC to reflect that feature services now correctly serve versioned FVs when the online versioning flag is enabled (automatic version_tag resolution on projections). Rename CLI command from `feast feature-views versions` to `feast feature-views list-versions` for consistency, and update all docs references. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): Add version display and Versions tab to feature view pages Show current version badge in feature view headers and listing table, and add a Versions tab with expandable version history across all feature view types (Regular, Stream, OnDemand). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(ui): Fix prettier formatting in feature view components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * updated utcnow Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * feat: Add version-aware materialization support Add --version flag to feast materialize/materialize-incremental CLI commands and corresponding Python SDK support. Gate versioned table IDs behind enable_online_feature_view_versioning config flag in SQLite online store. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Resolve three versioning regressions from review feedback 1. Stop injecting version_tag on FeatureService projections in utils.py, which was causing non-SQLite stores to reject FeatureService reads when versioning was enabled. 2. Persist version_tag in FeatureViewProjection proto (field 10) so it survives registry round-trips. 3. Fix _update_metadata_fields() to reset current_version_number to 0 when unpinning a feature view back to version="latest". Update tests to match new behavior and add test_unpin_from_versioned_to_latest. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Add --no-promote flag to feast apply and fix versioned ref parsing Add --no-promote flag that saves new version snapshots without promoting them to active, enabling phased rollouts without a transition window where unversioned consumers briefly see the new schema. Also audit and fix brittle feature reference parsing across the codebase to properly handle @v<N> version-qualified syntax: - Fix ibis offline store prefix matching to use name_to_use() - Fix Go ParseFeatureReference to strip @v<N> from view name - Fix passthrough_provider saved dataset column naming Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * docs: Consolidate versioning docs into alpha reference page Move detailed versioning documentation from the concepts page into the alpha reference page to avoid duplication. The concepts page now has a brief summary with a link to the full reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * docs: Add no_promote to apply_diff_to_registry docstring Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fix: Reject reserved chars in FV names and make version parser resilient Block `@` and `:` in feature view names via ensure_valid() to prevent ambiguous version-qualified reference parsing. Make _parse_feature_ref() fall back gracefully for legacy FV names containing `@` instead of raising, and update Go's ParseFeatureReference to only strip `@` suffixes matching `v<N>`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Add ensure_valid() call in Snowflake registry apply_feature_view The Snowflake registry was missing the ensure_valid() call that other registry implementations (SQL, Cask) already perform before applying a feature view. This ensures name validation (including reserved character rejection) runs consistently across all registry backends. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Make version_tag optional in proto and use HasField() for correct zero-value handling The proto field `version_tag` was a plain `int32`, making 0 and "not set" indistinguishable. Changed to `optional int32` so `HasField()` works correctly, and updated `FeatureViewProjection.from_proto` to use it instead of `> 0` which silently dropped version_tag=0. Also removes dead issue link from VersionedOnlineReadNotSupported error. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Address versioning review feedback (Snowflake, Go server, SQL registry) - Snowflake registry: raise NotImplementedError when no_promote=True since versioning is not supported - Go feature server: return error on versioned refs (@vn) instead of silently stripping the version and serving unversioned data - SQL registry: inline delete logic in delete_feature_view so the FV delete and version history cleanup run in a single transaction, preventing orphaned rows on partial failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Handle @latest in Go feature server and pre-compile version regex - Strip @latest suffix (equivalent to no version) instead of passing through as part of the FV name, which caused confusing "not found" errors - Pre-compile version tag regex to package-level var to avoid recompilation on every call in the hot path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1ac865e commit ed4a4f2

File tree

96 files changed

+5947
-1108
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+5947
-1108
lines changed

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
* [\[Alpha\] Vector Database](reference/alpha-vector-database.md)
167167
* [\[Alpha\] Data quality monitoring](reference/dqm.md)
168168
* [\[Alpha\] Streaming feature computation with Denormalized](reference/denormalized.md)
169+
* [\[Alpha\] Feature View Versioning](reference/alpha-feature-view-versioning.md)
169170
* [OpenLineage Integration](reference/openlineage.md)
170171
* [Feast CLI reference](reference/feast-cli-commands.md)
171172
* [Python API reference](http://rtd.feast.dev)

docs/getting-started/concepts/feature-retrieval.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,19 @@ feature_store.get_historical_features(features=feature_service, entity_df=entity
7878

7979
This mechanism of retrieving features is only recommended as you're experimenting. Once you want to launch experiments or serve models, feature services are recommended.
8080

81-
Feature references uniquely identify feature values in Feast. The structure of a feature reference in string form is as follows: `<feature_view>:<feature>`
81+
Feature references uniquely identify feature values in Feast. The structure of a feature reference in string form is as follows: `<feature_view>[@version]:<feature>`
82+
83+
The `@version` part is optional. When omitted, the latest (active) version is used. You can specify a version like `@v2` to read from a specific historical version snapshot.
8284

8385
Feature references are used for the retrieval of features from Feast:
8486

8587
```python
8688
online_features = fs.get_online_features(
8789
features=[
88-
'driver_locations:lon',
89-
'drivers_activity:trips_today'
90+
'driver_locations:lon', # latest version (default)
91+
'drivers_activity:trips_today', # latest version (default)
92+
'drivers_activity@v2:trips_today', # specific version
93+
'drivers_activity@latest:trips_today', # explicit latest
9094
],
9195
entity_rows=[
9296
# {join_key: entity_value}
@@ -95,6 +99,10 @@ online_features = fs.get_online_features(
9599
)
96100
```
97101

102+
{% hint style="info" %}
103+
Version-qualified reads (`@v<N>`) require `enable_online_feature_view_versioning: true` in your registry config and are currently supported only on the SQLite online store. See the [feature view versioning docs](feature-view.md#version-qualified-feature-references) for details.
104+
{% endhint %}
105+
98106
It is possible to retrieve features from multiple feature views with a single request, and Feast is able to join features from multiple tables in order to build a training dataset. However, it is not possible to reference (or retrieve) features from multiple projects at the same time.
99107

100108
{% hint style="info" %}

docs/getting-started/concepts/feature-view.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,25 @@ Feature names must be unique within a [feature view](feature-view.md#feature-vie
160160

161161
Each field can have additional metadata associated with it, specified as key-value [tags](https://rtd.feast.dev/en/master/feast.html#feast.field.Field).
162162

163+
## \[Alpha\] Versioning
164+
165+
Feature views support automatic version tracking. Every time `feast apply` detects a schema or UDF change, a versioned snapshot is saved to the registry. This enables auditing what changed, reverting to a prior version, querying specific versions via `@v<N>` syntax, and staging new versions without promoting them.
166+
167+
Version history tracking is **always active** with no configuration needed. The `version` parameter is fully optional — omitting it preserves existing behavior.
168+
169+
```python
170+
# Pin to a specific version (reverts the active definition to v2's snapshot)
171+
driver_stats = FeatureView(
172+
name="driver_stats",
173+
entities=[driver],
174+
schema=[...],
175+
source=my_source,
176+
version="v2",
177+
)
178+
```
179+
180+
For full details on version pinning, version-qualified reads, staged publishing (`--no-promote`), online store support, and known limitations, see the **[\[Alpha\] Feature View Versioning](../../reference/alpha-feature-view-versioning.md)** reference page.
181+
163182
## Schema Validation
164183

165184
Feature views support an optional `enable_validation` parameter that enables schema validation during materialization and historical feature retrieval. When enabled, Feast verifies that:

docs/how-to-guides/feast-snowflake-gcp-aws/build-a-training-dataset.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ feature_refs = [
2020
"driver_trips:maximum_daily_rides",
2121
"driver_trips:rating",
2222
"driver_trips:rating:trip_completed",
23+
# Optionally, reference a specific version: "driver_trips@v2:average_daily_rides"
2324
]
2425
```
2526

docs/how-to-guides/feast-snowflake-gcp-aws/read-features-from-the-online-store.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ Create a list of features that you would like to retrieve. This list typically c
2121
```python
2222
features = [
2323
"driver_hourly_stats:conv_rate",
24-
"driver_hourly_stats:acc_rate"
24+
"driver_hourly_stats:acc_rate",
25+
# Optionally, reference a specific version (requires enable_online_feature_view_versioning):
26+
# "driver_hourly_stats@v2:conv_rate"
2527
]
2628
```
2729

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# \[Alpha\] Feature View Versioning
2+
3+
{% hint style="warning" %}
4+
**Warning**: This is an _experimental_ feature. It is stable but there are still rough edges. Contributions are welcome!
5+
{% endhint %}
6+
7+
## Overview
8+
9+
Feature view versioning automatically tracks schema and UDF changes to feature views. Every time `feast apply` detects a change, a versioned snapshot is saved to the registry. This enables:
10+
11+
- **Audit trail** — see what a feature view looked like at any point in time
12+
- **Safe rollback** — pin serving to a prior version with `version="v0"` in your definition
13+
- **Multi-version serving** — serve both old and new schemas simultaneously using `@v<N>` syntax
14+
- **Staged publishing** — use `feast apply --no-promote` to publish a new version without making it the default
15+
16+
## How It Works
17+
18+
Version tracking is fully automatic. You don't need to set any version parameter — just use `feast apply` as usual:
19+
20+
1. **First apply** — Your feature view definition is saved as **v0**.
21+
2. **Change something and re-apply** — Feast detects the change, saves the old definition as a snapshot, and saves the new one as **v1**. The version number auto-increments on each real change.
22+
3. **Re-apply without changes** — Nothing happens. Feast compares the new definition against the active one and skips creating a version if they're identical (idempotent).
23+
4. **Another change** — Creates **v2**, and so on.
24+
25+
```
26+
feast apply # First apply → v0
27+
# ... edit schema ...
28+
feast apply # Detects change → v1
29+
feast apply # No change detected → still v1 (no new version)
30+
# ... edit source ...
31+
feast apply # Detects change → v2
32+
```
33+
34+
**Key details:**
35+
36+
* **Automatic snapshots**: Versions are created only when Feast detects an actual change to the feature view definition (schema or UDF). Metadata-only changes (description, tags, TTL) update in place without creating a new version.
37+
* **Separate history storage**: Version history is stored separately from the active feature view definition, keeping the main registry lightweight.
38+
* **Backward compatible**: The `version` parameter is fully optional. Omitting it (or setting `version="latest"`) preserves existing behavior — you get automatic versioning with zero changes to your code.
39+
40+
## Configuration
41+
42+
{% hint style="info" %}
43+
Version history tracking is **always active** — no configuration needed. Every `feast apply` that changes a feature view automatically records a version snapshot.
44+
45+
To enable **versioned online reads** (e.g., `fv@v2:feature`), add `enable_online_feature_view_versioning: true` to your registry config in `feature_store.yaml`:
46+
47+
```yaml
48+
registry:
49+
path: data/registry.db
50+
enable_online_feature_view_versioning: true
51+
```
52+
53+
When this flag is off, version-qualified refs (e.g., `fv@v2:feature`) in online reads will raise errors, but version history, version listing, version pinning, and version lookups all work normally.
54+
{% endhint %}
55+
56+
## Pinning to a Specific Version
57+
58+
You can pin a feature view to a specific historical version by setting the `version` parameter. When pinned, `feast apply` replaces the active feature view with the snapshot from that version. This is useful for reverting to a known-good definition.
59+
60+
```python
61+
from feast import FeatureView
62+
63+
# Default behavior: always use the latest version (auto-increments on changes)
64+
driver_stats = FeatureView(
65+
name="driver_stats",
66+
entities=[driver],
67+
schema=[...],
68+
source=my_source,
69+
)
70+
71+
# Pin to a specific version (reverts the active definition to v2's snapshot)
72+
driver_stats = FeatureView(
73+
name="driver_stats",
74+
entities=[driver],
75+
schema=[...],
76+
source=my_source,
77+
version="v2", # also accepts "version2"
78+
)
79+
```
80+
81+
When pinning, the feature view definition (schema, source, transformations, etc.) must match the currently active definition. If you've also modified the definition alongside the pin, `feast apply` will raise a `FeatureViewPinConflict` error. To apply changes, use `version="latest"`. To revert, only change the `version` parameter.
82+
83+
The snapshot's content replaces the active feature view. Version history is not modified by a pin; the existing v0, v1, v2, etc. snapshots remain intact.
84+
85+
After reverting with a pin, you can go back to normal auto-incrementing behavior by removing the `version` parameter (or setting it to `"latest"`) and running `feast apply` again. If the restored definition differs from the pinned snapshot, a new version will be created.
86+
87+
### Version string formats
88+
89+
| Format | Meaning |
90+
|--------|---------|
91+
| `"latest"` (or omitted) | Always use the latest version (auto-increments on changes) |
92+
| `"v0"`, `"v1"`, `"v2"`, ... | Pin to a specific version number |
93+
| `"version0"`, `"version1"`, ... | Equivalent long form (case-insensitive) |
94+
95+
## Staged Publishing (`--no-promote`)
96+
97+
By default, `feast apply` atomically saves a version snapshot **and** promotes it to the active definition. For breaking schema changes, you may want to stage the new version without disrupting unversioned consumers.
98+
99+
The `--no-promote` flag saves the version snapshot without updating the active feature view definition. The new version is accessible only via explicit `@v<N>` reads and `--version` materialization.
100+
101+
**CLI usage:**
102+
103+
```bash
104+
feast apply --no-promote
105+
```
106+
107+
**Python SDK equivalent:**
108+
109+
```python
110+
store.apply([entity, feature_view], no_promote=True)
111+
```
112+
113+
### Phased rollout workflow
114+
115+
1. **Stage the new version:**
116+
```bash
117+
feast apply --no-promote
118+
```
119+
This publishes v2 without promoting it. All unversioned consumers continue using v1.
120+
121+
2. **Populate the v2 online table:**
122+
```bash
123+
feast materialize --views driver_stats --version v2 ...
124+
```
125+
126+
3. **Migrate consumers one at a time:**
127+
- Consumer A switches to `driver_stats@v2:trips_today`
128+
- Consumer B switches to `driver_stats@v2:avg_rating`
129+
130+
4. **Promote v2 as the default:**
131+
```bash
132+
feast apply
133+
```
134+
Or pin to v2: set `version="v2"` in the definition and run `feast apply`.
135+
136+
## Listing Version History
137+
138+
Use the CLI to inspect version history:
139+
140+
```bash
141+
feast feature-views list-versions driver_stats
142+
```
143+
144+
```text
145+
VERSION TYPE CREATED VERSION_ID
146+
v0 feature_view 2024-01-15 10:30:00 a1b2c3d4-...
147+
v1 feature_view 2024-01-16 14:22:00 e5f6g7h8-...
148+
v2 feature_view 2024-01-20 09:15:00 i9j0k1l2-...
149+
```
150+
151+
Or programmatically via the Python SDK:
152+
153+
```python
154+
store = FeatureStore(repo_path=".")
155+
versions = store.list_feature_view_versions("driver_stats")
156+
for v in versions:
157+
print(f"{v['version']} created at {v['created_timestamp']}")
158+
```
159+
160+
## Version-Qualified Feature References
161+
162+
You can read features from a **specific version** of a feature view by using version-qualified feature references with the `@v<N>` syntax:
163+
164+
```python
165+
online_features = store.get_online_features(
166+
features=[
167+
"driver_stats:trips_today", # latest version (default)
168+
"driver_stats@v2:trips_today", # specific version
169+
"driver_stats@latest:trips_today", # explicit latest
170+
],
171+
entity_rows=[{"driver_id": 1001}],
172+
)
173+
```
174+
175+
**How it works:**
176+
177+
* `driver_stats:trips_today` is equivalent to `driver_stats@latest:trips_today` — it reads from the currently active version
178+
* `driver_stats@v2:trips_today` reads from the v2 snapshot stored in version history, using a version-specific online store table
179+
* Multiple versions of the same feature view can be queried in a single request (e.g., `driver_stats@v1:trips` and `driver_stats@v2:trips_daily`)
180+
181+
**Backward compatibility:**
182+
183+
* The unversioned online store table (e.g., `project_driver_stats`) is treated as v0
184+
* Only versions >= 1 get `_v{N}` suffixed tables (e.g., `project_driver_stats_v1`)
185+
* Pre-versioning users' existing data continues to work without changes — `@latest` resolves to the active version, which for existing unversioned FVs is v0
186+
187+
**Materialization:** Each version requires its own materialization. After applying a new version, run `feast materialize` to populate the versioned table before querying it with `@v<N>`.
188+
189+
## Supported Feature View Types
190+
191+
Versioning is supported on all three feature view types:
192+
193+
* `FeatureView` (and `BatchFeatureView`)
194+
* `StreamFeatureView`
195+
* `OnDemandFeatureView`
196+
197+
## Online Store Support
198+
199+
{% hint style="info" %}
200+
**Currently, version-qualified online reads (`@v<N>`) are only supported with the SQLite online store.** Support for additional online stores (Redis, DynamoDB, Bigtable, Postgres, etc.) will be added based on community priority.
201+
202+
If you need versioned online reads for a specific online store, please [open a GitHub issue](https://github.com/feast-dev/feast/issues/new) describing your use case and which store you need. This helps us prioritize development.
203+
{% endhint %}
204+
205+
Version history tracking in the registry (listing versions, pinning, `--no-promote`) works with **all** registry backends (file, SQL, Snowflake).
206+
207+
## Full Details
208+
209+
For the complete design, concurrency semantics, and feature service interactions, see the [Feature View Versioning RFC](../rfcs/feature-view-versioning.md).
210+
211+
## Naming Restrictions
212+
213+
Feature references use a structured format: `feature_view_name@v<N>:feature_name`. To avoid
214+
ambiguity, the following characters are reserved and must not appear in feature view or feature names:
215+
216+
- **`@`** — Reserved as the version delimiter (e.g., `driver_stats@v2:trips_today`). `feast apply`
217+
will reject feature views with `@` in their name. If you have existing feature views with `@` in
218+
their names, they will continue to work for unversioned reads, but we recommend renaming them to
219+
avoid ambiguity with the `@v<N>` syntax.
220+
- **`:`** — Reserved as the separator between feature view name and feature name in fully qualified
221+
feature references (e.g., `driver_stats:trips_today`).
222+
223+
## Known Limitations
224+
225+
- **Online store coverage** — Version-qualified reads (`@v<N>`) are SQLite-only today. Other online stores are follow-up work.
226+
- **Offline store versioning** — Versioned historical retrieval is not yet supported.
227+
- **Version deletion** — There is no mechanism to prune old versions from the registry.
228+
- **Cross-version joins** — Joining features from different versions of the same feature view in `get_historical_features` is not supported.
229+
- **Feature services** — Feature services always resolve to the active (promoted) version. `--no-promote` versions are not served until promoted.

docs/reference/feast-cli-commands.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@ NAME ENTITIES TYPE
176176
driver_hourly_stats {'driver'} FeatureView
177177
```
178178

179+
List version history for a feature view
180+
181+
```text
182+
feast feature-views list-versions FEATURE_VIEW_NAME
183+
```
184+
185+
```text
186+
VERSION TYPE CREATED VERSION_ID
187+
v0 feature_view 2024-01-15 10:30:00 a1b2c3d4-...
188+
v1 feature_view 2024-01-16 14:22:00 e5f6g7h8-...
189+
```
190+
179191
## Init
180192

181193
Creates a new feature repository

docs/reference/registries/metadata.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The metadata info of Feast `feature_store.yaml` is:
2020
| registry.warehouse | N | string | snowflake warehouse name |
2121
| registry.database | N | string | snowflake db name |
2222
| registry.schema | N | string | snowflake schema name |
23+
| registry.enable_online_feature_view_versioning | N | boolean | enable versioned online store tables and version-qualified reads (default: false). Version history tracking is always active. |
2324
| online_store | Y | | |
2425
| offline_store | Y | NA | | |
2526
| offline_store.type | Y | string | storage type |

0 commit comments

Comments
 (0)