Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

feast apply silently ignores ttl updates when the new ttl is None or timedelta(0) #6703

Closed

Description

@erald02

Expected Behavior

Re-applying an existing FeatureView with ttl=None or ttl=timedelta(0) should clear the ttl in the registry. After feast apply, store.get_feature_view("foo_fv").ttl should reflect the new "no expiry" value.

Current Behavior

The old ttl is silently retained. store.get_feature_view("foo_fv").ttl still returns the previous finite value, with no warning or error, while feast apply keeps printing the same "changed" diff on every run.

Registry.apply_feature_view() routes ttl changes through _update_metadata_fields() instead of creating a new version, and that method gates the ttl copy behind a truthiness check on the raw timedelta:

if (
    hasattr(existing_proto.spec, "ttl")
    and hasattr(updated_fv, "ttl")
    and updated_fv.ttl          # falsy for both None and timedelta(0)
):

Since None and timedelta(0) are both falsy — and both are the documented way to express "no ttl" — the update is dropped exactly in the case it should support. get_ttl_duration() already handles None correctly; only the outer guard is wrong. Still present on master as of 2026-08.

Practical impact: a FeatureView intended to never expire silently kept a stale finite ttl, causing get_historical_features to drop ~11.6% of otherwise-valid rows from a point-in-time join, with no error or warning.

Steps to reproduce

from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource, Project
from feast.types import Float32

project = Project(name="ttl_repro")
entity = Entity(name="foo", join_keys=["foo_id"])
source = FileSource(
    name="foo_source",
    path="./data/foo.parquet",
    timestamp_field="event_timestamp",
)

# 1. Apply with a finite ttl -> fv.ttl == 10 days (correct)
fv = FeatureView(
    name="foo_fv",
    entities=[entity],
    ttl=timedelta(days=10),
    schema=[Field(name="bar", dtype=Float32)],
    source=source,
)

# 2. Change ttl to "no expiry" and `feast apply` again
fv_v2 = FeatureView(
    name="foo_fv",
    entities=[entity],
    ttl=None,  # or timedelta(days=0) - same result
    schema=[Field(name="bar", dtype=Float32)],
    source=source,
)

Then check store.get_feature_view("foo_fv").ttl — it is still timedelta(days=10).

Specifications

  • Version: 0.65.0 (PyPI); also confirmed on master as of 2026-08
  • Platform:
  • Subsystem: Registry — sdk/python/feast/infra/registry/registry.py, Registry._update_metadata_fields()

Possible Solution

Drop the outer truthiness check and always sync ttl on update, relying on get_ttl_duration()'s existing None-handling and defaulting to an explicit zero Duration when unset:

if hasattr(existing_proto.spec, "ttl") and hasattr(updated_fv, "ttl"):
    if isinstance(updated_fv, FeatureView):
        existing_proto.spec.ttl.CopyFrom(updated_fv.get_ttl_duration() or Duration())
    elif isinstance(updated_fv, LabelView):
        ttl_duration = Duration()
        if updated_fv.ttl is not None:
            ttl_duration.FromTimedelta(updated_fv.ttl)
        existing_proto.spec.ttl.CopyFrom(ttl_duration)

Happy to open a PR with this plus a regression test (apply with finite ttl, re-apply with ttl=None, assert the registry reflects it)

Activity

  1. added 2 commits that reference this issue on Aug 14, 2026
    f9aaff6
    2c8a95d
  2. added 2 commits that reference this issue on Aug 14, 2026
    b20d2f6
    2ddc090
  3. added 2 commits that reference this issue on Aug 14, 2026
    73d3956
    cc340e8
  4. added a commit that references this issue on Aug 14, 2026
    97b0f25
  5. added a commit that references this issue on Aug 22, 2026
    dfddd85
  6. added 2 commits that reference this issue on Aug 31, 2026
    0c5753b
    a29c923
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions