Skip to content

Commit

Permalink
fix: Added test cases and fixed issue identified
Browse files Browse the repository at this point in the history
  • Loading branch information
Bhargav Dodla committed Aug 20, 2024
1 parent dd395f3 commit 314e794
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 25 deletions.
30 changes: 18 additions & 12 deletions sdk/python/feast/infra/registry/caching_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,20 +338,26 @@ def refresh(self, project: Optional[str] = None):
def _refresh_cached_registry_if_necessary(self):
if self.cache_mode == "sync":
with self._refresh_lock:
expired = (
self.cached_registry_proto is None
or self.cached_registry_proto_created is None
) or (
self.cached_registry_proto_ttl.total_seconds()
> 0 # 0 ttl means infinity
and (
_utc_now()
> (
self.cached_registry_proto_created
+ self.cached_registry_proto_ttl
if self.cached_registry_proto == RegistryProto():
# Avoids the need to refresh the registry when cache is not populated yet
# Specially during the __init__ phase
# proto() will populate the cache with project metadata if no objects are registered
expired = False
else:
expired = (
self.cached_registry_proto is None
or self.cached_registry_proto_created is None
) or (
self.cached_registry_proto_ttl.total_seconds()
> 0 # 0 ttl means infinity
and (
_utc_now()
> (
self.cached_registry_proto_created
+ self.cached_registry_proto_ttl
)
)
)
)
if expired:
logger.info("Registry cache expired, so refreshing")
self.refresh()
Expand Down
4 changes: 2 additions & 2 deletions sdk/python/feast/project_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ def from_proto(cls, project_metadata_proto: ProjectMetadataProto):
entity = cls(
project_name=project_metadata_proto.project,
project_uuid=project_metadata_proto.project_uuid,
last_updated_timestamp=project_metadata_proto.last_updated_timestamp.ToDatetime().astimezone(
tz=timezone.utc
last_updated_timestamp=project_metadata_proto.last_updated_timestamp.ToDatetime().replace(
tzinfo=timezone.utc
),
)

Expand Down
117 changes: 111 additions & 6 deletions sdk/python/tests/integration/registration/test_universal_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
import logging
import os
import time
from datetime import timedelta
from datetime import datetime, timedelta, timezone
from tempfile import mkstemp
from unittest import mock

import grpc_testing
import pandas as pd
import pytest
from pytest_lazyfixture import lazy_fixture
from pytz import utc
from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_for_logs
from testcontainers.minio import MinioContainer
Expand Down Expand Up @@ -343,6 +342,7 @@ def test_apply_entity_success(test_registry):
project_uuid = project_metadata[0].project_uuid
assert len(project_metadata[0].project_uuid) == 36
assert_project_uuid(project, project_uuid, test_registry)
assert project_metadata[0].last_updated_timestamp is not None

entities = test_registry.list_entities(project, tags=entity.tags)
assert_project_uuid(project, project_uuid, test_registry)
Expand Down Expand Up @@ -802,8 +802,8 @@ def odfv1(feature_df: pd.DataFrame) -> pd.DataFrame:

# Simulate materialization
current_date = _utc_now()
end_date = current_date.replace(tzinfo=utc)
start_date = (current_date - timedelta(days=1)).replace(tzinfo=utc)
end_date = current_date.replace(tzinfo=timezone.utc)
start_date = (current_date - timedelta(days=1)).replace(tzinfo=timezone.utc)
test_registry.apply_materialization(feature_view, project, start_date, end_date)
materialized_feature_view = test_registry.get_feature_view(
"my_feature_view_1", project
Expand Down Expand Up @@ -871,8 +871,8 @@ def odfv1(feature_df: pd.DataFrame) -> pd.DataFrame:

# Simulate materialization a second time
current_date = _utc_now()
end_date_1 = current_date.replace(tzinfo=utc)
start_date_1 = (current_date - timedelta(days=1)).replace(tzinfo=utc)
end_date_1 = current_date.replace(tzinfo=timezone.utc)
start_date_1 = (current_date - timedelta(days=1)).replace(tzinfo=timezone.utc)
test_registry.apply_materialization(
updated_feature_view, project, start_date_1, end_date_1
)
Expand Down Expand Up @@ -1344,3 +1344,108 @@ def validate_project_uuid(project_uuid, test_registry):
assert len(test_registry.cached_registry_proto.project_metadata) == 1
project_metadata = test_registry.cached_registry_proto.project_metadata[0]
assert project_metadata.project_uuid == project_uuid


@pytest.mark.integration
@pytest.mark.parametrize(
"test_registry",
sql_fixtures,
)
def test_project_metadata_success(test_registry):
project = "project"
project_metadata = test_registry.get_project_metadata(project)
assert project_metadata.project_name == project
assert project_metadata.last_updated_timestamp == datetime.fromtimestamp(
1, tz=timezone.utc
)

entity = Entity(
name="driver_car_id",
description="Car driver id",
tags={"team": "matchmaking"},
)

# Register Entity
test_registry.apply_entity(entity, project)

project_metadata = test_registry.get_project_metadata(project)
assert project_metadata.project_name == project
assert project_metadata.last_updated_timestamp > datetime.fromtimestamp(
1, tz=timezone.utc
)

project_metadata_list = test_registry.get_all_projects()
assert len(project_metadata_list) == 1

test_registry.delete_project(project)

project_metadata = test_registry.get_project_metadata(project)
assert project_metadata is None

project_metadata_list = test_registry.get_all_projects()
assert len(project_metadata_list) == 0

test_registry.teardown()


@pytest.mark.integration
@pytest.mark.parametrize(
"test_registry",
sql_fixtures,
)
def test_project_metadata_from_cache_on_init_success(test_registry):
# In Setup phase, proto() method is not executing fully due to lazy fixtures, so forcing the call
test_registry.cached_registry_proto = test_registry.proto()
project = "project"
project_metadata = test_registry.get_project_metadata(project, allow_cache=True)
assert project_metadata.project_name == project
assert project_metadata.last_updated_timestamp == datetime.fromtimestamp(
1, tz=timezone.utc
)

entity = Entity(
name="driver_car_id",
description="Car driver id",
tags={"team": "matchmaking"},
)

# Register Entity
test_registry.apply_entity(entity, project)

project_metadata = test_registry.get_project_metadata(project)
assert project_metadata.project_name == project
assert project_metadata.last_updated_timestamp > datetime.fromtimestamp(
1, tz=timezone.utc
)

test_registry.refresh()
project_metadata = test_registry.get_project_metadata(project, allow_cache=True)
assert project_metadata.project_name == project
assert project_metadata.last_updated_timestamp > datetime.fromtimestamp(
1, tz=timezone.utc
)

project_metadata_list = test_registry.get_all_projects()
assert len(project_metadata_list) == 1

test_registry.teardown()


@pytest.mark.integration
@pytest.mark.parametrize(
"test_registry",
async_sql_fixtures,
)
def test_registry_cache_project_metadata_thread_async(test_registry):
project = "project"
# Wait for cache to be refreshed
time.sleep(4)
# Now objects exist
project_metadata = test_registry.get_project_metadata(project, allow_cache=True)
assert project_metadata is not None
assert project_metadata.project_name == project

project_metadata_list = test_registry.get_all_projects()
assert len(project_metadata_list) == 1

test_registry.teardown()
9 changes: 4 additions & 5 deletions sdk/python/tests/unit/test_on_demand_feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,14 @@ def test_from_proto_backwards_compatible_udf():
proto.spec.feature_transformation.user_defined_function.body_text
)

# And now we're going to null the feature_transformation proto object before reserializing the entire proto
# proto.spec.user_defined_function.body_text = on_demand_feature_view.transformation.udf_string
proto.spec.feature_transformation.user_defined_function.name = ""
proto.spec.feature_transformation.user_defined_function.body = b""
proto.spec.feature_transformation.user_defined_function.body_text = ""
# For objects that are already registered, feature_transformation and mode is not set
proto.spec.feature_transformation.Clear()
proto.spec.ClearField("mode")

# And now we expect the to get the same object back under feature_transformation
reserialized_proto = OnDemandFeatureView.from_proto(proto)
assert (
reserialized_proto.feature_transformation.udf_string
== on_demand_feature_view.feature_transformation.udf_string
)
assert reserialized_proto.mode == "pandas"
99 changes: 99 additions & 0 deletions sdk/python/tests/unit/test_project_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import unittest
from datetime import datetime, timezone

from feast.project_metadata import ProjectMetadata
from feast.protos.feast.core.Registry_pb2 import ProjectMetadata as ProjectMetadataProto


class TestProjectMetadata(unittest.TestCase):
def setUp(self):
self.project_name = "test_project"
self.project_uuid = "123e4567-e89b-12d3-a456-426614174000"
self.timestamp = datetime(2021, 1, 1, tzinfo=timezone.utc)

def test_initialization(self):
metadata = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
self.assertEqual(metadata.project_name, self.project_name)
self.assertEqual(metadata.project_uuid, self.project_uuid)
self.assertEqual(metadata.last_updated_timestamp, self.timestamp)

def test_initialization_with_default_last_updated_timestamp(self):
metadata = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
)
self.assertEqual(metadata.project_name, self.project_name)
self.assertEqual(metadata.project_uuid, self.project_uuid)
self.assertEqual(
metadata.last_updated_timestamp, datetime.fromtimestamp(1, tz=timezone.utc)
)

def test_initialization_without_project_name(self):
with self.assertRaises(ValueError):
ProjectMetadata()

def test_equality(self):
metadata1 = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
metadata2 = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
self.assertEqual(metadata1, metadata2)

def test_hash(self):
metadata = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
self.assertEqual(
hash(metadata), hash((self.project_name, self.project_uuid, self.timestamp))
)

def test_from_proto(self):
proto = ProjectMetadataProto(
project=self.project_name,
project_uuid=self.project_uuid,
)
proto.last_updated_timestamp.FromDatetime(self.timestamp)
metadata = ProjectMetadata.from_proto(proto)
self.assertEqual(metadata.project_name, self.project_name)
self.assertEqual(metadata.project_uuid, self.project_uuid)
self.assertEqual(metadata.last_updated_timestamp, self.timestamp)

def test_to_proto(self):
metadata = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
proto = metadata.to_proto()
self.assertEqual(proto.project, self.project_name)
self.assertEqual(proto.project_uuid, self.project_uuid)
self.assertEqual(
proto.last_updated_timestamp.ToDatetime().replace(tzinfo=timezone.utc),
self.timestamp,
)

def test_conversion_to_proto_and_back(self):
metadata = ProjectMetadata(
project_name=self.project_name,
project_uuid=self.project_uuid,
last_updated_timestamp=self.timestamp,
)
proto = metadata.to_proto()
metadata_from_proto = ProjectMetadata.from_proto(proto)
self.assertEqual(metadata, metadata_from_proto)


if __name__ == "__main__":
unittest.main()

0 comments on commit 314e794

Please sign in to comment.