Skip to content

Commit a3073ec

Browse files
authored
Set created_timestamp and last_updated_timestamp fields (#2266)
* Add `last_updated_timestamp` field to ODFV Signed-off-by: Judah Rand <17158624+judahrand@users.noreply.github.com> * Add missing fields to FeatureView classes Signed-off-by: Judah Rand <17158624+judahrand@users.noreply.github.com> * Set `last_updated_timestamp` when applying FeatureView Signed-off-by: Judah Rand <17158624+judahrand@users.noreply.github.com> * Correctly set created and updated fields Signed-off-by: Judah Rand <17158624+judahrand@users.noreply.github.com> * Remove logic duplicated by parent class Signed-off-by: Judah Rand <17158624+judahrand@users.noreply.github.com>
1 parent 6e30457 commit a3073ec

File tree

5 files changed

+36
-6
lines changed

5 files changed

+36
-6
lines changed

protos/feast/core/OnDemandFeatureView.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ message OnDemandFeatureViewSpec {
5555
message OnDemandFeatureViewMeta {
5656
// Time where this Feature View is created
5757
google.protobuf.Timestamp created_timestamp = 1;
58+
59+
// Time where this Feature View is last updated
60+
google.protobuf.Timestamp last_updated_timestamp = 2;
5861
}
5962

6063
message OnDemandInput {

sdk/python/feast/base_feature_view.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@
2828
class BaseFeatureView(ABC):
2929
"""A FeatureView defines a logical grouping of features to be served."""
3030

31+
created_timestamp: Optional[datetime]
32+
last_updated_timestamp: Optional[datetime]
33+
3134
@abstractmethod
3235
def __init__(self, name: str, features: List[Feature]):
3336
self._name = name
3437
self._features = features
3538
self._projection = FeatureViewProjection.from_definition(self)
3639
self.created_timestamp: Optional[datetime] = None
40+
self.last_updated_timestamp: Optional[datetime] = None
3741

3842
@property
3943
def name(self) -> str:

sdk/python/feast/feature_view.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ class FeatureView(BaseFeatureView):
7474
online: bool
7575
input: DataSource
7676
batch_source: DataSource
77-
stream_source: Optional[DataSource] = None
78-
last_updated_timestamp: Optional[datetime] = None
77+
stream_source: Optional[DataSource]
7978
materialization_intervals: List[Tuple[datetime, datetime]]
8079

8180
@log_exceptions
@@ -136,9 +135,6 @@ def __init__(
136135

137136
self.materialization_intervals = []
138137

139-
self.created_timestamp: Optional[datetime] = None
140-
self.last_updated_timestamp: Optional[datetime] = None
141-
142138
# Note: Python requires redefining hash in child classes that override __eq__
143139
def __hash__(self):
144140
return super().__hash__()

sdk/python/feast/on_demand_feature_view.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def to_proto(self) -> OnDemandFeatureViewProto:
119119
meta = OnDemandFeatureViewMeta()
120120
if self.created_timestamp:
121121
meta.created_timestamp.FromDatetime(self.created_timestamp)
122+
if self.last_updated_timestamp:
123+
meta.last_updated_timestamp.FromDatetime(self.last_updated_timestamp)
122124
inputs = {}
123125
for input_ref, fv_projection in self.input_feature_view_projections.items():
124126
inputs[input_ref] = OnDemandInput(
@@ -194,6 +196,10 @@ def from_proto(cls, on_demand_feature_view_proto: OnDemandFeatureViewProto):
194196
on_demand_feature_view_obj.created_timestamp = (
195197
on_demand_feature_view_proto.meta.created_timestamp.ToDatetime()
196198
)
199+
if on_demand_feature_view_proto.meta.HasField("last_updated_timestamp"):
200+
on_demand_feature_view_obj.last_updated_timestamp = (
201+
on_demand_feature_view_proto.meta.last_updated_timestamp.ToDatetime()
202+
)
197203

198204
return on_demand_feature_view_obj
199205

sdk/python/feast/registry.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ def apply_entity(self, entity: Entity, project: str, commit: bool = True):
231231
commit: Whether the change should be persisted immediately
232232
"""
233233
entity.is_valid()
234+
235+
now = datetime.utcnow()
236+
if not entity.created_timestamp:
237+
entity._created_timestamp = now
238+
entity._last_updated_timestamp = now
239+
234240
entity_proto = entity.to_proto()
235241
entity_proto.spec.project = project
236242
self._prepare_registry_for_changes()
@@ -278,6 +284,11 @@ def apply_feature_service(
278284
feature_service: A feature service that will be registered
279285
project: Feast project that this entity belongs to
280286
"""
287+
now = datetime.utcnow()
288+
if not feature_service.created_timestamp:
289+
feature_service.created_timestamp = now
290+
feature_service.last_updated_timestamp = now
291+
281292
feature_service_proto = feature_service.to_proto()
282293
feature_service_proto.spec.project = project
283294

@@ -373,8 +384,12 @@ def apply_feature_view(
373384
commit: Whether the change should be persisted immediately
374385
"""
375386
feature_view.ensure_valid()
387+
388+
now = datetime.utcnow()
376389
if not feature_view.created_timestamp:
377-
feature_view.created_timestamp = datetime.utcnow()
390+
feature_view.created_timestamp = now
391+
feature_view.last_updated_timestamp = now
392+
378393
feature_view_proto = feature_view.to_proto()
379394
feature_view_proto.spec.project = project
380395
self._prepare_registry_for_changes()
@@ -498,6 +513,7 @@ def apply_materialization(
498513
existing_feature_view.materialization_intervals.append(
499514
(start_date, end_date)
500515
)
516+
existing_feature_view.last_updated_timestamp = datetime.utcnow()
501517
feature_view_proto = existing_feature_view.to_proto()
502518
feature_view_proto.spec.project = project
503519
del self.cached_registry_proto.feature_views[idx]
@@ -686,6 +702,11 @@ def apply_saved_dataset(
686702
project: Feast project that this dataset belongs to
687703
commit: Whether the change should be persisted immediately
688704
"""
705+
now = datetime.utcnow()
706+
if not saved_dataset.created_timestamp:
707+
saved_dataset.created_timestamp = now
708+
saved_dataset.last_updated_timestamp = now
709+
689710
saved_dataset_proto = saved_dataset.to_proto()
690711
saved_dataset_proto.spec.project = project
691712
self._prepare_registry_for_changes()

0 commit comments

Comments
 (0)