Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a90cb7f
feat: Add version tracking to FeatureView, StreamFeatureView, and OnD…
franciscojavierarceo Mar 12, 2026
f28942b
fix: Address PR review feedback from Devin
franciscojavierarceo Mar 12, 2026
171785e
docs: Add feature view versioning documentation
franciscojavierarceo Mar 13, 2026
f035e96
fix: Address second round of PR review feedback from Devin
franciscojavierarceo Mar 13, 2026
0c12655
fix: Clean up version history on delete and use write_engine consiste…
franciscojavierarceo Mar 13, 2026
d32ed52
docs: Clarify versioning auto-increment behavior and pin/revert flow
franciscojavierarceo Mar 13, 2026
f9e896f
fix: Add pin conflict detection to both file and SQL registries
franciscojavierarceo Mar 13, 2026
2069b22
fix: Address Devin review feedback on versioning
franciscojavierarceo Mar 13, 2026
83393aa
docs: Document concurrent multi-version serving limitations
franciscojavierarceo Mar 13, 2026
94afe6e
feat: Implement version-qualified feature references (@v<N>)
franciscojavierarceo Mar 14, 2026
76d1afc
fix: Resolve mypy type errors in proto_registry_utils.py
franciscojavierarceo Mar 15, 2026
2541e41
feat: Add version metadata to clean @v2 syntax from feature names
franciscojavierarceo Mar 16, 2026
bceb052
fix: Update provider implementations with version metadata parameter
franciscojavierarceo Mar 16, 2026
14b2da0
fix: Add version metadata parameter to all online store implementations
franciscojavierarceo Mar 16, 2026
fd776fc
fix: Resolve mypy type errors in versioning code
franciscojavierarceo Mar 17, 2026
e9c4c68
fix: Address Devin review feedback on versioning
franciscojavierarceo Mar 17, 2026
903bda5
fix: Address additional Devin review feedback
franciscojavierarceo Mar 17, 2026
af47911
Merge branch 'master' into featureview-versioning
franciscojavierarceo Mar 17, 2026
dd31cdb
feat: Make feature view versioning opt-in via registry config
franciscojavierarceo Mar 17, 2026
8809805
fix: Address Devin review feedback on versioning issues
franciscojavierarceo Mar 17, 2026
d23c4bb
fix: Preserve version tag in response column names for multi-version …
franciscojavierarceo Mar 17, 2026
c5d4b49
feat: Handle version race conditions gracefully with retry and forwar…
franciscojavierarceo Mar 18, 2026
2a3e544
feat: Gate feature services that reference versioned feature views
franciscojavierarceo Mar 18, 2026
66c280b
fix: Resolve mypy errors and rename config field for clarity
franciscojavierarceo Mar 18, 2026
cfc038b
feat: Enable feature service serving for versioned feature views
franciscojavierarceo Mar 18, 2026
c9aea43
docs: Update RFC for feature service support and rename CLI command
franciscojavierarceo Mar 18, 2026
221e0ed
feat(ui): Add version display and Versions tab to feature view pages
franciscojavierarceo Mar 19, 2026
3efccbf
style(ui): Fix prettier formatting in feature view components
franciscojavierarceo Mar 19, 2026
6878fb0
updated utcnow
franciscojavierarceo Mar 20, 2026
280daf6
feat: Add version-aware materialization support
franciscojavierarceo Mar 20, 2026
43674ac
fix: Resolve three versioning regressions from review feedback
franciscojavierarceo Mar 20, 2026
01e4e77
feat: Add --no-promote flag to feast apply and fix versioned ref parsing
franciscojavierarceo Mar 23, 2026
1876060
docs: Consolidate versioning docs into alpha reference page
franciscojavierarceo Mar 23, 2026
760c003
docs: Add no_promote to apply_diff_to_registry docstring
franciscojavierarceo Mar 24, 2026
bc986ef
fix: Reject reserved chars in FV names and make version parser resilient
franciscojavierarceo Mar 24, 2026
b2d6c09
Merge branch 'master' into featureview-versioning
franciscojavierarceo Mar 24, 2026
3a73c87
fix: Add ensure_valid() call in Snowflake registry apply_feature_view
franciscojavierarceo Mar 24, 2026
1468bc5
Merge branch 'master' into featureview-versioning
franciscojavierarceo Mar 25, 2026
3c1ddbe
fix: Make version_tag optional in proto and use HasField() for correc…
franciscojavierarceo Mar 25, 2026
8c1259f
fix: Address versioning review feedback (Snowflake, Go server, SQL re…
franciscojavierarceo Mar 26, 2026
ac0348d
Merge branch 'master' into featureview-versioning
franciscojavierarceo Mar 26, 2026
7dfc447
fix: Handle @latest in Go feature server and pre-compile version regex
franciscojavierarceo Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions docs/getting-started/concepts/feature-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,103 @@ Feature names must be unique within a [feature view](feature-view.md#feature-vie

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).

## Versioning

Feature views support automatic version tracking. Every time `feast apply` detects a change to a feature view, a version snapshot is saved to the registry's version history. This enables auditing what changed, reverting to a prior definition, or pinning serving to a known-good version.

### How it works

Version tracking is fully automatic. You don't need to set any version parameter — just use `feast apply` as usual:

1. **First apply** — Your feature view definition is saved as **v0**.
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.
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).
4. **Another change** — Creates **v2**, and so on.

