Skip to content

REST feature server drops feature_view_metadata when include_feature_view_version_metadata is set #6922

Description

@Zhuoxi2000

Expected Behavior

POST /get-online-features (and /search / /retrieve-online-documents with api_version: 2) accepts include_feature_view_version_metadata: true. With that flag set, the response metadata should contain feature_view_metadata, a list of {name, version} entries, like the Python SDK and gRPC paths return. Three things support this:

  • ServingService.proto: repeated FeatureViewMetadata feature_view_metadata = 2; // Only populated when requested.
  • The feast/feature_server_utils.py docstring says convert_response_to_dict matches MessageToDict(proto, preserving_proto_field_name=True). The only exception it documents is double_val precision. MessageToDict does emit feature_view_metadata.
  • RemoteOnlineStore sends the flag to the server, and its unit test says it does so "so versioned reads work end-to-end".

Current Behavior

The flag reaches store.get_online_features(...), and metadata.feature_view_metadata is filled on the response proto. It is then dropped during JSON serialization. The HTTP response metadata only ever contains feature_names, so over REST the flag has no effect.

The field is lost in three places:

  1. feature_server_utils._metadata_to_dict only reads metadata.feature_names.
  2. OnlineFeaturesMetadataResponse in feature_server.py has no feature_view_metadata field. Before perf: Replace MessageToDict with optimized custom dict builder #6015, the endpoint returned a dict through response_model=OnlineFeaturesResponse, so this model already filtered the field out. In practice the field has not been returned over REST since the flag was added in feat: Add version tracking to FeatureView #6101.
  3. RemoteOnlineStore._build_online_response_from_json (infra/online_stores/remote.py) rebuilds the metadata from feature_names only. Even if the server returned the field, a client using a remote online store would still lose it.

Steps to reproduce

Minimal, no server needed (master @ 810391f):

from google.protobuf.json_format import MessageToDict
from feast import proto_json
from feast.feature_server_utils import convert_response_to_dict
from feast.protos.feast.serving.ServingService_pb2 import GetOnlineFeaturesResponse

proto_json.patch()
r = GetOnlineFeaturesResponse()
r.metadata.feature_names.val.append("trips_today")
r.metadata.feature_view_metadata.add(name="driver_stats", version=2)

print(convert_response_to_dict(r)["metadata"])
# {'feature_names': ['trips_today']}
print(MessageToDict(r, preserving_proto_field_name=True)["metadata"])
# {'feature_names': ['trips_today'], 'feature_view_metadata': [{'name': 'driver_stats', 'version': 2}]}

End to end, with feast serve on any repo. The PR's test_get_online_features_returns_feature_view_version_metadata checks the same thing through the FastAPI test client:

curl -s -X POST localhost:6566/get-online-features -H 'Content-Type: application/json' -d '{
  "features": ["driver_hourly_stats:conv_rate"],
  "entities": {"driver_id": [1001]},
  "include_feature_view_version_metadata": true
}' | jq .metadata
# {"feature_names": ["driver_id", "conv_rate"]}   <- no feature_view_metadata

Specifications

  • Version: master (810391f), 0.66.x dev
  • Platform: macOS 26.5 arm64, Python 3.11 (not platform specific)
  • Subsystem: Python feature server (REST), remote online store

Possible Solution

  • In _metadata_to_dict, emit feature_view_metadata when it is non-empty, in the same shape MessageToDict uses (default-valued name/version are omitted).
  • Add feature_view_metadata: List[FeatureViewMetadataResponse] to OnlineFeaturesMetadataResponse so the OpenAPI schema documents it.
  • In RemoteOnlineStore._build_online_response_from_json, parse the field back into the proto.

I have a small PR with regression tests ready and will link it here.


AI assistance: drafted with an AI coding assistant (Claude). The reproduction above was run locally against the current default branch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions