From 9e0e7c7fa6040ec9ba7281c7e41710b304c1a9b7 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 26 Apr 2021 20:22:00 -0700 Subject: [PATCH 01/28] Add support for DynamoDB and S3 registry Signed-off-by: lblokhin --- docs/specs/dynamodb_online_example.monopic | Bin 0 -> 1601 bytes docs/specs/dynamodb_online_example.png | Bin 0 -> 90267 bytes sdk/python/feast/cli.py | 2 +- sdk/python/feast/errors.py | 20 ++ .../feast/infra/aws_dynamodb_provider.py | 207 ++++++++++++++++++ sdk/python/feast/infra/provider.py | 4 + sdk/python/feast/registry.py | 73 ++++++ sdk/python/feast/repo_config.py | 23 +- sdk/python/feast/templates/aws/bootstrap.py | 35 +++ .../feast/templates/aws/driver_example.py | 36 +++ .../feast/templates/aws/feature_store.yaml | 3 + sdk/python/feast/templates/aws/test.py | 41 ++++ sdk/python/setup.py | 1 + 13 files changed, 438 insertions(+), 7 deletions(-) create mode 100644 docs/specs/dynamodb_online_example.monopic create mode 100644 docs/specs/dynamodb_online_example.png create mode 100644 sdk/python/feast/infra/aws_dynamodb_provider.py create mode 100644 sdk/python/feast/templates/aws/bootstrap.py create mode 100644 sdk/python/feast/templates/aws/driver_example.py create mode 100644 sdk/python/feast/templates/aws/feature_store.yaml create mode 100644 sdk/python/feast/templates/aws/test.py diff --git a/docs/specs/dynamodb_online_example.monopic b/docs/specs/dynamodb_online_example.monopic new file mode 100644 index 0000000000000000000000000000000000000000..57a2e9e99bc5ef69ca8cb45b1cbec8f8e30202d5 GIT binary patch literal 1601 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 literal 0 HcmV?d00001 diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 8520e2715f..1c73f8c6f9 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -238,7 +238,7 @@ def materialize_incremental_command(end_ts: str, views: List[str]): @click.option( "--template", "-t", - type=click.Choice(["local", "gcp"], case_sensitive=False), + type=click.Choice(["local", "gcp", "aws"], case_sensitive=False), help="Specify a template for the created project", default="local", ) diff --git a/sdk/python/feast/errors.py b/sdk/python/feast/errors.py index 2b08868a45..2804fd9bfa 100644 --- a/sdk/python/feast/errors.py +++ b/sdk/python/feast/errors.py @@ -28,6 +28,26 @@ def __init__(self, name, project=None): super().__init__(f"Feature table {name} does not exist") +class FeatureBucketNotExist(FeastObjectNotFoundException): + def __init__(self, bucket, project=None): + if project: + super().__init__( + f"Feature bucket {bucket} does not exist in project {project}" + ) + else: + super().__init__(f"Feature bucket {bucket} does not exist") + + +class FeatureBucketForbiddenAccess(FeastObjectNotFoundException): + def __init__(self, bucket, project=None): + if project: + super().__init__( + f"Private Registry Bucket {bucket} forbidden Access in project {project}" + ) + else: + super().__init__(f"Private Registry Bucket {bucket} forbidden Access") + + class FeastProviderLoginError(Exception): """Error class that indicates a user has not authenticated with their provider.""" diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py new file mode 100644 index 0000000000..0c9e67a4d5 --- /dev/null +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -0,0 +1,207 @@ +from datetime import datetime +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + +import boto3 +import mmh3 +import pandas +from botocore.exceptions import ClientError + +from feast import FeatureTable, utils +from feast.entity import Entity +from feast.feature_view import FeatureView +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.helpers import get_offline_store_from_sources +from feast.infra.provider import ( + Provider, + RetrievalJob, + _convert_arrow_to_proto, + _get_column_names, + _run_field_mapping, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.registry import Registry +from feast.repo_config import DynamoDbOnlineStoreConfig, RepoConfig + + +class AwsDynamodbProvider(Provider): + def __init__(self, config: RepoConfig): + assert isinstance(config.online_store, DynamoDbOnlineStoreConfig) + + def _initialize_dynamodb(self): + return boto3.resource("dynamodb") + + def update_infra( + self, + project: str, + tables_to_delete: Sequence[Union[FeatureTable, FeatureView]], + tables_to_keep: Sequence[Union[FeatureTable, FeatureView]], + entities_to_delete: Sequence[Entity], + entities_to_keep: Sequence[Entity], + partial: bool, + ): + dynamodb = self._initialize_dynamodb() + + for table_name in tables_to_keep: + table = None + try: + table = dynamodb.create_table( + TableName=table_name.name, + KeySchema=[ + {"AttributeName": "Row", "KeyType": "HASH"}, + {"AttributeName": "Project", "KeyType": "RANGE"}, + ], + AttributeDefinitions=[ + {"AttributeName": "Row", "AttributeType": "S"}, + {"AttributeName": "Project", "AttributeType": "S"}, + ], + ProvisionedThroughput={ + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5, + }, + ) + table.meta.client.get_waiter("table_exists").wait( + TableName=table_name.name + ) + except ClientError as ce: + print(ce) + if ce.response["Error"]["Code"] == "ResourceNotFoundException": + table = dynamodb.Table(table_name.name) + + for table_name in tables_to_delete: + table = dynamodb.Table(table_name.name) + table.delete() + + def teardown_infra( + self, + project: str, + tables: Sequence[Union[FeatureTable, FeatureView]], + entities: Sequence[Entity], + ) -> None: + dynamodb = self._initialize_dynamodb() + + for table_name in tables: + table = dynamodb.Table(table_name) + table.delete() + + def online_write_batch( + self, + project: str, + table: Union[FeatureTable, FeatureView], + data: List[ + Tuple[EntityKeyProto, Dict[str, ValueProto], datetime, Optional[datetime]] + ], + progress: Optional[Callable[[int], Any]], + ) -> None: + dynamodb = self._initialize_dynamodb() + + table_instance = dynamodb.Table(table.name) + with table_instance.batch_writer() as batch: + for entity_key, features, timestamp, created_ts in data: + document_id = compute_datastore_entity_id(entity_key) # TODO check id + # TODO compression encoding + batch.put_item( + Item={ + "Row": document_id, # PartitionKey + "Project": project, # SortKey + "event_ts": str(utils.make_tzaware(timestamp)), + "values": { + k: v.SerializeToString() + for k, v in features.items() # Serialized Features + }, + } + ) + + def online_read( + self, + project: str, + table: Union[FeatureTable, FeatureView], + entity_keys: List[EntityKeyProto], + ) -> List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]]: + dynamodb = self._initialize_dynamodb() + + result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] + for entity_key in entity_keys: + table_instace = dynamodb.Table(table.name) + document_id = compute_datastore_entity_id(entity_key) # TODO check id + response = table_instace.get_item( + Key={"Row": document_id, "Project": project} + ) + value = response["Item"] + + if value is not None: + res = {} + for feature_name, value_bin in value["values"].items(): + val = ValueProto() + val.ParseFromString(value_bin.value) + res[feature_name] = val + result.append((value["event_ts"], res)) + else: + result.append((None, None)) + return result + + def materialize_single_feature_view( + self, + feature_view: FeatureView, + start_date: datetime, + end_date: datetime, + registry: Registry, + project: str, + ) -> None: + entities = [] + for entity_name in feature_view.entities: + entities.append(registry.get_entity(entity_name, project)) + + ( + join_key_columns, + feature_name_columns, + event_timestamp_column, + created_timestamp_column, + ) = _get_column_names(feature_view, entities) + + start_date = utils.make_tzaware(start_date) + end_date = utils.make_tzaware(end_date) + + offline_store = get_offline_store_from_sources([feature_view.input]) + table = offline_store.pull_latest_from_table_or_query( + data_source=feature_view.input, + join_key_columns=join_key_columns, + feature_name_columns=feature_name_columns, + event_timestamp_column=event_timestamp_column, + created_timestamp_column=created_timestamp_column, + start_date=start_date, + end_date=end_date, + ) + + if feature_view.input.field_mapping is not None: + table = _run_field_mapping(table, feature_view.input.field_mapping) + + join_keys = [entity.join_key for entity in entities] + rows_to_write = _convert_arrow_to_proto(table, feature_view, join_keys) + + self.online_write_batch(project, feature_view, rows_to_write, None) + + feature_view.materialization_intervals.append((start_date, end_date)) + registry.apply_feature_view(feature_view, project) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pandas.DataFrame, str], + registry: Registry, + project: str, + ) -> RetrievalJob: + # TODO implement me + pass + + +def compute_datastore_entity_id(entity_key: EntityKeyProto) -> str: + """ + Compute Datastore Entity id given Feast Entity Key. + + Remember that Datastore Entity is a concept from the Datastore data model, that has nothing to + do with the Entity concept we have in Feast. + """ + return mmh3.hash_bytes(serialize_entity_key(entity_key)).hex() diff --git a/sdk/python/feast/infra/provider.py b/sdk/python/feast/infra/provider.py index fbb8bb021e..a93ad5d204 100644 --- a/sdk/python/feast/infra/provider.py +++ b/sdk/python/feast/infra/provider.py @@ -142,6 +142,10 @@ def get_provider(config: RepoConfig, repo_path: Path) -> Provider: from feast.infra.gcp import GcpProvider return GcpProvider(config) + elif config.provider == "aws_dynamodb": + from feast.infra.aws_dynamodb_provider import AwsDynamodbProvider + + return AwsDynamodbProvider(config) elif config.provider == "local": from feast.infra.local import LocalProvider diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index 8832ea9f94..ad5a906dda 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -23,6 +23,8 @@ from feast.entity import Entity from feast.errors import ( EntityNotFoundException, + FeatureBucketForbiddenAccess, + FeatureBucketNotExist, FeatureTableNotFoundException, FeatureViewNotFoundException, ) @@ -56,6 +58,8 @@ def __init__(self, registry_path: str, repo_path: Path, cache_ttl: timedelta): uri = urlparse(registry_path) if uri.scheme == "gs": self._registry_store: RegistryStore = GCSRegistryStore(registry_path) + elif uri.scheme == "s3": + self._registry_store = S3RegistryStore(registry_path) elif uri.scheme == "file" or uri.scheme == "": self._registry_store = LocalRegistryStore( repo_path=repo_path, registry_path_string=registry_path @@ -495,3 +499,72 @@ def _write_registry(self, registry_proto: RegistryProto): file_obj.seek(0) blob.upload_from_file(file_obj) return + + +class S3RegistryStore(RegistryStore): + def __init__(self, uri: str): + self._uri = urlparse(uri) + self._bucket = self._uri.hostname + self._key = self._uri.path.lstrip("/") + return + + def get_registry_proto(self): + import boto3 + import botocore + + file_obj = TemporaryFile() + registry_proto = RegistryProto() + s3 = boto3.resource("s3") + try: + bucket = s3.Bucket(self._bucket) + s3.meta.client.head_bucket(Bucket=bucket.name) + except botocore.client.ClientError as e: + # If a client error is thrown, then check that it was a 404 error. + # If it was a 404 error, then the bucket does not exist. + error_code = int(e.response["Error"]["Code"]) + if error_code == 404: + raise FeatureBucketNotExist(self._bucket) + else: + raise FeatureBucketForbiddenAccess(self._bucket) + + try: + obj = bucket.Object(self._key) + obj.download_fileobj(file_obj) + file_obj.seek(0) + registry_proto.ParseFromString(file_obj.read()) + return registry_proto + except botocore.exceptions.ClientError as e: + if e.response["Error"]["Code"] == "404": + raise FileNotFoundError( + f'Registry not found at path "{self._uri.geturl()}". Have you run "feast apply"?' + ) + else: + raise FileNotFoundError( + f'Registry is not able to locate data under path "{self._uri.geturl()}" with [original error]: {e.response}' + ) + + def update_registry_proto( + self, updater: Optional[Callable[[RegistryProto], RegistryProto]] = None + ): + try: + registry_proto = self.get_registry_proto() + except FileNotFoundError: + registry_proto = RegistryProto() + registry_proto.registry_schema_version = REGISTRY_SCHEMA_VERSION + if updater: + registry_proto = updater(registry_proto) + self._write_registry(registry_proto) + return + + def _write_registry(self, registry_proto: RegistryProto): + import boto3 + + registry_proto.version_id = str(uuid.uuid4()) + registry_proto.last_updated.FromDatetime(datetime.utcnow()) + # we have already checked the bucket exists so no need to do it again + file_obj = TemporaryFile() + file_obj.write(registry_proto.SerializeToString()) + file_obj.seek(0) + s3 = boto3.client("s3") + s3.put_object(Bucket=self._bucket, Body=file_obj, Key=self._key) + return diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index bb18119b42..168a97cba5 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -34,7 +34,16 @@ class DatastoreOnlineStoreConfig(FeastBaseModel): """ (optional) GCP Project Id """ -OnlineStoreConfig = Union[DatastoreOnlineStoreConfig, SqliteOnlineStoreConfig] +class DynamoDbOnlineStoreConfig(FeastBaseModel): + """Online store config for DynamoDB store""" + + type: Literal["dynamodb"] = "dynamodb" + """Online store type selector""" + + +OnlineStoreConfig = Union[ + DatastoreOnlineStoreConfig, SqliteOnlineStoreConfig, DynamoDbOnlineStoreConfig +] class RegistryConfig(FeastBaseModel): @@ -63,7 +72,7 @@ class RepoConfig(FeastBaseModel): """ provider: StrictStr - """ str: local or gcp """ + """ str: local or gcp or aws_dynamodb """ online_store: OnlineStoreConfig = SqliteOnlineStoreConfig() """ OnlineStoreConfig: Online store configuration (optional depending on provider) """ @@ -100,18 +109,20 @@ def _validate_online_store_config(cls, values): values["online_store"]["type"] = "sqlite" elif values["provider"] == "gcp": values["online_store"]["type"] = "datastore" - + elif values["provider"] == "aws_dynamodb": + values["online_store"]["type"] = "dynamodb" online_store_type = values["online_store"]["type"] - # Make sure the user hasn't provided the wrong type - assert online_store_type in ["datastore", "sqlite"] + assert online_store_type in ["datastore", "sqlite", "dynamodb"] # Validate the dict to ensure one of the union types match try: if online_store_type == "sqlite": SqliteOnlineStoreConfig(**values["online_store"]) - elif values["online_store"]["type"] == "datastore": + elif online_store_type == "datastore": DatastoreOnlineStoreConfig(**values["online_store"]) + elif online_store_type == "dynamodb": + DynamoDbOnlineStoreConfig(**values["online_store"]) else: raise ValidationError( f"Invalid online store type {online_store_type}" diff --git a/sdk/python/feast/templates/aws/bootstrap.py b/sdk/python/feast/templates/aws/bootstrap.py new file mode 100644 index 0000000000..33a11a1c2e --- /dev/null +++ b/sdk/python/feast/templates/aws/bootstrap.py @@ -0,0 +1,35 @@ +def bootstrap(): + # Bootstrap() will automatically be called from the init_repo() during `feast init` + + import pathlib + from datetime import datetime, timedelta + + from feast.driver_test_data import create_driver_hourly_stats_df + + repo_path = pathlib.Path(__file__).parent.absolute() + data_path = repo_path / "data" + data_path.mkdir(exist_ok=True) + + end_date = datetime.now().replace(microsecond=0, second=0, minute=0) + start_date = end_date - timedelta(days=15) + + driver_entities = [1001, 1002, 1003, 1004, 1005] + driver_df = create_driver_hourly_stats_df(driver_entities, start_date, end_date) + + driver_stats_path = data_path / "driver_stats.parquet" + driver_df.to_parquet(path=str(driver_stats_path), allow_truncated_timestamps=True) + + example_py_file = repo_path / "driver_example.py" + replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) + + +def replace_str_in_file(file_path, match_str, sub_str): + with open(file_path, "r") as f: + contents = f.read() + contents = contents.replace(match_str, sub_str) + with open(file_path, "wt") as f: + f.write(contents) + + +if __name__ == "__main__": + bootstrap() diff --git a/sdk/python/feast/templates/aws/driver_example.py b/sdk/python/feast/templates/aws/driver_example.py new file mode 100644 index 0000000000..a66dbba120 --- /dev/null +++ b/sdk/python/feast/templates/aws/driver_example.py @@ -0,0 +1,36 @@ +# This is an example feature definition file + +from google.protobuf.duration_pb2 import Duration + +from feast import Entity, Feature, FeatureView, ValueType +from feast.data_source import FileSource + +# Read data from parquet files. Parquet is convenient for local development mode. For +# production, you can use your favorite DWH, such as BigQuery. See Feast documentation +# for more info. +driver_hourly_stats = FileSource( + path="%PARQUET_PATH%", + event_timestamp_column="datetime", + created_timestamp_column="created", +) + +# Define an entity for the driver. You can think of entity as a primary key used to +# fetch features. +driver = Entity(name="driver_id", value_type=ValueType.INT64, description="driver id",) + +# Our parquet files contain sample data that includes a driver_id column, timestamps and +# three feature column. Here we define a Feature View that will allow us to serve this +# data to our model online. +driver_hourly_stats_view = FeatureView( + name="driver_hourly_stats", + entities=["driver_id"], + ttl=Duration(seconds=86400 * 1), + features=[ + Feature(name="conv_rate", dtype=ValueType.FLOAT), + Feature(name="acc_rate", dtype=ValueType.FLOAT), + Feature(name="avg_daily_trips", dtype=ValueType.INT64), + ], + online=True, + input=driver_hourly_stats, + tags={}, +) diff --git a/sdk/python/feast/templates/aws/feature_store.yaml b/sdk/python/feast/templates/aws/feature_store.yaml new file mode 100644 index 0000000000..f534a413e7 --- /dev/null +++ b/sdk/python/feast/templates/aws/feature_store.yaml @@ -0,0 +1,3 @@ +project: my_project +registry: s3://data/registry.db +provider: aws_dynamodb diff --git a/sdk/python/feast/templates/aws/test.py b/sdk/python/feast/templates/aws/test.py new file mode 100644 index 0000000000..47a036e7ea --- /dev/null +++ b/sdk/python/feast/templates/aws/test.py @@ -0,0 +1,41 @@ +from datetime import datetime + +import pandas as pd +from driver_example import driver, driver_hourly_stats_view + +from feast import FeatureStore + + +def main(): + pd.set_option("display.max_columns", None) + pd.set_option("display.width", 1000) + + # Load the feature store from the current path + fs = FeatureStore(repo_path=".") + + # Deploy the feature store to GCP + print("Deploying feature store to AWS...") + fs.apply([driver, driver_hourly_stats_view]) + + # Select features + feature_refs = ["driver_hourly_stats:conv_rate", "driver_hourly_stats:acc_rate"] + + # print() + print("Loading features into the online store...") + fs.materialize_incremental(end_date=datetime.now()) + + # print() + print("Retrieving online features...") + + # Retrieve features from the online store (Firestore) + online_features = fs.get_online_features( + feature_refs=feature_refs, + entity_rows=[{"driver_id": 1001}, {"driver_id": 1002}], + ).to_dict() + + print() + print(pd.DataFrame.from_dict(online_features)) + + +if __name__ == "__main__": + main() diff --git a/sdk/python/setup.py b/sdk/python/setup.py index 93536e9cbf..554897d0b5 100644 --- a/sdk/python/setup.py +++ b/sdk/python/setup.py @@ -61,6 +61,7 @@ "tabulate==0.8.*", "toml==0.10.*", "tqdm==4.*", + "boto3==1.17.*", ] CI_REQUIRED = [ From 44aadd8428ecf6d9fec2251d7943422e719e9cab Mon Sep 17 00:00:00 2001 From: lblokhin Date: Tue, 27 Apr 2021 11:31:10 -0700 Subject: [PATCH 02/28] rcu and wcu as a parameter of dynamodb online store Signed-off-by: lblokhin --- sdk/python/feast/infra/aws_dynamodb_provider.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index 0c9e67a4d5..6b5211dc10 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -25,8 +25,18 @@ class AwsDynamodbProvider(Provider): + def __init__(self, config: RepoConfig): assert isinstance(config.online_store, DynamoDbOnlineStoreConfig) + if config and config.online_store and config.online_store.rcu: + self._rcu = config.online_store.rcu + else: + self._rcu = 5 + + if config and config.online_store and config.online_store.wcu: + self._wcu = config.online_store.wcu + else: + self._wcu = 5 def _initialize_dynamodb(self): return boto3.resource("dynamodb") @@ -56,8 +66,8 @@ def update_infra( {"AttributeName": "Project", "AttributeType": "S"}, ], ProvisionedThroughput={ - "ReadCapacityUnits": 5, - "WriteCapacityUnits": 5, + "ReadCapacityUnits": self._rcu, + "WriteCapacityUnits": self._wcu, }, ) table.meta.client.get_waiter("table_exists").wait( From 638379186852fd0e9d96166dffa83be55437558a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Tue, 4 May 2021 15:32:32 -0700 Subject: [PATCH 03/28] fix linter Signed-off-by: lblokhin --- sdk/python/feast/infra/aws_dynamodb_provider.py | 4 +++- sdk/python/feast/repo_config.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index 6b5211dc10..00e9b88a19 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -25,7 +25,9 @@ class AwsDynamodbProvider(Provider): - + _wcu: int + _rcu: int + def __init__(self, config: RepoConfig): assert isinstance(config.online_store, DynamoDbOnlineStoreConfig) if config and config.online_store and config.online_store.rcu: diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index 168a97cba5..0feb8c7fa0 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -1,7 +1,14 @@ from pathlib import Path import yaml -from pydantic import BaseModel, StrictInt, StrictStr, ValidationError, root_validator +from pydantic import ( + BaseModel, + PositiveInt, + StrictInt, + StrictStr, + ValidationError, + root_validator, +) from pydantic.error_wrappers import ErrorWrapper from pydantic.typing import Dict, Literal, Optional, Union @@ -40,6 +47,12 @@ class DynamoDbOnlineStoreConfig(FeastBaseModel): type: Literal["dynamodb"] = "dynamodb" """Online store type selector""" + rcu: Optional[PositiveInt] = 5 + """ Read capacity unit """ + + wcu: Optional[PositiveInt] = 5 + """ Write capacity unit """ + OnlineStoreConfig = Union[ DatastoreOnlineStoreConfig, SqliteOnlineStoreConfig, DynamoDbOnlineStoreConfig From 73ff67aa0cf0f5dc716757f579bab25d4af02ba0 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 17 May 2021 22:06:20 -0700 Subject: [PATCH 04/28] aws dependency to extras Signed-off-by: lblokhin --- Makefile | 3 +++ sdk/python/setup.py | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3c3e6e1b24..08aced0810 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,9 @@ install-ci-dependencies: install-python-ci-dependencies install-go-ci-dependenci install-python-ci-dependencies: pip install -e "sdk/python[ci]" +install-python-aws-dependencies: + pip install -e "sdk/python[aws]" + package-protos: cp -r ${ROOT_DIR}/protos ${ROOT_DIR}/sdk/python/feast/protos diff --git a/sdk/python/setup.py b/sdk/python/setup.py index 554897d0b5..4e03874cb8 100644 --- a/sdk/python/setup.py +++ b/sdk/python/setup.py @@ -61,7 +61,6 @@ "tabulate==0.8.*", "toml==0.10.*", "tqdm==4.*", - "boto3==1.17.*", ] CI_REQUIRED = [ @@ -93,6 +92,10 @@ "assertpy==1.1", ] +AWS_REQUIRED = [ + "boto3==1.17.*", +] + # README file from Feast repo root directory repo_root = ( subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE) @@ -183,7 +186,8 @@ def run(self): # Install dev requirements with: pip install -e .[dev] extras_require={ "dev": ["mypy-protobuf==1.*", "grpcio-testing==1.*"], - "ci": CI_REQUIRED + "ci": CI_REQUIRED, + "aws": AWS_REQUIRED }, include_package_data=True, license="Apache", From aa6d0da20d55d121cd2dacd2a2143abe0f249fab Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 17 May 2021 22:07:31 -0700 Subject: [PATCH 05/28] FEAST_S3_ENDPOINT_URL Signed-off-by: lblokhin --- sdk/python/feast/registry.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index ad5a906dda..594814c7ff 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import uuid from abc import ABC, abstractmethod from datetime import datetime, timedelta @@ -514,7 +515,7 @@ def get_registry_proto(self): file_obj = TemporaryFile() registry_proto = RegistryProto() - s3 = boto3.resource("s3") + s3 = boto3.resource("s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL")) try: bucket = s3.Bucket(self._bucket) s3.meta.client.head_bucket(Bucket=bucket.name) @@ -565,6 +566,6 @@ def _write_registry(self, registry_proto: RegistryProto): file_obj = TemporaryFile() file_obj.write(registry_proto.SerializeToString()) file_obj.seek(0) - s3 = boto3.client("s3") + s3 = boto3.resource("s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL")) s3.put_object(Bucket=self._bucket, Body=file_obj, Key=self._key) return From 0a870509db75f4635d4bd139ee82725853d469a9 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 17 May 2021 22:08:34 -0700 Subject: [PATCH 06/28] tests Signed-off-by: lblokhin --- sdk/python/feast/templates/aws/test.py | 4 +- sdk/python/tests/test_cli_aws.py | 55 ++++++++++++++++++++++++++ sdk/python/tests/test_feature_store.py | 18 +++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 sdk/python/tests/test_cli_aws.py diff --git a/sdk/python/feast/templates/aws/test.py b/sdk/python/feast/templates/aws/test.py index 47a036e7ea..806b268dea 100644 --- a/sdk/python/feast/templates/aws/test.py +++ b/sdk/python/feast/templates/aws/test.py @@ -13,7 +13,7 @@ def main(): # Load the feature store from the current path fs = FeatureStore(repo_path=".") - # Deploy the feature store to GCP + # Deploy the feature store to AWS print("Deploying feature store to AWS...") fs.apply([driver, driver_hourly_stats_view]) @@ -27,7 +27,7 @@ def main(): # print() print("Retrieving online features...") - # Retrieve features from the online store (Firestore) + # Retrieve features from the online store (Dynamodb) online_features = fs.get_online_features( feature_refs=feature_refs, entity_rows=[{"driver_id": 1001}, {"driver_id": 1002}], diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py new file mode 100644 index 0000000000..ba98d83226 --- /dev/null +++ b/sdk/python/tests/test_cli_aws.py @@ -0,0 +1,55 @@ +import random +import string +import tempfile +from pathlib import Path +from textwrap import dedent + +import pytest + +from feast.feature_store import FeatureStore +from tests.cli_utils import CliRunner +from tests.online_read_write_test import basic_rw_test + + +@pytest.mark.integration +def test_basic() -> None: + project_id = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(10) + ) + runner = CliRunner() + with tempfile.TemporaryDirectory() as repo_dir_name, tempfile.TemporaryDirectory() as data_dir_name: + + repo_path = Path(repo_dir_name) + data_path = Path(data_dir_name) + + repo_config = repo_path / "feature_store.yaml" + + repo_config.write_text( + dedent( + f""" + project: {project_id} + registry: {data_path / "registry.db"} + provider: aws_dynamodb + """ + ) + ) + + repo_example = repo_path / "example.py" + repo_example.write_text( + (Path(__file__).parent / "example_feature_repo_1.py").read_text() + ) + + result = runner.run(["apply"], cwd=repo_path) + assert result.returncode == 0 + + # Doing another apply should be a no op, and should not cause errors + result = runner.run(["apply"], cwd=repo_path) + assert result.returncode == 0 + + basic_rw_test( + FeatureStore(repo_path=str(repo_path), config=None), + view_name="driver_locations", + ) + + result = runner.run(["teardown"], cwd=repo_path) + assert result.returncode == 0 diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 015f0ab2ad..4696325d50 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -66,6 +66,24 @@ def feature_store_with_gcs_registry(): ) +@pytest.fixture +def feature_store_with_s3_registry(): + import boto3 + + s3 = boto3.resource("s3") + bucket_name = f"feast-registry-test-{int(time.time() * 1000)}" + bucket = s3.Bucket(bucket_name) + s3.meta.client.head_bucket(Bucket=bucket.name) + + return FeatureStore( + config=RepoConfig( + registry=f"s3://{bucket_name}/registry.db", + project="default", + provider="aws_dynamodb", + ) + ) + + @pytest.mark.parametrize( "test_feature_store", [lazy_fixture("feature_store_with_local_registry")], ) From 00e86751caa7a07eb047a3513dfc1ac4c0f9635a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 17 May 2021 22:23:47 -0700 Subject: [PATCH 07/28] fix signature, after merge Signed-off-by: lblokhin --- sdk/python/feast/infra/aws_dynamodb_provider.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index 00e9b88a19..688f2aa7db 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -5,6 +5,7 @@ import mmh3 import pandas from botocore.exceptions import ClientError +from tqdm import tqdm from feast import FeatureTable, utils from feast.entity import Entity @@ -159,6 +160,7 @@ def materialize_single_feature_view( end_date: datetime, registry: Registry, project: str, + tqdm_builder: Callable[[int], tqdm], ) -> None: entities = [] for entity_name in feature_view.entities: @@ -191,7 +193,10 @@ def materialize_single_feature_view( join_keys = [entity.join_key for entity in entities] rows_to_write = _convert_arrow_to_proto(table, feature_view, join_keys) - self.online_write_batch(project, feature_view, rows_to_write, None) + with tqdm_builder(len(rows_to_write)) as pbar: + self.online_write_batch( + project, feature_view, rows_to_write, lambda x: pbar.update(x) + ) feature_view.materialization_intervals.append((start_date, end_date)) registry.apply_feature_view(feature_view, project) From 6a99cd9e07a98559f7db2d5cddaf64994c480cb4 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 17 May 2021 22:45:06 -0700 Subject: [PATCH 08/28] aws default region name configurable Signed-off-by: lblokhin --- sdk/python/feast/infra/aws_dynamodb_provider.py | 10 +++++++++- sdk/python/feast/repo_config.py | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index 688f2aa7db..ba659b7845 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -1,3 +1,4 @@ +import os from datetime import datetime from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union @@ -41,8 +42,15 @@ def __init__(self, config: RepoConfig): else: self._wcu = 5 + if config and config.online_store and config.online_store.region_name: + self.region_name = config.online_store.region_name + os.environ["AWS_DEFAULT_REGION"] = self.region_name + else: + self.region_name = os.environ.get("AWS_DEFAULT_REGION", "us-west-2") + os.environ["AWS_DEFAULT_REGION"] = self.region_name + def _initialize_dynamodb(self): - return boto3.resource("dynamodb") + return boto3.resource("dynamodb", region_name=self.region_name) def update_infra( self, diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index 0feb8c7fa0..9bafb5b271 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -53,6 +53,9 @@ class DynamoDbOnlineStoreConfig(FeastBaseModel): wcu: Optional[PositiveInt] = 5 """ Write capacity unit """ + region_name: Optional[StrictStr] = None + """ AWS Region Name """ + OnlineStoreConfig = Union[ DatastoreOnlineStoreConfig, SqliteOnlineStoreConfig, DynamoDbOnlineStoreConfig From db616c46a3882327990811dbcd00df0bec6dbfe5 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Thu, 10 Jun 2021 18:37:42 -0700 Subject: [PATCH 09/28] add offlinestore config type to test Signed-off-by: lblokhin --- sdk/python/feast/infra/aws_dynamodb_provider.py | 7 ++++--- sdk/python/feast/repo_config.py | 2 ++ sdk/python/tests/test_cli_aws.py | 10 ++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index ba659b7845..5a97296639 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -12,7 +12,7 @@ from feast.entity import Entity from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.helpers import get_offline_store_from_sources +from feast.infra.offline_stores.helpers import get_offline_store_from_config from feast.infra.provider import ( Provider, RetrievalJob, @@ -48,6 +48,8 @@ def __init__(self, config: RepoConfig): else: self.region_name = os.environ.get("AWS_DEFAULT_REGION", "us-west-2") os.environ["AWS_DEFAULT_REGION"] = self.region_name + assert config.offline_store is not None + self.offline_store = get_offline_store_from_config(config.offline_store) def _initialize_dynamodb(self): return boto3.resource("dynamodb", region_name=self.region_name) @@ -184,8 +186,7 @@ def materialize_single_feature_view( start_date = utils.make_tzaware(start_date) end_date = utils.make_tzaware(end_date) - offline_store = get_offline_store_from_sources([feature_view.input]) - table = offline_store.pull_latest_from_table_or_query( + table = self.offline_store.pull_latest_from_table_or_query( data_source=feature_view.input, join_key_columns=join_key_columns, feature_name_columns=feature_name_columns, diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index b58a011013..498ba62711 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -224,6 +224,8 @@ def _validate_offline_store_config(cls, values): if "type" not in values["offline_store"]: if values["provider"] == "local" or values["provider"] == "redis": values["offline_store"]["type"] = "file" + elif values["provider"] == "aws_dynamodb": + values["offline_store"]["type"] = "bigquery" elif values["provider"] == "gcp": values["offline_store"]["type"] = "bigquery" diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py index ba98d83226..2f65a81bc3 100644 --- a/sdk/python/tests/test_cli_aws.py +++ b/sdk/python/tests/test_cli_aws.py @@ -27,10 +27,12 @@ def test_basic() -> None: repo_config.write_text( dedent( f""" - project: {project_id} - registry: {data_path / "registry.db"} - provider: aws_dynamodb - """ + project: {project_id} + registry: {data_path / "registry.db"} + provider: aws_dynamodb + offline_store: + type: file + """ ) ) From 8dcbd5a133f69b5c6d41a0f346aa94a645b2971a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Fri, 11 Jun 2021 02:18:13 -0700 Subject: [PATCH 10/28] review changes Signed-off-by: lblokhin --- .../feast/infra/aws_dynamodb_provider.py | 8 ++++++-- sdk/python/feast/registry.py | 20 +++++++++---------- sdk/python/tests/test_cli_aws.py | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/sdk/python/feast/infra/aws_dynamodb_provider.py b/sdk/python/feast/infra/aws_dynamodb_provider.py index 5a97296639..ab231cff71 100644 --- a/sdk/python/feast/infra/aws_dynamodb_provider.py +++ b/sdk/python/feast/infra/aws_dynamodb_provider.py @@ -2,10 +2,14 @@ from datetime import datetime from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -import boto3 import mmh3 import pandas -from botocore.exceptions import ClientError +try: + import boto3 + from botocore.exceptions import ClientError +except ImportError as e: + from feast.errors import FeastExtrasDependencyImportError + raise FeastExtrasDependencyImportError("aws", str(e)) from tqdm import tqdm from feast import FeatureTable, utils diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index ee59b88d24..6dafb6252b 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -549,18 +549,21 @@ def __init__(self, uri: str): self._uri = urlparse(uri) self._bucket = self._uri.hostname self._key = self._uri.path.lstrip("/") + try: + import boto3 + import botocore + except ImportError as e: + from feast.errors import FeastExtrasDependencyImportError + raise FeastExtrasDependencyImportError("aws", str(e)) + self.s3 = boto3.resource("s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL")) return def get_registry_proto(self): - import boto3 - import botocore - file_obj = TemporaryFile() registry_proto = RegistryProto() - s3 = boto3.resource("s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL")) try: - bucket = s3.Bucket(self._bucket) - s3.meta.client.head_bucket(Bucket=bucket.name) + bucket = self.s3.Bucket(self._bucket) + self.s3.meta.client.head_bucket(Bucket=bucket.name) except botocore.client.ClientError as e: # If a client error is thrown, then check that it was a 404 error. # If it was a 404 error, then the bucket does not exist. @@ -600,14 +603,11 @@ def update_registry_proto( return def _write_registry(self, registry_proto: RegistryProto): - import boto3 - registry_proto.version_id = str(uuid.uuid4()) registry_proto.last_updated.FromDatetime(datetime.utcnow()) # we have already checked the bucket exists so no need to do it again file_obj = TemporaryFile() file_obj.write(registry_proto.SerializeToString()) file_obj.seek(0) - s3 = boto3.resource("s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL")) - s3.put_object(Bucket=self._bucket, Body=file_obj, Key=self._key) + self.s3.Bucket(self._bucket).put_object(Body=file_obj, Key=self._key) return diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py index 2f65a81bc3..cc8693cfcf 100644 --- a/sdk/python/tests/test_cli_aws.py +++ b/sdk/python/tests/test_cli_aws.py @@ -31,7 +31,7 @@ def test_basic() -> None: registry: {data_path / "registry.db"} provider: aws_dynamodb offline_store: - type: file + type: bigquery """ ) ) From 7b99cdeb6b99e8d071634aaeecf7311cf4118d5e Mon Sep 17 00:00:00 2001 From: lblokhin Date: Wed, 23 Jun 2021 14:41:16 -0500 Subject: [PATCH 11/28] review requested changes Signed-off-by: lblokhin --- Makefile | 3 --- docs/specs/dynamodb_online_example.monopic | Bin 1601 -> 1601 bytes docs/specs/dynamodb_online_example.png | Bin 90267 -> 92813 bytes sdk/python/feast/errors.py | 22 +++++------------- .../feast/infra/{aws_provider.py => aws.py} | 5 ---- sdk/python/feast/infra/provider.py | 2 +- sdk/python/feast/registry.py | 8 +++---- sdk/python/feast/templates/aws/bootstrap.py | 2 +- .../aws/{driver_example.py => example.py} | 0 .../feast/templates/aws/feature_store.yaml | 2 +- sdk/python/feast/templates/aws/test.py | 7 ++---- 11 files changed, 15 insertions(+), 36 deletions(-) rename sdk/python/feast/infra/{aws_provider.py => aws.py} (94%) rename sdk/python/feast/templates/aws/{driver_example.py => example.py} (100%) diff --git a/Makefile b/Makefile index 95f9304bce..475d729e9e 100644 --- a/Makefile +++ b/Makefile @@ -41,9 +41,6 @@ install-ci-dependencies: install-python-ci-dependencies install-go-ci-dependenci install-python-ci-dependencies: pip install -e "sdk/python[ci]" -install-python-aws-dependencies: - pip install -e "sdk/python[aws]" - package-protos: cp -r ${ROOT_DIR}/protos ${ROOT_DIR}/sdk/python/feast/protos diff --git a/docs/specs/dynamodb_online_example.monopic b/docs/specs/dynamodb_online_example.monopic index 57a2e9e99bc5ef69ca8cb45b1cbec8f8e30202d5..6531749dd94641272f886b7c90060f89bfc3883b 100644 GIT binary patch delta 1591 zcmV-72FUrr48aVL9e>wRnG4sqB(17;x7uDGR&|8NFt+JpZ~;4;Mwa$#s(#mgNzV)n z1{<=$UXmzTDI&vo=FGXzxp1o`)4#Z%XPSE}hG$p|qA=!Oz_NwMV>XR=pt&;^Wqhnf ziT}R-suqe}E6vr%+FCvncy7nqA`Ai^SIe^n3zF*tpfOu=kADYYUYjeI`bm_enoF@| zBS+V50PQDn&g0wz5OE-O710e}9K{4vB1IY4TFwJV^Mv%rg!oQWOs)FAQ2 zPJk#@iPhOUoqsW(djNRHg93X+R13WspD)EvKy4F~CIZ=dT993?x!M8E!w*Zgsv%?G9^fXXA-r|nQ|phpB&e!fVP^?XsumE}3} z7lQVJ^ld?dDHNzfvdbjkk(cuid5J#3aOQHAdDA2fcz@c;v;!RX4=y42@8@m%Q?LmZ z&({%4%TpvHW~oqB7W<1Nt??5Za6pNSJkpz{#i@!xU&l zrR24_J>63JU0Dd$yQE*xmxccbLm*^qX|0OYVaG~-dq5jJNV68kwh%50sCdJOrhu}X)I)d8d}aJhoy>0GgXFejZw9854k%GuPM+H#a8=h^%PPm zsX*2gY-d9K{B#{~B^Gob z`qEGi4X%l3=_imbICE$*L6}OZmZs||iGYUSnt$etmr7W(h|K{pq(I}|pw0V&rULYC zsoiGuiczo-T%A`cW?^u0W|e@}NekSTa53tQvU6XQrAN-LIWN>Oi~*t-@mT?O2T^aM zrC6OO#oiX>U{N@amy$kRc=!#kUW&b7x{K;P40z>*3$5LEag}Sc6o6@x=gG2ge8tuA zYkzV*qK1xx8E4eO4)uM1h6%H5U$>Yun-XqJ;-Aw*1VrwLU1xCVWceiCuxQOQcM^a4 z_}>I>5uldk;Gkara(;gP`qB>8?YF#bwptkFGR{4#yB@ zFc(UjcI9+10iYLf6|v@E_YgHYL?i=}(?s-w@5r22LwN3f`uOj?Q+$l;DZXjdXnC+4AE}HUsf=D4vP4f63L!~*Q8_*)C+7Hu zeq0D`Ib+D-OCS=)p3OZ*RDqguGJii8MqDyTH?t9H2_&w*U~sT!-DT-tg>t3aSM_C0 z)o7L5F8<4#x<@#0=$`Mq=H-;SfxbqU5MAQV=u));=p*$a_7Lb5R6am2g7)e~*nPqh zWInO5#Q7SPeBr31Pf&trBPg*SBPdZ32vQLUQr3x-i$~;T#U#i$weNritACpzGFSx) z>%+A)jDd>Lco?|IIxeV0))?<9@Ms{S>2qwhxFc@zfDjuyj?HJ>a%_q?^=%*Plv?&Q zxVzhO&}#Yq;;r+_Zf~``J%9Diy6$YX{PbzgA*~$7qh*cxQ8O&>9d$=lj zSCLNk?@y|v5o5S(L_lWI3V*^%0jbJ6Gyb<1ua{GvN}oh!Dq_k7qT>z!9QnlZ!Up{8 zZS6#%WHKXdL>RhurFb<^p_EvIU5aF&YNl~qyT|C0ZBYG*WgCP%%P*hMBYpXF=t=MW z!IP~W)DRvx?;xshL{#O7sL~NpwIgD^un(S`6B7?=AJ;@C$A!Ci>@cXyo0`Xgut}{9 pYjk}SAitwMspIS%8PJ=8#BBjzSk$ey(Ok0K{tqcqYZnVG005(h1Ze;O delta 1591 zcmV-72FUrr48aVL9e?Af%mo8}OVX-pcdPC7VO2+H41-MC0hTqLln+Kl9qjGsVXI^}_2Q*>}?tgGE$SQNiQa29cL>p6V zTFBOQ3qZSZl<_EY07T@89a*x14cYyYI{`czax*-L8EuRUEYs{hghv9nCBZBJq_j&k z)HT@2W)f$C2F$*N)dLFi%Qy|PAdZ}b`@o;yC4UUDLk^G{VC@zt<;-)UIAdamJk^U` zu@fMQRbq9zN`HLjat8o!d5~kTh-#oW<+Fu&1=Q9tsUwiCra9Tgn%gbVEcm!!%L+1v zAg|zX?E5Lt;Ds)kQOpKnCy;*+xD*V`*-6SJ0F{TZPurqcLyrio+-#o2tJ%DiE6XzG z&IRo`>6@H}kx-x($u44#hfc;nW(E2L!P+Lr<9|sb)An%OJ-C42ewa1wPrxQv zG+Tu%DNd1$@Drh`EOO^@QsE~y;D7=dd89K<@>6S*VVY0w7}36>?3m3S6Q>N&hAGg9 zQpqcGJG!OxyR;Cjw@E*zFAe?@hCs;B)EX75#g3Kyc7WD+kfaTa$%@Is0)G`?#{(}3 z*05wAuYZ#89_*LFGKHcWCy|f|s%bfw9F{5~%~Tn>IYi~o9pnxeUR|ICijDTs=qaR9 z$iYYrOf|67z!pYQ98`C>jsCRGwXnv@+zoHtXXydyLC;gCQh}@}*w%!)`ROX)QY>gs z^o6198(baH!cQPwaAx0PydaTMElk%@5&;dt6@Sea52djDkj(%wq(I}&phu4dO$F%f zQme`66{BDwxH_j$%-rDQ%rXY8lNPus;9}GnW&5!xOOKr1ah|JT5CKFdfM`5mcPU&tX*P-0EL`z){NLaI zPT&p!T3H6}>CI19S66RtEN}IG^3^1oWFWD1_1(?&FW+0=83xD~m6#^l8PdY^UflKg)K8IGJLqF0nP>a9s5X-gWZ3I(YO1*v(Sq;ZRh*?%=@ zq;~&FBel9Djl}Bb>N0u?*9_cZfXMD(fT+m;so4UWj!p(`_rnddOUjSwGfVlA`4Urp ziiY-CQhtcdA-?|S(fK{b)%<>BR!DiW1Rtq%9;tL*+Ob4WRS6+OdsPWOCMPENhJIWG zZQ4Uf;0quU#*WFIBd9=iIgLIQMSt8d$TiaeYKS9lKVxjLW8FpFUqy0-*H^`5O%-UB z*UtaTi@HNJu3)|T_FiFPVG6M-l}GZ^nX@=!g_!0 z45Oz~H0}p3GLMTXkvYWM`a9~0X!;TxE#mK*E| z$c{<9(emx}JNv?FZnV6+x_EEiwKiIQ__|Z01&?|wH&gw^$$C$SWgElp&1+(-zZ!X4 ziB9(JPpYF4qrYoJKxW|*qJK&OsZ2Xl{-+bI7E_)`pG0LQGLj2Kw;TRpc~#qT4ER}_ z%85eBWI|etFm&x!@v5gXDKUGy6iH9DOyjs_kI^OBp!zdQHVAoiAgXReRN;uI$`MheBVxX?kDcrj6Ax+~*Fq-8g}WHi>@ZE)r#uRTMJiQT pqwSLb`3>z!{bu{ffPN%M+~n|uMLlS1ZA> diff --git a/docs/specs/dynamodb_online_example.png b/docs/specs/dynamodb_online_example.png index 7b5ccd3e9cfe64d6c1fa6dd47517b4d26e4fc870..34121fbff51be6d11f2d1e8f87a1eb6b77910ac4 100644 GIT binary patch literal 92813 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%0A4SX0PE_yK$pG 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

+7Y=`TcG|;o4%)J-bBPh$MF+_6y_1Yv zGVd<}%xOtTaP2S9so^bAbU&>GiiH0qeZvPt6mnO0eHi+A-}$kSm$Px6)in}g?2Rc@ zFCMB}VJ^;Aqn)fA$Z}G<>^FQ5P;WEC##F6*fJHS?-fvTN0`hF9?DOihlMZ7J%R0M} zvVB$^QbXmgW4n3n3d*UsD69k9B0WP?EpCqs_zZjt87ML}B>)9(O8`%oajzLsbwloU zW5~ZUY4vJh)R^}-y-a~44tpzX55shV&BNZzNE8Qt-R#`Ng;HBf#^g70fXNz`<`(a? z+~3`k?GAm;s$(pK>V1SYM z?G)WoEDvteW7ka0DQyJHeQoQtf`-e-BkH)c`Wx3Y*w1L#o#m^lIJXGSuC+$^C!d?M zAbW@GPk)rVud0~a1uS_Zn~-T>u+!F{W~ft_nc4wjiO6}R<*?)fLe}k_>}PcKA&pTV zRD3ZEcAUj49YFrn!7Lhl4C?#c?%fL z^?d(%4&jHTz1y;>No1OAd8+ScpzhA%#rFw!CW$e@j(9-2Ku+)?n9jRg(6LbH?d!e| zmwZA_SYY*}Jil*+y4jegms89G{FE7w;w_3%-zbUp-pwPssd#IXxONH4ozDQsNTx$V zw#^slKxxH@&z455T)VDTb&=z4noD7wM2V~5hZe!KZX7!ly?v(dKfKW&S&_RbPb!7b zIw=Xk-+ikQnhiqCvcQCQ=}bBr(;CGLvIPKuD&-EpD@@7(l20@ z1sb2(vtTbaV`HS1H3NeGlC%`lnRgiYHlds$#<9%W997EKBW|E=QfPrj^$DWm1TCXy zdt$%`DI2|$S(#K4owTG5#6uC|qQ(7w zhlCYolyi@s2q}xe_1M_F%HCnE)crt7HA;Cyy10lfs(doo>3XcUwGJDQ@|leH2JX#FW4n z!y}W^gQvk;8M$c5>%3)UTf7(Nnd@~AxWE5+hRHyP*TFy^nlE*ZP=MhitYJ z?tgTlBcIFD;47Qw>|p2(&Nl-XU)fi7pZoIM2ucJ!OU7BtFrEFnFvT_e>+Pwc!?Avo z0n2r|mvrVxUYNw7zI=}uDvPNy^uXt=?;WeVFyC2Xbm%?m#F-RrrFQj2Z~(9~<2GMA zwW+Gwgr+@=ne6<0uk+Zpjy{XttA70>XGzJE8}HQ_GZ#khvI>OSrU6jEB$Fi~Y_$Ke zPg~QsN)i=#p%}~CmU|z3qi?U)5=@bd1ldF9B?bw~nFt^v)90XbWUbO?AbkQ_Q$MeM zK3e&u?859sX|!}|$=(iscI_dt%&$t}0}4F_eW9eN{rGJ`i*fOuGvtH>3-+1Zy2 zhemv~4_G3$-b^ox9haw(*ztWUb>WhG)XOV*z*UEZga!2FWmg-)xyUs1ph#ra$}7!nwg=o#+3wc&YpM`% zkrb)z=A*53q@mlvukuMDIzt;2bLZFSz9krsL5B@!bfohpMOD#j?H9Oi1d{TGD6|GU z9cGTGAUT(0Gn=r+so37xVVSbGJ-e}vFoxzdimojJP

+gW^1k<|}8F<(8^u)4ZVz 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 diff --git a/sdk/python/feast/errors.py b/sdk/python/feast/errors.py index 5c6515772b..5d9bbffac3 100644 --- a/sdk/python/feast/errors.py +++ b/sdk/python/feast/errors.py @@ -40,24 +40,14 @@ def __init__(self, name, project=None): super().__init__(f"Feature table {name} does not exist") -class FeatureBucketNotExist(FeastObjectNotFoundException): - def __init__(self, bucket, project=None): - if project: - super().__init__( - f"Feature bucket {bucket} does not exist in project {project}" - ) - else: - super().__init__(f"Feature bucket {bucket} does not exist") +class S3RegistryBucketNotExist(FeastObjectNotFoundException): + def __init__(self, bucket): + super().__init__(f"S3 bucket {bucket} for the Feast registry does not exist") -class FeatureBucketForbiddenAccess(FeastObjectNotFoundException): - def __init__(self, bucket, project=None): - if project: - super().__init__( - f"Private Registry Bucket {bucket} forbidden Access in project {project}" - ) - else: - super().__init__(f"Private Registry Bucket {bucket} forbidden Access") +class S3RegistryBucketForbiddenAccess(FeastObjectNotFoundException): + def __init__(self, bucket): + super().__init__(f"S3 bucket {bucket} for the Feast registry can't be accessed") class FeastProviderLoginError(Exception): diff --git a/sdk/python/feast/infra/aws_provider.py b/sdk/python/feast/infra/aws.py similarity index 94% rename from sdk/python/feast/infra/aws_provider.py rename to sdk/python/feast/infra/aws.py index 343f53a750..4e19476c86 100644 --- a/sdk/python/feast/infra/aws_provider.py +++ b/sdk/python/feast/infra/aws.py @@ -96,9 +96,6 @@ def materialize_single_feature_view( created_timestamp_column, ) = _get_column_names(feature_view, entities) - start_date = utils.make_tzaware(start_date) - end_date = utils.make_tzaware(end_date) - table = self.offline_store.pull_latest_from_table_or_query( data_source=feature_view.input, join_key_columns=join_key_columns, @@ -120,8 +117,6 @@ def materialize_single_feature_view( self.repo_config, feature_view, rows_to_write, lambda x: pbar.update(x) ) - feature_view.materialization_intervals.append((start_date, end_date)) - registry.apply_feature_view(feature_view, project) @staticmethod def get_historical_features( diff --git a/sdk/python/feast/infra/provider.py b/sdk/python/feast/infra/provider.py index 24199af2ed..779feee593 100644 --- a/sdk/python/feast/infra/provider.py +++ b/sdk/python/feast/infra/provider.py @@ -145,7 +145,7 @@ def get_provider(config: RepoConfig, repo_path: Path) -> Provider: return GcpProvider(config) elif config.provider == "aws": - from feast.infra.aws_provider import AwsProvider + from feast.infra.aws import AwsProvider return AwsProvider(config) elif config.provider == "local": diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index 01e314aba1..8a44f3b2e3 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -24,8 +24,8 @@ from feast.entity import Entity from feast.errors import ( EntityNotFoundException, - FeatureBucketForbiddenAccess, - FeatureBucketNotExist, + S3RegistryBucketForbiddenAccess, + S3RegistryBucketNotExist, FeatureTableNotFoundException, FeatureViewNotFoundException, ) @@ -577,9 +577,9 @@ def get_registry_proto(self): # If it was a 404 error, then the bucket does not exist. error_code = int(e.response["Error"]["Code"]) if error_code == 404: - raise FeatureBucketNotExist(self._bucket) + raise S3RegistryBucketNotExist(self._bucket) else: - raise FeatureBucketForbiddenAccess(self._bucket) + raise S3RegistryBucketForbiddenAccess(self._bucket) try: obj = bucket.Object(self._key) diff --git a/sdk/python/feast/templates/aws/bootstrap.py b/sdk/python/feast/templates/aws/bootstrap.py index 33a11a1c2e..4013ca5a8d 100644 --- a/sdk/python/feast/templates/aws/bootstrap.py +++ b/sdk/python/feast/templates/aws/bootstrap.py @@ -19,7 +19,7 @@ def bootstrap(): driver_stats_path = data_path / "driver_stats.parquet" driver_df.to_parquet(path=str(driver_stats_path), allow_truncated_timestamps=True) - example_py_file = repo_path / "driver_example.py" + example_py_file = repo_path / "example.py" replace_str_in_file(example_py_file, "%PARQUET_PATH%", str(driver_stats_path)) diff --git a/sdk/python/feast/templates/aws/driver_example.py b/sdk/python/feast/templates/aws/example.py similarity index 100% rename from sdk/python/feast/templates/aws/driver_example.py rename to sdk/python/feast/templates/aws/example.py diff --git a/sdk/python/feast/templates/aws/feature_store.yaml b/sdk/python/feast/templates/aws/feature_store.yaml index a0d7d62ec7..7f7be8527e 100644 --- a/sdk/python/feast/templates/aws/feature_store.yaml +++ b/sdk/python/feast/templates/aws/feature_store.yaml @@ -1,3 +1,3 @@ project: my_project -registry: s3://data/registry.db +registry: data/registry.db provider: aws diff --git a/sdk/python/feast/templates/aws/test.py b/sdk/python/feast/templates/aws/test.py index 806b268dea..cc2cf7e984 100644 --- a/sdk/python/feast/templates/aws/test.py +++ b/sdk/python/feast/templates/aws/test.py @@ -1,7 +1,7 @@ from datetime import datetime import pandas as pd -from driver_example import driver, driver_hourly_stats_view +from example import driver, driver_hourly_stats_view from feast import FeatureStore @@ -20,20 +20,17 @@ def main(): # Select features feature_refs = ["driver_hourly_stats:conv_rate", "driver_hourly_stats:acc_rate"] - # print() print("Loading features into the online store...") fs.materialize_incremental(end_date=datetime.now()) - # print() print("Retrieving online features...") - # Retrieve features from the online store (Dynamodb) + # Retrieve features from the online store (DynamoDB) online_features = fs.get_online_features( feature_refs=feature_refs, entity_rows=[{"driver_id": 1001}, {"driver_id": 1002}], ).to_dict() - print() print(pd.DataFrame.from_dict(online_features)) From 3a985b075a84e34780be7533336fcf902e45e161 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Wed, 23 Jun 2021 15:22:08 -0500 Subject: [PATCH 12/28] integration test for Dynamo Signed-off-by: lblokhin --- docs/specs/online_store_format.md | 2 +- sdk/python/feast/infra/aws.py | 16 ++- .../feast/infra/online_stores/datastore.py | 17 +--- .../feast/infra/online_stores/dynamodb.py | 99 ++++++++----------- .../feast/infra/online_stores/helpers.py | 11 +++ sdk/python/feast/registry.py | 21 ++-- sdk/python/feast/repo_config.py | 2 +- .../test_offline_online_store_consistency.py | 41 ++++++++ 8 files changed, 117 insertions(+), 92 deletions(-) diff --git a/docs/specs/online_store_format.md b/docs/specs/online_store_format.md index 107863001d..9f901ae69c 100644 --- a/docs/specs/online_store_format.md +++ b/docs/specs/online_store_format.md @@ -7,7 +7,7 @@ This format is considered part of Feast public API contract; that allows other c The format is not entirely technology or cloud agnostic. Since users may opt to use different key-value stores as an underlying engine to store feature data, and we don't want to aim for the lowest common denominator across them, we have to provide different "flavors" of this data format, specialized for every supported store. -This version of the Online Store Format supports only Redis as the underlying storage engine. We envision adding more storage engines to this document in the future. +This version of the Online Store Format supports Redis and DynamoDB as storage engine. We envision adding more storage engines to this document in the future. ## Overview diff --git a/sdk/python/feast/infra/aws.py b/sdk/python/feast/infra/aws.py index 4e19476c86..a9a6f74ea0 100644 --- a/sdk/python/feast/infra/aws.py +++ b/sdk/python/feast/infra/aws.py @@ -4,7 +4,7 @@ import pandas from tqdm import tqdm -from feast import FeatureTable, utils +from feast import FeatureTable from feast.entity import Entity from feast.feature_view import FeatureView from feast.infra.offline_stores.helpers import get_offline_store_from_config @@ -117,9 +117,8 @@ def materialize_single_feature_view( self.repo_config, feature_view, rows_to_write, lambda x: pbar.update(x) ) - - @staticmethod def get_historical_features( + self, config: RepoConfig, feature_views: List[FeatureView], feature_refs: List[str], @@ -127,5 +126,12 @@ def get_historical_features( registry: Registry, project: str, ) -> RetrievalJob: - # TODO implement me - pass + job = self.offline_store.get_historical_features( + config=config, + feature_views=feature_views, + feature_refs=feature_refs, + entity_df=entity_df, + registry=registry, + project=project, + ) + return job diff --git a/sdk/python/feast/infra/online_stores/datastore.py b/sdk/python/feast/infra/online_stores/datastore.py index c623af1c1f..e5328a2725 100644 --- a/sdk/python/feast/infra/online_stores/datastore.py +++ b/sdk/python/feast/infra/online_stores/datastore.py @@ -16,13 +16,12 @@ from multiprocessing.pool import ThreadPool from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Tuple, Union -import mmh3 from pydantic import PositiveInt, StrictStr from pydantic.typing import Literal from feast import Entity, FeatureTable, utils from feast.feature_view import FeatureView -from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.online_stores.helpers import compute_entity_id from feast.infra.online_stores.online_store import OnlineStore from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto @@ -191,7 +190,7 @@ def _write_minibatch( ): entities = [] for entity_key, features, timestamp, created_ts in data: - document_id = compute_datastore_entity_id(entity_key) + document_id = compute_entity_id(entity_key) key = client.key( "Project", project, "Table", table.name, "Row", document_id, @@ -236,7 +235,7 @@ def online_read( result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] for entity_key in entity_keys: - document_id = compute_datastore_entity_id(entity_key) + document_id = compute_entity_id(entity_key) key = client.key( "Project", feast_project, "Table", table.name, "Row", document_id ) @@ -253,16 +252,6 @@ def online_read( return result -def compute_datastore_entity_id(entity_key: EntityKeyProto) -> str: - """ - Compute Datastore Entity id given Feast Entity Key. - - Remember that Datastore Entity is a concept from the Datastore data model, that has nothing to - do with the Entity concept we have in Feast. - """ - return mmh3.hash_bytes(serialize_entity_key(entity_key)).hex() - - def _delete_all_values(client, key) -> None: """ Delete all data under the key path in datastore. diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 46ceee1c54..2d9a1d9e86 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -14,12 +14,11 @@ from datetime import datetime from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -import mmh3 -from pydantic import PositiveInt, StrictStr +from pydantic import StrictStr from pydantic.typing import Literal from feast import Entity, FeatureTable, FeatureView, utils -from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.online_stores.helpers import compute_entity_id from feast.infra.online_stores.online_store import OnlineStore from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto @@ -34,25 +33,24 @@ raise FeastExtrasDependencyImportError("aws", str(e)) -class DynamoDbOnlineStoreConfig(FeastConfigBaseModel): +class DynamoDBOnlineStoreConfig(FeastConfigBaseModel): """Online store config for DynamoDB store""" type: Literal["dynamodb"] = "dynamodb" """Online store type selector""" - rcu: Optional[PositiveInt] = 5 - """ Read capacity unit """ - - wcu: Optional[PositiveInt] = 5 - """ Write capacity unit """ - - region_name: Optional[StrictStr] = None + region: Optional[StrictStr] = None """ AWS Region Name """ -class DynamoDbOnlineStore(OnlineStore): - def _initialize_dynamodb(self, online_config: DynamoDbOnlineStoreConfig): - return boto3.resource("dynamodb", region_name=online_config.region_name) +class DynamoDBOnlineStore(OnlineStore): + """ + OnlineStore is an object used for all interaction between Feast and the service used for offline storage of + features. + """ + + def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): + return boto3.resource("dynamodb", region_name=online_config.region) def update( self, @@ -64,37 +62,33 @@ def update( partial: bool, ): online_config = config.online_store - assert isinstance(online_config, DynamoDbOnlineStoreConfig) + assert isinstance(online_config, DynamoDBOnlineStoreConfig) dynamodb = self._initialize_dynamodb(online_config) + client = boto3.client("dynamodb") + for table_name in tables_to_keep: - table = None try: table = dynamodb.create_table( - TableName=table_name.name, - KeySchema=[ - {"AttributeName": "Row", "KeyType": "HASH"}, - {"AttributeName": "Project", "KeyType": "RANGE"}, - ], + TableName=f"{config.project}.{table_name.name}", + KeySchema=[{"AttributeName": "Row", "KeyType": "HASH"}], AttributeDefinitions=[ - {"AttributeName": "Row", "AttributeType": "S"}, - {"AttributeName": "Project", "AttributeType": "S"}, + {"AttributeName": "Row", "AttributeType": "S"} ], - ProvisionedThroughput={ - "ReadCapacityUnits": online_config.rcu, - "WriteCapacityUnits": online_config.wcu, - }, - ) - table.meta.client.get_waiter("table_exists").wait( - TableName=table_name.name + BillingMode="PAY_PER_REQUEST", ) except ClientError as ce: print(ce) if ce.response["Error"]["Code"] == "ResourceNotFoundException": - table = dynamodb.Table(table_name.name) + table = dynamodb.Table(f"{config.project}.{table_name.name}") + + for table_name in tables_to_keep: + client.get_waiter("table_exists").wait( + TableName=f"{config.project}.{table_name.name}" + ) for table_name in tables_to_delete: - table = dynamodb.Table(table_name.name) + table = dynamodb.Table(f"{config.project}.{table_name.name}") table.delete() def teardown( @@ -104,15 +98,12 @@ def teardown( entities: Sequence[Entity], ): online_config = config.online_store - assert isinstance(online_config, DynamoDbOnlineStoreConfig) + assert isinstance(online_config, DynamoDBOnlineStoreConfig) dynamodb = self._initialize_dynamodb(online_config) for table_name in tables: - try: - table = dynamodb.Table(table_name) - table.delete() - except Exception as e: - print(str(e)) + table = dynamodb.Table(f"{config.project}.{table_name.name}") + table.delete() def online_write_batch( self, @@ -124,18 +115,16 @@ def online_write_batch( progress: Optional[Callable[[int], Any]], ) -> None: online_config = config.online_store - assert isinstance(online_config, DynamoDbOnlineStoreConfig) + assert isinstance(online_config, DynamoDBOnlineStoreConfig) dynamodb = self._initialize_dynamodb(online_config) - table_instance = dynamodb.Table(table.name) + table_instance = dynamodb.Table(f"{config.project}.{table.name}") with table_instance.batch_writer() as batch: for entity_key, features, timestamp, created_ts in data: - document_id = compute_datastore_entity_id(entity_key) # TODO check id - # TODO compression encoding + document_id = compute_entity_id(entity_key) batch.put_item( Item={ "Row": document_id, # PartitionKey - "Project": config.project, # SortKey "event_ts": str(utils.make_tzaware(timestamp)), "values": { k: v.SerializeToString() @@ -143,6 +132,8 @@ def online_write_batch( }, } ) + if progress: + progress(1) def online_read( self, @@ -152,17 +143,15 @@ def online_read( requested_features: Optional[List[str]] = None, ) -> List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]]: online_config = config.online_store - assert isinstance(online_config, DynamoDbOnlineStoreConfig) + assert isinstance(online_config, DynamoDBOnlineStoreConfig) dynamodb = self._initialize_dynamodb(online_config) result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] for entity_key in entity_keys: - table_instace = dynamodb.Table(table.name) - document_id = compute_datastore_entity_id(entity_key) # TODO check id - response = table_instace.get_item( - Key={"Row": document_id, "Project": config.project} - ) - value = response["Item"] + table_instace = dynamodb.Table(f"{config.project}.{table.name}") + document_id = compute_entity_id(entity_key) # TODO check id + response = table_instace.get_item(Key={"Row": document_id}) + value = response.get("Item", None) if value is not None: res = {} @@ -174,13 +163,3 @@ def online_read( else: result.append((None, None)) return result - - -def compute_datastore_entity_id(entity_key: EntityKeyProto) -> str: - """ - Compute Datastore Entity id given Feast Entity Key. - - Remember that Datastore Entity is a concept from the Datastore data model, that has nothing to - do with the Entity concept we have in Feast. - """ - return mmh3.hash_bytes(serialize_entity_key(entity_key)).hex() diff --git a/sdk/python/feast/infra/online_stores/helpers.py b/sdk/python/feast/infra/online_stores/helpers.py index 9c42c5ea00..d040d35386 100644 --- a/sdk/python/feast/infra/online_stores/helpers.py +++ b/sdk/python/feast/infra/online_stores/helpers.py @@ -5,6 +5,7 @@ import mmh3 from feast import errors +from feast.infra.key_encoding_utils import serialize_entity_key from feast.infra.online_stores.online_store import OnlineStore from feast.protos.feast.storage.Redis_pb2 import RedisKeyV2 as RedisKeyProto from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto @@ -53,3 +54,13 @@ def _mmh3(key: str): """ key_hash = mmh3.hash(key, signed=False) return bytes.fromhex(struct.pack(" str: + """ + Compute Datastore Entity id given Feast Entity Key. + + Remember that Datastore Entity is a concept from the Datastore data model, that has nothing to + do with the Entity concept we have in Feast. + """ + return mmh3.hash_bytes(serialize_entity_key(entity_key)).hex() diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index 8a44f3b2e3..52f6354582 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -24,10 +24,10 @@ from feast.entity import Entity from feast.errors import ( EntityNotFoundException, - S3RegistryBucketForbiddenAccess, - S3RegistryBucketNotExist, FeatureTableNotFoundException, FeatureViewNotFoundException, + S3RegistryBucketForbiddenAccess, + S3RegistryBucketNotExist, ) from feast.feature_table import FeatureTable from feast.feature_view import FeatureView @@ -546,16 +546,17 @@ def _write_registry(self, registry_proto: RegistryProto): class S3RegistryStore(RegistryStore): def __init__(self, uri: str): - self._uri = urlparse(uri) - self._bucket = self._uri.hostname - self._key = self._uri.path.lstrip("/") try: import boto3 except ImportError as e: from feast.errors import FeastExtrasDependencyImportError raise FeastExtrasDependencyImportError("aws", str(e)) - self.s3 = boto3.resource( + self._uri = urlparse(uri) + self._bucket = self._uri.hostname + self._key = self._uri.path.lstrip("/") + + self.s3_client = boto3.resource( "s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL") ) return @@ -570,8 +571,8 @@ def get_registry_proto(self): raise FeastExtrasDependencyImportError("aws", str(e)) try: - bucket = self.s3.Bucket(self._bucket) - self.s3.meta.client.head_bucket(Bucket=bucket.name) + bucket = self.s3_client.Bucket(self._bucket) + self.s3_client.meta.client.head_bucket(Bucket=bucket.name) except botocore.client.ClientError as e: # If a client error is thrown, then check that it was a 404 error. # If it was a 404 error, then the bucket does not exist. @@ -608,7 +609,6 @@ def update_registry_proto( if updater: registry_proto = updater(registry_proto) self._write_registry(registry_proto) - return def _write_registry(self, registry_proto: RegistryProto): registry_proto.version_id = str(uuid.uuid4()) @@ -617,5 +617,4 @@ def _write_registry(self, registry_proto: RegistryProto): file_obj = TemporaryFile() file_obj.write(registry_proto.SerializeToString()) file_obj.seek(0) - self.s3.Bucket(self._bucket).put_object(Body=file_obj, Key=self._key) - return + self.s3_client.Bucket(self._bucket).put_object(Body=file_obj, Key=self._key) diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index 2662283b27..a382d00128 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -16,7 +16,7 @@ "sqlite": "feast.infra.online_stores.sqlite.SqliteOnlineStore", "datastore": "feast.infra.online_stores.datastore.DatastoreOnlineStore", "redis": "feast.infra.online_stores.redis.RedisOnlineStore", - "dynamodb": "feast.infra.online_stores.dynamodb.DynamoDbOnlineStore", + "dynamodb": "feast.infra.online_stores.dynamodb.DynamoDBOnlineStore", } OFFLINE_STORE_CLASS_FOR_TYPE = { diff --git a/sdk/python/tests/test_offline_online_store_consistency.py b/sdk/python/tests/test_offline_online_store_consistency.py index d30541d8e7..a2fa2710cd 100644 --- a/sdk/python/tests/test_offline_online_store_consistency.py +++ b/sdk/python/tests/test_offline_online_store_consistency.py @@ -18,6 +18,7 @@ from feast.feature_store import FeatureStore from feast.feature_view import FeatureView from feast.infra.online_stores.datastore import DatastoreOnlineStoreConfig +from feast.infra.online_stores.dynamodb import DynamoDBOnlineStoreConfig from feast.infra.online_stores.redis import RedisOnlineStoreConfig, RedisType from feast.infra.online_stores.sqlite import SqliteOnlineStoreConfig from feast.repo_config import RepoConfig @@ -183,6 +184,40 @@ def prep_redis_fs_and_fv() -> Iterator[Tuple[FeatureStore, FeatureView]]: yield fs, fv +@contextlib.contextmanager +def prep_dynamodb_fs_and_fv() -> Iterator[Tuple[FeatureStore, FeatureView]]: + with tempfile.NamedTemporaryFile(suffix=".parquet") as f: + df = create_dataset() + f.close() + df.to_parquet(f.name) + file_source = FileSource( + file_format=ParquetFormat(), + file_url=f"file://{f.name}", + event_timestamp_column="ts", + created_timestamp_column="created_ts", + date_partition_column="", + field_mapping={"ts_1": "ts", "id": "driver_id"}, + ) + fv = get_feature_view(file_source) + e = Entity( + name="driver", + description="id for driver", + join_key="driver_id", + value_type=ValueType.INT32, + ) + with tempfile.TemporaryDirectory() as repo_dir_name, tempfile.TemporaryDirectory(): + config = RepoConfig( + registry=str(Path(repo_dir_name) / "registry.db"), + project=f"test_bq_correctness_{str(uuid.uuid4()).replace('-', '')}", + provider="aws", + online_store=DynamoDBOnlineStoreConfig(type="dynamodb",), + ) + fs = FeatureStore(config=config) + fs.apply([fv, e]) + + yield fs, fv + + # Checks that both offline & online store values are as expected def check_offline_and_online_features( fs: FeatureStore, @@ -264,6 +299,12 @@ def test_redis_offline_online_store_consistency(): run_offline_online_store_consistency_test(fs, fv) +@pytest.mark.integration +def test_dynamodb_offline_online_store_consistency(): + with prep_dynamodb_fs_and_fv() as (fs, fv): + run_offline_online_store_consistency_test(fs, fv) + + def test_local_offline_online_store_consistency(): with prep_local_fs_and_fv() as (fs, fv): run_offline_online_store_consistency_test(fs, fv) From 697358197e9bdee0178b008f0f51084f0071733a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 12:58:37 -0500 Subject: [PATCH 13/28] change the rest of table_name to table_instance (where table_name is actually an instance of DynamoDB Table object) Signed-off-by: lblokhin --- .../feast/infra/online_stores/dynamodb.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 2d9a1d9e86..414935114c 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -67,10 +67,10 @@ def update( client = boto3.client("dynamodb") - for table_name in tables_to_keep: + for table_instance in tables_to_keep: try: table = dynamodb.create_table( - TableName=f"{config.project}.{table_name.name}", + TableName=f"{config.project}.{table_instance.name}", KeySchema=[{"AttributeName": "Row", "KeyType": "HASH"}], AttributeDefinitions=[ {"AttributeName": "Row", "AttributeType": "S"} @@ -80,15 +80,15 @@ def update( except ClientError as ce: print(ce) if ce.response["Error"]["Code"] == "ResourceNotFoundException": - table = dynamodb.Table(f"{config.project}.{table_name.name}") + table = dynamodb.Table(f"{config.project}.{table_instance.name}") - for table_name in tables_to_keep: + for table_instance in tables_to_keep: client.get_waiter("table_exists").wait( - TableName=f"{config.project}.{table_name.name}" + TableName=f"{config.project}.{table_instance.name}" ) - for table_name in tables_to_delete: - table = dynamodb.Table(f"{config.project}.{table_name.name}") + for table_instance in tables_to_delete: + table = dynamodb.Table(f"{config.project}.{table_instance.name}") table.delete() def teardown( @@ -101,8 +101,8 @@ def teardown( assert isinstance(online_config, DynamoDBOnlineStoreConfig) dynamodb = self._initialize_dynamodb(online_config) - for table_name in tables: - table = dynamodb.Table(f"{config.project}.{table_name.name}") + for table_instance in tables: + table = dynamodb.Table(f"{config.project}.{table_instance.name}") table.delete() def online_write_batch( From e928424686753a5e018bcac88c7b5197f054429e Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 13:02:37 -0500 Subject: [PATCH 14/28] fix DynamoDBOnlineStore commit Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 414935114c..dc8dc9781b 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -45,8 +45,7 @@ class DynamoDBOnlineStoreConfig(FeastConfigBaseModel): class DynamoDBOnlineStore(OnlineStore): """ - OnlineStore is an object used for all interaction between Feast and the service used for offline storage of - features. + Online feature store for AWS DynamoDB. """ def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): From 59d7e4cb6decd516aa70dcbeee62b6e9990cf3cd Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 14:39:56 -0500 Subject: [PATCH 15/28] move client to _initialize_dynamodb Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index dc8dc9781b..b2edfc04c3 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -48,8 +48,10 @@ class DynamoDBOnlineStore(OnlineStore): Online feature store for AWS DynamoDB. """ + _client: Optional[Union[Redis, RedisCluster]] = None + def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): - return boto3.resource("dynamodb", region_name=online_config.region) + return boto3.client("dynamodb", region_name=online_config.region), boto3.resource("dynamodb", region_name=online_config.region) def update( self, @@ -62,9 +64,7 @@ def update( ): online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - dynamodb = self._initialize_dynamodb(online_config) - - client = boto3.client("dynamodb") + client, dynamodb = self._initialize_dynamodb(online_config) for table_instance in tables_to_keep: try: @@ -98,7 +98,7 @@ def teardown( ): online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb = self._initialize_dynamodb(online_config) for table_instance in tables: table = dynamodb.Table(f"{config.project}.{table_instance.name}") @@ -115,7 +115,7 @@ def online_write_batch( ) -> None: online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb = self._initialize_dynamodb(online_config) table_instance = dynamodb.Table(f"{config.project}.{table.name}") with table_instance.batch_writer() as batch: @@ -143,7 +143,7 @@ def online_read( ) -> List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]]: online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb = self._initialize_dynamodb(online_config) result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] for entity_key in entity_keys: From 594b932c54415d49e27d50131695423b8a7dcfbe Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 14:44:24 -0500 Subject: [PATCH 16/28] rename document_id to entity_id and Row to entity_id Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index b2edfc04c3..64a20012d2 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -70,9 +70,9 @@ def update( try: table = dynamodb.create_table( TableName=f"{config.project}.{table_instance.name}", - KeySchema=[{"AttributeName": "Row", "KeyType": "HASH"}], + KeySchema=[{"AttributeName": "entity_id", "KeyType": "HASH"}], AttributeDefinitions=[ - {"AttributeName": "Row", "AttributeType": "S"} + {"AttributeName": "entity_id", "AttributeType": "S"} ], BillingMode="PAY_PER_REQUEST", ) @@ -120,10 +120,10 @@ def online_write_batch( table_instance = dynamodb.Table(f"{config.project}.{table.name}") with table_instance.batch_writer() as batch: for entity_key, features, timestamp, created_ts in data: - document_id = compute_entity_id(entity_key) + entity_id = compute_entity_id(entity_key) batch.put_item( Item={ - "Row": document_id, # PartitionKey + "entity_id": entity_id, # PartitionKey "event_ts": str(utils.make_tzaware(timestamp)), "values": { k: v.SerializeToString() @@ -147,9 +147,9 @@ def online_read( result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] for entity_key in entity_keys: - table_instace = dynamodb.Table(f"{config.project}.{table.name}") - document_id = compute_entity_id(entity_key) # TODO check id - response = table_instace.get_item(Key={"Row": document_id}) + table_instance = dynamodb.Table(f"{config.project}.{table.name}") + entity_id = compute_entity_id(entity_key) # TODO check id + response = table_instance.get_item(Key={"entity_id": entity_id}) value = response.get("Item", None) if value is not None: From 15a787c2d7348c713aa3281f8db50fcdeee0dc59 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 14:46:09 -0500 Subject: [PATCH 17/28] The default value is None Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 64a20012d2..6db64108fa 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -150,7 +150,7 @@ def online_read( table_instance = dynamodb.Table(f"{config.project}.{table.name}") entity_id = compute_entity_id(entity_key) # TODO check id response = table_instance.get_item(Key={"entity_id": entity_id}) - value = response.get("Item", None) + value = response.get("Item") if value is not None: res = {} From 7eaa654bef005e6ba276edf763b4d2b4fda744fc Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 17:48:57 -0500 Subject: [PATCH 18/28] Remove Datastore from the docstring. Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/helpers.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/helpers.py b/sdk/python/feast/infra/online_stores/helpers.py index d040d35386..788be68b8d 100644 --- a/sdk/python/feast/infra/online_stores/helpers.py +++ b/sdk/python/feast/infra/online_stores/helpers.py @@ -58,9 +58,8 @@ def _mmh3(key: str): def compute_entity_id(entity_key: EntityKeyProto) -> str: """ - Compute Datastore Entity id given Feast Entity Key. - - Remember that Datastore Entity is a concept from the Datastore data model, that has nothing to - do with the Entity concept we have in Feast. + Compute Entity id given Feast Entity Key for online stores. + Remember that Entity here refers to `EntityKeyProto` which is used in some online stores to encode the keys. + It has nothing to do with the Entity concept we have in Feast. """ return mmh3.hash_bytes(serialize_entity_key(entity_key)).hex() From 1468117c2ee9a42b2fa9209d9850159922de468e Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 17:52:10 -0500 Subject: [PATCH 19/28] get rid of the return call from S3RegistryStore Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 7 ++++--- sdk/python/feast/registry.py | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 6db64108fa..81027189a4 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -48,10 +48,11 @@ class DynamoDBOnlineStore(OnlineStore): Online feature store for AWS DynamoDB. """ - _client: Optional[Union[Redis, RedisCluster]] = None - def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): - return boto3.client("dynamodb", region_name=online_config.region), boto3.resource("dynamodb", region_name=online_config.region) + return ( + boto3.client("dynamodb", region_name=online_config.region), + boto3.resource("dynamodb", region_name=online_config.region), + ) def update( self, diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index 52f6354582..bcda465b2e 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -559,7 +559,6 @@ def __init__(self, uri: str): self.s3_client = boto3.resource( "s3", endpoint_url=os.environ.get("FEAST_S3_ENDPOINT_URL") ) - return def get_registry_proto(self): file_obj = TemporaryFile() From 5dbe429ba8b128c03a26a5743ae84b77069c5f8d Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:24:43 -0500 Subject: [PATCH 20/28] merge two exceptions Signed-off-by: lblokhin --- sdk/python/feast/registry.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index bcda465b2e..1ef86f8b80 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -588,14 +588,9 @@ def get_registry_proto(self): registry_proto.ParseFromString(file_obj.read()) return registry_proto except botocore.exceptions.ClientError as e: - if e.response["Error"]["Code"] == "404": - raise FileNotFoundError( - f'Registry not found at path "{self._uri.geturl()}". Have you run "feast apply"?' - ) - else: - raise FileNotFoundError( - f'Registry is not able to locate data under path "{self._uri.geturl()}" with [original error]: {e.response}' - ) + raise FileNotFoundError( + f'Error while trying to locate Registry at path "{self._uri.geturl()}"with [original error]: {e.response}' + ) def update_registry_proto( self, updater: Optional[Callable[[RegistryProto], RegistryProto]] = None From 986d45e5715d6f81bdc2355af13af7cf0be466fa Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:27:54 -0500 Subject: [PATCH 21/28] For ci requirement Signed-off-by: lblokhin --- sdk/python/setup.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sdk/python/setup.py b/sdk/python/setup.py index d5b9f600a5..0f74945337 100644 --- a/sdk/python/setup.py +++ b/sdk/python/setup.py @@ -104,12 +104,10 @@ "google-cloud-storage>=1.20.*", "google-cloud-core==1.4.*", "redis-py-cluster==2.1.2", -] - -AWS_REQUIRED = [ "boto3==1.17.*", ] + # README file from Feast repo root directory repo_root = ( subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE) @@ -201,7 +199,6 @@ def run(self): extras_require={ "dev": ["mypy-protobuf==1.*", "grpcio-testing==1.*"], "ci": CI_REQUIRED, - "aws": AWS_REQUIRED, "gcp": GCP_REQUIRED, "redis": REDIS_REQUIRED, }, From 79d85c7d57e6b012cb1f03dbf48fa17f913f891c Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:29:41 -0500 Subject: [PATCH 22/28] remove configuration from test Signed-off-by: lblokhin --- sdk/python/tests/test_cli_aws.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py index d93d57cf24..66a06317ed 100644 --- a/sdk/python/tests/test_cli_aws.py +++ b/sdk/python/tests/test_cli_aws.py @@ -30,10 +30,6 @@ def test_basic() -> None: project: {project_id} registry: {data_path / "registry.db"} provider: aws - offline_store: - type: file - online_store: - type: dynamodb """ ) ) From f50b2fbb138a39dac710fa205a4afb23835180b2 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:33:13 -0500 Subject: [PATCH 23/28] feast-integration-tests for tests Signed-off-by: lblokhin --- sdk/python/tests/test_feature_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 14c36e8df4..5b4dbbccf6 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -77,7 +77,7 @@ def feature_store_with_s3_registry(): import boto3 s3 = boto3.resource("s3") - bucket_name = f"feast-registry-test-{int(time.time() * 1000)}" + bucket_name = "feast-integration-tests" bucket = s3.Bucket(bucket_name) s3.meta.client.head_bucket(Bucket=bucket.name) From 509c5213518ba082ce338d21cf129834ee6f930a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:38:07 -0500 Subject: [PATCH 24/28] change test path Signed-off-by: lblokhin --- sdk/python/tests/test_feature_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 5b4dbbccf6..022e3a4fbf 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -83,7 +83,7 @@ def feature_store_with_s3_registry(): return FeatureStore( config=RepoConfig( - registry=f"s3://{bucket_name}/registry.db", + registry=f"s3://{bucket_name}/registries/{int(time.time() * 1000)}/registry.db", project="default", provider="aws", ) From cd67973bd917a3344537df14e97a057ff0eacf72 Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 20:43:23 -0500 Subject: [PATCH 25/28] add fixture feature_store_with_s3_registry to test Signed-off-by: lblokhin --- sdk/python/tests/test_feature_store.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 022e3a4fbf..6341ef037b 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -119,7 +119,11 @@ def test_apply_entity_success(test_feature_store): @pytest.mark.integration @pytest.mark.parametrize( - "test_feature_store", [lazy_fixture("feature_store_with_gcs_registry")], + "test_feature_store", + [ + lazy_fixture("feature_store_with_gcs_registry"), + lazy_fixture("feature_store_with_s3_registry"), + ], ) def test_apply_entity_integration(test_feature_store): entity = Entity( @@ -268,7 +272,11 @@ def test_feature_view_inference_success(test_feature_store, dataframe_source): @pytest.mark.integration @pytest.mark.parametrize( - "test_feature_store", [lazy_fixture("feature_store_with_gcs_registry")], + "test_feature_store", + [ + lazy_fixture("feature_store_with_gcs_registry"), + lazy_fixture("feature_store_with_s3_registry"), + ], ) def test_apply_feature_view_integration(test_feature_store): # Create Feature Views From 3d1b78c09ec4b200c68b6a329206038123a42d0a Mon Sep 17 00:00:00 2001 From: lblokhin Date: Mon, 28 Jun 2021 21:17:28 -0500 Subject: [PATCH 26/28] region required Signed-off-by: lblokhin --- sdk/python/feast/infra/online_stores/dynamodb.py | 2 +- sdk/python/tests/test_cli_aws.py | 5 +++++ sdk/python/tests/test_feature_store.py | 4 ++++ sdk/python/tests/test_offline_online_store_consistency.py | 4 +++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 81027189a4..32ee638304 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -39,7 +39,7 @@ class DynamoDBOnlineStoreConfig(FeastConfigBaseModel): type: Literal["dynamodb"] = "dynamodb" """Online store type selector""" - region: Optional[StrictStr] = None + region: StrictStr """ AWS Region Name """ diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py index 66a06317ed..e73f46c53b 100644 --- a/sdk/python/tests/test_cli_aws.py +++ b/sdk/python/tests/test_cli_aws.py @@ -30,6 +30,11 @@ def test_basic() -> None: project: {project_id} registry: {data_path / "registry.db"} provider: aws + online_store: + type: dynamodb + region: us-west-2 + offline_store: + type: file """ ) ) diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 6341ef037b..21a90861d6 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -24,6 +24,8 @@ from feast.feature import Feature from feast.feature_store import FeatureStore from feast.feature_view import FeatureView +from feast.infra.offline_stores.file import FileOfflineStoreConfig +from feast.infra.online_stores.dynamodb import DynamoDBOnlineStoreConfig from feast.infra.online_stores.sqlite import SqliteOnlineStoreConfig from feast.protos.feast.types import Value_pb2 as ValueProto from feast.repo_config import RepoConfig @@ -86,6 +88,8 @@ def feature_store_with_s3_registry(): registry=f"s3://{bucket_name}/registries/{int(time.time() * 1000)}/registry.db", project="default", provider="aws", + online_store=DynamoDBOnlineStoreConfig(region="us-west-2"), + offline_store=FileOfflineStoreConfig(), ) ) diff --git a/sdk/python/tests/test_offline_online_store_consistency.py b/sdk/python/tests/test_offline_online_store_consistency.py index a2fa2710cd..1635d67d33 100644 --- a/sdk/python/tests/test_offline_online_store_consistency.py +++ b/sdk/python/tests/test_offline_online_store_consistency.py @@ -17,6 +17,7 @@ from feast.feature import Feature from feast.feature_store import FeatureStore from feast.feature_view import FeatureView +from feast.infra.offline_stores.file import FileOfflineStoreConfig from feast.infra.online_stores.datastore import DatastoreOnlineStoreConfig from feast.infra.online_stores.dynamodb import DynamoDBOnlineStoreConfig from feast.infra.online_stores.redis import RedisOnlineStoreConfig, RedisType @@ -210,7 +211,8 @@ def prep_dynamodb_fs_and_fv() -> Iterator[Tuple[FeatureStore, FeatureView]]: registry=str(Path(repo_dir_name) / "registry.db"), project=f"test_bq_correctness_{str(uuid.uuid4()).replace('-', '')}", provider="aws", - online_store=DynamoDBOnlineStoreConfig(type="dynamodb",), + online_store=DynamoDBOnlineStoreConfig(region="us-west-2"), + offline_store=FileOfflineStoreConfig(), ) fs = FeatureStore(config=config) fs.apply([fv, e]) From 57a607cd5c52a3ddc1e799eb24d508c921d361df Mon Sep 17 00:00:00 2001 From: Tsotne Tabidze Date: Fri, 2 Jul 2021 12:07:09 -0700 Subject: [PATCH 27/28] Address the rest of the comments Signed-off-by: Tsotne Tabidze --- .../feast/infra/online_stores/dynamodb.py | 65 ++++++++++++------- sdk/python/feast/registry.py | 12 ++-- sdk/python/feast/repo_config.py | 2 +- sdk/python/setup.py | 5 ++ sdk/python/tests/test_cli_aws.py | 2 - sdk/python/tests/test_feature_store.py | 9 +-- .../test_offline_online_store_consistency.py | 4 +- 7 files changed, 56 insertions(+), 43 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/dynamodb.py b/sdk/python/feast/infra/online_stores/dynamodb.py index 32ee638304..722a081f2e 100644 --- a/sdk/python/feast/infra/online_stores/dynamodb.py +++ b/sdk/python/feast/infra/online_stores/dynamodb.py @@ -48,12 +48,6 @@ class DynamoDBOnlineStore(OnlineStore): Online feature store for AWS DynamoDB. """ - def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): - return ( - boto3.client("dynamodb", region_name=online_config.region), - boto3.resource("dynamodb", region_name=online_config.region), - ) - def update( self, config: RepoConfig, @@ -65,11 +59,11 @@ def update( ): online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - client, dynamodb = self._initialize_dynamodb(online_config) + dynamodb_client, dynamodb_resource = self._initialize_dynamodb(online_config) for table_instance in tables_to_keep: try: - table = dynamodb.create_table( + dynamodb_resource.create_table( TableName=f"{config.project}.{table_instance.name}", KeySchema=[{"AttributeName": "entity_id", "KeyType": "HASH"}], AttributeDefinitions=[ @@ -78,18 +72,18 @@ def update( BillingMode="PAY_PER_REQUEST", ) except ClientError as ce: - print(ce) - if ce.response["Error"]["Code"] == "ResourceNotFoundException": - table = dynamodb.Table(f"{config.project}.{table_instance.name}") + # If the table creation fails with ResourceInUseException, + # it means the table already exists or is being created. + # Otherwise, re-raise the exception + if ce.response["Error"]["Code"] != "ResourceInUseException": + raise for table_instance in tables_to_keep: - client.get_waiter("table_exists").wait( + dynamodb_client.get_waiter("table_exists").wait( TableName=f"{config.project}.{table_instance.name}" ) - for table_instance in tables_to_delete: - table = dynamodb.Table(f"{config.project}.{table_instance.name}") - table.delete() + self._delete_tables_idempotent(dynamodb_resource, config, tables_to_delete) def teardown( self, @@ -99,11 +93,9 @@ def teardown( ): online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - _, dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb_resource = self._initialize_dynamodb(online_config) - for table_instance in tables: - table = dynamodb.Table(f"{config.project}.{table_instance.name}") - table.delete() + self._delete_tables_idempotent(dynamodb_resource, config, tables) def online_write_batch( self, @@ -116,9 +108,9 @@ def online_write_batch( ) -> None: online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - _, dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb_resource = self._initialize_dynamodb(online_config) - table_instance = dynamodb.Table(f"{config.project}.{table.name}") + table_instance = dynamodb_resource.Table(f"{config.project}.{table.name}") with table_instance.batch_writer() as batch: for entity_key, features, timestamp, created_ts in data: entity_id = compute_entity_id(entity_key) @@ -144,12 +136,12 @@ def online_read( ) -> List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]]: online_config = config.online_store assert isinstance(online_config, DynamoDBOnlineStoreConfig) - _, dynamodb = self._initialize_dynamodb(online_config) + _, dynamodb_resource = self._initialize_dynamodb(online_config) result: List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]] = [] for entity_key in entity_keys: - table_instance = dynamodb.Table(f"{config.project}.{table.name}") - entity_id = compute_entity_id(entity_key) # TODO check id + table_instance = dynamodb_resource.Table(f"{config.project}.{table.name}") + entity_id = compute_entity_id(entity_key) response = table_instance.get_item(Key={"entity_id": entity_id}) value = response.get("Item") @@ -163,3 +155,28 @@ def online_read( else: result.append((None, None)) return result + + def _initialize_dynamodb(self, online_config: DynamoDBOnlineStoreConfig): + return ( + boto3.client("dynamodb", region_name=online_config.region), + boto3.resource("dynamodb", region_name=online_config.region), + ) + + def _delete_tables_idempotent( + self, + dynamodb_resource, + config: RepoConfig, + tables: Sequence[Union[FeatureTable, FeatureView]], + ): + for table_instance in tables: + try: + table = dynamodb_resource.Table( + f"{config.project}.{table_instance.name}" + ) + table.delete() + except ClientError as ce: + # If the table deletion fails with ResourceNotFoundException, + # it means the table has already been deleted. + # Otherwise, re-raise the exception + if ce.response["Error"]["Code"] != "ResourceNotFoundException": + raise diff --git a/sdk/python/feast/registry.py b/sdk/python/feast/registry.py index 1ef86f8b80..53c3cae1e7 100644 --- a/sdk/python/feast/registry.py +++ b/sdk/python/feast/registry.py @@ -564,7 +564,7 @@ def get_registry_proto(self): file_obj = TemporaryFile() registry_proto = RegistryProto() try: - import botocore + from botocore.exceptions import ClientError except ImportError as e: from feast.errors import FeastExtrasDependencyImportError @@ -572,14 +572,14 @@ def get_registry_proto(self): try: bucket = self.s3_client.Bucket(self._bucket) self.s3_client.meta.client.head_bucket(Bucket=bucket.name) - except botocore.client.ClientError as e: + except ClientError as e: # If a client error is thrown, then check that it was a 404 error. # If it was a 404 error, then the bucket does not exist. error_code = int(e.response["Error"]["Code"]) if error_code == 404: raise S3RegistryBucketNotExist(self._bucket) else: - raise S3RegistryBucketForbiddenAccess(self._bucket) + raise S3RegistryBucketForbiddenAccess(self._bucket) from e try: obj = bucket.Object(self._key) @@ -587,10 +587,10 @@ def get_registry_proto(self): file_obj.seek(0) registry_proto.ParseFromString(file_obj.read()) return registry_proto - except botocore.exceptions.ClientError as e: + except ClientError as e: raise FileNotFoundError( - f'Error while trying to locate Registry at path "{self._uri.geturl()}"with [original error]: {e.response}' - ) + f"Error while trying to locate Registry at path {self._uri.geturl()}" + ) from e def update_registry_proto( self, updater: Optional[Callable[[RegistryProto], RegistryProto]] = None diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index cde0994286..e7547d1f24 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -159,7 +159,7 @@ def _validate_offline_store_config(cls, values): elif values["provider"] == "gcp": values["offline_store"]["type"] = "bigquery" elif values["provider"] == "aws": - values["offline_store"]["type"] = "redshift" + values["offline_store"]["type"] = "file" offline_store_type = values["offline_store"]["type"] diff --git a/sdk/python/setup.py b/sdk/python/setup.py index 0f74945337..bd51956160 100644 --- a/sdk/python/setup.py +++ b/sdk/python/setup.py @@ -71,6 +71,10 @@ "redis-py-cluster==2.1.2", ] +AWS_REQUIRED = [ + "boto3==1.17.*", +] + CI_REQUIRED = [ "cryptography==3.3.2", "flake8", @@ -200,6 +204,7 @@ def run(self): "dev": ["mypy-protobuf==1.*", "grpcio-testing==1.*"], "ci": CI_REQUIRED, "gcp": GCP_REQUIRED, + "aws": AWS_REQUIRED, "redis": REDIS_REQUIRED, }, include_package_data=True, diff --git a/sdk/python/tests/test_cli_aws.py b/sdk/python/tests/test_cli_aws.py index e73f46c53b..2792858e8d 100644 --- a/sdk/python/tests/test_cli_aws.py +++ b/sdk/python/tests/test_cli_aws.py @@ -33,8 +33,6 @@ def test_basic() -> None: online_store: type: dynamodb region: us-west-2 - offline_store: - type: file """ ) ) diff --git a/sdk/python/tests/test_feature_store.py b/sdk/python/tests/test_feature_store.py index 21a90861d6..f169c1336a 100644 --- a/sdk/python/tests/test_feature_store.py +++ b/sdk/python/tests/test_feature_store.py @@ -76,16 +76,9 @@ def feature_store_with_gcs_registry(): @pytest.fixture def feature_store_with_s3_registry(): - import boto3 - - s3 = boto3.resource("s3") - bucket_name = "feast-integration-tests" - bucket = s3.Bucket(bucket_name) - s3.meta.client.head_bucket(Bucket=bucket.name) - return FeatureStore( config=RepoConfig( - registry=f"s3://{bucket_name}/registries/{int(time.time() * 1000)}/registry.db", + registry=f"s3://feast-integration-tests/registries/{int(time.time() * 1000)}/registry.db", project="default", provider="aws", online_store=DynamoDBOnlineStoreConfig(region="us-west-2"), diff --git a/sdk/python/tests/test_offline_online_store_consistency.py b/sdk/python/tests/test_offline_online_store_consistency.py index 1635d67d33..18fbf705ae 100644 --- a/sdk/python/tests/test_offline_online_store_consistency.py +++ b/sdk/python/tests/test_offline_online_store_consistency.py @@ -168,7 +168,7 @@ def prep_redis_fs_and_fv() -> Iterator[Tuple[FeatureStore, FeatureView]]: join_key="driver_id", value_type=ValueType.INT32, ) - with tempfile.TemporaryDirectory() as repo_dir_name, tempfile.TemporaryDirectory(): + with tempfile.TemporaryDirectory() as repo_dir_name: config = RepoConfig( registry=str(Path(repo_dir_name) / "registry.db"), project=f"test_bq_correctness_{str(uuid.uuid4()).replace('-', '')}", @@ -206,7 +206,7 @@ def prep_dynamodb_fs_and_fv() -> Iterator[Tuple[FeatureStore, FeatureView]]: join_key="driver_id", value_type=ValueType.INT32, ) - with tempfile.TemporaryDirectory() as repo_dir_name, tempfile.TemporaryDirectory(): + with tempfile.TemporaryDirectory() as repo_dir_name: config = RepoConfig( registry=str(Path(repo_dir_name) / "registry.db"), project=f"test_bq_correctness_{str(uuid.uuid4()).replace('-', '')}", From 3cd959773013a2dd91565237495bc6bb48972667 Mon Sep 17 00:00:00 2001 From: Tsotne Tabidze Date: Fri, 2 Jul 2021 12:17:09 -0700 Subject: [PATCH 28/28] Update to_table to to_arrow Signed-off-by: Tsotne Tabidze --- sdk/python/feast/infra/aws.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/aws.py b/sdk/python/feast/infra/aws.py index c7a7c56d2a..272f39840e 100644 --- a/sdk/python/feast/infra/aws.py +++ b/sdk/python/feast/infra/aws.py @@ -108,7 +108,7 @@ def materialize_single_feature_view( end_date=end_date, ) - table = offline_job.to_table() + table = offline_job.to_arrow() if feature_view.input.field_mapping is not None: table = _run_field_mapping(table, feature_view.input.field_mapping)