From 1f26c622ed2e07d57ad0e0a5ee792dfe95387bec Mon Sep 17 00:00:00 2001 From: Jatin Kumar Date: Thu, 1 Jan 2026 19:18:30 -0300 Subject: [PATCH 1/4] Update redis.py Add millisecond-precision timestamp support to Redis online store Signed-off-by: Jatin Kumar --- sdk/python/feast/infra/online_stores/redis.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/redis.py b/sdk/python/feast/infra/online_stores/redis.py index 59892fcbe0f..2dc72c84e52 100644 --- a/sdk/python/feast/infra/online_stores/redis.py +++ b/sdk/python/feast/infra/online_stores/redis.py @@ -304,22 +304,31 @@ def online_write_batch( for redis_key_bin, prev_event_time, (_, values, timestamp, _) in zip( keys, prev_event_timestamps, data ): - event_time_seconds = int(utils.make_tzaware(timestamp).timestamp()) + # Convert incoming timestamp to millisecond-aware datetime + aware_ts = utils.make_tzaware(timestamp) - # ignore if event_timestamp is before the event features that are currently in the feature store + # Build protobuf timestamp with nanos + ts = Timestamp() + ts.FromDatetime(aware_ts) + + # New timestamp in nanoseconds + new_total_nanos = ts.seconds * 1_000_000_000 + ts.nanos + + # Compare against existing timestamp (nanosecond precision) if prev_event_time: prev_ts = Timestamp() prev_ts.ParseFromString(prev_event_time) - if prev_ts.seconds and event_time_seconds <= prev_ts.seconds: - # TODO: somehow signal that it's not overwriting the current record? + + prev_total_nanos = prev_ts.seconds * 1_000_000_000 + prev_ts.nanos + + # Skip only if older OR exact same instant + if prev_total_nanos and new_total_nanos <= prev_total_nanos: if progress: progress(1) continue - - ts = Timestamp() - ts.seconds = event_time_seconds - entity_hset = dict() - entity_hset[ts_key] = ts.SerializeToString() + + # Store full timestamp (seconds + nanos) + entity_hset = {ts_key: ts.SerializeToString()} for feature_name, val in values.items(): f_key = _mmh3(f"{feature_view}:{feature_name}") From 828da5b63e2bb6bbce2f840bf95b4e893b67aa44 Mon Sep 17 00:00:00 2001 From: Jatin Kumar Date: Thu, 1 Jan 2026 19:28:42 -0300 Subject: [PATCH 2/4] Update redis.py sub-second precision when returning timestamps to client Signed-off-by: Jatin Kumar --- sdk/python/feast/infra/online_stores/redis.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/redis.py b/sdk/python/feast/infra/online_stores/redis.py index 2dc72c84e52..5e68772c11e 100644 --- a/sdk/python/feast/infra/online_stores/redis.py +++ b/sdk/python/feast/infra/online_stores/redis.py @@ -306,11 +306,11 @@ def online_write_batch( ): # Convert incoming timestamp to millisecond-aware datetime aware_ts = utils.make_tzaware(timestamp) - + # Build protobuf timestamp with nanos ts = Timestamp() ts.FromDatetime(aware_ts) - + # New timestamp in nanoseconds new_total_nanos = ts.seconds * 1_000_000_000 + ts.nanos @@ -318,9 +318,7 @@ def online_write_batch( if prev_event_time: prev_ts = Timestamp() prev_ts.ParseFromString(prev_event_time) - prev_total_nanos = prev_ts.seconds * 1_000_000_000 + prev_ts.nanos - # Skip only if older OR exact same instant if prev_total_nanos and new_total_nanos <= prev_total_nanos: if progress: @@ -465,5 +463,7 @@ def _get_features_for_entity( if not res: return None, None else: - timestamp = datetime.fromtimestamp(res_ts.seconds, tz=timezone.utc) + # reconstruct full timestamp including nanos + total_seconds = res_ts.seconds + res_ts.nanos / 1_000_000_000.0 + timestamp = datetime.fromtimestamp(total_seconds, tz=timezone.utc) return timestamp, res From a7dca65027b49dd54e206cdb933d09d884358806 Mon Sep 17 00:00:00 2001 From: Jatin Kumar Date: Thu, 1 Jan 2026 19:38:25 -0300 Subject: [PATCH 3/4] Update redis.py fix(redis): preserve millisecond timestamp precision Signed-off-by: Jatin Kumar --- sdk/python/feast/infra/online_stores/redis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/python/feast/infra/online_stores/redis.py b/sdk/python/feast/infra/online_stores/redis.py index 5e68772c11e..e91070a4d9a 100644 --- a/sdk/python/feast/infra/online_stores/redis.py +++ b/sdk/python/feast/infra/online_stores/redis.py @@ -310,7 +310,6 @@ def online_write_batch( # Build protobuf timestamp with nanos ts = Timestamp() ts.FromDatetime(aware_ts) - # New timestamp in nanoseconds new_total_nanos = ts.seconds * 1_000_000_000 + ts.nanos From ea3be2798dbf3e0dc7b6e9b6343e56e09d32b936 Mon Sep 17 00:00:00 2001 From: Jatin Kumar Date: Tue, 6 Jan 2026 00:34:25 -0300 Subject: [PATCH 4/4] Update redis.py fix: Remove whitespace on blank lines (W293) Signed-off-by: Jatin Kumar --- sdk/python/feast/infra/online_stores/redis.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/redis.py b/sdk/python/feast/infra/online_stores/redis.py index e91070a4d9a..9a4e908810d 100644 --- a/sdk/python/feast/infra/online_stores/redis.py +++ b/sdk/python/feast/infra/online_stores/redis.py @@ -306,13 +306,11 @@ def online_write_batch( ): # Convert incoming timestamp to millisecond-aware datetime aware_ts = utils.make_tzaware(timestamp) - # Build protobuf timestamp with nanos ts = Timestamp() ts.FromDatetime(aware_ts) # New timestamp in nanoseconds new_total_nanos = ts.seconds * 1_000_000_000 + ts.nanos - # Compare against existing timestamp (nanosecond precision) if prev_event_time: prev_ts = Timestamp() @@ -323,7 +321,6 @@ def online_write_batch( if progress: progress(1) continue - # Store full timestamp (seconds + nanos) entity_hset = {ts_key: ts.SerializeToString()}