```
feast apply # First apply → v0
# ... edit schema ...
feast apply # Detects change → v1
feast apply # No change detected → still v1 (no new version)
# ... edit source ...
feast apply # Detects change → v2
```

**Key details:**

* **Automatic snapshots**: Versions are created only when Feast detects an actual change to the feature view definition. No new version is created for identical re-applies.
* **Separate history storage**: Version history is stored separately from the active feature view definition, keeping the main registry lightweight.
* **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.

### Pinning to a specific version

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.

```python
from feast import FeatureView

# Default behavior: always use the latest version (auto-increments on changes)
driver_stats = FeatureView(
name="driver_stats",
entities=[driver],
schema=[...],
source=my_source,
)

# Pin to a specific version (reverts the active definition to v2's snapshot)
driver_stats = FeatureView(
name="driver_stats",
entities=[driver],
schema=[...],
source=my_source,
version="v2", # also accepts "version2"
)
```

When pinning, the other constructor parameters (schema, source, etc.) are ignored — the snapshot's content is used instead. Version history is not modified by a pin; the existing v0, v1, v2, etc. snapshots remain intact.

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.

### Version string formats

| Format | Meaning |
|--------|---------|
| `"latest"` (or omitted) | Always use the latest version (auto-increments on changes) |
| `"v0"`, `"v1"`, `"v2"`, ... | Pin to a specific version number |
| `"version0"`, `"version1"`, ... | Equivalent long form (case-insensitive) |

### Listing version history

Use the CLI to inspect version history:

```bash
feast feature-views versions driver_stats
```

```text
VERSION TYPE CREATED VERSION_ID
v0 feature_view 2024-01-15 10:30:00 a1b2c3d4-...
v1 feature_view 2024-01-16 14:22:00 e5f6g7h8-...
v2 feature_view 2024-01-20 09:15:00 i9j0k1l2-...
```

Or programmatically via the Python SDK:

```python
store = FeatureStore(repo_path=".")
versions = store.list_feature_view_versions("driver_stats")
for v in versions:
print(f"{v['version']} created at {v['created_timestamp']}")
```

### Supported feature view types

Versioning is supported on all three feature view types:

* `FeatureView` (and `BatchFeatureView`)
* `StreamFeatureView`
* `OnDemandFeatureView`

## Schema Validation

