Skip to content

Commit c6bcb46

Browse files
authored
Hookup pytest-benchmark to online retreival (feast-dev#1858)
* Hookup pytest-benchmark to online retreival Signed-off-by: Achal Shah <achals@gmail.com> * Fix conftest Signed-off-by: Achal Shah <achals@gmail.com> * Add dep to setup.py Signed-off-by: Achal Shah <achals@gmail.com> * Run benchmarks on push to master only Signed-off-by: Achal Shah <achals@gmail.com> * Run benchmarks on PRs too Signed-off-by: Achal Shah <achals@gmail.com> * Updates per CR Signed-off-by: Achal Shah <achals@gmail.com>
1 parent 57baf71 commit c6bcb46

7 files changed

Lines changed: 98 additions & 4 deletions

File tree

.github/workflows/integration_tests.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,10 @@ jobs:
6262
env_vars: OS,PYTHON
6363
fail_ci_if_error: true
6464
verbose: true
65+
- name: Benchmark python
66+
run: FEAST_USAGE=False IS_TEST=True pytest --verbose --color=yes sdk/python/tests --integration --benchmark --benchmark-json=./benchmarks.json
67+
- name: Upload Benchmark Artifact
68+
uses: actions/upload-artifact@v2
69+
with:
70+
name: benchmarks.json
71+
path: benchmarks.json

.github/workflows/pr_integration_tests.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,10 @@ jobs:
7373
env_vars: OS,PYTHON
7474
fail_ci_if_error: true
7575
verbose: true
76+
- name: Benchmark python
77+
run: FEAST_USAGE=False IS_TEST=True pytest --verbose --color=yes sdk/python/tests --integration --benchmark --benchmark-json=./benchmarks.json
78+
- name: Upload Benchmark Artifact
79+
uses: actions/upload-artifact@v2
80+
with:
81+
name: benchmarks.json
82+
path: benchmarks.json

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ compile-protos-python:
5252
install-python:
5353
python -m pip install -e sdk/python -U --use-deprecated=legacy-resolver
5454

55+
benchmark-python:
56+
FEAST_USAGE=False IS_TEST=True pytest --integration --benchmark sdk/python/tests
57+
5558
test-python:
5659
FEAST_USAGE=False pytest -n 8 sdk/python/tests
5760

sdk/python/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
"pytest==6.0.0",
9898
"pytest-cov",
9999
"pytest-xdist",
100+
"pytest-benchmark>=3.4.1",
100101
"pytest-lazy-fixture==0.6.3",
101102
"pytest-timeout==1.4.2",
102103
"pytest-ordering==0.6.*",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import random
2+
3+
import pytest
4+
5+
from feast import FeatureService
6+
from tests.integration.feature_repos.repo_configuration import (
7+
construct_universal_feature_views,
8+
)
9+
from tests.integration.feature_repos.universal.entities import customer, driver
10+
11+
12+
@pytest.mark.benchmark
13+
@pytest.mark.integration
14+
def test_online_retrieval(environment, universal_data_sources, benchmark):
15+
16+
fs = environment.feature_store
17+
entities, datasets, data_sources = universal_data_sources
18+
feature_views = construct_universal_feature_views(data_sources)
19+
20+
feature_service = FeatureService(
21+
"convrate_plus100",
22+
features=[feature_views["driver"][["conv_rate"]], feature_views["driver_odfv"]],
23+
)
24+
25+
feast_objects = []
26+
feast_objects.extend(feature_views.values())
27+
feast_objects.extend([driver(), customer(), feature_service])
28+
fs.apply(feast_objects)
29+
fs.materialize(environment.start_date, environment.end_date)
30+
31+
sample_drivers = random.sample(entities["driver"], 10)
32+
33+
sample_customers = random.sample(entities["customer"], 10)
34+
35+
entity_rows = [
36+
{"driver": d, "customer_id": c, "val_to_add": 50}
37+
for (d, c) in zip(sample_drivers, sample_customers)
38+
]
39+
40+
feature_refs = [
41+
"driver_stats:conv_rate",
42+
"driver_stats:avg_daily_trips",
43+
"customer_profile:current_balance",
44+
"customer_profile:avg_passenger_count",
45+
"customer_profile:lifetime_trip_count",
46+
"conv_rate_plus_100:conv_rate_plus_100",
47+
"conv_rate_plus_100:conv_rate_plus_val_to_add",
48+
"global_stats:num_rides",
49+
"global_stats:avg_ride_length",
50+
]
51+
unprefixed_feature_refs = [f.rsplit(":", 1)[-1] for f in feature_refs if ":" in f]
52+
# Remove the on demand feature view output features, since they're not present in the source dataframe
53+
unprefixed_feature_refs.remove("conv_rate_plus_100")
54+
unprefixed_feature_refs.remove("conv_rate_plus_val_to_add")
55+
56+
benchmark(
57+
fs.get_online_features, features=feature_refs, entity_rows=entity_rows,
58+
)

sdk/python/tests/conftest.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def pytest_configure(config):
3737
config.addinivalue_line(
3838
"markers", "integration: mark test that has external dependencies"
3939
)
40+
config.addinivalue_line("markers", "benchmark: mark benchmarking tests")
4041

4142

4243
def pytest_addoption(parser):
@@ -46,17 +47,23 @@ def pytest_addoption(parser):
4647
default=False,
4748
help="Run tests with external dependencies",
4849
)
50+
parser.addoption(
51+
"--benchmark", action="store_true", default=False, help="Run benchmark tests",
52+
)
4953

5054

5155
def pytest_collection_modifyitems(config, items):
52-
if config.getoption("--integration"):
53-
return
56+
should_run_integration = config.getoption("--integration") is True
57+
should_run_benchmark = config.getoption("--benchmark") is True
5458
skip_integration = pytest.mark.skip(
5559
reason="not running tests with external dependencies"
5660
)
61+
skip_benchmark = pytest.mark.skip(reason="not running benchmarks")
5762
for item in items:
58-
if "integration" in item.keywords:
63+
if "integration" in item.keywords and not should_run_integration:
5964
item.add_marker(skip_integration)
65+
if "benchmark" in item.keywords and not should_run_benchmark:
66+
item.add_marker(skip_benchmark)
6067

6168

6269
@pytest.fixture

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
)
3333

3434

35-
@dataclass(frozen=True, repr=True)
35+
@dataclass(frozen=True)
3636
class IntegrationTestRepoConfig:
3737
"""
3838
This class should hold all possible parameters that may need to be varied by individual tests.
@@ -47,6 +47,17 @@ class IntegrationTestRepoConfig:
4747
infer_event_timestamp_col: bool = True
4848
infer_features: bool = False
4949

50+
def __repr__(self) -> str:
51+
return "-".join(
52+
[
53+
f"Provider: {self.provider}",
54+
f"{self.offline_store_creator.__name__.split('.')[-1].rstrip('DataSourceCreator')}",
55+
self.online_store
56+
if isinstance(self.online_store, str)
57+
else self.online_store["type"],
58+
]
59+
)
60+
5061

5162
DYNAMO_CONFIG = {"type": "dynamodb", "region": "us-west-2"}
5263
REDIS_CONFIG = {"type": "redis", "connection_string": "localhost:6379,db=0"}

0 commit comments

Comments
 (0)