From 3143b5572895bd2aa773916b880e114d6f4adc14 Mon Sep 17 00:00:00 2001 From: Oleksii Moskalenko Date: Tue, 10 Nov 2020 12:48:40 +0800 Subject: [PATCH 01/32] 0.8.1-SNAPSHOT Signed-off-by: Oleksii Moskalenko --- datatypes/java/README.md | 2 +- docs/advanced/audit-logging.md | 2 +- docs/contributing/development-guide.md | 8 ++++---- infra/charts/feast/Chart.yaml | 2 +- infra/charts/feast/README.md | 8 ++++---- infra/charts/feast/charts/feast-core/Chart.yaml | 2 +- infra/charts/feast/charts/feast-core/README.md | 2 +- infra/charts/feast/charts/feast-jobservice/Chart.yaml | 2 +- infra/charts/feast/charts/feast-jobservice/README.md | 2 +- infra/charts/feast/charts/feast-jupyter/Chart.yaml | 2 +- infra/charts/feast/charts/feast-jupyter/README.md | 2 +- infra/charts/feast/charts/feast-serving/Chart.yaml | 2 +- infra/charts/feast/charts/feast-serving/README.md | 2 +- .../feast/charts/prometheus-statsd-exporter/Chart.yaml | 2 +- .../feast/charts/prometheus-statsd-exporter/README.md | 2 +- infra/charts/feast/requirements.lock | 6 +++--- infra/charts/feast/requirements.yaml | 6 +++--- pom.xml | 2 +- .../src/main/scala/feast/ingestion/IngestionJob.scala | 2 +- 19 files changed, 29 insertions(+), 29 deletions(-) diff --git a/datatypes/java/README.md b/datatypes/java/README.md index 0996af74b1c..8785fd2cd90 100644 --- a/datatypes/java/README.md +++ b/datatypes/java/README.md @@ -16,7 +16,7 @@ Dependency Coordinates dev.feast datatypes-java - 0.8.0 + 0.8.1-SNAPSHOT ``` diff --git a/docs/advanced/audit-logging.md b/docs/advanced/audit-logging.md index 269f063c597..45238dc12a0 100644 --- a/docs/advanced/audit-logging.md +++ b/docs/advanced/audit-logging.md @@ -44,7 +44,7 @@ Audit Logs produced by Feast are written to the console similar to normal logs b "service": "CoreService", "component": "feast-core", "id": "45329ea9-0d48-46c5-b659-4604f6193711", - "version": "0.8.0" + "version": "0.8.1-SNAPSHOT" }, "hostname": "feast.core" "timestamp": "2020-10-20T04:45:24Z", diff --git a/docs/contributing/development-guide.md b/docs/contributing/development-guide.md index 9c9ec2bdf51..07b1237b7b6 100644 --- a/docs/contributing/development-guide.md +++ b/docs/contributing/development-guide.md @@ -108,10 +108,10 @@ Feast Serving has a dependency on Feast Core, thus always start Feast Core first ```bash # Start Feast Core locally -java -jar core/target/feast-core-0.8.0-exec.jar +java -jar core/target/feast-core-0.8.1-SNAPSHOT-exec.jar # Start Feast Serving locally -java -jar serving/target/feast-serving-0.8.0-exec.jar +java -jar serving/target/feast-serving-0.8.1-SNAPSHOT-exec.jar ``` Test whether Feast Core, Feast Serving are started and running correctly: @@ -122,8 +122,8 @@ feast version --core-url="localhost:6565" --serving-url="localhost:6566" ```javascript { - 'serving': {'url': 'localhost:6566', 'version': '0.8.0'}, - 'core': {'url': 'localhost:6565', 'version': '0.8.0'} + 'serving': {'url': 'localhost:6566', 'version': '0.8.1-SNAPSHOT'}, + 'core': {'url': 'localhost:6565', 'version': '0.8.1-SNAPSHOT'} } ``` diff --git a/infra/charts/feast/Chart.yaml b/infra/charts/feast/Chart.yaml index 323f4f3389d..4d3f9270b9b 100644 --- a/infra/charts/feast/Chart.yaml +++ b/infra/charts/feast/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: Feature store for machine learning. name: feast -version: 0.8.0 +version: 0.8.1-SNAPSHOT diff --git a/infra/charts/feast/README.md b/infra/charts/feast/README.md index 5260efc4823..1a3d280501e 100644 --- a/infra/charts/feast/README.md +++ b/infra/charts/feast/README.md @@ -1,7 +1,7 @@ feast ===== -Feature store for machine learning. Current chart version is `0.8.0` +Feature store for machine learning. Current chart version is `0.8.1-SNAPSHOT` ## Installation @@ -11,9 +11,9 @@ https://docs.feast.dev/v/master/getting-started/deploying-feast/kubernetes | Repository | Name | Version | |------------|------|---------| -| | feast-core | 0.8.0 | -| | feast-jupyter | 0.8.0 | -| | feast-serving | 0.8.0 | +| | feast-core | 0.8.1-SNAPSHOT | +| | feast-jupyter | 0.8.1-SNAPSHOT | +| | feast-serving | 0.8.1-SNAPSHOT | | | prometheus-statsd-exporter | 0.1.2 | | https://charts.bitnami.com/bitnami/ | kafka | 11.8.8 | | https://kubernetes-charts.storage.googleapis.com/ | grafana | 5.0.5 | diff --git a/infra/charts/feast/charts/feast-core/Chart.yaml b/infra/charts/feast/charts/feast-core/Chart.yaml index 0857cfe4a5d..6b38d8fcb4e 100644 --- a/infra/charts/feast/charts/feast-core/Chart.yaml +++ b/infra/charts/feast/charts/feast-core/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: Feast Core registers feature specifications. name: feast-core -version: 0.8.0 +version: 0.8.1-SNAPSHOT diff --git a/infra/charts/feast/charts/feast-core/README.md b/infra/charts/feast/charts/feast-core/README.md index 45607047a3c..4ee08678e83 100644 --- a/infra/charts/feast/charts/feast-core/README.md +++ b/infra/charts/feast/charts/feast-core/README.md @@ -2,7 +2,7 @@ feast-core ========== Feast Core registers feature specifications. -Current chart version is `0.8.0` +Current chart version is `0.8.1-SNAPSHOT` diff --git a/infra/charts/feast/charts/feast-jobservice/Chart.yaml b/infra/charts/feast/charts/feast-jobservice/Chart.yaml index d4183b197ab..24fb3da0f10 100644 --- a/infra/charts/feast/charts/feast-jobservice/Chart.yaml +++ b/infra/charts/feast/charts/feast-jobservice/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: Feast Job Service manage ingestion jobs. name: feast-jobservice -version: 0.8.0 +version: 0.8.1-SNAPSHOT diff --git a/infra/charts/feast/charts/feast-jobservice/README.md b/infra/charts/feast/charts/feast-jobservice/README.md index 859889978ac..264ce8604d0 100644 --- a/infra/charts/feast/charts/feast-jobservice/README.md +++ b/infra/charts/feast/charts/feast-jobservice/README.md @@ -2,7 +2,7 @@ feast-jobservice ================ Feast Job Service manage ingestion jobs. -Current chart version is `0.8.0` +Current chart version is `0.8.1-SNAPSHOT` diff --git a/infra/charts/feast/charts/feast-jupyter/Chart.yaml b/infra/charts/feast/charts/feast-jupyter/Chart.yaml index 3f8131fa427..a22af5dd9e5 100644 --- a/infra/charts/feast/charts/feast-jupyter/Chart.yaml +++ b/infra/charts/feast/charts/feast-jupyter/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: Feast Jupyter provides a Jupyter server with pre-installed Feast SDK name: feast-jupyter -version: 0.8.0 +version: 0.8.1-SNAPSHOT diff --git a/infra/charts/feast/charts/feast-jupyter/README.md b/infra/charts/feast/charts/feast-jupyter/README.md index 5bb26b4357a..18d0488b007 100644 --- a/infra/charts/feast/charts/feast-jupyter/README.md +++ b/infra/charts/feast/charts/feast-jupyter/README.md @@ -2,7 +2,7 @@ feast-jupyter ============= Feast Jupyter provides a Jupyter server with pre-installed Feast SDK -Current chart version is `0.8.0` +Current chart version is `0.8.1-SNAPSHOT` diff --git a/infra/charts/feast/charts/feast-serving/Chart.yaml b/infra/charts/feast/charts/feast-serving/Chart.yaml index fb007dd04dc..da5896829e2 100644 --- a/infra/charts/feast/charts/feast-serving/Chart.yaml +++ b/infra/charts/feast/charts/feast-serving/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: Feast Serving serves low-latency latest features and historical batch features. name: feast-serving -version: 0.8.0 +version: 0.8.1-SNAPSHOT diff --git a/infra/charts/feast/charts/feast-serving/README.md b/infra/charts/feast/charts/feast-serving/README.md index acfdc0040f9..426c58c1a8d 100644 --- a/infra/charts/feast/charts/feast-serving/README.md +++ b/infra/charts/feast/charts/feast-serving/README.md @@ -2,7 +2,7 @@ feast-serving ============= Feast Serving serves low-latency latest features and historical batch features. -Current chart version is `0.8.0` +Current chart version is `0.8.1-SNAPSHOT` diff --git a/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml b/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml index 98a9356dcd5..f480197bbb4 100644 --- a/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml +++ b/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: 0.8.0 +appVersion: 0.8.1-SNAPSHOT description: A Helm chart for prometheus statsd-exporter Scrape metrics stored statsd home: https://github.com/prometheus/statsd_exporter keywords: diff --git a/infra/charts/feast/charts/prometheus-statsd-exporter/README.md b/infra/charts/feast/charts/prometheus-statsd-exporter/README.md index 8a6739f393b..6f5f39754bd 100644 --- a/infra/charts/feast/charts/prometheus-statsd-exporter/README.md +++ b/infra/charts/feast/charts/prometheus-statsd-exporter/README.md @@ -38,7 +38,7 @@ $ helm delete my-release |`extraArgs` | key:value list of extra arguments to give the binary | `{}` | |`image.pullPolicy` | Image pull policy | `IfNotPresent` | |`image.repository` | Image repository | `prom/statsd-exporter` | -|`image.tag` | Image tag | `v0.8.0` | +|`image.tag` | Image tag | `v0.8.1-SNAPSHOT` | |`ingress.enabled` | enable ingress | `false` | |`ingress.path` | ingress base path | `/` | |`ingress.host` | Ingress accepted hostnames | `nil` | diff --git a/infra/charts/feast/requirements.lock b/infra/charts/feast/requirements.lock index 2936f5007d4..25549f8df3d 100644 --- a/infra/charts/feast/requirements.lock +++ b/infra/charts/feast/requirements.lock @@ -1,13 +1,13 @@ dependencies: - name: feast-core repository: "" - version: 0.8.0 + version: 0.8.1-SNAPSHOT - name: feast-serving repository: "" - version: 0.8.0 + version: 0.8.1-SNAPSHOT - name: feast-jupyter repository: "" - version: 0.8.0 + version: 0.8.1-SNAPSHOT - name: postgresql repository: https://kubernetes-charts.storage.googleapis.com/ version: 8.6.1 diff --git a/infra/charts/feast/requirements.yaml b/infra/charts/feast/requirements.yaml index dd47411da23..8d35a93697e 100644 --- a/infra/charts/feast/requirements.yaml +++ b/infra/charts/feast/requirements.yaml @@ -1,13 +1,13 @@ dependencies: - name: feast-core - version: 0.8.0 + version: 0.8.1-SNAPSHOT condition: feast-core.enabled - name: feast-serving alias: feast-online-serving - version: 0.8.0 + version: 0.8.1-SNAPSHOT condition: feast-online-serving.enabled - name: feast-jupyter - version: 0.8.0 + version: 0.8.1-SNAPSHOT condition: feast-jupyter.enabled - name: postgresql version: 8.6.1 diff --git a/pom.xml b/pom.xml index 409e60e6f4e..72755932982 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 0.8.0 + 0.8.1-SNAPSHOT https://github.com/feast-dev/feast UTF-8 diff --git a/spark/ingestion/src/main/scala/feast/ingestion/IngestionJob.scala b/spark/ingestion/src/main/scala/feast/ingestion/IngestionJob.scala index f4d0dcc1159..e67d0942173 100644 --- a/spark/ingestion/src/main/scala/feast/ingestion/IngestionJob.scala +++ b/spark/ingestion/src/main/scala/feast/ingestion/IngestionJob.scala @@ -31,7 +31,7 @@ object IngestionJob { val parser = new scopt.OptionParser[IngestionJobConfig]("IngestionJon") { // ToDo: read version from Manifest - head("feast.ingestion.IngestionJob", "0.8.0") + head("feast.ingestion.IngestionJob", "0.8.1-SNAPSHOT") opt[Modes]("mode") .action((x, c) => c.copy(mode = x)) From 0a6047214cae34c856de11d232c3f2399d203e46 Mon Sep 17 00:00:00 2001 From: Oleksii Moskalenko Date: Tue, 10 Nov 2020 12:56:02 +0800 Subject: [PATCH 02/32] revert some versions Signed-off-by: Oleksii Moskalenko --- infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml | 2 +- infra/charts/feast/charts/prometheus-statsd-exporter/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml b/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml index f480197bbb4..98a9356dcd5 100644 --- a/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml +++ b/infra/charts/feast/charts/prometheus-statsd-exporter/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: 0.8.1-SNAPSHOT +appVersion: 0.8.0 description: A Helm chart for prometheus statsd-exporter Scrape metrics stored statsd home: https://github.com/prometheus/statsd_exporter keywords: diff --git a/infra/charts/feast/charts/prometheus-statsd-exporter/README.md b/infra/charts/feast/charts/prometheus-statsd-exporter/README.md index 6f5f39754bd..8a6739f393b 100644 --- a/infra/charts/feast/charts/prometheus-statsd-exporter/README.md +++ b/infra/charts/feast/charts/prometheus-statsd-exporter/README.md @@ -38,7 +38,7 @@ $ helm delete my-release |`extraArgs` | key:value list of extra arguments to give the binary | `{}` | |`image.pullPolicy` | Image pull policy | `IfNotPresent` | |`image.repository` | Image repository | `prom/statsd-exporter` | -|`image.tag` | Image tag | `v0.8.1-SNAPSHOT` | +|`image.tag` | Image tag | `v0.8.0` | |`ingress.enabled` | enable ingress | `false` | |`ingress.path` | ingress base path | `/` | |`ingress.host` | Ingress accepted hostnames | `nil` | From dc71813b7abbdb01139c0ba80da539350c9eef83 Mon Sep 17 00:00:00 2001 From: Willem Pienaar Date: Mon, 16 Nov 2020 14:22:34 +0800 Subject: [PATCH 03/32] Backport documentation to Feast 0.8 --- docs/.gitbook/assets/blank-diagram-4 (4).svg | 1 + .../.gitbook/assets/concept_hierarchy (1).png | Bin 0 -> 32112 bytes docs/.gitbook/assets/image (4).png | Bin 0 -> 33294 bytes docs/README.md | 2 +- docs/SUMMARY.md | 24 +- docs/advanced/audit-logging.md | 22 +- docs/advanced/metrics.md | 8 +- docs/advanced/security.md | 34 ++- docs/advanced/troubleshooting.md | 22 +- docs/advanced/upgrading.md | 18 +- docs/{getting-help.md => community.md} | 17 +- docs/concepts/architecture.md | 81 ++++--- docs/concepts/concepts.md | 124 ---------- docs/concepts/entities.md | 45 ++-- docs/concepts/feature-references.md | 36 --- docs/concepts/feature-tables.md | 52 ++--- docs/concepts/glossary.md | 36 +++ docs/concepts/overview.md | 65 +----- docs/concepts/sources.md | 42 ++-- docs/concepts/stores.md | 26 +++ docs/contributing/adding-a-new-store-1.md | 87 -------- docs/contributing/contributing.md | 6 +- docs/contributing/development-guide.md | 8 +- .../connect-to-feast/README.md | 2 +- .../{connecting-to-feast.md => feast-cli.md} | 0 .../install-feast/kubernetes-with-helm.md | 2 +- docs/getting-started/learn-feast.md | 2 +- docs/getting-started/quickstart.md | 56 ----- docs/installation/docker-compose.md | 112 ---------- docs/installation/gke.md | 211 ------------------ docs/installation/overview.md | 14 -- docs/introduction/getting-help.md | 36 --- docs/introduction/roadmap.md | 50 ----- docs/introduction/why-feast.md | 34 --- docs/quickstart.md | 25 ++- docs/reference/api.md | 13 +- docs/reference/api/README.md | 17 -- .../README.md => configuration-reference.md} | 14 +- docs/reference/limitations.md | 7 +- .../README.md => metrics-reference.md} | 22 +- docs/roadmap.md | 34 ++- docs/user-guide/data-ingestion.md | 42 ---- docs/user-guide/define-and-ingest-features.md | 56 +++++ docs/user-guide/feature-retrieval.md | 61 ----- docs/user-guide/getting-online-features.md | 40 +--- docs/user-guide/getting-training-features.md | 72 ++++++ docs/user-guide/overview.md | 32 +++ 47 files changed, 522 insertions(+), 1188 deletions(-) create mode 100644 docs/.gitbook/assets/blank-diagram-4 (4).svg create mode 100644 docs/.gitbook/assets/concept_hierarchy (1).png create mode 100644 docs/.gitbook/assets/image (4).png rename docs/{getting-help.md => community.md} (70%) delete mode 100644 docs/concepts/concepts.md delete mode 100644 docs/concepts/feature-references.md create mode 100644 docs/concepts/glossary.md create mode 100644 docs/concepts/stores.md delete mode 100644 docs/contributing/adding-a-new-store-1.md rename docs/getting-started/connect-to-feast/{connecting-to-feast.md => feast-cli.md} (100%) delete mode 100644 docs/getting-started/quickstart.md delete mode 100644 docs/installation/docker-compose.md delete mode 100644 docs/installation/gke.md delete mode 100644 docs/installation/overview.md delete mode 100644 docs/introduction/getting-help.md delete mode 100644 docs/introduction/roadmap.md delete mode 100644 docs/introduction/why-feast.md delete mode 100644 docs/reference/api/README.md rename docs/reference/{configuration-reference/README.md => configuration-reference.md} (84%) rename docs/reference/{metrics-reference/README.md => metrics-reference.md} (92%) delete mode 100644 docs/user-guide/data-ingestion.md create mode 100644 docs/user-guide/define-and-ingest-features.md delete mode 100644 docs/user-guide/feature-retrieval.md create mode 100644 docs/user-guide/getting-training-features.md create mode 100644 docs/user-guide/overview.md diff --git a/docs/.gitbook/assets/blank-diagram-4 (4).svg b/docs/.gitbook/assets/blank-diagram-4 (4).svg new file mode 100644 index 00000000000..fb5e0659e55 --- /dev/null +++ b/docs/.gitbook/assets/blank-diagram-4 (4).svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/.gitbook/assets/concept_hierarchy (1).png b/docs/.gitbook/assets/concept_hierarchy (1).png new file mode 100644 index 0000000000000000000000000000000000000000..f5cf59ad67357103dcf208f88b0f3c7a6c426fbe GIT binary patch literal 32112 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
uAhA>y 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 DA-b1A literal 0 HcmV?d00001 diff --git a/docs/.gitbook/assets/image (4).png b/docs/.gitbook/assets/image (4).png new file mode 100644 index 0000000000000000000000000000000000000000..cd77f27cc4563f8f952f9e956661c5651665d8e9 GIT binary patch literal 33294 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 literal 0 HcmV?d00001 diff --git a/docs/README.md b/docs/README.md index e84deb4ae77..31a8d12dbcb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -50,7 +50,7 @@ The best way to learn Feast is to use it. Head over to our [Quickstart](quicksta * [Getting Started](getting-started/) provides guides on [Installing Feast](getting-started/install-feast/) and [Connecting to Feast](getting-started/connect-to-feast/). * [Concepts](./) describes all important Feast API concepts. -* [User guide](user-guide/data-ingestion.md) provides guidance on completing Feast workflows. +* [User guide](user-guide/define-and-ingest-features.md) provides guidance on completing Feast workflows. * [Examples](https://github.com/feast-dev/feast/tree/master/examples) contains a Jupyter notebook that you can run on your Feast deployment. * [Advanced](advanced/troubleshooting.md) contains information about both advanced and operational aspects of Feast. * [Reference](reference/api/) contains detailed API and design documents for advanced users. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 81c35f4e9eb..e7384c06b39 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -8,11 +8,11 @@ * [Amazon EKS \(with Terraform\)](getting-started/install-feast/kubernetes-amazon-eks-with-terraform.md) * [Connect to Feast](getting-started/connect-to-feast/README.md) * [Python SDK](getting-started/connect-to-feast/python-sdk.md) - * [Feast CLI](getting-started/connect-to-feast/connecting-to-feast.md) + * [Feast CLI](getting-started/connect-to-feast/feast-cli.md) * [Learn Feast](getting-started/learn-feast.md) * [Roadmap](roadmap.md) * [Changelog](https://github.com/feast-dev/feast/blob/master/CHANGELOG.md) -* [Community](getting-help.md) +* [Community](community.md) ## Concepts @@ -21,12 +21,14 @@ * [Entities](concepts/entities.md) * [Sources](concepts/sources.md) * [Feature Tables](concepts/feature-tables.md) -* [Feature References](concepts/feature-references.md) +* [Stores](concepts/stores.md) +* [Glossary](concepts/glossary.md) ## User Guide -* [Getting data into Feast](user-guide/data-ingestion.md) -* [Getting training features](user-guide/feature-retrieval.md) +* [Overview](user-guide/overview.md) +* [Define and ingest features](user-guide/define-and-ingest-features.md) +* [Getting training features](user-guide/getting-training-features.md) * [Getting online features](user-guide/getting-online-features.md) ## Tutorials @@ -43,15 +45,15 @@ ## Reference -* [API Reference](reference/api/README.md) - * [Core gRPC API](https://api.docs.feast.dev/grpc/feast.core.pb.html) - * [Serving gRPC API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) - * [gRPC Types](https://api.docs.feast.dev/grpc/feast.types.pb.html) -* [Configuration Reference](reference/configuration-reference/README.md) +* [API Reference](reference/api.md) * [Go SDK](https://godoc.org/github.com/feast-dev/feast/sdk/go) -* [Metrics Reference](reference/metrics-reference/README.md) * [Java SDK](https://javadoc.io/doc/dev.feast/feast-sdk) + * [Core gRPC API](https://api.docs.feast.dev/grpc/feast.core.pb.html) * [Python SDK](https://api.docs.feast.dev/python/) + * [Serving gRPC API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) + * [gRPC Types](https://api.docs.feast.dev/grpc/feast.types.pb.html) +* [Configuration Reference](reference/configuration-reference.md) +* [Metrics Reference](reference/metrics-reference.md) * [Limitations](reference/limitations.md) ## Contributing diff --git a/docs/advanced/audit-logging.md b/docs/advanced/audit-logging.md index 45238dc12a0..6759277b45d 100644 --- a/docs/advanced/audit-logging.md +++ b/docs/advanced/audit-logging.md @@ -1,10 +1,14 @@ # Audit Logging -## Introduction +{% hint style="warning" %} +This page applies to Feast 0.7. The content may be out of date for Feast 0.8+ +{% endhint %} + +### Introduction Feast provides audit logging functionality in order to debug problems and to trace the lineage of events. -## Audit Log Types +### Audit Log Types Audit Logs produced by Feast come in three favors: @@ -14,7 +18,7 @@ Audit Logs produced by Feast come in three favors: | Transition Audit Log | Logs transitions in status in resources managed by Feast \(ie an Ingestion Job becoming RUNNING\). | | Action Audit Log | Logs actions performed on a specific resource managed by Feast \(ie an Ingestion Job is aborted\). | -## Configuration +### Configuration | Audit Log Type | Description | | :--- | :--- | @@ -22,7 +26,7 @@ Audit Logs produced by Feast come in three favors: | Transition Audit Log | Enabled when `feast.logging.audit.enabled` is set to `true` | | Action Audit Log | Enabled when `feast.logging.audit.enabled` is set to `true` | -## JSON Format +### JSON Format Audit Logs produced by Feast are written to the console similar to normal logs but in a structured, machine parsable JSON. Example of a Message Audit Log JSON entry produced: @@ -44,7 +48,7 @@ Audit Logs produced by Feast are written to the console similar to normal logs b "service": "CoreService", "component": "feast-core", "id": "45329ea9-0d48-46c5-b659-4604f6193711", - "version": "0.8.1-SNAPSHOT" + "version": "0.8.0" }, "hostname": "feast.core" "timestamp": "2020-10-20T04:45:24Z", @@ -52,7 +56,7 @@ Audit Logs produced by Feast are written to the console similar to normal logs b } ``` -## Log Entry Schema +### Log Entry Schema Fields common to all Audit Log Types: @@ -91,11 +95,11 @@ Fields in Transition Audit Log Type | `resource.type` | Type of resource of which the transition occurred \(i.e `FeatureTable`\) | | `resource.id` | Identifier specifying the specific resource of which the transition occurred. | -## Log Forwarder +### Log Forwarder Feast currently only supports forwarding Request/Response \(Message Audit Log Type\) logs to an external fluentD service with `feast.**` Fluentd tag. -### Request/Response Log Example +#### Request/Response Log Example ```text { @@ -114,7 +118,7 @@ Feast currently only supports forwarding Request/Response \(Message Audit Log Ty } ``` -### Configuration +#### Configuration The Fluentd Log Forwarder configured with the with the following configuration options in `application.yml`: diff --git a/docs/advanced/metrics.md b/docs/advanced/metrics.md index 57b9ebfc059..fd457b5120d 100644 --- a/docs/advanced/metrics.md +++ b/docs/advanced/metrics.md @@ -1,5 +1,9 @@ # Metrics +{% hint style="warning" %} +This page applies to Feast 0.7. The content may be out of date for Feast 0.8+ +{% endhint %} + ### Overview Feast Components export metrics that can provide insight into Feast behavior: @@ -7,7 +11,7 @@ Feast Components export metrics that can provide insight into Feast behavior: * [Feast Ingestion Jobs can be configured to push metrics into StatsD](metrics.md#2-exporting-feast-metrics-to-prometheus) * [Prometheus can be configured to scrape metrics from Feast Core and Serving.](metrics.md#2-exporting-feast-metrics-to-prometheus) -See the [Metrics Reference ](../reference/metrics-reference/)for documentation on metrics are exported by Feast. +See the [Metrics Reference ](../reference/metrics-reference.md)for documentation on metrics are exported by Feast. {% hint style="info" %} Feast Job Controller currently does not export any metrics on its own. However its `application.yml` is used to configure metrics export for ingestion jobs. @@ -51,7 +55,7 @@ server: ### Further Reading -See the [Metrics Reference ](../reference/metrics-reference/)for documentation on metrics are exported by Feast. +See the [Metrics Reference ](../reference/metrics-reference.md)for documentation on metrics are exported by Feast. ## diff --git a/docs/advanced/security.md b/docs/advanced/security.md index 937dadb3bdc..27c1056dd66 100644 --- a/docs/advanced/security.md +++ b/docs/advanced/security.md @@ -5,9 +5,7 @@ description: 'Secure Feast with SSL/TLS, Authentication and Authorization.' # Security {% hint style="warning" %} -Currently, Security functionality applies only to Feast Core and Feast Online Serving. - -Security for Historical Serving will become available once offline storage is introduced in Feast 0.9. +This page applies to Feast 0.7. The content may be out of date for Feast 0.8+ {% endhint %} ### Overview @@ -22,11 +20,11 @@ Feast supports the following security methods: [Important considerations when integrating Authentication/Authorization](security.md#5-authentication-and-authorization). -## **1. SSL/TLS** +### **SSL/TLS** Feast supports SSL/TLS encrypted inter-service communication among Feast Core, Feast Online Serving, and Feast SDKs. -### Configuring SSL/TLS on Feast Core and Feast Serving +#### Configuring SSL/TLS on Feast Core and Feast Serving The following properties configure SSL/TLS. These properties are located in their corresponding `application.yml`files: @@ -38,9 +36,9 @@ The following properties configure SSL/TLS. These properties are located in thei > Read more on enabling SSL/TLS in the[ gRPC starter docs.](https://yidongnan.github.io/grpc-spring-boot-starter/en/server/security.html#enable-transport-layer-security) -### Configuring SSL/TLS on Python SDK/CLI +#### Configuring SSL/TLS on Python SDK/CLI -To enable SSL/TLS in the [Feast Python SDK](https://api.docs.feast.dev/python/#feast.client.Client) or [Feast CLI](../getting-started/connect-to-feast/connecting-to-feast.md), set the config options via `feast config`: +To enable SSL/TLS in the [Feast Python SDK](https://api.docs.feast.dev/python/#feast.client.Client) or [Feast CLI](../getting-started/connect-to-feast/feast-cli.md), set the config options via `feast config`: | Configuration Option | Description | | :--- | :--- | @@ -53,7 +51,7 @@ To enable SSL/TLS in the [Feast Python SDK](https://api.docs.feast.dev/python/#f The Python SDK automatically uses SSL/TLS when connecting to Feast Core and Feast Online Serving via port 443. {% endhint %} -### Configuring SSL/TLS on Go SDK +#### Configuring SSL/TLS on Go SDK Configure SSL/TLS on the [Go SDK](https://godoc.org/github.com/feast-dev/feast/sdk/go) by passing configuration via `SecurityConfig`: @@ -69,7 +67,7 @@ cli, err := feast.NewSecureGrpcClient("localhost", 6566, feast.SecurityConfig{ | `EnableTLS` | Enables SSL/TLS functionality when connecting to Feast if `true` | | `TLSCertPath` | Optional. Provides the path of the root certificate used to verify Feast Service's identity. If omitted, uses system certificates. | -### Configuring SSL/TLS on **Java** SDK +#### Configuring SSL/TLS on **Java** SDK Configure SSL/TLS on the [Feast Java SDK](https://javadoc.io/doc/dev.feast/feast-sdk) by passing configuration via `SecurityConfig`: @@ -86,7 +84,7 @@ FeastClient client = FeastClient.createSecure("localhost", 6566, | `setTLSEnabled()` | Enables SSL/TLS functionality when connecting to Feast if `true` | | `setCertificatesPath()` | Optional. Set the path of the root certificate used to verify Feast Service's identity. If omitted, uses system certificates. | -## **2. Authentication** +### **Authentication** {% hint style="warning" %} To prevent man in the middle attacks, we recommend that SSL/TLS be implemented prior to authentication. @@ -118,7 +116,7 @@ Behind the scenes, Feast Core and Feast Online Serving authenticate by: * Validates token's authenticity using the JWK retrieved from the `jwkEndpointURI` -### **Authenticating Serving with Feast Core** +#### **Authenticating Serving with Feast Core** Feast Online Serving communicates with Feast Core during normal operation. When both authentication and authorization are enabled on Feast Core, Feast Online Serving is forced to authenticate its requests to Feast Core. Otherwise, Feast Online Serving produces an Authentication failure error when connecting to Feast Core. @@ -186,9 +184,9 @@ OAuth Provider makes an OAuth [client credentials](https://auth0.com/docs/flows/ {% endtab %} {% endtabs %} -### **Enabling Authentication in Python SDK/CLI** +#### **Enabling Authentication in Python SDK/CLI** -Configure the [Feast Python SDK](https://api.docs.feast.dev/python/) and [Feast CLI](../getting-started/connect-to-feast/connecting-to-feast.md) to use authentication via `feast config`: +Configure the [Feast Python SDK](https://api.docs.feast.dev/python/) and [Feast CLI](../getting-started/connect-to-feast/feast-cli.md) to use authentication via `feast config`: ```python $ feast config set enable_auth true @@ -262,7 +260,7 @@ OAuth Provider makes an OAuth [client credentials](https://auth0.com/docs/flows/ {% endtab %} {% endtabs %} -### **Enabling Authentication in Go SDK** +#### **Enabling Authentication in Go SDK** Configure the [Feast Java SDK](https://javadoc.io/doc/dev.feast/feast-sdk/latest/com/gojek/feast/package-summary.html) to use authentication by specifying the credential via `SecurityConfig`: @@ -340,7 +338,7 @@ cred := feast.NewOAuthCredential("localhost:6566", "client_id", "secret", "https {% endtab %} {% endtabs %} -### **Enabling Authentication in Java SDK** +#### **Enabling Authentication in Java SDK** Configure the [Feast Java SDK](https://javadoc.io/doc/dev.feast/feast-sdk/latest/com/gojek/feast/package-summary.html) to use authentication by setting credentials via `SecurityConfig`: @@ -438,7 +436,7 @@ CallCredentials credentials = new OAuthCredentials(Map.of( {% endtab %} {% endtabs %} -## 3. Authorization +### Authorization {% hint style="info" %} Authorization requires that authentication be configured to obtain a user identity for use in authorizing requests. @@ -449,7 +447,7 @@ Authorization provides access control to FeatureTables and/or Features based on * Create and/or Update a Feature Table in the Project. * Retrieve Feature Values for Features in that Project. -### **Authorization API/Server** +#### **Authorization API/Server** ![Feast Authorization Flow](../.gitbook/assets/rsz_untitled23.jpg) @@ -471,7 +469,7 @@ Authorization can be configured for Feast Core and Feast Online Serving via prop This example of the [Authorization Server with Keto](https://github.com/feast-dev/feast-keto-auth-server) can be used as a reference implementation for implementing an Authorization Server that Feast supports. {% endhint %} -## **4. Authentication & Authorization** +### **Authentication & Authorization** When using Authentication & Authorization, consider: diff --git a/docs/advanced/troubleshooting.md b/docs/advanced/troubleshooting.md index 8936a6b77c7..1060466d300 100644 --- a/docs/advanced/troubleshooting.md +++ b/docs/advanced/troubleshooting.md @@ -1,10 +1,14 @@ # Troubleshooting -If at any point in time you cannot resolve a problem, please see the [Community](../getting-help.md) section for reaching out to the Feast community. +{% hint style="warning" %} +This page applies to Feast 0.7. The content may be out of date for Feast 0.8+ +{% endhint %} -## How can I verify that all services are operational? +If at any point in time you cannot resolve a problem, please see the [Community](../community.md) section for reaching out to the Feast community. -### Docker Compose +### How can I verify that all services are operational? + +#### Docker Compose The containers should be in an `up` state: @@ -12,7 +16,7 @@ The containers should be in an `up` state: docker ps ``` -### Google Kubernetes Engine +#### Google Kubernetes Engine All services should either be in a `RUNNING` state or `COMPLETED`state: @@ -20,7 +24,7 @@ All services should either be in a `RUNNING` state or `COMPLETED`state: kubectl get pods ``` -## How can I verify that I can connect to all services? +### How can I verify that I can connect to all services? First locate the the host and port of the Feast Services. @@ -59,7 +63,7 @@ export FEAST_HISTORICAL_SERVING_URL=${FEAST_IP}:32092 `netcat`, `telnet`, or even `curl` can be used to test whether all services are available and ports are open, but `grpc_cli` is the most powerful. It can be installed from [here](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md). -### Testing Connectivity From Feast Services: +#### Testing Connectivity From Feast Services: Use `grpc_cli` to test connetivity by listing the gRPC methods exposed by Feast services: @@ -79,7 +83,7 @@ grpc_cli ls ${FEAST_HISTORICAL_SERVING_URL} feast.serving.ServingService grpc_cli ls ${FEAST_ONLINE_SERVING_URL} feast.serving.ServingService ``` -## How can I print logs from the Feast Services? +### How can I print logs from the Feast Services? Feast will typically have three services that you need to monitor if something goes wrong. @@ -90,7 +94,7 @@ Feast will typically have three services that you need to monitor if something g In order to print the logs from these services, please run the commands below. -### Docker Compose +#### Docker Compose Use `docker-compose logs` to obtain Feast component logs: @@ -110,7 +114,7 @@ docker logs -f feast_historical_serving_1 docker logs -f feast_online_serving_1 ``` -### Google Kubernetes Engine +#### Google Kubernetes Engine Use `kubectl logs` to obtain Feast component logs: diff --git a/docs/advanced/upgrading.md b/docs/advanced/upgrading.md index 8e1c426584c..3c7b95d5441 100644 --- a/docs/advanced/upgrading.md +++ b/docs/advanced/upgrading.md @@ -1,8 +1,8 @@ # Upgrading Feast -## Migration v0.6 to v0.7 +### Migration from v0.6 to v0.7 -### Feast Core Validation changes +#### Feast Core Validation changes In v0.7, Feast Core no longer accepts starting with number \(0-9\) and using dash in names for: @@ -20,9 +20,9 @@ Feast now prevents feature sets from being applied if no store is subscribed to * Ensure that a store is configured to subscribe to the Feature Set before applying the Feature Set. -### Feast Core's Job Coordinator is now Feast Job Controller +#### Feast Core's Job Coordinator is now Feast Job Controller -In v0.7, Feast Core's Job Coordinator has been decoupled from Feast Core and runs as a separate Feast Job Controller application. See its [Configuration reference](../reference/configuration-reference/#2-feast-core-serving-and-job-controller) for how to configure Feast Job Controller. +In v0.7, Feast Core's Job Coordinator has been decoupled from Feast Core and runs as a separate Feast Job Controller application. See its [Configuration reference](../reference/configuration-reference.md#2-feast-core-serving-and-job-controller) for how to configure Feast Job Controller. **Ingestion Job API** @@ -42,15 +42,15 @@ Users of Ingestion Job via Python SDK \(ie `feast ingest-jobs list` or `client.s * `ingest_job()`methods only: Create a new separate [Job Controller client](https://github.com/feast-dev/feast/blob/master/sdk/python/feast/contrib/job_controller/client.py) to connect to the job controller and call `ingest_job()` methods using the new client. * Configure the Feast Job Controller endpoint url via `jobcontroller_url` config option. -### Configuration Properties Changes +#### Configuration Properties Changes * Rename `feast.jobs.consolidate-jobs-per-source property` to `feast.jobs.controller.consolidate-jobs-per-sources` * Rename`feast.security.authorization.options.subjectClaim` to `feast.security.authentication.options.subjectClaim` * Rename `feast.logging.audit.messageLoggingEnabled` to `feast.audit.messageLogging.enabled` -## Migration v0.5 to v0.6 +### Migration from v0.5 to v0.6 -### Database schema +#### Database schema In Release 0.6 we introduced [Flyway](https://flywaydb.org/) to handle schema migrations in PostgreSQL. Flyway is integrated into `core` and for now on all migrations will be run automatically on `core` start. It uses table `flyway_schema_history` in the same database \(also created automatically\) to keep track of already applied migrations. So no specific maintenance should be needed. @@ -94,9 +94,9 @@ Minor changes: has now `version` and `delivery_status`. -## Migrate v0.4 to v0.6 +### Migration from v0.4 to v0.6 -### Database +#### Database For all versions earlier than 0.5 seamless migration is not feasible due to earlier breaking changes and creation of new database will be required. diff --git a/docs/getting-help.md b/docs/community.md similarity index 70% rename from docs/getting-help.md rename to docs/community.md index e9ead181caa..a9ce14c3d95 100644 --- a/docs/getting-help.md +++ b/docs/community.md @@ -11,19 +11,26 @@ * User surveys and meeting minutes. * Slide decks of conferences our contributors have spoken at. * [Feast GitHub Repository](https://github.com/feast-dev/feast/): Find the complete Feast codebase on GitHub. +* [Feast Linux Foundation Wiki](https://wiki.lfaidata.foundation/display/FEAST/Feast+Home): Our LFAI wiki page contains links to resources for contributors and maintainers. ### How can I get help? -* **Slack:** Need to speak to a human? Come ask a question in our Slack channel \(link above\) +* **Slack:** Need to speak to a human? Come ask a question in our Slack channel \(link above\). * **GitHub Issues:** Found a bug or need a feature? [Create an issue on GitHub](https://github.com/feast-dev/feast/issues/new). * **StackOverflow:** Need to ask a question on how to use Feast? We also monitor and respond to [StackOverflow](https://stackoverflow.com/questions/tagged/feast). ### Community Call -We have a community call every 2 weeks. Alternating between two times: +We have a user and contributor community call every two weeks \(Asia & US friendly\). -* 11 am \(UTC + 8\) -* 5 pm \(UTC + 8\) +#### Frequency \(every 2 weeks\) -Join the [feast-dev](getting-help.md#feast-development) mailing list to receive a Google calendar invitation. +* **Asia \(UTC+08:00\):** Wednesday 10:00 am to 10:30 am. +* **US West Coast \(PT\):** Tuesday 18:00 pm to 18:30 pm. + +#### Links + +* Calendar: [Feast Community Calendar \(Linux Foundation\)](https://wiki.lfaidata.foundation/pages/viewpage.action?pageId=30408973) +* Zoom: [https://zoom.us/j/6325193230](https://zoom.us/j/6325193230) +* Meeting notes: [https://bit.ly/feast-notes](https://bit.ly/feast-notes%20) diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index eab6941d8e7..2ace340063c 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -1,36 +1,51 @@ # Architecture -![Feast high-level flow](../.gitbook/assets/blank-diagram-4.svg) - -### **Feast Core** - -Feast Core is the central management service of a Feast deployment. It's role is to: - -* Allow users to create [entities](entities.md). -* Allow users to create features through the creation of [feature tables](feature-tables.md). -* Act as a source of truth and central registry of feature tables. - -### **Feast Ingestion** - -Before you ingest data into Feast, first register one or more entity, then register feature tables. These [feature tables](feature-tables.md) tell Feast where to find their data and how to ingest it. The feature tables also describe the characteristics of the data for validation purposes. After a feature table is registered, you can start a Spark job to populate a store with data from the defined source in the feature table specification. - -To ensure stores are populated with data, you must publish the data to a [source](sources.md). Currently, Feast supports a few batch and stream sources. Feast users \(or pipelines\) ingest batch data through the [Feast Python SDK](../getting-started/connect-to-feast/python-sdk.md) using its `ingest()` method. The SDK publishes the data into the batch source specified for the feature table's batch source. - -Streaming systems can also ingest data into Feast. This is done by publishing to the correct stream source from the feature table specification in the expected format. The topic and brokers can be found on the feature table's stream source if specified during registration. - -### **Stores** - -Stores are nothing more than databases used to store feature data. Feast loads data into stores through an ingestion process, after which the data can be served through the [Feast Online Serving API](https://api.docs.feast.dev/grpc/feast.serving.pb.html). Stores are documented in the following section. - -### **Feast Online Serving** - -`Feast Online Serving` is the data-access layer through which end users and production systems retrieve feature data. Each `Serving` instance is backed by a [store](). - -Because Feast supports multiple store types \(online, historical\), multiple instances of a deployed `Feast Online Serving` is common: those for online serving and those for historical. This means Feast allows for any number of `Feast Online Serving` deployments, presenting the possibility to use a `Feast Online Serving` deployment per production system, with its own stores and population jobs. - -`Feast Online Serving` deployments subscribe to all feature data, consuming all features known to a `Feast Core` deployment. - -Feature retrieval \(and feature references\) are documented in more detail in subsequent sections. - -{% page-ref page="../user-guide/feature-retrieval.md" %} +![Feast high-level flow](https://lucid.app/publicSegments/view/04341727-9efb-4433-8e7d-ed7588a3d594/image.png) + +### Sequence description + +1. **Log Raw Events:** Production backend applications are configured to emit internal state changes as events to a stream. +2. **Create Stream Features:** Stream processing systems like Flink, Spark, and Beam are used to transform and refine events and to produce features that are logged back to the stream. +3. **Log Streaming Features:** Both raw and refined events are logged into a data lake or batch storage location. +4. **Create Batch Features:** ELT/ETL systems like Spark and SQL are used to transform data in the batch store. +5. **Define and Ingest Features:** The Feast user defines [feature tables](feature-tables.md) based on the features available in batch and streaming sources and publish these definitions to Feast Core. +6. **Poll Feature Definitions:** The Feast Job Service polls for new or changed feature definitions. +7. **Start Ingestion Jobs:** Every new feature table definition results in a new ingestion job being provisioned \(see limitations\). +8. **Batch Ingestion:** Batch ingestion jobs are short-lived jobs that load data from batch sources into either an offline or online store \(see limitations\). +9. **Stream Ingestion:** Streaming ingestion jobs are long-lived jobs that load data from stream sources into online stores. A stream source and batch source on a feature table must have the same features/fields. +10. **Model Training:** A model training pipeline is launched. It uses the Feast Python SDK to retrieve a training dataset and trains a model. +11. **Get Historical Features:** Feast exports a point-in-time correct training dataset based on the list of features and entity DataFrame provided by the model training pipeline. +12. **Deploy Model:** The trained model binary \(and list of features\) are deployed into a model serving system. +13. **Get Prediction:** A backend system makes a request for a prediction from the model serving service. +14. **Retrieve Online Features:** The model serving service makes a request to the Feast Online Serving service for online features using a Feast SDK. +15. **Return Prediction:** The model serving service makes a prediction using the returned features and returns the outcome. + +{% hint style="warning" %} +Limitations + +* Feast 0.8 has no offline store. Batch retrieval is direct from source. We plan to implement an optional offline store in Feast 0.9 +* Only Redis is supported for online storage. +* Batch ingestion jobs must be triggered from your own scheduler like Airflow. Streaming ingestion jobs are automatically launched by the Feast Job Service. +{% endhint %} + +### Components: + +A complete Feast deployment contains the following components: + +* **Feast Core:** Acts as the central registry for feature and entity definitions in Feast. +* **Feast Job Service:** Manages data processing jobs that load data from sources into stores, and jobs that export training datasets. +* **Feast Online Serving:** Provides low-latency access to feature values in an online store. +* **Feast Python SDK:** The primary user facing SDK. Used to: + * Manage feature definitions with Feast Core. + * Launch jobs through the Feast Job Service. + * Retrieve training datasets. + * Retrieve online features. +* **Online Store:** The online store is a database that stores only the latest feature values for each entity entity. The online store can be populated by either batch ingestion jobs \(in the case the user has no streaming source\), or can be populated by a streaming ingestion job from a streaming source. Feast Online Serving looks up feature values from the online store. +* **Offline Store:** The offline store persists batch data that has been ingested into Feast. This data is used for producing training datasets. + +Please see the [configuration reference](../reference/configuration-reference.md#overview) for more details on configuring these components. + +{% hint style="info" %} +Java and Go SDKs are also available for online feature retrieval. See [API Reference](../reference/api/). +{% endhint %} diff --git a/docs/concepts/concepts.md b/docs/concepts/concepts.md deleted file mode 100644 index e54bd488d6a..00000000000 --- a/docs/concepts/concepts.md +++ /dev/null @@ -1,124 +0,0 @@ -# Concepts - -## Architecture - -![Logical diagram of a typical Feast deployment](../.gitbook/assets/basic-architecture-diagram%20%282%29.svg) - -The core components of a Feast deployment are - -* **Feast Core:** Feast Core is a centralized service that acts as the authority on features within an organization. Typically there is only one "Core" deployment per organization, with all feature management happening through it. -* **Feast Ingestion Jobs:** Feast ingestion jobs retrieve feature data from user defined data sources and populate serving stores with this feature data. These jobs are managed by Feast Core. Data can either be sources from existing sources \(like [Kafka](https://kafka.apache.org/)\), or it can be loaded into Feast through its API. -* **Feast Serving:** Feast Serving is the data access layer through which end users and production systems retrieve feature data. Each Serving store is backed by one or more databases. These databases are updated by the Feast ingestion jobs. There are two types of stores: batch and online. Batch stores hold large volumes historical data, while online stores only hold the latest feature values. - -## Data Model - -### Feature Set - -User data is typically in the form of dataframes, tables in data warehouses, or events on a stream. These data sources are loaded into Feast in order to serve features for model training or serving. - -Feature sets allow for groups of fields in these data sources to be ingested and stored together. This allows for efficient storage and logical namespacing of data. - -When data is loaded from these sources, each field in the feature set must be found in every record of the data source. Fields from these data sources must be either a timestamp, an entity, or a feature. - -{% hint style="info" %} -Feature sets are a grouping of feature sets based on how they are loaded into Feast. They ensure that data is efficiently stored during ingestion. Feature sets are not a grouping of features for retrieval of features. During retrieval it is possible to retrieve feature values from any number of feature sets. -{% endhint %} - -#### Customer Transactions Example - -Below is an example of a basic `customer transactions` feature set that has been exported to YAML: - -{% tabs %} -{% tab title="customer\_transactions\_feature\_set.yaml" %} -```yaml -name: customer_transactions -kind: feature_set -entities: -- name: customer_id - valueType: INT64 -features: -- name: daily_transactions - valueType: FLOAT -- name: total_transactions - valueType: FLOAT - maxAge: 3600s -``` -{% endtab %} -{% endtabs %} - -The dataframe below \(`customer_data.csv`\) contains the features and entities of the above feature set - -| datetime | customer\_id | daily\_transactions | total\_tra**nsactions** | -| :--- | :--- | :--- | :--- | -| 2019-01-01 01:00:00 | 20001 | 5.0 | 14.0 | -| 2019-01-01 01:00:00 | 20002 | 2.6 | 43.0 | -| 2019-01-01 01:00:00 | 20003 | 4.1 | 154.0 | -| 2019-01-01 01:00:00 | 20004 | 3.4 | 74.0 | - -In order to ingest feature data into Feast for this specific feature set: - -```python -# Load dataframe -customer_df = pd.read_csv("customer_data.csv") - -# Create feature set from YAML (using YAML is optional) -cust_trans_fs = FeatureSet.from_yaml("customer_transactions_feature_set.yaml") - -# Load feature data into Feast for this specific feature set -client.ingest(cust_trans_fs, customer_data) -``` - -### Feature - -A feature is an individual measurable property or characteristic of a phenomenon being observed. Features are the most important concepts within a feature store. Feature data is used both as input to models during training and when models are served in production. - -In the context of Feast, features are values that are associated with either one or more entities over time. In Feast, these values are either primitives or lists of primitives. Each feature can also have additional information attached to it. For example whether it is a categorical feature or numerical. - -{% hint style="info" %} -Features in Feast are defined within Feature Sets and are not treated as standalone concepts. -{% endhint %} - -### Entity - -An entity type is any object in an organization that needs to be modeled and on which information should be stored. Entity types are usually recognizable concepts, either concrete or abstract, such as persons, places, things, or events which have relevance to the modeled system. - -An entity is an instance of an entity type. - -* Examples of entity types in the context of ride-hailing and food delivery: `customer`, `order`, `driver`, `restaurant`, `dish`, `area`. -* A specific driver, for example a driver with ID `D011234` would be an entity of the entity type `driver` - -An entity is the object on which features are observed. For example we could have a feature `total_trips_24h` on the driver `D01123` with a feature value of `11`. - -In the context of Feast, entities are important because they are used as keys when looking up feature values. Entities are also used when joining feature values between different feature sets in order to build one large data set to train a model, or to serve a model. - -{% hint style="info" %} -Entities in Feast are defined within Feature Sets and are not treated as standalone concepts. -{% endhint %} - -### Types - -Feast supports the following types for feature values - -* BYTES -* STRING -* INT32 -* INT64 -* DOUBLE -* FLOAT -* BOOL -* BYTES\_LIST -* STRING\_LIST -* INT32\_LIST -* INT64\_LIST -* DOUBLE\_LIST -* FLOAT\_LIST -* BOOL\_LIST - -## Glossary - -| Term | Description | -| :--- | :--- | -| Feast deployment | A complete Feast system as it is deployed. Consists out of a single Feast Core deployment and one or more Feast Serving deployments. | -| Feast Core | The centralized service which acts as a registry and authority of features. Organizations should only deploy a single Feast Core instance. Feast Core also manages the ingestion of feature data and population of Feast Serving data stores. | -| Feast Serving | Feast Serving is a service used to access both online and batch feature data. Feast Serving deployments are backed by one or more databases. | - diff --git a/docs/concepts/entities.md b/docs/concepts/entities.md index 3691f711431..7969f90d8ce 100644 --- a/docs/concepts/entities.md +++ b/docs/concepts/entities.md @@ -2,40 +2,40 @@ ### Overview -An entity is any domain object that can be modelled and about which information can be stored. Entities are usually recognisable concepts, either concrete or abstract, such as persons, places, things, or events which have relevance to the modelled system. +An entity is any domain object that can be modeled and about which information can be stored. Entities are usually recognizable concepts, either concrete or abstract, such as persons, places, things, or events. -* Examples of entity types in the context of ride-hailing and food delivery: `customer`, `order`, `driver`, `restaurant`, `dish`, `area`. -* A specific driver, for example a driver with ID `D011234` would be an entity of the entity type `driver` +Examples of entities in the context of ride-hailing and food delivery: `customer`, `order`, `driver`, `restaurant`, `dish`, `area`. -An entity is the domain object on which features are observed. For example, we could have a feature `total_trips_24h` for driver `D011234` with a feature value of `11`. +Entities are important in the context of feature stores since features are always properties of a specific entity. For example, we could have a feature `total_trips_24h` for driver `D011234` with a feature value of `11`. -Entities are important for Feast because they are used as keys when searching for feature values. Entities are also used when joining feature values from different feature tables to build a large data set that is used to train or serve models. +Feast uses entities in the following way: + +* Entities serve as the keys used to look up features for producing training datasets and online feature values. +* Entities serve as a natural grouping of features in a feature table. A feature table must belong to an entity \(which could be a composite entity\) ### Structure of an Entity When creating an entity specification, consider the following fields: -* **name**: Name of the entity -* **description**: Description of the entity -* **value\_type**: Value type of the entity -* **labels**: User-defined metadata +* **Name**: Name of the entity +* **Description**: Description of the entity +* **Value Type**: Value type of the entity. Feast will attempt to coerce entity columns in your data sources into this type. +* **Labels**: Labels are maps that allow users to attach their own metadata to entities A valid entity specification is shown below: ```python -from feast import Entity, ValueType - -# Create a customer entity customer = Entity( - "customer_id", - "Customer id for ride customer", - ValueType.INT64 + name="customer_id", + description="Customer id for ride customer", + value_type=ValueType.INT64, + labels={} ) ``` ### Working with an Entity -Creating an Entity: +#### Creating an Entity: ```python # Create a customer entity @@ -43,7 +43,7 @@ customer_entity = Entity(name="customer_id", description="ID of car customer") client.apply_entity(customer_entity) ``` -Updating an Entity: +#### Updating an Entity: ```python # Update a customer entity @@ -56,12 +56,9 @@ Permitted changes include: * The entity's description and labels -{% hint style="warning" %} -You **cannot** change the following: - -* Project or name of an entity -* Types of entity -{% endhint %} +The following changes are note permitted: -Visit [EntitySpec](https://api.docs.feast.dev/grpc/feast.core.pb.html#EntitySpecV2) for the entity-specification API. +* Project +* Name of an entity +* Type diff --git a/docs/concepts/feature-references.md b/docs/concepts/feature-references.md deleted file mode 100644 index 4f712a24c45..00000000000 --- a/docs/concepts/feature-references.md +++ /dev/null @@ -1,36 +0,0 @@ -# Feature References - -## Overview - -In Feast, each feature can be uniquely addressed through a feature reference. A feature reference is composed of the following components: - -* Feature Table name -* Feature name - -## Structure of a Feature Reference - -A string based feature reference takes on the following format: - -`:` - -```python -# Feature references -feature_refs = [ - "driver_trips:average_daily_rides", - "driver_trips:maximum_daily_rides", - "driver_trips:rating", -] -``` - -Feature references only apply to a single `project`. Features cannot be retrieved across projects in a single request. - -## Working with a Feature Reference - -#### Feature Retrieval - -Feature retrieval \(or serving\) is the process of retrieving either historical features or online features from Feast, for the purposes of training or serving a model. - -Feast attempts to unify the process of retrieving features in both the historical and online case. It does this through the creation of feature references. One of the major advantages of using Feast is that you have a single semantic reference to a feature. These feature references can then be stored alongside your model and loaded into a serving layer where it can be used for online feature retrieval. - -More information about how to perform feature retrieval for historical and online features can be found in the sections under **User Guide**. - diff --git a/docs/concepts/feature-tables.md b/docs/concepts/feature-tables.md index 470be7a755e..92eb9963f62 100644 --- a/docs/concepts/feature-tables.md +++ b/docs/concepts/feature-tables.md @@ -6,33 +6,25 @@ Feature tables are both a schema and a logical means of grouping features, data Feature tables serve the following purposes: -* They are a means for defining the location and properties of data [sources](sources.md). -* They are used to create within Feast a database-level structure for the storage of feature values. -* The data sources described within feature tables enable Feast to ingest and store features within Feast. -* They ensure data is efficiently stored during [ingestion](../user-guide/data-ingestion.md). +* Feature tables are a means for defining the location and properties of data [sources](sources.md). +* Feature tables are used to create within Feast a database-level structure for the storage of feature values. +* The data sources described within feature tables allow Feast to find and ingest feature data into stores within Feast. +* Feature tables ensure data is efficiently stored during [ingestion](../user-guide/define-and-ingest-features.md) by providing a grouping mechanism of features values that occur on the same event timestamp. {% hint style="info" %} -Feast does not yet apply feature transformations. Transformations are currently expected to happen before data is ingested into Feast. The data sources described within feature tables should reference feature values in their already computed form. +Feast does not yet apply feature transformations. Transformations are currently expected to happen before data is ingested into Feast. The data sources described within feature tables should reference feature values in their already transformed form. {% endhint %} ### Features -A feature is an individual measurable property or characteristic of an observable phenomenon. For example, in a bank, a feature could be `total_foreign_transactions_24h` for a specific class of credit cards the bank issues. Feature data is the input both for training models, and for models served in production. +A feature is an individual measurable property observed on an entity. For example the amount of transactions \(feature\) a customer \(entity\) has completed. Features are used for both model training and scoring \(batch, online\). -{% hint style="info" %} -Features are the most important concepts within a feature store. -{% endhint %} - -In Feast, features are values that are associated with one or more [entities](entities.md). These values are either primitives or lists of primitives. Each feature can also have additional information attached to it. - -You define a feature by providing a name and value type. In our example, we use a name and value type that might be used in a ride-hailing company: +Features are defined as part of feature tables. Since Feast does not apply transformations, a feature is basically a schema that only contains a name and a type: ```python avg_daily_ride = Feature("average_daily_rides", ValueType.FLOAT) ``` -Features act purely as a schema within feature tables. Feature tables and features act as normal database tables and columns. - Visit [FeatureSpec](https://api.docs.feast.dev/grpc/feast.core.pb.html#FeatureSpecV2) for the complete feature specification API. ## Structure of a Feature Table @@ -41,20 +33,20 @@ Feature tables contain the following fields: * **Name:** Name of feature table. This name must be unique within a project. * **Entities:** List of [entities](entities.md) to associate with the features defined in this feature table. Entities are used as lookup keys when retrieving features from a feature table. -* **Features:** List of features within this feature table. +* **Features:** List of features within a feature table. * **Labels:** Labels are arbitrary key-value properties that can be defined by users. -* **Max age:** Max age affect the retrieval of features from a feature table. Age is measured as the duration of time between the event timestamp of a feature and the lookup time on an entity key used to retrieve the feature. Feature values outside max age will be returned as unset values. Max age allows for eviction of keys from online stores and limits the amount of scanning for historical feature values during retrieval. -* **Batch Source:** The batch data source from which you can ingest feature values into Feast. Visit [Sources](sources.md) to learn more about them. -* **Stream Source:** The streaming data source from which you can ingest streaming feature values into Feast. Visit [Sources](sources.md) to learn more about them. +* **Max age:** Max age affect the retrieval of features from a feature table. Age is measured as the duration of time between the event timestamp of a feature and the lookup time on an [entity key](glossary.md#entity-key) used to retrieve the feature. Feature values outside max age will be returned as unset values. Max age allows for eviction of keys from online stores and limits the amount of historical scanning required for historical feature values during retrieval. +* **Batch Source:** The batch data source from which Feast will ingest feature values into stores. This can either be used to back-fill stores before switching over to a streaming source, or it can be used as the primary source of data for a feature table. Visit [Sources](sources.md) to learn more about batch sources. +* **Stream Source:** The streaming data source from which you can ingest streaming feature values into Feast. Streaming sources must be paired with a batch source containing the same feature values. A streaming source is only used to populate online stores. The batch equivalent source that is paired with a streaming source is used during the generation of historical feature datasets. Visit [Sources](sources.md) to learn more about stream sources. -Here is a ride-hailing example of a valid feature-table specification: +Here is a ride-hailing example of a valid feature table specification: {% tabs %} {% tab title="driver\_trips\_feature\_table.py" %} ```python from feast import BigQuerySource, FeatureTable, Feature, ValueType +from google.protobuf.duration_pb2 import Duration -# Create an empty feature table driver_ft = FeatureTable( name="driver_trips", entities=["driver_id"], @@ -62,7 +54,7 @@ driver_ft = FeatureTable( Feature("average_daily_rides", ValueType.FLOAT), Feature("rating", ValueType.FLOAT) ], - max_age=14400, + max_age=Duration(seconds=3600), labels={ "team": "driver_matching" }, @@ -79,13 +71,11 @@ driver_ft = FeatureTable( {% endtab %} {% endtabs %} -When you register a feature table, at a minimum specify a batch source to populate the feature table. Stream sources are optional. They are used to stream feature values into online stores. - By default, Feast assumes that features specified in the feature-table specification corresponds one-to-one to the fields found in the sources. All features defined in a feature table should be available in the defined sources. -However, if the names of the fields in the batch source are different from the names of features, you can use `field_mappings` to ensure the names correspond. +Field mappings can be used to map features defined in Feast to fields as they occur in data sources. -In the example feature-specification table above, we use `field_mappings` to ensure the field named `rating` in the batch source is mapped to the feature named `driver_rating`. +In the example feature-specification table above, we use field mappings to ensure the feature named `rating` in the batch source is mapped to the field named `driver_rating`. ## Working with a Feature Table @@ -98,8 +88,6 @@ client.apply_feature_table(driver_ft) #### Updating a Feature Table -Feature table definitions may need to change over time to reflect more accurately your use case. In our ride-hailing example below, we update the max age: - ```python driver_ft = FeatureTable() @@ -110,17 +98,17 @@ driver_ft.labels = {"team": "marketplace"} client.apply_feature_table(driver_ft) ``` -Feast currently supports the following changes to feature tables: +#### Feast currently supports the following changes to feature tables: * Adding new features. -* Deleting existing features -* Changing the feature table's source, max age, and labels. +* Removing features. +* Updating source, max age, and labels. {% hint style="warning" %} Deleted features are archived, rather than removed completely. Importantly, new features cannot use the names of these deleted features. {% endhint %} -Feast currently does not support the following changes to feature tables: +#### Feast currently does not support the following changes to feature tables: * Changes to the project or name of a feature table. * Changes to entities related to a feature table. diff --git a/docs/concepts/glossary.md b/docs/concepts/glossary.md new file mode 100644 index 00000000000..2c521e57ac8 --- /dev/null +++ b/docs/concepts/glossary.md @@ -0,0 +1,36 @@ +# Glossary + +#### **Entity key** + +The combination of entities that uniquely identify a row. For example a feature table with the composite entity of \(customer, country\) might have an entity key of \(1001, 5\). They key is used during lookups of feature values and for deduplicating historical rows. + +#### Entity timestamp + +The timestamp on which an event occurred. The entity timestamp could describe the event time at which features were calculated, or it could describe the event timestamps at which outcomes were observed. + +Entity timestamps are commonly found on the entity dataframe and associated with the target variable \(outcome\) that needs to be predicted. These timestamps are the target on which point-in-time joins should be made. + +#### Entity rows + +A combination of a single [entity key ](glossary.md#entity-key)and a single [entity timestamp](glossary.md#entity-timestamp). + +#### Entity dataframe + +A collection of [entity rows](glossary.md#entity-rows). This dataframe is enriched with feature values before being used for model training. + +#### Feature References + +Feature references uniquely identify feature values throughout Feast. Feature references can either be defined as objects or as strings. + +The structure of a feature reference in string form is as follows: + +`feature_table:feature` + +Example: + +`drivers_stream:unique_drivers` + +Feature references are unique within a project. It is not possible to reference \(or retrieve\) features from multiple projects at the same time. + +\*\*\*\* + diff --git a/docs/concepts/overview.md b/docs/concepts/overview.md index 0becb135531..536c064cb04 100644 --- a/docs/concepts/overview.md +++ b/docs/concepts/overview.md @@ -1,64 +1,21 @@ # Overview -## Using Feast - -Feast is the bridge between your ML models and data. Feast enables your team to: - -1. Create feature specifications to manage features, and load data that you want managed -2. Retrieve historical features for training models -3. Retrieve online features for serving models - -{% hint style="info" %} -Feast currently does not apply feature transformations to data. -{% endhint %} - -### Creating and managing features - -Feature creators model the data within their organization into Feast through the creation of [feature tables](feature-tables.md). - -Feature tables are both a schema and a means of identifying data sources for features. They allow Feast to know how to interpret your data, and optionally where to find it. Feature tables allow you to define domain [entities](entities.md) along with the features that are available on these entities. Feature tables also allow you to define schemas that describe properties of the respective data, which in turn can be used for validation purposes. - -After you register a feature table, Feast creates the relevant schemas to store feature data within its feature [stores](). These stores are then populated by [ingestion jobs](../user-guide/data-ingestion.md) that ingest data from data [sources](sources.md). The now data-rich stores enable Feast to provide access to features for training and serving. Alternatively, you can [ingest](../user-guide/data-ingestion.md) data into Feast instead of using an external source. - -Visit [feature tables](feature-tables.md) to learn more about them. - -### Retrieving historical features during training - -Historical retrieval uses [feature references](../user-guide/feature-retrieval.md) through the[ Feast SDK](https://api.docs.feast.dev/python/) to retrieve historical features. For historical serving, Feast requires that you provide the entities and timestamps for the corresponding feature data. Feast produces a point-in-time correct dataset using the requested features. These features can be requested from an unlimited number of feature sets. - -{% hint style="info" %} -For historical serving, Feast stores all historical values. -{% endhint %} - -Stores supported: [BigQuery](https://cloud.google.com/bigquery) - -### Retrieving online features during serving - -Online retrieval uses feature references through the [Feast Online Serving API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) to retrieve online features. Online serving allows for very low latency requests to feature data at very high throughput. - -{% hint style="info" %} -During online serving, Feast stores **only** the latest values for each feature. -{% endhint %} - -Stores supported: [Redis](https://redis.io/), [Redis Cluster](https://redis.io/topics/cluster-tutorial) - -## Concept Hierarchy - -![](../.gitbook/assets/concept_hierarchy.png) - -Feast resources are arranged in the above hierarchy, with projects grouping one or more [entities](entities.md), which in turn groups [feature tables](feature-tables.md). These feature tables consist of [data sources](sources.md) and multiple features. +### Concepts -The logical grouping of these resources is important for namespacing and retrieval. Retrieval requires referencing individual features through feature references. These references uniquely identify a feature within a Feast deployment. +[Entities](entities.md) are objects in an organization like customers, transactions, and drivers, products, etc. -### Concepts +[Sources](sources.md) are external sources of data where feature data can be found. -[Entities](entities.md) are objects in an organization that model a specific construct. Examples of these include customers, transactions, and drivers. +[Feature Tables](feature-tables.md) are objects that define logical groupings of features, data sources, and other related metadata. -[Sources](sources.md) are either internal or external data sources where feature data can be found. +### Concept Hierarchy -[Feature Tables](feature-tables.md) are schemas that define logical groupings of features, data sources, and other related metadata. +![](../.gitbook/assets/image%20%284%29.png) -[Stores]() are databases that maintain feature data that gets served to models during training or inference. +Feast contains the following core concepts: -[Ingestion](../user-guide/data-ingestion.md) is the process of loading data into Feast. +* **Projects:** Serve as a top level namespace for all Feast resources. Each project is a completely independent environment in Feast. Users can only work in a single project at a time. +* **Entities:** Entities are the objects in an organization on which features occur. They map to your business domain \(users, products, transactions, locations\). +* **Feature Tables:** Defines a group of features that occur on a specific entity. +* **Features:** Individual feature within a feature table. diff --git a/docs/concepts/sources.md b/docs/concepts/sources.md index 1069e8dfaa7..e032b2f5950 100644 --- a/docs/concepts/sources.md +++ b/docs/concepts/sources.md @@ -2,25 +2,33 @@ ### Overview -A `source` is a data source that can be used to find feature data. Users define sources as part of [feature tables](feature-tables.md). Currently, Feast supports the following source types: +Sources are descriptions of external feature data and are registered to Feast as part of [feature tables](feature-tables.md). Once registered, Feast can ingest feature data from these sources into stores. -* Batch Source - * File - * [BigQuery](https://cloud.google.com/bigquery) -* Stream Source - * [Kafka](https://kafka.apache.org/) - * [Kinesis](https://aws.amazon.com/kinesis/) +Currently, Feast supports the following source types: -### Structure of a Source +#### Batch Source + +* File \(as in Spark\): Parquet and CSV files supported. +* BigQuery + +#### Stream Source + +* Kafka +* Kinesis -For both batch and stream sources, the following configurations are **necessary**: +The following encodings are supported on streams -* **created\_timestamp\_column**: Name of column containing timestamp when data is created. -* **event\_timestamp\_column**: Name of column containing timestamp when event data occurred. +* Avro +* Protobuf -When configuring data source options, see the [Feast Python API documentation](https://api.docs.feast.dev/python/) for more details. +### Structure of a Source + +For both batch and stream sources, the following configurations are necessary: -Some valid source specifications are shown below: +* **Event timestamp column**: Name of column containing timestamp when event data occurred. Used during point-in-time join of feature values to [entity timestamps](glossary.md#entity-timestamp). +* **Created timestamp column**: Name of column containing timestamp when data is created. Used to deduplicate data when multiple copies of the same [entity key](glossary.md#entity-key) is ingested. + +Example data source specifications: {% tabs %} {% tab title="batch\_sources.py" %} @@ -30,7 +38,7 @@ from feast.data_format import ParquetFormat batch_file_source = FileSource( file_format=ParquetFormat(), - file_url="file://feast/*", + file_url="file:///feast/customer.parquet", event_timestamp_column="event_timestamp", created_timestamp_column="created_timestamp", ) @@ -55,15 +63,11 @@ stream_kafka_source = KafkaSource( The [Feast Python API documentation](https://api.docs.feast.dev/python/) provides more information about options to specify for the above sources. -{% hint style="info" %} -When creating a Feature Table for use in training datasets, specify a batch source already containing materialized data. -{% endhint %} - ### Working with a Source #### Creating a Source -Sources are required when specifying a [feature table](feature-tables.md): +Sources are defined as part of [feature tables](feature-tables.md): ```python batch_bigquery_source = BigQuerySource( diff --git a/docs/concepts/stores.md b/docs/concepts/stores.md new file mode 100644 index 00000000000..3695f6c37d0 --- /dev/null +++ b/docs/concepts/stores.md @@ -0,0 +1,26 @@ +# Stores + +In Feast, a store is a database that is populated with feature data that will ultimately be served to models. + +### Offline \(Historical\) Store + +The offline store maintains historical copies of feature values. These features are grouped and stored in feature tables. During retrieval of historical data, features are queries from these feature tables in order to produce training datasets. + +{% hint style="warning" %} +Feast 0.8 does not support offline storage. Support will be added in Feast 0.9. +{% endhint %} + +### Online Store + +The online store maintains only the latest values for a specific feature. + +* Feature values are stored based on their [entity keys](glossary.md#entity-key) +* Feast currently supports Redis as an online store. +* Online stores are meant for very high throughput writes from ingestion jobs and very low latency access to features during online serving. + +{% hint style="info" %} +Feast only supports a single online store in production +{% endhint %} + + + diff --git a/docs/contributing/adding-a-new-store-1.md b/docs/contributing/adding-a-new-store-1.md deleted file mode 100644 index 56c06b9b4b6..00000000000 --- a/docs/contributing/adding-a-new-store-1.md +++ /dev/null @@ -1,87 +0,0 @@ -# Adding a New Store - -The following guide will explain the process of adding a new store through the introduction of a storage connector. - -## 1. Storage API - -Feast has an external module where storage interfaces are defined: [Storage API](https://github.com/gojek/feast/tree/master/storage/api/src/main/java/feast/storage/api) - -Feast interacts with a store at three points . - -1. **During initialization:** Store configuration is loaded into memory by Feast Serving and synchronized with Feast Core -2. **During ingestion of feature data.** [writer interfaces](https://github.com/gojek/feast/tree/master/storage/api/src/main/java/feast/storage/api/writer) are used by the Apache Beam ingestion jobs in order to populate stores \(historical or online\). -3. **During retrieval of feature data:** [Retrieval interfaces](https://github.com/gojek/feast/tree/master/storage/api/src/main/java/feast/storage/api/retriever) are used by Feast Serving in order to read data from stores in order to create training datasets or to serve online data. - -All three of these components should be implemented in order to have a complete storage connector. - -## 2. Adding a Storage Connector - -### 2.1 Initialization and configuration - -Stores are configured in Feast Serving. Feast Serving publishes its store configuration to Feast Core, after which Feast Core can start ingestion/population jobs to populate it. - -Store configuration is always in the form of a map<String, String>. The keys and configuration for stores are defined in [protos](https://github.com/gojek/feast/blob/master/protos/feast/core/Store.proto). This must be added in order to define a new store - -Then the store must be configured to be loaded through Feast Serving. The above configuration is loaded through [FeastProperties.java](https://github.com/gojek/feast/blob/a1937c374a4e39b7a75d828e7b7c3b87a64d9d6e/serving/src/main/java/feast/serving/config/FeastProperties.java#L175). - -Once configuration is loaded, the store will then be instantiated. - -* Feast Core: The [StoreUtil.java](https://github.com/gojek/feast/blob/master/ingestion/src/main/java/feast/ingestion/utils/StoreUtil.java#L85) instantiates new stores for the purposes of feature ingestion. -* Feast Serving: The [ServingServiceConfig](https://github.com/gojek/feast/blob/a1937c374a4e39b7a75d828e7b7c3b87a64d9d6e/serving/src/main/java/feast/serving/config/ServingServiceConfig.java#L56) instantiates new stores for the purposes of retrieval - -{% hint style="info" %} -In the future we plan to provide a plugin interface for adding stores. -{% endhint %} - -### 2.2 Feature Ingestion \(Writer\) - -Feast creates and manages ingestion/population jobs that stream in data from upstream data sources. Currently Feast only supports Kafka as a data source, meaning these jobs are all long running. Batch ingestion \(from users\) results in data being pushed to Kafka topics after which they are picked up by these "population" jobs and written to stores. - -In order for ingestion to succeed, the destination store must be writable. This means that Feast must be able to create the appropriate tables/schemas in the store and also write data from the population job into the store. - -Currently Feast Core starts and manages these population jobs that ingest data into stores \(although we are planning to move this responsibility to the serving layer\). Feast Core starts an [Apache Beam](https://beam.apache.org/) job which synchronously runs migrations on the destination store and subsequently starts consuming [FeatureRows](https://github.com/gojek/feast/blob/master/protos/feast/types/FeatureRow.proto) from Kafka and writing it into stores using a [writer](https://github.com/gojek/feast/tree/master/storage/api/src/main/java/feast/storage/api/writer). - -Below is a "happy path" of a batch ingestion process which includes a blocking step at the Python SDK. - -![](https://user-images.githubusercontent.com/6728866/74807906-91e73c00-5324-11ea-8ba5-2b43c7c5282b.png) - - - -The complete ingestion flow is executed by a [FeatureSink](https://github.com/gojek/feast/blob/master/storage/api/src/main/java/feast/storage/api/writer/FeatureSink.java). Two methods should be implemented - -* [prepareWrite\(\)](https://github.com/gojek/feast/blob/a1937c374a4e39b7a75d828e7b7c3b87a64d9d6e/storage/api/src/main/java/feast/storage/api/writer/FeatureSink.java#L45): Sets up storage backend for writing/ingestion. This method will be called once during pipeline initialisation. Typically this is used to apply schemas. -* [writer\(\)](https://github.com/gojek/feast/blob/a1937c374a4e39b7a75d828e7b7c3b87a64d9d6e/storage/api/src/main/java/feast/storage/api/writer/FeatureSink.java#L53): Retrieves an Apache Beam PTransform that is used to write data to this store. - -### 2.2 Feature Serving \(Retriever\) - -Feast Serving can serve both historical/batch features and online features. Depending on the store that is being added, you should implement either a historical/batch store or an online storage. - -#### 2.2.1 Historical Serving - -The historical serving interface is defined through the [HistoricalRetriever](https://github.com/gojek/feast/blob/master/storage/api/src/main/java/feast/storage/api/retriever/HistoricalRetriever.java) interface. Historical retrieval is an asynchronous process. The client submits a request for a dataset to be produced, and polls until it is ready. - -![High-level flow for batch retrieval](https://user-images.githubusercontent.com/6728866/74797157-702a8c80-5305-11ea-8901-bf6f4eb075f9.png) - -The current implementation of batch retrieval starts and ends with a file \(dataset\) in a Google Cloud Storage bucket. The user ingests an entity dataset. This dataset is loaded into a store \(BigQuery0, joined to features in a point-in-time correct way, then exported again to the bucket. - -Additionally, we have also implemented a [batch retrieval method ](https://github.com/gojek/feast/blob/a1937c374a4e39b7a75d828e7b7c3b87a64d9d6e/sdk/python/feast/client.py#L509)in the Python SDK. Depending on the means through which this new store will export data, this client may have to change. At the very least it would change if Google Cloud Storage isn't used as the staging bucket. - -The means through which you implement the export/import of data into the store will depend on your store. - -#### 2.2.2 Online Serving - -In the case of online serving it is necessary to implement an [OnlineRetriever](https://github.com/gojek/feast/blob/master/storage/api/src/main/java/feast/storage/api/retriever/OnlineRetriever.java). This online retriever will read rows directly and synchronously from an online database. The exact encoding strategy you use to store your data in the store would be defined in the FeatureSink. The OnlineRetriever is expected to read and decode those rows. - -## 3. Storage Connectors Examples - -Feast currently provides support for the following storage types - -Historical storage - -* [BigQuery](https://github.com/gojek/feast/tree/master/storage/connectors/bigquery) - -Online storage - -* [Redis](https://github.com/gojek/feast/tree/master/storage/connectors/redis) -* [Redis Cluster](https://github.com/gojek/feast/tree/master/storage/connectors/rediscluster) - diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 2be097d6ffd..d9378ca6847 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -1,10 +1,10 @@ # Contribution Process -We use [RFCs](https://en.wikipedia.org/wiki/Request_for_Comments) and [GitHub issues](https://github.com/gojek/feast/issues) to communicate development ideas. The simplest way to contribute to Feast is to leave comments in our [RFCs](https://drive.google.com/drive/u/0/folders/1Lj1nIeRB868oZvKTPLYqAvKQ4O0BksjY) in the [Feast Google Drive](https://drive.google.com/drive/u/0/folders/0AAe8j7ZK3sxSUk9PVA) or our GitHub issues. You will need to join our [Google Group](../getting-help.md) in order to get access. +We use [RFCs](https://en.wikipedia.org/wiki/Request_for_Comments) and [GitHub issues](https://github.com/feast-dev/feast/issues) to communicate development ideas. The simplest way to contribute to Feast is to leave comments in our [RFCs](https://drive.google.com/drive/u/0/folders/1Lj1nIeRB868oZvKTPLYqAvKQ4O0BksjY) in the [Feast Google Drive](https://drive.google.com/drive/u/0/folders/0AAe8j7ZK3sxSUk9PVA) or our GitHub issues. You will need to join our [Google Group](../community.md) in order to get access. -We follow a process of [lazy consensus](http://community.apache.org/committers/lazyConsensus.html). If you believe you know what the project needs then just start development. If you are unsure about which direction to take with development then please communicate your ideas through a GitHub issue or through our [Slack Channel](../getting-help.md) before starting development. +We follow a process of [lazy consensus](http://community.apache.org/committers/lazyConsensus.html). If you believe you know what the project needs then just start development. If you are unsure about which direction to take with development then please communicate your ideas through a GitHub issue or through our [Slack Channel](../community.md) before starting development. -Please [submit a PR ](https://github.com/gojek/feast/pulls)to the master branch of the Feast repository once you are ready to submit your contribution. Code submission to Feast \(including submission from project maintainers\) require review and approval from maintainers or code owners. +Please [submit a PR ](https://github.com/feast-dev/feast/pulls)to the master branch of the Feast repository once you are ready to submit your contribution. Code submission to Feast \(including submission from project maintainers\) require review and approval from maintainers or code owners. PRs that are submitted by the general public need to be identified as `ok-to-test`. Once enabled, [Prow](https://github.com/kubernetes/test-infra/tree/master/prow) will run a range of tests to verify the submission, after which community members will help to review the pull request. diff --git a/docs/contributing/development-guide.md b/docs/contributing/development-guide.md index 07b1237b7b6..9c9ec2bdf51 100644 --- a/docs/contributing/development-guide.md +++ b/docs/contributing/development-guide.md @@ -108,10 +108,10 @@ Feast Serving has a dependency on Feast Core, thus always start Feast Core first ```bash # Start Feast Core locally -java -jar core/target/feast-core-0.8.1-SNAPSHOT-exec.jar +java -jar core/target/feast-core-0.8.0-exec.jar # Start Feast Serving locally -java -jar serving/target/feast-serving-0.8.1-SNAPSHOT-exec.jar +java -jar serving/target/feast-serving-0.8.0-exec.jar ``` Test whether Feast Core, Feast Serving are started and running correctly: @@ -122,8 +122,8 @@ feast version --core-url="localhost:6565" --serving-url="localhost:6566" ```javascript { - 'serving': {'url': 'localhost:6566', 'version': '0.8.1-SNAPSHOT'}, - 'core': {'url': 'localhost:6565', 'version': '0.8.1-SNAPSHOT'} + 'serving': {'url': 'localhost:6566', 'version': '0.8.0'}, + 'core': {'url': 'localhost:6565', 'version': '0.8.0'} } ``` diff --git a/docs/getting-started/connect-to-feast/README.md b/docs/getting-started/connect-to-feast/README.md index 214cf89fa3b..4333359f902 100644 --- a/docs/getting-started/connect-to-feast/README.md +++ b/docs/getting-started/connect-to-feast/README.md @@ -19,7 +19,7 @@ The Feast CLI is a command line implementation of the Feast Python SDK. * Ingest data into Feast * Manage ingestion jobs -{% page-ref page="connecting-to-feast.md" %} +{% page-ref page="feast-cli.md" %} ### Online Serving Clients diff --git a/docs/getting-started/connect-to-feast/connecting-to-feast.md b/docs/getting-started/connect-to-feast/feast-cli.md similarity index 100% rename from docs/getting-started/connect-to-feast/connecting-to-feast.md rename to docs/getting-started/connect-to-feast/feast-cli.md diff --git a/docs/getting-started/install-feast/kubernetes-with-helm.md b/docs/getting-started/install-feast/kubernetes-with-helm.md index 1f38a8ba50c..50fab75bd7f 100644 --- a/docs/getting-started/install-feast/kubernetes-with-helm.md +++ b/docs/getting-started/install-feast/kubernetes-with-helm.md @@ -64,5 +64,5 @@ You can now connect to the bundled Jupyter Notebook Server at `localhost:8888` a * [Feast Concepts](../../concepts/overview.md) * [Feast Examples/Tutorials](https://github.com/feast-dev/feast/tree/master/examples) * [Feast Helm Chart Documentation](https://github.com/feast-dev/feast/blob/master/infra/charts/feast/README.md) -* [Configuring Feast components](../../reference/configuration-reference/) +* [Configuring Feast components](../../reference/configuration-reference.md) diff --git a/docs/getting-started/learn-feast.md b/docs/getting-started/learn-feast.md index 004b1075aaa..10f2eb6d291 100644 --- a/docs/getting-started/learn-feast.md +++ b/docs/getting-started/learn-feast.md @@ -3,7 +3,7 @@ Explore the following resources to learn more about Feast: * [Concepts](../) describes all important Feast API concepts. -* [User guide](../user-guide/data-ingestion.md) provides guidance on completing Feast workflows. +* [User guide](../user-guide/define-and-ingest-features.md) provides guidance on completing Feast workflows. * [Examples](https://github.com/feast-dev/feast/tree/master/examples) contains Jupyter notebooks that you can run on your Feast deployment. * [Advanced](../advanced/troubleshooting.md) contains information about both advanced and operational aspects of Feast. * [Reference](../reference/api/) contains detailed API and design documents for advanced users. diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md deleted file mode 100644 index ce315dbf27f..00000000000 --- a/docs/getting-started/quickstart.md +++ /dev/null @@ -1,56 +0,0 @@ -# Quickstart - -## Overview - -This guide will give a walkthrough on deploying Feast using Docker Compose, which allows the user to quickly explore the functionalities in Feast with minimal infrastructure setup. It includes a built in Jupyter Notebook Server that is preloaded with PySpark and Feast SDK, as well as Feast example notebooks to get you started. - -## 0. Requirements - -* [Docker Compose](https://docs.docker.com/compose/install/) should be installed. -* Optional dependancies: - * a [GCP service account](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) that has access to [Google Cloud Storage](https://cloud.google.com/storage). - -## 1. Set up environment - -Clone the latest stable version of the [Feast repository](https://github.com/gojek/feast/) and setup before we deploy: - -```text -git clone https://github.com/feast-dev/feast.git -cd feast/infra/docker-compose -cp .env.sample .env -``` - -## 2. Start Feast Services - -Start the Feast services. Make sure that the following ports are free on the host machines: 6565, 6566, 8888, 9094, 5432. Alternatively, change the port mapping to use a different port on the host. - -```javascript -docker-compose up -d -``` - -{% hint style="info" %} -The Docker Compose deployment will take some time fully startup: - -* During this time Feast Serving container may restart, which should be automatically corrected after Feast Core is up and ready. -* If container restarts do not stop after 10 minutes, check the docker compose log to see if there is any error that prevents Feast Core from starting successfully. -{% endhint %} - -Once deployed, you should be able to connect at `localhost:8888` to the bundled Jupyter Notebook Server and follow the example notebooks: - -{% embed url="http://localhost:8888/tree?" caption="" %} - -## 3. Optional dependancies - -### 3.1 Set up Google Cloud Platform - -The example Jupyter notebook does not require any GCP dependancies by default. If you would like to modify the example such that a GCP service is required \(eg. Google Cloud Storage\), you would need to set up a [service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts) that is associated with the notebook. Make sure that the service account has sufficient privileges to access the required GCP services. - -Once the service account is created, download the associated JSON key file and copy the file to the path configured in `.env` , under `GCP_SERVICE_ACCOUNT` . - -## 4. Further Reading - -* [Feast Concepts](../concepts/overview.md) -* [Feast Examples/Tutorials](https://github.com/feast-dev/feast/tree/master/examples) -* [Configuring Feast Components](../reference/configuration-reference.md) -* [Configuration Reference](https://app.gitbook.com/@feast/s/docs/v/master/reference/configuration-reference) - diff --git a/docs/installation/docker-compose.md b/docs/installation/docker-compose.md deleted file mode 100644 index 3c8c50862e3..00000000000 --- a/docs/installation/docker-compose.md +++ /dev/null @@ -1,112 +0,0 @@ -# Docker Compose - -### Overview - -This guide will bring Feast up using Docker Compose. This will allow you to: - -* Create, register, and manage feature sets -* Ingest feature data into Feast -* Retrieve features for online serving -* Retrieve features for batch serving \(only if using Google Cloud Platform\) - -This guide is split into three parts: - -1. Setting up your environment -2. Starting Feast with **online serving support only** \(does not require GCP\). -3. Starting Feast with support for **both online and batch** serving \(requires GCP\) - -{% hint style="info" %} -The docker compose setup uses Direct Runner for the Apache Beam jobs that populate data stores. Running Beam with the Direct Runner means it does not need a dedicated runner like Flink or Dataflow, but this comes at the cost of performance. We recommend the use of a dedicated runner when running Feast with very large workloads. -{% endhint %} - -### 0. Requirements - -* [Docker compose](https://docs.docker.com/compose/install/) must be installed. -* The following list of TCP ports must be free: - * 6565, 6566, 8888, and 9094. - * Alternatively it is possible to modify port mappings in `/docker-compose/docker-compose.yml`. -* \(for batch serving only\) For batch serving you will also need a [GCP service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) that has access to [Google Cloud Storage](https://cloud.google.com/storage) and [BigQuery](https://cloud.google.com/bigquery). -* \(for batch serving only\) [Google Cloud SDK ](https://cloud.google.com/sdk/install)installed, authenticated, and configured to the project you will use. - -## 1. Set up environment - -Clone the [Feast repository](https://github.com/feast-dev/feast/) and navigate to the `docker-compose` sub-directory: - -```bash -git clone https://github.com/feast-dev/feast.git && \ -cd feast && export FEAST_HOME_DIR=$(pwd) && \ -cd infra/docker-compose -``` - -Make a copy of the `.env.sample` file: - -```bash -cp .env.sample .env -``` - -## 2. Docker Compose for Online Serving Only - -### 2.1 Start Feast \(without batch retrieval support\) - -If you do not require batch serving, then its possible to simply bring up Feast: - -```javascript -docker-compose up -d -``` - -A Jupyter Notebook environment is now available to use Feast: - -[http://localhost:8888/tree/feast/examples](http://localhost:8888/tree/feast/examples) - -## 3. Docker Compose for Online and Batch Serving - -{% hint style="info" %} -Batch serving requires Google Cloud Storage to function, specifically Google Cloud Storage \(GCP\) and BigQuery. -{% endhint %} - -### 3.1 Set up Google Cloud Platform - -Create a [service account ](https://cloud.google.com/iam/docs/creating-managing-service-accounts)from the GCP console and copy it to the `infra/docker-compose/gcp-service-accounts` folder: - -```javascript -cp my-service-account.json ${FEAST_HOME_DIR}/infra/docker-compose/gcp-service-accounts -``` - -Create a Google Cloud Storage bucket. Make sure that your service account above has read/write permissions to this bucket: - -```bash -gsutil mb gs://my-feast-staging-bucket -``` - -### 3.2 Configure .env - -Configure the `.env` file based on your environment. At the very least you have to modify: - -| Parameter | Description | -| :--- | :--- | -| FEAST\_CORE\_GCP\_SERVICE\_ACCOUNT\_KEY | This should be your service account file name, for example `key.json`. | -| FEAST\_HISTORICAL\_SERVING\_GCP\_SERVICE\_ACCOUNT\_KEY | This should be your service account file name, for example `key.json` | -| FEAST\_JUPYTER\_GCP\_SERVICE\_ACCOUNT\_KEY | This should be your service account file name, for example `key.json` | -| FEAST\_JOB\_STAGING\_LOCATION | Google Cloud Storage bucket that Feast will use to stage data exports and batch retrieval requests, for example `gs://your-gcs-bucket/staging` | - -### 3.3 Configure .bq-store.yml - -We will also need to configure the `bq-store.yml` file inside `infra/docker-compose/serving/` to configure the BigQuery storage configuration as well as the feature sets that the store subscribes to. At a minimum you will need to set: - -| Parameter | Description | -| :--- | :--- | -| bigquery\_config.project\_id | This is you [GCP project Id](https://cloud.google.com/resource-manager/docs/creating-managing-projects). | -| bigquery\_config.dataset\_id | This is the name of the BigQuery dataset that tables will be created in. Each feature set will have one table in BigQuery. | - -### 3.4 Start Feast \(with batch retrieval support\) - -Start Feast: - -```javascript -docker-compose up -d -``` - -A Jupyter Notebook environment is now available to use Feast: - -[http://localhost:8888/tree/feast/examples](http://localhost:8888/tree/feast/examples) - diff --git a/docs/installation/gke.md b/docs/installation/gke.md deleted file mode 100644 index 66041887786..00000000000 --- a/docs/installation/gke.md +++ /dev/null @@ -1,211 +0,0 @@ -# Google Kubernetes Engine \(GKE\) - -### Overview - -This guide will install Feast into a Kubernetes cluster on GCP. It assumes that all of your services will run within a single Kubernetes cluster. Once Feast is installed you will be able to: - -* Define and register features. -* Load feature data from both batch and streaming sources. -* Retrieve features for model training. -* Retrieve features for online serving. - -{% hint style="info" %} -This guide requires [Google Cloud Platform](https://cloud.google.com/) for installation. - -* [BigQuery](https://cloud.google.com/bigquery/) is used for storing historical features. -* [Google Cloud Storage](https://cloud.google.com/storage/) is used for intermediate data storage. -{% endhint %} - -## 0. Requirements - -1. [Google Cloud SDK ](https://cloud.google.com/sdk/install)installed, authenticated, and configured to the project you will use. -2. [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed. -3. [Helm](https://helm.sh/3) \(2.16.0 or greater\) installed on your local machine with Tiller installed in your cluster. Helm 3 has not been tested yet. - -## 1. Set up GCP - -First define the environmental variables that we will use throughout this installation. Please customize these to reflect your environment. - -```bash -export FEAST_GCP_PROJECT_ID=my-gcp-project -export FEAST_GCP_REGION=us-central1 -export FEAST_GCP_ZONE=us-central1-a -export FEAST_BIGQUERY_DATASET_ID=feast -export FEAST_GCS_BUCKET=${FEAST_GCP_PROJECT_ID}_feast_bucket -export FEAST_GKE_CLUSTER_NAME=feast -export FEAST_SERVICE_ACCOUNT_NAME=feast-sa -``` - -Create a Google Cloud Storage bucket for Feast to stage batch data exports: - -```bash -gsutil mb gs://${FEAST_GCS_BUCKET} -``` - -Create the service account that Feast will run as: - -```bash -gcloud iam service-accounts create ${FEAST_SERVICE_ACCOUNT_NAME} - -gcloud projects add-iam-policy-binding ${FEAST_GCP_PROJECT_ID} \ - --member serviceAccount:${FEAST_SERVICE_ACCOUNT_NAME}@${FEAST_GCP_PROJECT_ID}.iam.gserviceaccount.com \ - --role roles/editor - -gcloud iam service-accounts keys create key.json --iam-account \ -${FEAST_SERVICE_ACCOUNT_NAME}@${FEAST_GCP_PROJECT_ID}.iam.gserviceaccount.com -``` - -## 2. Set up a Kubernetes \(GKE\) cluster - -{% hint style="warning" %} -Provisioning a GKE cluster can expose your services publicly. This guide does not cover securing access to the cluster. -{% endhint %} - -Create a GKE cluster: - -```bash -gcloud container clusters create ${FEAST_GKE_CLUSTER_NAME} \ - --machine-type n1-standard-4 -``` - -Create a secret in the GKE cluster based on your local key `key.json`: - -```bash -kubectl create secret generic feast-gcp-service-account --from-file=key.json -``` - -For this guide we will use `NodePort` for exposing Feast services. In order to do so, we must find an External IP of at least one GKE node. This should be a public IP. - -```bash -export FEAST_IP=$(kubectl describe nodes | grep ExternalIP | awk '{print $2}' | head -n 1) -export FEAST_CORE_URL=${FEAST_IP}:32090 -export FEAST_ONLINE_SERVING_URL=${FEAST_IP}:32091 -export FEAST_HISTORICAL_SERVING_URL=${FEAST_IP}:32092 -``` - -Add firewall rules to open up ports on your Google Cloud Platform project: - -```bash -gcloud compute firewall-rules create feast-core-port --allow tcp:32090 -gcloud compute firewall-rules create feast-online-port --allow tcp:32091 -gcloud compute firewall-rules create feast-batch-port --allow tcp:32092 -gcloud compute firewall-rules create feast-redis-port --allow tcp:32101 -gcloud compute firewall-rules create feast-kafka-ports --allow tcp:31090-31095 -``` - -## 3. Set up Helm - -Run the following command to provide Tiller with authorization to install Feast: - -```bash -kubectl apply -f - < 2020-08-21T08:40:19.000` | This ensures that floating point rounding errors do not occur during the retrieval of feature data, since this step requires time based joins | + +### Storage + +| Limitation | Motivation | +| :--- | :--- | +| Feast does not support offline storage in Feast 0.8 | As part of our re-architecture of Feast, we moved from GCP to cloud-agnostic deployments. Developing offline storage support that is available in all cloud environments is a pending action. | diff --git a/docs/reference/metrics-reference/README.md b/docs/reference/metrics-reference.md similarity index 92% rename from docs/reference/metrics-reference/README.md rename to docs/reference/metrics-reference.md index 4ccd6cb9ce0..34c97c7be60 100644 --- a/docs/reference/metrics-reference/README.md +++ b/docs/reference/metrics-reference.md @@ -1,12 +1,16 @@ # Metrics Reference +{% hint style="warning" %} +This page applies to Feast 0.7. The content may be out of date for Feast 0.8+ +{% endhint %} + Reference of the metrics that each Feast component exports: -* [Feast Core](./#feast-core) -* [Feast Serving](./#feast-serving) -* [Feast Ingestion Job](./#feast-ingestion-job) +* [Feast Core](metrics-reference.md#feast-core) +* [Feast Serving](metrics-reference.md#feast-serving) +* [Feast Ingestion Job](metrics-reference.md#feast-ingestion-job) -For how to configure Feast to export Metrics, see the [Metrics user guide.](../../advanced/metrics.md) +For how to configure Feast to export Metrics, see the [Metrics user guide.](../advanced/metrics.md) ## Feast Core @@ -44,8 +48,8 @@ Feast Serving exports the following metrics: | :--- | :--- | :--- | | `feast_serving_request_latency_seconds` | Feast Serving's latency in serving Requests in Seconds. | `method` | | `feast_serving_request_feature_count` | No. of requests retrieving a Feature from Feast Serving. | `project`, `feature_name` | -| `feast_serving_not_found_feature_count` | No. of requests retrieving a Feature has resulted in a [`NOT_FOUND` field status.](../../user-guide/feature-retrieval.md#online-field-statuses) | `project`, `feature_name` | -| `feast_serving_stale_feature_count` | No. of requests retrieving a Feature resulted in a [`OUTSIDE_MAX_AGE` field status.](../../user-guide/feature-retrieval.md#online-field-statuses) | `project`, `feature_name` | +| `feast_serving_not_found_feature_count` | No. of requests retrieving a Feature has resulted in a [`NOT_FOUND` field status.](../user-guide/getting-training-features.md#online-field-statuses) | `project`, `feature_name` | +| `feast_serving_stale_feature_count` | No. of requests retrieving a Feature resulted in a [`OUTSIDE_MAX_AGE` field status.](../user-guide/getting-training-features.md#online-field-statuses) | `project`, `feature_name` | | `feast_serving_grpc_request_count` | Total gRPC requests served. | `method` | **Metric Tags** @@ -61,11 +65,7 @@ Exported Feast Serving metrics may be filtered by the following tags/keys ## Feast Ingestion Job -Feast Ingestion computes both metrics an statistics on [data ingestion.](../../user-guide/data-ingestion.md) Make sure you familar with data ingestion concepts before proceeding. - -{% hint style="info" %} -For documentation on Feature value statistics computed by the Ingestion Job see [Statistics]() -{% endhint %} +Feast Ingestion computes both metrics an statistics on [data ingestion.](../user-guide/define-and-ingest-features.md) Make sure you familar with data ingestion concepts before proceeding. **Metrics Namespace** diff --git a/docs/roadmap.md b/docs/roadmap.md index 5ec26f85a1c..ab54c68253f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,5 +1,17 @@ # Roadmap +### Feast 0.9 + +[Discussion](https://github.com/feast-dev/feast/issues/1131) + +#### New Functionality + +* Feast Job Service +* Delta offline store support. Optional for users +* Push based ingestion into offline store +* On-prem support \(Open source storage and launcher\) +* Azure support + ### Feast 0.8 [Discussion](https://github.com/feast-dev/feast/issues/1018) @@ -62,22 +74,22 @@ ### Feast 0.5 -[Discussion](https://github.com/gojek/feast/issues/527) +[Discussion](https://github.com/feast-dev/feast/issues/527) #### New functionality 1. Streaming statistics and validation \(M1 from [Feature Validation RFC](https://docs.google.com/document/d/1TPmd7r4mniL9Y-V_glZaWNo5LMXLshEAUpYsohojZ-8/edit)\) -2. Support for Redis Clusters \([\#478](https://github.com/gojek/feast/issues/478), [\#502](https://github.com/gojek/feast/issues/502)\) -3. Add feature and feature set labels, i.e. key/value registry metadata \([\#463](https://github.com/gojek/feast/issues/463)\) -4. Job management API \([\#302](https://github.com/gojek/feast/issues/302)\) +2. Support for Redis Clusters \([\#478](https://github.com/feast-dev/feast/issues/478), [\#502](https://github.com/feast-dev/feast/issues/502)\) +3. Add feature and feature set labels, i.e. key/value registry metadata \([\#463](https://github.com/feast-dev/feast/issues/463)\) +4. Job management API \([\#302](https://github.com/feast-dev/feast/issues/302)\) #### Technical debt, refactoring, or housekeeping -1. Clean up and document all configuration options \([\#525](https://github.com/gojek/feast/issues/525)\) -2. Externalize storage interfaces \([\#402](https://github.com/gojek/feast/issues/402)\) -3. Reduce memory usage in Redis \([\#515](https://github.com/gojek/feast/issues/515)\) -4. Support for handling out of order ingestion \([\#273](https://github.com/gojek/feast/issues/273)\) -5. Remove feature versions and enable automatic data migration \([\#386](https://github.com/gojek/feast/issues/386)\) \([\#462](https://github.com/gojek/feast/issues/462)\) -6. Tracking of batch ingestion by with dataset\_id/job\_id \([\#461](https://github.com/gojek/feast/issues/461)\) -7. Write Beam metrics after ingestion to store \(not prior\) \([\#489](https://github.com/gojek/feast/issues/489)\) +1. Clean up and document all configuration options \([\#525](https://github.com/feast-dev/feast/issues/525)\) +2. Externalize storage interfaces \([\#402](https://github.com/feast-dev/feast/issues/402)\) +3. Reduce memory usage in Redis \([\#515](https://github.com/feast-dev/feast/issues/515)\) +4. Support for handling out of order ingestion \([\#273](https://github.com/feast-dev/feast/issues/273)\) +5. Remove feature versions and enable automatic data migration \([\#386](https://github.com/feast-dev/feast/issues/386)\) \([\#462](https://github.com/feast-dev/feast/issues/462)\) +6. Tracking of batch ingestion by with dataset\_id/job\_id \([\#461](https://github.com/feast-dev/feast/issues/461)\) +7. Write Beam metrics after ingestion to store \(not prior\) \([\#489](https://github.com/feast-dev/feast/issues/489)\) diff --git a/docs/user-guide/data-ingestion.md b/docs/user-guide/data-ingestion.md deleted file mode 100644 index e29953d173e..00000000000 --- a/docs/user-guide/data-ingestion.md +++ /dev/null @@ -1,42 +0,0 @@ -# Getting data into Feast - -In order to retrieve features for both training and serving, Feast requires data being ingested into the offline and online stores. - -{% hint style="warning" %} -Offline storage support will not be available until v0.9. Only Online storage support exists currently. -{% endhint %} - -Users are expected to already have either a batch or stream source with data materialized in it, ready to be ingested into Feast. Upon providing their external data sources in feature table specifications and registering them, users can now ingest data into Feast using Spark jobs. - -The following depicts an example ingestion flow from the specified data source to online store. - -### Batch Source to Online Store - -```python -from feast import Client -from datetime import datetime, timedelta - -client = Client(core_url="localhost:6565") -driver_ft = client.get_feature_table("driver_trips") - -# Initialize date ranges -today = datetime.now() -yesterday = today - timedelta(1) - -client.start_offline_to_online_ingestion( - driver_ft, yesterday, today -) -``` - -### Stream Source to Online Store - -```python -from feast import Client -from datetime import datetime, timedelta - -client = Client(core_url="localhost:6565") -driver_ft = client.get_feature_table("driver_trips") - -client.start_stream_to_online_ingestion(driver_ft) -``` - diff --git a/docs/user-guide/define-and-ingest-features.md b/docs/user-guide/define-and-ingest-features.md new file mode 100644 index 00000000000..d55fcb1d857 --- /dev/null +++ b/docs/user-guide/define-and-ingest-features.md @@ -0,0 +1,56 @@ +# Define and ingest features + +In order to retrieve features for both training and serving, Feast requires data being ingested into its offline and online stores. + +{% hint style="warning" %} +Feast 0.8 does not have an offline store. Only Online storage support exists currently. Feast 0.9 will have offline storage support. In Feast 0.8, historical data is retrieved directly from batch sources. +{% endhint %} + +Users are expected to already have either a batch or stream source with data stored in it, ready to be ingested into Feast. Once a feature table \(with the corresponding sources\) has been registered with Feast, it is possible to load data from this source into stores. + +The following depicts an example ingestion flow from a data source to the online store. + +### Batch Source to Online Store + +```python +from feast import Client +from datetime import datetime, timedelta + +client = Client(core_url="localhost:6565") +driver_ft = client.get_feature_table("driver_trips") + +# Initialize date ranges +today = datetime.now() +yesterday = today - timedelta(1) + +# Launches a short-lived job that ingests data over the provided date range. +client.start_offline_to_online_ingestion( + driver_ft, yesterday, today +) +``` + +### Stream Source to Online Store + +```python +from feast import Client +from datetime import datetime, timedelta + +client = Client(core_url="localhost:6565") +driver_ft = client.get_feature_table("driver_trips") + +# Launches a long running streaming ingestion job +client.start_stream_to_online_ingestion(driver_ft) +``` + +### Batch Source to Offline Store + +{% hint style="danger" %} +Not supported in Feast 0.8 +{% endhint %} + +### Stream Source to Offline Store + +{% hint style="danger" %} +Not supported in Feast 0.8 +{% endhint %} + diff --git a/docs/user-guide/feature-retrieval.md b/docs/user-guide/feature-retrieval.md deleted file mode 100644 index 3a6d072a410..00000000000 --- a/docs/user-guide/feature-retrieval.md +++ /dev/null @@ -1,61 +0,0 @@ -# Getting training features - -Feast provides a historical retrieval interface for exporting feature data to train machine learning models. Essentially, users are able to retrieve features from any feature tables and join them together in a single response dataset. The only requirement is that the user provides the correct entities and timestamps in order to look up the features. - -Historical feature retrieval can be done through the [Feast SDK](https://api.docs.feast.dev/python). - -{% hint style="warning" %} -Historical Retrieval currently pulls from batch sources for Feast v0.8, and offline storage support will not be available until v0.9. -{% endhint %} - -{% hint style="info" %} -By default, Feast infers that the features specified belong to the `default` project. To retrieve from another project, specify the `project` parameter when retrieving features. -{% endhint %} - -## **Point-in-time-correct Join** - -Feast does a point in time correct query from a single feature table. For each entity key and event timestamp combination that is provided by `entity_source`, Feast determines the values of all the features in the `feature_refs` list at that respective point in time and then joins features values to that specific entity value and event timestamp, and repeats this process for all timestamps. - -This is called a point in time correct join. - -Below is an example of how a point-in-time-correct join works. We have two DataFrames. The first is the `entity dataframe` that contains timestamps, entities, and labels. The user would like to have driver features joined onto this `entity dataframe` from the `driver dataframe` to produce a `joined dataframe` upon materializing the view that contains both labels and features. They would then like to train their model on this output - -![](../.gitbook/assets/point_in_time_join%20%281%29.png) - -Typically the `input 1` DataFrame would be provided by the user through `entity_source`, and the `input 2` DataFrame would already be ingested into Feast. To join these two, the user would call Feast as follows: - -```python -# Feature references with target feature -feature_refs = [ - "driver_trips:average_daily_rides", - "driver_trips:maximum_daily_rides", - "driver_trips:rating", - "trip_completed", -] - -# Define entity source -entity_source = FileSource( - "event_timestamp", - ParquetFormat(), - "gs://some-bucket/customer" -) - -# Retrieve historical dataset from Feast. -historical_feature_retrieval_job = client.get_historical_features( - feature_refs=feature_refs, - entity_rows=entity_source -) - -# Retrieve the output uri to materialize the dataset object into a Pandas DataFrame etc. -# Eg. gs://some-bucket/output/, s3://*, file://* -output_file_uri = historical_feature_retrieval_job.get_output_file_uri() -``` - -Feast is able to intelligently join feature data with different timestamps to a single basis table in a point-in-time-correct way. This allows users to join daily batch data with high-frequency event data transparently. They simply need to provide the feature references. - -{% hint style="info" %} -Feast can retrieve features from any amount of feature tables, as long as they occur on the same entities. -{% endhint %} - -Point-in-time-correct joins also prevents the occurrence of feature leakage by trying to accurate the state of the world at a single point in time, instead of just joining features based on the nearest timestamps. - diff --git a/docs/user-guide/getting-online-features.md b/docs/user-guide/getting-online-features.md index 722bb2ade4c..c16dc08a013 100644 --- a/docs/user-guide/getting-online-features.md +++ b/docs/user-guide/getting-online-features.md @@ -1,10 +1,10 @@ # Getting online features -Feast provides an online retrieval interface for serving. Data ingested into the online store comes from both batch and stream sources. +Feast provides an API through which online feature values can be retrieved. This allows teams to look up feature values at low latency in production during model serving, in order to make online predictions. -When data is ingested from a batch source, users can retrieve the same features used in training models from the low latency online store to be used in production. When data is ingested from a stream source, features that are retrieved by users are of the latest values, which are not yet used in training models. - -Online feature retrieval works in much the same way as batch retrieval, with one important distinction: Online stores only maintain the current state of features, i.e latest feature values. No historical data is served. +{% hint style="info" %} +Online stores only maintain the current state of features, i.e latest feature values. No historical data is stored or served. +{% endhint %} ```python from feast import Client @@ -36,35 +36,19 @@ response_dict = response.to_dict() print(response_dict) ``` -{% hint style="info" %} -When no project is specified when retrieving features with get\_online\_feature\(\), Feast infers that the features specified belong to the default project. To retrieve from another project, specify the project parameter when retrieving features. -{% endhint %} +The online store must be populated through [ingestion jobs](define-and-ingest-features.md#batch-source-to-online-store) prior to being used for online serving. -Feast Serving provides a [gRPC API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) that is backed by [Redis](https://redis.io/). We also provide support for [Python](https://api.docs.feast.dev/python/), [Go](https://godoc.org/github.com/gojek/feast/sdk/go), and [Java](https://javadoc.io/doc/dev.feast) clients. +Feast Serving provides a [gRPC API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) that is backed by [Redis](https://redis.io/). We have native clients in [Python](https://api.docs.feast.dev/python/), [Go](https://godoc.org/github.com/gojek/feast/sdk/go), and [Java](https://javadoc.io/doc/dev.feast). ### Online Field Statuses -Online Serving also returns Online Field Statuses when retrieving features. These status values gives useful insight into situations where Online Serving returns unset values. It also allows better of handling of the different possible cases represented by each status:for feature in features: - -```python -response_dict = response.to_dict() - -for feature_ref in feature_refs: - # field status can be obtained from the response's field values - status = response_dict["field_values"]["statuses"][feature_ref] - - if status == GetOnlineFeaturesResponse.FieldStatus.NOT_FOUND: - # handle case where feature value has not been ingested - elif status == GetOnlineFeaturesResponse.FieldStatus.PRESENT: - # feature value is present and can be used - value = response_dict["field_values"]["statuses"][feature_ref] -``` +Feast also returns status codes when retrieving features from the Feast Serving API. These status code give useful insight into the quality of data being served. | Status | Meaning | | :--- | :--- | -| NOT\_FOUND | Unset values returned as the feature value was not found in the online store. This might mean that no feature value was ingested for this feature. | -| NULL\_VALUE | Unset values returned as the ingested feature value was also unset. | -| OUTSIDE\_MAX\_AGE | Unset values returned as the age of the feature value \(time since the value was ingested\) has exceeded the Feature Set's max age, which the feature was defined in. | -| PRESENT | Set values are returned for the requested feature. | -| UNKNOWN | Status signifies the field status is unset for the requested feature. Might mean that the Feast version does not support Field Statuses. | +| NOT\_FOUND | The feature value was not found in the online store. This might mean that no feature value was ingested for this feature. | +| NULL\_VALUE | A entity key was successfully found but no feature values had been set. This status code should not occur during normal operation. | +| OUTSIDE\_MAX\_AGE | The age of the feature row in the online store \(in terms of its event timestamp\) has exceeded the maximum age defined within the feature table. | +| PRESENT | The feature values have been found and are within the maximum age. | +| UNKNOWN | Indicates a system failure. | diff --git a/docs/user-guide/getting-training-features.md b/docs/user-guide/getting-training-features.md new file mode 100644 index 00000000000..16735c731ef --- /dev/null +++ b/docs/user-guide/getting-training-features.md @@ -0,0 +1,72 @@ +# Getting training features + +Feast provides a historical retrieval interface for exporting feature data in order to train machine learning models. Essentially, users are able to enrich their data with features from any feature tables. + +### Retrieving historical features + +Below is an example of the process required to produce a training dataset: + +```python +# Feature references with target feature +feature_refs = [ + "driver_trips:average_daily_rides", + "driver_trips:maximum_daily_rides", + "driver_trips:rating", + "driver_trips:rating:trip_completed", +] + +# Define entity source +entity_source = FileSource( + "event_timestamp", + ParquetFormat(), + "gs://some-bucket/customer" +) + +# Retrieve historical dataset from Feast. +historical_feature_retrieval_job = client.get_historical_features( + feature_refs=feature_refs, + entity_rows=entity_source +) + +output_file_uri = historical_feature_retrieval_job.get_output_file_uri() +``` + +#### 1. Define feature references + +[Feature references](../concepts/glossary.md#feature-references) define the specific features that will be retrieved from Feast. These features can come from multiple feature tables. The only requirement is that the feature tables that make up the feature references have the same entity \(or composite entity\). + +**2. Define an entity dataframe** + +Feast needs to join feature values onto specific entities at specific points in time. Thus, it is necessary to provide an [entity dataframe](../concepts/glossary.md#entity-dataframe) as part of the `get_historical_features` method. In the example above we are defining an entity source. This source is an external file that provides Feast with the entity dataframe. + +**3. Launch historical retrieval job** + +Once the feature references and an entity source are defined, it is possible to call `get_historical_features()`. This method launches a job that extracts features from the sources defined in the provided feature tables, joins them onto the provided entity source, and returns a reference to the training dataset that is produced. + +Please see the [Feast SDK](https://api.docs.feast.dev/python) for more details. + +### Point-in-time Joins + +Feast always joins features onto entity data in a point-in-time correct way. The process can be described through an example. + +In the example below there are two tables \(or dataframes\): + +* The dataframe on the left is the [entity dataframe](../concepts/glossary.md#entity-dataframe) that contains timestamps, entities, and the target variable \(trip\_completed\). This dataframe is provided to Feast through an entity source. +* The dataframe on the right contains driver features. This dataframe is represented in Feast through a feature table and its accompanying data source\(s\). + +The user would like to have the driver features joined onto the entity dataframe to produce a training dataset that contains both the target \(trip\_completed\) and features \(average\_daily\_rides, maximum\_daily\_rides, rating\). This dataset will then be used to train their model. + +![](../.gitbook/assets/point_in_time_join%20%281%29.png) + +Feast is able to intelligently join feature data with different timestamps to a single entity dataframe. It does this through a point-in-time join as follows: + +1. Feast loads the entity dataframe and all feature tables \(driver dataframe\) into the same location. This can either be a database or in memory. +2. For each [entity row](../concepts/glossary.md#entity-rows) in the [entity dataframe](getting-online-features.md), Feast tries to find feature values in each feature table to join to it. Feast extracts the timestamp and entity key of each row in the entity dataframe and scans backward through the feature table until it finds a matching entity key. +3. If the event timestamp of the matching entity key within the driver feature table is within the maximum age configured for the feature table, then the features at that entity key are joined onto the entity dataframe. If the event timestamp is outside of the maximum age, then only null values are returned. +4. If multiple entity keys are found with the same event timestamp, then they are deduplicated by the created timestamp, with newer values taking precedence. +5. Feast repeats this joining process for all feature tables and returns the resulting dataset. + +{% hint style="info" %} +Point-in-time correct joins attempts to prevent the occurrence of feature leakage by trying to recreate the state of the world at a single point in time, instead of joining features based on exact timestamps only. +{% endhint %} + diff --git a/docs/user-guide/overview.md b/docs/user-guide/overview.md new file mode 100644 index 00000000000..2d6eb9981bb --- /dev/null +++ b/docs/user-guide/overview.md @@ -0,0 +1,32 @@ +# Overview + +### Using Feast + +Feast development happens through three key workflows: + +1. [Define and load feature data into Feast](define-and-ingest-features.md) +2. [Retrieve historical features for training models](getting-training-features.md) +3. [Retrieve online features for serving models](getting-online-features.md) + +### Defining feature tables and ingesting data into Feast + +Feature creators model the data within their organization into Feast through the definition of [feature tables](../concepts/feature-tables.md) that contain [data sources](../concepts/sources.md). Feature tables are both a schema and a means of identifying data sources for features, and allow Feast to know how to interpret your data, and where to find it. + +After registering a feature table with Feast, users can trigger an ingestion from their data source into Feast. This loads feature values from an upstream data source into Feast stores through ingestion jobs. + +Visit [feature tables](../concepts/feature-tables.md#overview) to learn more about them. + +{% page-ref page="define-and-ingest-features.md" %} + +### Retrieving historical features for training + +In order to generate a training dataset it is necessary to provide both an [entity dataframe ](../concepts/glossary.md#entity-dataframe)and feature references through the[ Feast SDK](https://api.docs.feast.dev/python/) to retrieve historical features. For historical serving, Feast requires that you provide the entities and timestamps for the corresponding feature data. Feast produces a point-in-time correct dataset using the requested features. These features can be requested from an unlimited number of feature sets. + +{% page-ref page="getting-training-features.md" %} + +### Retrieving online features for online serving + +Online retrieval uses feature references through the [Feast Online Serving API](https://api.docs.feast.dev/grpc/feast.serving.pb.html) to retrieve online features. Online serving allows for very low latency requests to feature data at very high throughput. + +{% page-ref page="getting-online-features.md" %} + From 5fbc863e622fcc62c1f3edd22a1d41f8d24f06d7 Mon Sep 17 00:00:00 2001 From: Willem Pienaar Date: Mon, 16 Nov 2020 06:34:27 +0000 Subject: [PATCH 04/32] GitBook: [v0.8-branch] 36 pages and 6 assets modified --- .../feast-architecture-diagrams (1).svg | 1 + docs/.gitbook/assets/feast-on-aws-3- (1).png | Bin 0 -> 65249 bytes docs/.gitbook/assets/image (4) (1).png | Bin 0 -> 33294 bytes .../assets/point_in_time_join (1) (1).png | Bin 0 -> 297335 bytes docs/.gitbook/assets/rsz_untitled23 (2).jpg | Bin 0 -> 23499 bytes docs/.gitbook/assets/untitled-25-1- (2).jpg | Bin 0 -> 76205 bytes docs/README.md | 16 +- docs/SUMMARY.md | 2 +- docs/advanced/audit-logging.md | 16 +- docs/advanced/metrics.md | 18 +- docs/advanced/security.md | 289 ++++++++---------- docs/advanced/troubleshooting.md | 22 +- docs/advanced/upgrading.md | 18 +- docs/community.md | 10 +- docs/concepts/architecture.md | 6 +- docs/concepts/entities.md | 10 +- docs/concepts/glossary.md | 12 +- docs/concepts/overview.md | 6 +- docs/concepts/sources.md | 12 +- docs/concepts/stores.md | 8 +- docs/contributing/release-process.md | 6 +- docs/getting-started/README.md | 6 +- .../connect-to-feast/README.md | 6 +- docs/getting-started/install-feast/README.md | 4 +- .../kubernetes-amazon-eks-with-terraform.md | 16 +- .../install-feast/kubernetes-with-helm.md | 14 +- docs/getting-started/learn-feast.md | 2 +- docs/quickstart.md | 20 +- docs/reference/api/README.md | 17 ++ docs/reference/configuration-reference.md | 20 +- docs/reference/limitations.md | 6 +- docs/reference/metrics-reference.md | 8 +- docs/roadmap.md | 28 +- docs/user-guide/define-and-ingest-features.md | 8 +- docs/user-guide/getting-online-features.md | 4 +- docs/user-guide/getting-training-features.md | 10 +- docs/user-guide/overview.md | 10 +- 37 files changed, 308 insertions(+), 323 deletions(-) create mode 100644 docs/.gitbook/assets/feast-architecture-diagrams (1).svg create mode 100644 docs/.gitbook/assets/feast-on-aws-3- (1).png create mode 100644 docs/.gitbook/assets/image (4) (1).png create mode 100644 docs/.gitbook/assets/point_in_time_join (1) (1).png create mode 100644 docs/.gitbook/assets/rsz_untitled23 (2).jpg create mode 100644 docs/.gitbook/assets/untitled-25-1- (2).jpg create mode 100644 docs/reference/api/README.md diff --git a/docs/.gitbook/assets/feast-architecture-diagrams (1).svg b/docs/.gitbook/assets/feast-architecture-diagrams (1).svg new file mode 100644 index 00000000000..7335c131c44 --- /dev/null +++ b/docs/.gitbook/assets/feast-architecture-diagrams (1).svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/.gitbook/assets/feast-on-aws-3- (1).png b/docs/.gitbook/assets/feast-on-aws-3- (1).png new file mode 100644 index 0000000000000000000000000000000000000000..e6de77dde9b5785f428234dd36dcb6187df3cf7c GIT binary patch literal 65249 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 UI-ERrL->g2PMe=9F>$*0p9ZMe0ssI2 literal 0 HcmV?d00001 diff --git a/docs/.gitbook/assets/image (4) (1).png b/docs/.gitbook/assets/image (4) (1).png new file mode 100644 index 0000000000000000000000000000000000000000..cd77f27cc4563f8f952f9e956661c5651665d8e9 GIT binary patch literal 33294 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 literal 0 HcmV?d00001 diff --git a/docs/.gitbook/assets/point_in_time_join (1) (1).png b/docs/.gitbook/assets/point_in_time_join (1) (1).png new file mode 100644 index 0000000000000000000000000000000000000000..331a090d719151b914c5dc75a0a9f3caa8498e67 GIT binary patch literal 297335 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