Feature views support an optional `enable_validation` parameter that enables schema validation during materialization and historical feature retrieval. When enabled, Feast verifies that:
Expand Down
12 changes: 12 additions & 0 deletions docs/reference/feast-cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ NAME ENTITIES TYPE
driver_hourly_stats {'driver'} FeatureView
```

List version history for a feature view

```text
feast feature-views versions FEATURE_VIEW_NAME
```

```text
VERSION TYPE CREATED VERSION_ID
v0 feature_view 2024-01-15 10:30:00 a1b2c3d4-...
v1 feature_view 2024-01-16 14:22:00 e5f6g7h8-...
```

## Init

Creates a new feature repository
Expand Down
11 changes: 10 additions & 1 deletion protos/feast/core/FeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ message FeatureView {
FeatureViewMeta meta = 2;
}

// Next available id: 18
// Next available id: 19
// TODO(adchia): refactor common fields from this and ODFV into separate metadata proto
message FeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
Expand Down Expand Up @@ -94,6 +94,9 @@ message FeatureViewSpec {

// Whether schema validation is enabled during materialization
bool enable_validation = 17;

// User-specified version pin (e.g. "latest", "v2", "version2")
string version = 18;
}

message FeatureViewMeta {
Expand All @@ -105,6 +108,12 @@ message FeatureViewMeta {

// List of pairs (start_time, end_time) for which this feature view has been materialized.
repeated MaterializationInterval materialization_intervals = 3;

// The current version number of this feature view in the version history.
int32 current_version_number = 4;

// Auto-generated UUID identifying this specific version.
string version_id = 5;
}

message MaterializationInterval {
Expand Down
42 changes: 42 additions & 0 deletions protos/feast/core/FeatureViewVersion.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Copyright 2024 The Feast Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

syntax = "proto3";
package feast.core;

option go_package = "github.com/feast-dev/feast/go/protos/feast/core";
option java_outer_classname = "FeatureViewVersionProto";
option java_package = "feast.proto.core";

import "google/protobuf/timestamp.proto";

message FeatureViewVersionRecord {
string feature_view_name = 1;
string project_id = 2;
int32 version_number = 3;
// "feature_view" | "stream_feature_view" | "on_demand_feature_view"
string feature_view_type = 4;
// serialized FV proto snapshot
bytes feature_view_proto = 5;
google.protobuf.Timestamp created_timestamp = 6;
string description = 7;
// auto-generated UUID for unique identification
string version_id = 8;
}

message FeatureViewVersionHistory {
repeated FeatureViewVersionRecord records = 1;
}
10 changes: 9 additions & 1 deletion protos/feast/core/OnDemandFeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ message OnDemandFeatureView {
OnDemandFeatureViewMeta meta = 2;
}

// Next available id: 9
// Next available id: 18
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
message OnDemandFeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
string name = 1;
Expand Down Expand Up @@ -75,6 +75,8 @@ message OnDemandFeatureViewSpec {
// Aggregation definitions
repeated Aggregation aggregations = 16;

// User-specified version pin (e.g. "latest", "v2", "version2")
string version = 17;
}

message OnDemandFeatureViewMeta {
Expand All @@ -83,6 +85,12 @@ message OnDemandFeatureViewMeta {

// Time where this Feature View is last updated
google.protobuf.Timestamp last_updated_timestamp = 2;

// The current version number of this feature view in the version history.
int32 current_version_number = 3;

// Auto-generated UUID identifying this specific version.
string version_id = 4;
}

message OnDemandSource {
Expand Down
128 changes: 65 additions & 63 deletions protos/feast/core/Registry.proto
Original file line number Diff line number Diff line change
@@ -1,63 +1,65 @@
//
// * Copyright 2020 The Feast Authors
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * https://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
//

syntax = "proto3";

package feast.core;
option java_package = "feast.proto.core";
option java_outer_classname = "RegistryProto";
option go_package = "github.com/feast-dev/feast/go/protos/feast/core";

import "feast/core/Entity.proto";
import "feast/core/FeatureService.proto";
import "feast/core/FeatureTable.proto";
import "feast/core/FeatureView.proto";
import "feast/core/InfraObject.proto";
import "feast/core/OnDemandFeatureView.proto";
import "feast/core/StreamFeatureView.proto";
import "feast/core/DataSource.proto";
import "feast/core/SavedDataset.proto";
import "feast/core/ValidationProfile.proto";
import "google/protobuf/timestamp.proto";
import "feast/core/Permission.proto";
import "feast/core/Project.proto";

// Next id: 18
message Registry {
repeated Entity entities = 1;
repeated FeatureTable feature_tables = 2;
repeated FeatureView feature_views = 6;
repeated DataSource data_sources = 12;
repeated OnDemandFeatureView on_demand_feature_views = 8;
repeated StreamFeatureView stream_feature_views = 14;
repeated FeatureService feature_services = 7;
repeated SavedDataset saved_datasets = 11;
repeated ValidationReference validation_references = 13;
Infra infra = 10;
// Tracking metadata of Feast by project
repeated ProjectMetadata project_metadata = 15 [deprecated = true];

string registry_schema_version = 3; // to support migrations; incremented when schema is changed
string version_id = 4; // version id, random string generated on each update of the data; now used only for debugging purposes
google.protobuf.Timestamp last_updated = 5;
repeated Permission permissions = 16;
repeated Project projects = 17;
}

message ProjectMetadata {
string project = 1;
string project_uuid = 2;
}
//
// * Copyright 2020 The Feast Authors
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * https://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
//

syntax = "proto3";

package feast.core;
option java_package = "feast.proto.core";
option java_outer_classname = "RegistryProto";
option go_package = "github.com/feast-dev/feast/go/protos/feast/core";

import "feast/core/Entity.proto";
import "feast/core/FeatureService.proto";
import "feast/core/FeatureTable.proto";
import "feast/core/FeatureView.proto";
import "feast/core/InfraObject.proto";
import "feast/core/OnDemandFeatureView.proto";
import "feast/core/StreamFeatureView.proto";
import "feast/core/DataSource.proto";
import "feast/core/SavedDataset.proto";
import "feast/core/ValidationProfile.proto";
import "google/protobuf/timestamp.proto";
import "feast/core/Permission.proto";
import "feast/core/Project.proto";
import "feast/core/FeatureViewVersion.proto";

// Next id: 19
message Registry {
repeated Entity entities = 1;
repeated FeatureTable feature_tables = 2;
repeated FeatureView feature_views = 6;
repeated DataSource data_sources = 12;
repeated OnDemandFeatureView on_demand_feature_views = 8;
repeated StreamFeatureView stream_feature_views = 14;
repeated FeatureService feature_services = 7;
repeated SavedDataset saved_datasets = 11;
repeated ValidationReference validation_references = 13;
Infra infra = 10;
// Tracking metadata of Feast by project
repeated ProjectMetadata project_metadata = 15 [deprecated = true];

string registry_schema_version = 3; // to support migrations; incremented when schema is changed
string version_id = 4; // version id, random string generated on each update of the data; now used only for debugging purposes
google.protobuf.Timestamp last_updated = 5;
repeated Permission permissions = 16;
repeated Project projects = 17;
FeatureViewVersionHistory feature_view_version_history = 18;
}

message ProjectMetadata {
string project = 1;
string project_uuid = 2;
}
5 changes: 4 additions & 1 deletion protos/feast/core/StreamFeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ message StreamFeatureView {
FeatureViewMeta meta = 2;
}

// Next available id: 21
// Next available id: 22
message StreamFeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
string name = 1;
Expand Down Expand Up @@ -102,5 +102,8 @@ message StreamFeatureViewSpec {

// Whether schema validation is enabled during materialization
bool enable_validation = 20;

// User-specified version pin (e.g. "latest", "v2", "version2")
string version = 21;
}

6 changes: 6 additions & 0 deletions sdk/python/feast/base_feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class BaseFeatureView(ABC):
projection: FeatureViewProjection
created_timestamp: Optional[datetime]
last_updated_timestamp: Optional[datetime]
version: str
current_version_number: Optional[int]

@abstractmethod
def __init__(
Expand Down Expand Up @@ -92,6 +94,10 @@ def __init__(
self.projection = FeatureViewProjection.from_definition(self)
self.created_timestamp = None
self.last_updated_timestamp = None
if not hasattr(self, "version"):
self.version = "latest"
if not hasattr(self, "current_version_number"):
self.current_version_number = None

self.source = source

Expand Down
Loading
Loading