Skip to content

Commit 0db56a2

Browse files
authored
fix: Performance regression in /get-online-features (feast-dev#4892)
* put blocking opps in get online features in threadpool Signed-off-by: Rob Howley <howley.robert@gmail.com> * chore: leave comment in path operation Signed-off-by: Rob Howley <howley.robert@gmail.com> * use quoted type hint Signed-off-by: Rob Howley <howley.robert@gmail.com> * remove hint Signed-off-by: Rob Howley <howley.robert@gmail.com> --------- Signed-off-by: Rob Howley <howley.robert@gmail.com>
1 parent bd9f071 commit 0db56a2

File tree

1 file changed

+39
-29
lines changed

1 file changed

+39
-29
lines changed

sdk/python/feast/feature_server.py

+39-29
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ class GetOnlineFeaturesRequest(BaseModel):
7676
full_feature_names: bool = False
7777

7878

79+
def _get_features(request: GetOnlineFeaturesRequest, store: "feast.FeatureStore"):
80+
if request.feature_service:
81+
feature_service = store.get_feature_service(
82+
request.feature_service, allow_cache=True
83+
)
84+
assert_permissions(
85+
resource=feature_service, actions=[AuthzedAction.READ_ONLINE]
86+
)
87+
features = feature_service # type: ignore
88+
else:
89+
all_feature_views, all_on_demand_feature_views = (
90+
utils._get_feature_views_to_use(
91+
store.registry,
92+
store.project,
93+
request.features,
94+
allow_cache=True,
95+
hide_dummy_entity=False,
96+
)
97+
)
98+
for feature_view in all_feature_views:
99+
assert_permissions(
100+
resource=feature_view, actions=[AuthzedAction.READ_ONLINE]
101+
)
102+
for od_feature_view in all_on_demand_feature_views:
103+
assert_permissions(
104+
resource=od_feature_view, actions=[AuthzedAction.READ_ONLINE]
105+
)
106+
features = request.features # type: ignore
107+
108+
return features
109+
110+
79111
def get_app(
80112
store: "feast.FeatureStore",
81113
registry_ttl_sec: int = DEFAULT_FEATURE_SERVER_REGISTRY_TTL,
@@ -121,33 +153,7 @@ async def lifespan(app: FastAPI):
121153
)
122154
async def get_online_features(request: GetOnlineFeaturesRequest) -> Dict[str, Any]:
123155
# Initialize parameters for FeatureStore.get_online_features(...) call
124-
if request.feature_service:
125-
feature_service = store.get_feature_service(
126-
request.feature_service, allow_cache=True
127-
)
128-
assert_permissions(
129-
resource=feature_service, actions=[AuthzedAction.READ_ONLINE]
130-
)
131-
features = feature_service # type: ignore
132-
else:
133-
all_feature_views, all_on_demand_feature_views = (
134-
utils._get_feature_views_to_use(
135-
store.registry,
136-
store.project,
137-
request.features,
138-
allow_cache=True,
139-
hide_dummy_entity=False,
140-
)
141-
)
142-
for feature_view in all_feature_views:
143-
assert_permissions(
144-
resource=feature_view, actions=[AuthzedAction.READ_ONLINE]
145-
)
146-
for od_feature_view in all_on_demand_feature_views:
147-
assert_permissions(
148-
resource=od_feature_view, actions=[AuthzedAction.READ_ONLINE]
149-
)
150-
features = request.features # type: ignore
156+
features = await run_in_threadpool(_get_features, request, store)
151157

152158
read_params = dict(
153159
features=features,
@@ -163,9 +169,13 @@ async def get_online_features(request: GetOnlineFeaturesRequest) -> Dict[str, An
163169
)
164170

165171
# Convert the Protobuf object to JSON and return it
166-
return MessageToDict(
167-
response.proto, preserving_proto_field_name=True, float_precision=18
172+
response_dict = await run_in_threadpool(
173+
MessageToDict,
174+
response.proto,
175+
preserving_proto_field_name=True,
176+
float_precision=18,
168177
)
178+
return response_dict
169179

170180
@app.post("/push", dependencies=[Depends(inject_user_details)])
171181
async def push(request: PushFeaturesRequest) -> None:

0 commit comments

Comments
 (0)