Skip to content

Commit acadb17

Browse files
fix: Stabilize materialization checks across hour boundaries (#6948)
Derive the shared consistency test materialization window from fixture timestamps so setup crossing an hour does not exclude its oldest rows. Cover normal setup, hour rollover, and a longer delay with real Parquet-to-SQLite regression tests. Signed-off-by: Francisco Javier Arceo <[email protected]>
1 parent 3710f78 commit acadb17

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime, timedelta, timezone
4+
from pathlib import Path
5+
from unittest.mock import patch
6+
7+
import pytest
8+
9+
from feast import FeatureStore, RepoConfig
10+
from feast.infra.offline_stores.file_source import FileSource
11+
from tests.data.data_creator import create_basic_driver_dataset
12+
from tests.universal.feature_repos.universal.entities import driver
13+
from tests.universal.feature_repos.universal.feature_views import driver_feature_view
14+
from tests.utils.e2e_test_validation import validate_offline_online_store_consistency
15+
16+
17+
@pytest.mark.parametrize("setup_seconds", [0, 2, 3602])
18+
def test_consistency_validation_survives_hour_rollover(
19+
tmp_path: Path, setup_seconds: int
20+
) -> None:
21+
fixture_time = datetime.now(timezone.utc).replace(
22+
minute=0, second=0, microsecond=0
23+
) - timedelta(seconds=1)
24+
with patch("tests.data.data_creator._utc_now", return_value=fixture_time):
25+
df = create_basic_driver_dataset()
26+
source_path = tmp_path / "drivers.parquet"
27+
df.to_parquet(source_path)
28+
source = FileSource(
29+
path=str(source_path),
30+
timestamp_field="ts",
31+
created_timestamp_column="created_ts",
32+
field_mapping={"ts_1": "ts"},
33+
)
34+
fv = driver_feature_view(data_source=source, infer_features=True)
35+
fs = FeatureStore(
36+
config=RepoConfig(
37+
project="clock_rollover",
38+
registry=str(tmp_path / "registry.db"),
39+
provider="local",
40+
online_store={"type": "sqlite", "path": str(tmp_path / "online.db")},
41+
offline_store={"type": "file"},
42+
)
43+
)
44+
try:
45+
fs.apply([driver(), fv])
46+
split_dt = df["ts_1"][4].to_pydatetime() - timedelta(seconds=1)
47+
with (
48+
patch(
49+
"tests.utils.e2e_test_validation._utc_now",
50+
return_value=fixture_time + timedelta(seconds=setup_seconds),
51+
),
52+
patch("tests.utils.e2e_test_validation.time.sleep"),
53+
):
54+
validate_offline_online_store_consistency(fs, fv, split_dt)
55+
finally:
56+
fs.teardown()

‎sdk/python/tests/utils/e2e_test_validation.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def validate_offline_online_store_consistency(
3131

3232
# Run materialize()
3333
# use both tz-naive & tz-aware timestamps to test that they're both correctly handled
34-
start_date = (now - timedelta(hours=5)).replace(tzinfo=timezone.utc)
34+
# Anchor the range to the fixture so setup crossing an hour cannot exclude rows.
35+
start_date = (split_dt - timedelta(hours=4)).astimezone(timezone.utc)
3536
end_date = split_dt
3637
fs.materialize(feature_views=[fv.name], start_date=start_date, end_date=end_date)
3738

0 commit comments

Comments
 (0)