Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
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>
  • Loading branch information
franciscojavierarceo and claude committed Mar 23, 2026
commit 1876060f915fac3992daacaa8c9982656f58cc8b
133 changes: 4 additions & 129 deletions docs/getting-started/concepts/feature-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,147 +162,22 @@ Each field can have additional metadata associated with it, specified as key-val

## \[Alpha\] 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.
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.

{% hint style="info" %}
Version history tracking is **always active** — no configuration needed. Every `feast apply` that changes a feature view automatically records a version snapshot.

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`:

```yaml
registry:
path: data/registry.db
enable_online_feature_view_versioning: true
```

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.
{% endhint %}

### 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.
Version history tracking is **always active** with no configuration needed. The `version` parameter is fully optional — omitting it preserves existing behavior.

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

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.

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 list-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']}")
```

### Version-qualified feature references

You can read features from a **specific version** of a feature view by using version-qualified feature references with the `@v<N>` syntax:

```python
online_features = store.get_online_features(
features=[
"driver_stats:trips_today", # latest version (default)
"driver_stats@v2:trips_today", # specific version
"driver_stats@latest:trips_today", # explicit latest
],
entity_rows=[{"driver_id": 1001}],
version="v2",
)
```

**How it works:**

* `driver_stats:trips_today` is equivalent to `driver_stats@latest:trips_today` — it reads from the currently active version
* `driver_stats@v2:trips_today` reads from the v2 snapshot stored in version history, using a version-specific online store table
* 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`)

**Backward compatibility:**

* The unversioned online store table (e.g., `project_driver_stats`) is treated as v0
* Only versions >= 1 get `_v{N}` suffixed tables (e.g., `project_driver_stats_v1`)
* Pre-versioning users' existing data continues to work without changes — `@latest` resolves to the active version, which for existing unversioned FVs is v0

**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>`.

### Supported feature view types

Versioning is supported on all three feature view types:

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

**Note:** Version-qualified reads (`@v<N>`) are currently supported only on the **SQLite** online store. Other online stores will raise a clear error if versioned queries are attempted. Support for additional stores is tracked in [#6200](https://github.com/feast-dev/feast/issues/6200).
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.

## Schema Validation

Expand Down
175 changes: 152 additions & 23 deletions docs/reference/alpha-feature-view-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,187 @@ Feature view versioning automatically tracks schema and UDF changes to feature v
- **Multi-version serving** — serve both old and new schemas simultaneously using `@v<N>` syntax
- **Staged publishing** — use `feast apply --no-promote` to publish a new version without making it the default

## Quick Start
## How It Works

Version history tracking is **always active** with no configuration required. Every `feast apply` that changes a feature view automatically records a version snapshot.
Version tracking is fully automatic. You don't need to set any version parameter — just use `feast apply` as usual:

### List versions
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.

```bash
feast version-history driver_stats
```
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 (schema or UDF). Metadata-only changes (description, tags, TTL) update in place without creating a new version.
* **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.

## Configuration

{% hint style="info" %}
Version history tracking is **always active** — no configuration needed. Every `feast apply` that changes a feature view automatically records a version snapshot.

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`:

### Pin to a prior version
```yaml
registry:
path: data/registry.db
enable_online_feature_view_versioning: true
```

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.
{% endhint %}

## 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",
version="v0", # Pin to v0
...
entities=[driver],
schema=[...],
source=my_source,
version="v2", # also accepts "version2"
)
```

### Staged publishing (no-promote)
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.

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.

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

## Staged Publishing (`--no-promote`)

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.

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.

**CLI usage:**

```bash
# Publish v2 without promoting it to active
feast apply --no-promote
```

# Populate v2 online table
feast materialize --views driver_stats --version v2 ...
**Python SDK equivalent:**

# Migrate consumers to @v2 refs, then promote
feast apply
```python
store.apply([entity, feature_view], no_promote=True)
```

### Version-qualified online reads
### Phased rollout workflow

To enable version-qualified reads (e.g., `driver_stats@v2:trips_today`), add the following to your `feature_store.yaml`:
1. **Stage the new version:**
```bash
feast apply --no-promote
```
This publishes v2 without promoting it. All unversioned consumers continue using v1.

```yaml
registry:
path: data/registry.db
enable_online_feature_view_versioning: true
2. **Populate the v2 online table:**
```bash
feast materialize --views driver_stats --version v2 ...
```

3. **Migrate consumers one at a time:**
- Consumer A switches to `driver_stats@v2:trips_today`
- Consumer B switches to `driver_stats@v2:avg_rating`

4. **Promote v2 as the default:**
```bash
feast apply
```
Or pin to v2: set `version="v2"` in the definition and run `feast apply`.

## Listing Version History

Use the CLI to inspect version history:

```bash
feast feature-views list-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-...
```

Then query specific versions:
Or programmatically via the Python SDK:

```python
features = store.get_online_features(
features=["driver_stats@v2:trips_today"],
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']}")
```

## Version-Qualified Feature References

You can read features from a **specific version** of a feature view by using version-qualified feature references with the `@v<N>` syntax:

```python
online_features = store.get_online_features(
features=[
"driver_stats:trips_today", # latest version (default)
"driver_stats@v2:trips_today", # specific version
"driver_stats@latest:trips_today", # explicit latest
],
entity_rows=[{"driver_id": 1001}],
)
```

**How it works:**

* `driver_stats:trips_today` is equivalent to `driver_stats@latest:trips_today` — it reads from the currently active version
* `driver_stats@v2:trips_today` reads from the v2 snapshot stored in version history, using a version-specific online store table
* 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`)

**Backward compatibility:**

* The unversioned online store table (e.g., `project_driver_stats`) is treated as v0
* Only versions >= 1 get `_v{N}` suffixed tables (e.g., `project_driver_stats_v1`)
* Pre-versioning users' existing data continues to work without changes — `@latest` resolves to the active version, which for existing unversioned FVs is v0

**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>`.

## Supported Feature View Types

Versioning is supported on all three feature view types:

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

## Online Store Support

{% hint style="info" %}
Expand Down