Skip to content

Commit aeaeb91

Browse files
authored
Fix flaky tests (test_online_store_cleanup & test_feature_get_online_features_types_match) (feast-dev#2276)
* debug flaky types test Signed-off-by: pyalex <moskalenko.alexey@gmail.com> * wait for table to be actually deleted Signed-off-by: pyalex <moskalenko.alexey@gmail.com> * refresh end & start dates for each new environment instance Signed-off-by: pyalex <moskalenko.alexey@gmail.com> * fix tests Signed-off-by: pyalex <moskalenko.alexey@gmail.com>
1 parent 3b36334 commit aeaeb91

4 files changed

Lines changed: 37 additions & 13 deletions

File tree

sdk/python/feast/type_map.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,6 @@ def _python_value_to_proto_value(
300300
"""
301301
# ToDo: make a better sample for type checks (more than one element)
302302
sample = next(filter(_non_empty_value, values), None) # first not empty value
303-
if sample is None:
304-
# all input values are None or empty lists
305-
return [ProtoValue()] * len(values)
306303

307304
# Detect list type and handle separately
308305
if "list" in feast_value_type.name.lower():
@@ -312,7 +309,9 @@ def _python_value_to_proto_value(
312309
feast_value_type
313310
]
314311

315-
if not all(type(item) in valid_types for item in sample):
312+
if sample is not None and not all(
313+
type(item) in valid_types for item in sample
314+
):
316315
first_invalid = next(
317316
item for item in sample if type(item) not in valid_types
318317
)
@@ -337,6 +336,10 @@ def _python_value_to_proto_value(
337336

338337
# Handle scalar types below
339338
else:
339+
if sample is None:
340+
# all input values are None
341+
return [ProtoValue()] * len(values)
342+
340343
if feast_value_type == ValueType.UNIX_TIMESTAMP:
341344
int_timestamps = _python_datetime_to_int_timestamp(values)
342345
# ProtoValue does actually accept `np.int_` but the typing complains.

sdk/python/tests/integration/feature_repos/repo_configuration.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import re
55
import tempfile
66
import uuid
7-
from dataclasses import dataclass, field
7+
from dataclasses import dataclass
88
from datetime import datetime, timedelta
99
from pathlib import Path
1010
from typing import Any, Dict, List, Optional, Union
@@ -245,11 +245,8 @@ class Environment:
245245
python_feature_server: bool
246246
worker_id: str
247247

248-
end_date: datetime = field(
249-
default=datetime.utcnow().replace(microsecond=0, second=0, minute=0)
250-
)
251-
252248
def __post_init__(self):
249+
self.end_date = datetime.utcnow().replace(microsecond=0, second=0, minute=0)
253250
self.start_date: datetime = self.end_date - timedelta(days=3)
254251

255252
def get_feature_server_endpoint(self) -> str:

sdk/python/tests/integration/online_store/test_universal_online.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@
44
import time
55
import unittest
66
from datetime import timedelta
7-
from typing import Any, Dict, List, Union
7+
from typing import Any, Dict, List, Tuple, Union
88

99
import assertpy
1010
import numpy as np
1111
import pandas as pd
1212
import pytest
1313
import requests
14+
from botocore.exceptions import BotoCoreError
1415

1516
from feast import Entity, Feature, FeatureService, FeatureView, ValueType
1617
from feast.errors import (
1718
FeatureNameCollisionError,
1819
RequestDataNotFoundInEntityRowsException,
1920
)
21+
from feast.wait import wait_retry_backoff
2022
from tests.integration.feature_repos.repo_configuration import (
2123
Environment,
2224
construct_universal_feature_views,
@@ -569,7 +571,18 @@ def test_online_store_cleanup(environment, universal_data_sources):
569571
assert np.allclose(expected_values["value"], online_features["value"])
570572

571573
fs.apply(objects=[], objects_to_delete=[simple_driver_fv], partial=False)
572-
fs.apply([simple_driver_fv])
574+
575+
def eventually_apply() -> Tuple[None, bool]:
576+
try:
577+
fs.apply([simple_driver_fv])
578+
except BotoCoreError:
579+
return None, False
580+
581+
return None, True
582+
583+
# Online store backend might have eventual consistency in schema update
584+
# So recreating table that was just deleted might need some retries
585+
wait_retry_backoff(eventually_apply, timeout_secs=60)
573586

574587
online_features = fs.get_online_features(
575588
features=features, entity_rows=entity_rows

sdk/python/tests/integration/registration/test_universal_types.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,12 @@ def test_feature_get_online_features_types_match(online_types_test_fixtures):
221221
features = [fv.name + ":value"]
222222
entity = driver(value_type=config.entity_type)
223223
fs.apply([fv, entity])
224-
fs.materialize(environment.start_date, environment.end_date)
224+
fs.materialize(
225+
environment.start_date,
226+
environment.end_date
227+
- timedelta(hours=1) # throwing out last record to make sure
228+
# we can successfully infer type even from all empty values
229+
)
225230

226231
driver_id_value = "1" if config.entity_type == ValueType.STRING else 1
227232
online_features = fs.get_online_features(
@@ -239,9 +244,15 @@ def test_feature_get_online_features_types_match(online_types_test_fixtures):
239244
expected_dtype = feature_list_dtype_to_expected_online_response_value_type[
240245
config.feature_dtype
241246
]
247+
248+
assert len(online_features["value"]) == 1
249+
242250
if config.feature_is_list:
243251
for feature in online_features["value"]:
244-
assert isinstance(feature, list)
252+
assert isinstance(feature, list), "Feature value should be a list"
253+
assert (
254+
config.has_empty_list or len(feature) > 0
255+
), "List of values should not be empty"
245256
for element in feature:
246257
assert isinstance(element, expected_dtype)
247258
else:

0 commit comments

Comments
 (0)