Skip to content

Commit ce883f8

Browse files
Vperiodtntkathole
authored andcommitted
add-new-metric
Signed-off-by: Vanshika Vanshika <vvanshik@redhat.com> rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED
1 parent 212504b commit ce883f8

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

sdk/python/feast/metrics.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ def build_metrics_flags(metrics_config: Optional[object] = None) -> _MetricsFlag
198198
"Number of entity rows per online feature request",
199199
buckets=(1, 5, 10, 25, 50, 100, 250, 500, 1000),
200200
)
201+
online_features_status_total = Counter(
202+
"feast_online_features_status_total",
203+
"Count of individual feature values by retrieval status per feature view",
204+
["feature_view", "status"],
205+
)
201206

202207
# ---------------------------------------------------------------------------
203208
# Push / write metrics
@@ -334,6 +339,22 @@ def track_online_store_read(duration_seconds: float):
334339
online_store_read_duration_seconds.observe(duration_seconds)
335340

336341

342+
def track_feature_statuses(
343+
feature_view_name: str, present_count: int, not_found_count: int
344+
):
345+
"""Record the number of PRESENT vs NOT_FOUND feature values for a feature view."""
346+
if not _config.online_features:
347+
return
348+
if present_count > 0:
349+
online_features_status_total.labels(
350+
feature_view=feature_view_name, status="present"
351+
).inc(present_count)
352+
if not_found_count > 0:
353+
online_features_status_total.labels(
354+
feature_view=feature_view_name, status="not_found"
355+
).inc(not_found_count)
356+
357+
337358
def track_transformation(odfv_name: str, mode: str, duration_seconds: float):
338359
"""Record the duration of an on-demand feature view read-path transformation."""
339360
if not _config.online_features:

sdk/python/feast/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,15 @@ def _populate_response_from_feature_data(
15661566
feat_values[f_idx][out_idx] = feat_val
15671567
feat_statuses[f_idx][out_idx] = PRESENT
15681568

1569+
try:
1570+
from feast.metrics import track_feature_statuses
1571+
1572+
_present = sum(s == PRESENT for row in feat_statuses for s in row)
1573+
_not_found = (n_features * output_len) - _present
1574+
track_feature_statuses(table.name, _present, _not_found)
1575+
except Exception:
1576+
pass
1577+
15691578
for f_idx in range(n_features):
15701579
online_features_response.results.append(
15711580
GetOnlineFeaturesResponse.FeatureVector(

sdk/python/tests/unit/test_metrics.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
materialization_result_total,
2424
online_features_entity_count,
2525
online_features_request_count,
26+
online_features_status_total,
2627
online_store_read_duration_seconds,
2728
push_request_count,
2829
request_count,
2930
request_latency,
31+
track_feature_statuses,
3032
track_materialization,
3133
track_online_features_entities,
3234
track_online_store_read,
@@ -435,6 +437,71 @@ def test_records_entity_count(self):
435437
assert online_features_entity_count._sum.get() >= before_count + 42
436438

437439

440+
class TestTrackFeatureStatuses:
441+
def test_increments_present_and_not_found(self):
442+
before_present = online_features_status_total.labels(
443+
feature_view="fv_status", status="present"
444+
)._value.get()
445+
before_not_found = online_features_status_total.labels(
446+
feature_view="fv_status", status="not_found"
447+
)._value.get()
448+
449+
track_feature_statuses("fv_status", present_count=3, not_found_count=2)
450+
451+
assert (
452+
online_features_status_total.labels(
453+
feature_view="fv_status", status="present"
454+
)._value.get()
455+
== before_present + 3
456+
)
457+
assert (
458+
online_features_status_total.labels(
459+
feature_view="fv_status", status="not_found"
460+
)._value.get()
461+
== before_not_found + 2
462+
)
463+
464+
@patch("feast.metrics.track_feature_statuses")
465+
def test_populate_response_passes_correct_counts(self, mock_track):
466+
"""Integration: _populate_response_from_feature_data computes and
467+
forwards the right present/not_found counts.
468+
469+
3 entities, 2 features each:
470+
Entity 0 — both present (2 PRESENT)
471+
Entity 1 — missing (2 NOT_FOUND)
472+
Entity 2 — partial (1 PRESENT, 1 NOT_FOUND)
473+
"""
474+
from feast.protos.feast.serving.ServingService_pb2 import (
475+
GetOnlineFeaturesResponse,
476+
)
477+
from feast.protos.feast.types.Value_pb2 import Value as ValueProto
478+
from feast.utils import _populate_response_from_feature_data
479+
480+
now = datetime.now(tz=timezone.utc)
481+
val = ValueProto(int64_val=1)
482+
table = MagicMock()
483+
table.name = "driver_fv"
484+
table.projection.name_to_use.return_value = "driver_fv"
485+
table.projection.name_alias = None
486+
table.projection.name = "driver_fv"
487+
488+
_populate_response_from_feature_data(
489+
requested_features=["feat_a", "feat_b"],
490+
read_rows=[
491+
(now, {"feat_a": val, "feat_b": val}),
492+
(None, None),
493+
(now, {"feat_a": val}),
494+
],
495+
indexes=([0], [1], [2]),
496+
online_features_response=GetOnlineFeaturesResponse(),
497+
full_feature_names=False,
498+
table=table,
499+
output_len=3,
500+
)
501+
502+
mock_track.assert_called_once_with("driver_fv", 3, 3)
503+
504+
438505
class TestTrackPush:
439506
def test_increments_push_counter(self):
440507
before = push_request_count.labels(

0 commit comments

Comments
 (0)