Repository: gojek/feast
Pull request #451 (2020-01-30 17:06:41)

Upgrade to Java 11
**What this PR does / why we need it**:

Upgrade Feast to next LTS - Java 11 - since Java 8 is outdated and no longer supported.

Our internal build and testing of Feast has been on Java 11 all along, so it's fair to say there shouldn't be any problems. I'm not able to fully test because 1) slight variation in base-images for building/packaging, 2) we use a different Dockerfile that uses pre-built Jars from another step rather than a 'builder' pattern

**Which issue(s) this PR fixes**:
NONE

**Does this PR introduce a user-facing change?**:
```release-note
Feast now requires a Java 11 runtime. JVM options should be set accordingly.
```


feast-ci-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/gojek/feast/pull/451#" title="Author self-approved">Yanson</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **woop**
You can assign the PR to them by writing `/assign @woop` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=gojek%2Ffeast).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/gojek/feast/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["woop"]} -->
feast-ci-bot: Hi @Yanson. Thanks for your PR.

I'm waiting for a [gojek](https://github.com/orgs/gojek/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/gojek/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=gojek%2Ffeast).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Yanson: /assign @woop
khorshuheng: @Yanson One of the reason which prevented us from using Java 11, is due to Apache Beam / Dataflow does not officially supporting Java 11. So we certainly need to check if this change will affect ingestion, especially for Dataflow runner.

In any case there shouldn't be any issue running the serving component on Java 11.
khorshuheng: /ok-to-test
feast-ci-bot: @Yanson: The following test **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
test-java-sdk | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-java-sdk/1223067445145636869) | `/test test-java-sdk`

[Full PR test history](https://prow.feast.ai/pr-history?org=gojek&repo=feast&pr=451)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
feast-ci-bot: @Yanson: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
test-java-sdk | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-java-sdk/1223067445145636869) | `/test test-java-sdk`
test-core-and-ingestion | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-core-and-ingestion/1223067445145636867) | `/test test-core-and-ingestion`

[Full PR test history](https://prow.feast.ai/pr-history?org=gojek&repo=feast&pr=451)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
feast-ci-bot: @Yanson: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
test-java-sdk | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-java-sdk/1223067445145636869) | `/test test-java-sdk`
test-core-and-ingestion | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-core-and-ingestion/1223067445145636867) | `/test test-core-and-ingestion`
test-serving | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-serving/1223067445145636868) | `/test test-serving`

[Full PR test history](https://prow.feast.ai/pr-history?org=gojek&repo=feast&pr=451)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
feast-ci-bot: @Yanson: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
test-java-sdk | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-java-sdk/1223067445145636869) | `/test test-java-sdk`
test-core-and-ingestion | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-core-and-ingestion/1223067445145636867) | `/test test-core-and-ingestion`
test-serving | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-serving/1223067445145636868) | `/test test-serving`
test-end-to-end | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-end-to-end/1223067445145636866) | `/test test-end-to-end`

[Full PR test history](https://prow.feast.ai/pr-history?org=gojek&repo=feast&pr=451)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
feast-ci-bot: @Yanson: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
test-java-sdk | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-java-sdk/1223067445145636869) | `/test test-java-sdk`
test-core-and-ingestion | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-core-and-ingestion/1223067445145636867) | `/test test-core-and-ingestion`
test-serving | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-serving/1223067445145636868) | `/test test-serving`
test-end-to-end | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-end-to-end/1223067445145636866) | `/test test-end-to-end`
test-end-to-end-batch | 20b00b0893920b2bc9b0aed08ae75abbd4e9c3b1 | [link](http://prow.feast.ai/view/gcs/feast-templocation-kf-feast/pr-logs/pull/gojek_feast/451/test-end-to-end-batch/1223067445145636864) | `/test test-end-to-end-batch`

[Full PR test history](https://prow.feast.ai/pr-history?org=gojek&repo=feast&pr=451)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
woop: This change makes sense, but we'd need to ensure compatibility first before making the move like @khorshuheng said.
Yanson: Looking at the test failures, it seems as though they are still running on `maven:3.6-jdk-8`

e.g. http://prow.feast.ai/prowjob?prowjob=9bf59c76-43cf-11ea-9ebf-e2420cc0b547

Is it possible to re-run with the `.prow/config.yaml` changes in this PR?
https://github.com/gojek/feast/pull/451/files#diff-7c2c7257959d7451ee7dc1de3fb9889e
Yanson: > @Yanson One of the reason which prevented us from using Java 11, is due to Apache Beam / Dataflow does not officially supporting Java 11. So we certainly need to check if this change will affect ingestion, especially for Dataflow runner.
> 
> In any case there shouldn't be any issue running the serving component on Java 11.

We are using Java 11 for all Dataflow projects internally, and as noted our Feast build is also on Java 11. The only compatibility problem is the way in which files to upload are detected.

Of course, I don't expect you to take my word for it so let's get it building on Prow as the first step.

Patch:
diff --git a/.prow/config.yaml b/.prow/config.yaml
index 0c0e979b1..e947f4401 100644
--- a/.prow/config.yaml
+++ b/.prow/config.yaml
@@ -66,7 +66,7 @@ presubmits:
     always_run: true
     spec:
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command: [".prow/scripts/test-core-ingestion.sh"]
         resources:
           requests:
@@ -78,7 +78,7 @@ presubmits:
     always_run: true
     spec:
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command: [".prow/scripts/test-serving.sh"]
 
   - name: test-java-sdk
@@ -86,7 +86,7 @@ presubmits:
     always_run: true
     spec:
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command: [".prow/scripts/test-java-sdk.sh"]
 
   - name: test-python-sdk
@@ -110,7 +110,7 @@ presubmits:
     always_run: true
     spec:
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command: [".prow/scripts/test-end-to-end.sh"]
         resources:
           requests:
@@ -126,7 +126,7 @@ presubmits:
         secret:
           secretName: feast-service-account
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command: [".prow/scripts/test-end-to-end-batch.sh"]
         resources:
           requests:
@@ -167,7 +167,7 @@ postsubmits:
     decorate: true
     spec:
       containers:
-      - image: maven:3.6-jdk-8
+      - image: maven:3.6-jdk-11
         command:
         - bash
         - -c
diff --git a/README.md b/README.md
index fb36f6cab..ca94a1f56 100644
--- a/README.md
+++ b/README.md
@@ -28,15 +28,30 @@ my_model = ml.fit(data)
 prediction = my_model.predict(fs.get_online_features(customer_features, customer_entities))
 ```
 
+## Getting Started with Docker Compose
+The following commands will start Feast in online-only mode. 
+```
+git clone https://github.com/gojek/feast.git
+cd feast/infra/docker-compose
+cp .env.sample .env
+docker-compose up -d
+```
+
+A [Jupyter Notebook](http://localhost:8888/tree/feast/examples) is now available to start using Feast.
+
+Please see the links below to set up Feast for batch/historical serving with BigQuery.
+
 ## Important resources
 
-Please refer to the official docs at <https://feast.dev>
+Please refer to the official documentation at <https://docs.feast.dev>
 
  * [Why Feast?](https://docs.feast.dev/why-feast)
  * [Concepts](https://docs.feast.dev/concepts)
- * [Installation](https://docs.feast.dev/getting-started/installing-feast)
- * [Getting Help](https://docs.feast.dev/getting-help)
- * [Example Notebook](https://github.com/gojek/feast/blob/master/examples/basic/basic.ipynb)
+ * [Installation](https://docs.feast.dev/installing-feast/overview)
+ * [Examples](https://github.com/gojek/feast/blob/master/examples/)
+ * [Roadmap](https://docs.google.com/document/d/1ZZY59j_c2oNN3N6TmavJIyLPMzINdea44CRIe2nhUIo/edit#)
+ * [Change Log](https://github.com/gojek/feast/blob/master/CHANGELOG.md)
+ * [Slack (#Feast)](https://join.slack.com/t/kubeflow/shared_invite/enQtNDg5MTM4NTQyNjczLTdkNTVhMjg1ZTExOWI0N2QyYTQ2MTIzNTJjMWRiOTFjOGRlZWEzODc1NzMwNTMwM2EzNjY1MTFhODczNjk4MTk)
 
 ## Notice
 
diff --git a/core/pom.xml b/core/pom.xml
index e1567ae8f..720f80a8a 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -197,5 +197,11 @@
             <artifactId>spring-boot-test-autoconfigure</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+        </dependency>
+
     </dependencies>
 </project>
diff --git a/core/src/main/java/feast/core/util/PipelineUtil.java b/core/src/main/java/feast/core/util/PipelineUtil.java
index ef1b50d8c..71cbc892b 100644
--- a/core/src/main/java/feast/core/util/PipelineUtil.java
+++ b/core/src/main/java/feast/core/util/PipelineUtil.java
@@ -24,7 +24,9 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class PipelineUtil {
 
@@ -45,12 +47,7 @@ public class PipelineUtil {
   public static List<String> detectClassPathResourcesToStage(ClassLoader classLoader)
       throws IOException {
     if (!(classLoader instanceof URLClassLoader)) {
-      String message =
-          String.format(
-              "Unable to use ClassLoader to detect classpath elements. "
-                  + "Current ClassLoader is %s, only URLClassLoaders are supported.",
-              classLoader);
-      throw new IllegalArgumentException(message);
+      return getClasspathFiles();
     }
 
     List<String> files = new ArrayList<>();
@@ -69,4 +66,11 @@ public class PipelineUtil {
     }
     return files;
   }
+
+  private static List<String> getClasspathFiles() {
+    return Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator))
+        .map(entry -> new File(entry).getPath())
+        .collect(Collectors.toList());
+  }
+
 }
diff --git a/datatypes/java/pom.xml b/datatypes/java/pom.xml
index a6dfa8e34..efda2e8ba 100644
--- a/datatypes/java/pom.xml
+++ b/datatypes/java/pom.xml
@@ -68,5 +68,11 @@
         <groupId>io.grpc</groupId>
         <artifactId>grpc-services</artifactId>
       </dependency>
+
+      <dependency>
+          <groupId>javax.annotation</groupId>
+          <artifactId>javax.annotation-api</artifactId>
+      </dependency>
     </dependencies>
+
 </project>
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index 535fbe608..d2cc03a20 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -6,9 +6,15 @@
 * [Getting Help](getting-help.md)
 * [Contributing](contributing.md)
 
-## Getting Started
+## Installing Feast
+
+* [Overview](installing-feast/overview.md)
+* [Docker Compose](installing-feast/docker-compose.md)
+* [Google Kubernetes Engine \(GKE\)](installing-feast/gke.md)
+* [Troubleshooting](installing-feast/troubleshooting.md)
+
+## Using Feast
 
-* [Installing Feast](getting-started/installing-feast.md)
 * [Using Feast](https://github.com/gojek/feast/blob/master/examples/basic/basic.ipynb)
 
 ## Reference
diff --git a/docs/concepts.md b/docs/concepts.md
index 860515c36..ae158f8f8 100644
--- a/docs/concepts.md
+++ b/docs/concepts.md
@@ -2,7 +2,7 @@
 
 ## Architecture
 
-![Logical diagram of a typical Feast deployment](.gitbook/assets/basic-architecture-diagram.svg)
+![Logical diagram of a typical Feast deployment](.gitbook/assets/basic-architecture-diagram%20%282%29.svg)
 
 The core components of a Feast deployment are
 
@@ -106,13 +106,13 @@ Feast supports the following types for feature values
 * DOUBLE
 * FLOAT
 * BOOL
-* BYTES_LIST
-* STRING_LIST
-* INT32_LIST
-* INT64_LIST
-* DOUBLE_LIST
-* FLOAT_LIST
-* BOOL_LIST
+* BYTES\_LIST
+* STRING\_LIST
+* INT32\_LIST
+* INT64\_LIST
+* DOUBLE\_LIST
+* FLOAT\_LIST
+* BOOL\_LIST
 
 ## Glossary
 
diff --git a/docs/contributing.md b/docs/contributing.md
index 9fb39880f..fdac047be 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -18,7 +18,7 @@ The main components of Feast are:
 
 **Pre-requisites**
 
-* Java SDK version 8
+* Java SE Development Kit 11
 * Python version 3.6 \(or above\) and pip
 * Access to Postgres database \(version 11 and above\)
 * Access to [Redis](https://redis.io/topics/quickstart) instance \(tested on version 5.x\)
@@ -329,7 +329,7 @@ docker run --rm \
 
 ## Code reviews
 
-Code submission to Feast \(including submission from project maintainers\) requires review and approval. Please submit a **pull request** to initiate the code review process. We use [prow](https://github.com/kubernetes/test-infra/tree/master/prow) to manage the testing and reviewing of pull requests. Please refer to [config.yaml](../.prow/config.yaml) for details on the test jobs.
+Code submission to Feast \(including submission from project maintainers\) requires review and approval. Please submit a **pull request** to initiate the code review process. We use [prow](https://github.com/kubernetes/test-infra/tree/master/prow) to manage the testing and reviewing of pull requests. Please refer to [config.yaml](https://github.com/gojek/feast/tree/4cd928d1d3b7972b15f0c5dd29593fcedecea9f5/.prow/config.yaml) for details on the test jobs.
 
 ## Code conventions
 
diff --git a/docs/getting-help.md b/docs/getting-help.md
index d8180ab78..597a782d6 100644
--- a/docs/getting-help.md
+++ b/docs/getting-help.md
@@ -1,23 +1,36 @@
 # Getting Help
 
-## Chat
+### Chat
 
-* Come and chat with us in the [\#Feast Slack channel in the Kubeflow workspace](https://join.slack.com/t/kubeflow/shared_invite/enQtNDg5MTM4NTQyNjczLTdkNTVhMjg1ZTExOWI0N2QyYTQ2MTIzNTJjMWRiOTFjOGRlZWEzODc1NzMwNTMwM2EzNjY1MTFhODczNjk4MTk) and catch up on all things Feast!
+* Come and say hello in [\#Feast](https://join.slack.com/t/kubeflow/shared_invite/enQtNDg5MTM4NTQyNjczLTdkNTVhMjg1ZTExOWI0N2QyYTQ2MTIzNTJjMWRiOTFjOGRlZWEzODc1NzMwNTMwM2EzNjY1MTFhODczNjk4MTk) over in the Kubeflow Slack.
 
-## GitHub
+### GitHub
 
 * Feast's GitHub repo can be [found here](https://github.com/gojek/feast/).
 * Found a bug or need a feature? [Create an issue on GitHub](https://github.com/gojek/feast/issues/new)
 
-## Mailing list
+### Community Call
 
-### Feast discussion
+We have a community call every 2 weeks. Alternating between two times.
+
+* 11 am \(UTC + 8\)
+* 5 pm \(UTC + 8\)
+
+Please join the [**feast-dev**](getting-help.md#feast-development) mailing list to receive the the calendar invitation.
+
+### Mailing list
+
+#### Feast discussion
 
 * Google Group: [https://groups.google.com/d/forum/feast-discuss](https://groups.google.com/d/forum/feast-discuss)
 * Mailing List: [feast-discuss@googlegroups.com](mailto:feast-discuss@googlegroups.com)
 
-### Feast development
+#### Feast development
 
 * Google Group: [https://groups.google.com/d/forum/feast-dev](https://groups.google.com/d/forum/feast-dev)
 * Mailing List: [feast-dev@googlegroups.com](mailto:feast-dev@googlegroups.com)
 
+### Google Drive
+
+The Feast community also maintains a [Google Drive](https://drive.google.com/drive/u/0/folders/0AAe8j7ZK3sxSUk9PVA) with documents like RFCs, meeting notes, or roadmaps. Please join one of the above mailing lists \(feast-dev or feast-discuss\) to gain access to the drive.
+
diff --git a/docs/getting-started/installing-feast.md b/docs/getting-started/installing-feast.md
deleted file mode 100644
index 0b212037c..000000000
--- a/docs/getting-started/installing-feast.md
+++ /dev/null
@@ -1,433 +0,0 @@
-# Installing Feast
-
-## Overview
-
-This installation guide will demonstrate three ways of installing Feast:
-
-* \*\*\*\*[**Docker Compose \(Quickstart\):**](installing-feast.md#docker-compose) Fastest way to get Feast up and running. Provides a pre-installed Jupyter Notebook with the Feast Python SDK and sample code.
-* [**Minikube**](installing-feast.md#minikube)**:** This installation has no external dependencies, but does not have a historical feature store installed. It allows users to quickly get a feel for Feast.
-* [**Google Kubernetes Engine:**](installing-feast.md#google-kubernetes-engine) This guide installs a single cluster Feast installation on Google's GKE. It has Google Cloud specific dependencies like BigQuery, Dataflow, and Google Cloud Storage.
-
-## Docker Compose \(Quickstart\)
-
-### Overview
-
-A docker compose file is provided to quickly test Feast with the official docker images. There is no hard dependency on GCP, unless batch serving is required. Once you have set up Feast using Docker Compose, you will be able to:
-
-* Create, register, and manage feature sets
-* Ingest feature data into Feast
-* Retrieve features for online serving
-
-{% hint style="info" %}
-The docker compose setup uses Direct Runner for the Apache Beam jobs. 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 full runner when running Feast with very large workloads.
-{% endhint %}
-
-### 0. Requirements
-
-* [Docker compose](https://docs.docker.com/compose/install/) should be installed.
-* TCP ports 6565, 6566, 8888, and 9094 should not be in use. Otherwise, modify the port mappings in  `infra/docker-compose/docker-compose.yml` to use unoccupied ports.
-* \(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 GCS and BigQuery. Port 6567 will be used for the batch serving endpoint.
-* \(for batch serving only\) [Google Cloud SDK ](https://cloud.google.com/sdk/install)installed, authenticated, and configured to the project you will use.
-
-### 1. Step-by-step guide \(Online serving only\)
-
-Clone the [Feast repository](https://github.com/gojek/feast/) and navigate to the `docker-compose` sub-directory:
-
-```bash
-git clone https://github.com/gojek/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
-```
-
-Start Feast:
-
-```javascript
-docker-compose up -d
-```
-
-A Jupyter notebook is now available to use Feast:
-
-[http://localhost:8888/notebooks/feast-notebooks/feast-quickstart.ipynb](http://localhost:8888/notebooks/feast-notebooks/feast-quickstart.ipynb)
-
-### 2. Step-by-step guide \(Batch and online serving\)
-
-Clone the [Feast repository](https://github.com/gojek/feast/) and navigate to the `docker-compose` sub-directory:
-
-```bash
-git clone https://github.com/gojek/feast.git && \
-cd feast && export FEAST_HOME_DIR=$(pwd) && \
-cd infra/docker-compose
-```
-
-Create a [service account ](https://cloud.google.com/iam/docs/creating-managing-service-accounts)from the GCP console and copy it to the `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
-```
-
-Make a copy of the `.env.sample` file:
-
-```bash
-cp .env.sample .env
-```
-
-Customize the `.env` file based on your environment. At the very least you have to modify:
-
-* **FEAST\_CORE\_GCP\_SERVICE\_ACCOUNT\_KEY:** This should be your service account file name without the .json extension.
-* **FEAST\_BATCH\_SERVING\_GCP\_SERVICE\_ACCOUNT\_KEY:** This should be your service account file name without the .json extension.
-* **FEAST\_JUPYTER\_GCP\_SERVICE\_ACCOUNT\_KEY:** This should be your service account file name without the .json extension.
-* **FEAST\_JOB\_STAGING\_LOCATION:** Google Cloud Storage bucket that Feast will use to stage data exports and batch retrieval requests.
-
-We will also need to customize 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:
-
-* **project\_id:** This is you GCP project id.
-* **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.
-
-Start Feast:
-
-```javascript
-docker-compose -f docker-compose.yml -f docker-compose.batch.yml up -d
-```
-
-A Jupyter notebook is now available to use Feast:
-
-[http://localhost:8888/notebooks/feast-notebooks](http://localhost:8888/tree/feast-notebooks)
-
-## Minikube
-
-### Overview
-
-This guide will install Feast into [Minikube](https://github.com/kubernetes/minikube). 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 online serving.
-
-{% hint style="warning" %}
-This Minikube installation guide is for demonstration purposes only. It is not meant for production use, and does not install a historical feature store.
-{% endhint %}
-
-### 0. Requirements
-
-The following software should be installed prior to starting:
-
-1. [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) should be installed.
-2. [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed and configured to work with Minikube.
-3. [Helm](https://helm.sh/3) \(2.16.0 or greater\). Helm 3 has not been tested yet.
-
-### 1. Set up Minikube
-
-Start Minikube. Note the minimum cpu and memory below:
-
-```bash
-minikube start --cpus=3 --memory=4096 --kubernetes-version='v1.15.5'
-```
-
-Set up your Feast environmental variables
-
-```bash
-export FEAST_IP=$(minikube ip)
-export FEAST_CORE_URL=${FEAST_IP}:32090
-export FEAST_SERVING_URL=${FEAST_IP}:32091
-```
-
-### 2. Install Feast with Helm
-
-Clone the [Feast repository](https://github.com/gojek/feast/) and navigate to the `charts` sub-directory:
-
-```bash
-git clone https://github.com/gojek/feast.git && \
-cd feast && export FEAST_HOME_DIR=$(pwd) && \
-cd infra/charts/feast
-```
-
-Copy the `values-demo.yaml` file for your installation:
-
-```bash
-cp values-demo.yaml my-feast-values.yaml
-```
-
-Update all occurrences of the domain `feast.example.com` inside of `my-feast-values.yaml` with your Minikube IP. This is to allow external access to the services in the cluster. You can find your Minikube IP by running the following command `minikube ip`, or simply replace the text from the command line:
-
-```bash
-sed -i "s/feast.example.com/${FEAST_IP}/g" my-feast-values.yaml
-```
-
-Install Tiller:
-
-```bash
-helm init
-```
-
-Install the Feast Helm chart:
-
-```bash
-helm install --name feast -f my-feast-values.yaml .
-```
-
-Ensure that the system comes online. This will take a few minutes
-
-```bash
-watch kubectl get pods
-```
-
-```bash
-NAME                                           READY   STATUS      RESTARTS   AGE
-pod/feast-feast-core-666fd46db4-l58l6          1/1     Running     0          5m
-pod/feast-feast-serving-online-84d99ddcbd      1/1     Running     0          6m
-pod/feast-kafka-0                              1/1     Running     0          3m
-pod/feast-kafka-1                              1/1     Running     0          4m
-pod/feast-kafka-2                              1/1     Running     0          4m
-pod/feast-postgresql-0                         1/1     Running     0          5m
-pod/feast-redis-master-0                       1/1     Running     0          5m
-pod/feast-zookeeper-0                          1/1     Running     0          5m
-pod/feast-zookeeper-1                          1/1     Running     0          5m
-pod/feast-zookeeper-2                          1/1     Running     0          5m
-```
-
-### 3. Connect to Feast with the Python SDK
-
-Install the Python SDK using pip:
-
-```bash
-pip install -e ${FEAST_HOME_DIR}/sdk/python
-```
-
-Configure the Feast Python SDK:
-
-```bash
-feast config set core_url ${FEAST_CORE_URL}
-feast config set serving_url ${FEAST_SERVING_URL}
-```
-
-That's it! You can now start to use Feast!
-
-## Google Kubernetes Engine
-
-### Overview <a id="m_5245424069798496115gmail-overview-1"></a>
-
-This guide will install Feast into a Kubernetes cluster on GCP. It assumes that all of your services will run within a single K8s 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.
-* [Cloud Dataflow](https://cloud.google.com/dataflow/) is used for running data ingestion jobs.
-* [Google Cloud Storage](https://cloud.google.com/storage/) is used for intermediate data storage.
-{% endhint %}
-
-### 0. Requirements <a id="m_5245424069798496115gmail-requirements"></a>
-
-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_S_ACCOUNT_NAME=feast-sa
-```
-
-Create a Google Cloud Storage bucket for Feast to stage data during exports:
-
-```bash
-gsutil mb gs://${FEAST_GCS_BUCKET}
-```
-
-Create a BigQuery dataset for storing historical features:
-
-```bash
-bq mk ${FEAST_BIGQUERY_DATASET_ID}
-```
-
-Create the service account that Feast will run as:
-
-```bash
-gcloud iam service-accounts create ${FEAST_S_ACCOUNT_NAME}
-
-gcloud projects add-iam-policy-binding ${FEAST_GCP_PROJECT_ID} \
-  --member serviceAccount:${FEAST_S_ACCOUNT_NAME}@${FEAST_GCP_PROJECT_ID}.iam.gserviceaccount.com \
-  --role roles/editor
-
-gcloud iam service-accounts keys create key.json --iam-account \
-${FEAST_S_ACCOUNT_NAME}@${FEAST_GCP_PROJECT_ID}.iam.gserviceaccount.com
-```
-
-Ensure that [Dataflow API is enabled](https://console.cloud.google.com/apis/api/dataflow.googleapis.com/overview):
-
-```bash
-gcloud services enable dataflow.googleapis.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 internal IP of at least one GKE node.
-
-```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_BATCH_SERVING_URL=${FEAST_IP}:32092
-```
-
-Confirm that you are able to access this node \(please make sure that no firewall rules are preventing access to these ports\):
-
-```bash
-ping $FEAST_IP
-```
-
-```bash
-PING 10.123.114.11 (10.203.164.22) 56(84) bytes of data.
-64 bytes from 10.123.114.11: icmp_seq=1 ttl=63 time=54.2 ms
-64 bytes from 10.123.114.11: icmp_seq=2 ttl=63 time=51.2 ms
-```
-
-Add firewall rules in gcloud to open up ports:
-```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 - <<EOF
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: tiller
-  namespace: kube-system
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: tiller
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: cluster-admin
-subjects:
-  - kind: ServiceAccount
-    name: tiller
-    namespace: kube-system
-EOF
-```
-
-Install Tiller:
-
-```bash
-helm init --service-account tiller
-```
-
-### 4. Install Feast with Helm
-
-Clone the [Feast repository](https://github.com/gojek/feast/) and navigate to the `charts` sub-directory:
-
-```bash
-git clone https://github.com/gojek/feast.git && cd feast && \
-git checkout 0.3-dev && \
-export FEAST_HOME_DIR=$(pwd) && \
-cd infra/charts/feast
-```
-
-Make a copy of the Helm `values.yaml` so that it can be customized for your Feast deployment:
-
-```bash
-cp values.yaml my-feast-values.yaml
-```
-
-Update `my-feast-values.yaml` based on your GCP and GKE environment.
-
-* Required fields are paired with comments which indicate whether they need to be replaced.
-* All occurrences of `EXTERNAL_IP` should be replaced with either your domain name or the IP stored in `$FEAST_IP`.
-* Replace all occurrences of `YOUR_BUCKET_NAME` with your bucket name stored in `$FEAST_GCS_BUCKET`
-
-Install the Feast Helm chart:
-
-```bash
-helm install --name feast -f my-feast-values.yaml .
-```
-
-Ensure that the system comes online. This will take a few minutes
-
-```bash
-watch kubectl get pods
-```
-
-```bash
-NAME                                           READY   STATUS      RESTARTS   AGE
-pod/feast-feast-core-666fd46db4-l58l6          1/1     Running     0          5m
-pod/feast-feast-serving-online-84d99ddcbd      1/1     Running     0          6m
-pod/feast-kafka-0                              1/1     Running     0          3m
-pod/feast-kafka-1                              1/1     Running     0          4m
-pod/feast-kafka-2                              1/1     Running     0          4m
-pod/feast-postgresql-0                         1/1     Running     0          5m
-pod/feast-redis-master-0                       1/1     Running     0          5m
-pod/feast-zookeeper-0                          1/1     Running     0          5m
-pod/feast-zookeeper-1                          1/1     Running     0          5m
-pod/feast-zookeeper-2                          1/1     Running     0          5m
-```
-
-### 5. Connect to Feast with the Python SDK
-
-Install the Python SDK using pip:
-
-```bash
-pip install -e ${FEAST_HOME_DIR}/sdk/python
-```
-
-Configure the Feast Python SDK:
-
-```bash
-feast config set core_url ${FEAST_CORE_URL}
-feast config set serving_url ${FEAST_ONLINE_SERVING_URL}
-```
-
-That's it! You can now start to use Feast!
diff --git a/docs/installing-feast/docker-compose.md b/docs/installing-feast/docker-compose.md
new file mode 100644
index 000000000..fbc491b43
--- /dev/null
+++ b/docs/installing-feast/docker-compose.md
@@ -0,0 +1,112 @@
+# 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/gojek/feast/) and navigate to the `docker-compose` sub-directory:
+
+```bash
+git clone https://github.com/gojek/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\_BATCH\_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/installing-feast/gke.md b/docs/installing-feast/gke.md
new file mode 100644
index 000000000..162f0a260
--- /dev/null
+++ b/docs/installing-feast/gke.md
@@ -0,0 +1,211 @@
+# Google Kubernetes Engine \(GKE\)
+
+### Overview <a id="m_5245424069798496115gmail-overview-1"></a>
+
+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_BATCH_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 - <<EOF
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: tiller
+  namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: tiller
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: cluster-admin
+subjects:
+  - kind: ServiceAccount
+    name: tiller
+    namespace: kube-system
+EOF
+```
+
+Install Tiller:
+
+```bash
+helm init --service-account tiller
+```
+
+## 4. Install Feast with Helm
+
+Clone the [Feast repository](https://github.com/gojek/feast/) and navigate to the `charts` sub-directory:
+
+```bash
+git clone https://github.com/gojek/feast.git && cd feast && \
+export FEAST_HOME_DIR=$(pwd) && \
+cd infra/charts/feast
+```
+
+Make a copy of the Helm `values.yaml` so that it can be customized for your Feast deployment:
+
+```bash
+cp values.yaml my-feast-values.yaml
+```
+
+Update `my-feast-values.yaml` based on your GCP and GKE environment.
+
+* Required fields are paired with comments which indicate whether they need to be replaced.
+* All occurrences of `EXTERNAL_IP` should be replaced with either a domain pointing to a load balancer for the cluster or the IP stored in `$FEAST_IP`.
+* Replace all occurrences of `YOUR_BUCKET_NAME` with your bucket name stored in `$FEAST_GCS_BUCKET`
+* Change `feast-serving-batch.store.yaml.bigquery_config.project_id` to your GCP project Id.
+* Change `feast-serving-batch.store.yaml.bigquery_config.dataset_id` to the BigQuery dataset that Feast should use.
+
+Install the Feast Helm chart:
+
+```bash
+helm install --name feast -f my-feast-values.yaml .
+```
+
+Ensure that the system comes online. This will take a few minutes. 
+
+```bash
+kubectl get pods
+```
+
+There may be pod restarts while waiting for Kafka to come online.
+
+```bash
+NAME                                           READY   STATUS      RESTARTS   AGE
+pod/feast-feast-core-666fd46db4-l58l6          1/1     Running     2          5m
+pod/feast-feast-serving-online-84d99ddcbd      1/1     Running     3          6m
+pod/feast-kafka-0                              1/1     Running     0          3m
+pod/feast-kafka-1                              1/1     Running     0          4m
+pod/feast-kafka-2                              1/1     Running     0          4m
+pod/feast-postgresql-0                         1/1     Running     0          5m
+pod/feast-redis-master-0                       1/1     Running     0          5m
+pod/feast-zookeeper-0                          1/1     Running     0          5m
+pod/feast-zookeeper-1                          1/1     Running     0          5m
+pod/feast-zookeeper-2                          1/1     Running     0          5m
+```
+
+## 5. Connect to Feast with the Python SDK
+
+Install the Python SDK using pip:
+
+```bash
+pip install feast
+```
+
+Configure the Feast Python SDK:
+
+```bash
+feast config set core_url ${FEAST_CORE_URL}
+feast config set serving_url ${FEAST_ONLINE_SERVING_URL}
+```
+
+Test whether you are able to connect to Feast Core
+
+```text
+feast projects list
+```
+
+Should print an empty list:
+
+```text
+NAME
+```
+
+That's it! You can now start to use Feast!
+
+Please see our [examples](https://github.com/gojek/feast/blob/master/examples/) to get started.
+
diff --git a/docs/installing-feast/overview.md b/docs/installing-feast/overview.md
new file mode 100644
index 000000000..e0cd44e96
--- /dev/null
+++ b/docs/installing-feast/overview.md
@@ -0,0 +1,14 @@
+# Overview
+
+This installation guide will demonstrate three ways of installing Feast:
+
+* \*\*\*\*[**Docker Compose \(Quickstart\)**](docker-compose.md)**:**
+  * Fastest way to get Feast up and running.
+  * Provides a pre-installed Jupyter Notebook with sample code.
+  * Does not officially support a production job manager like Dataflow
+* \*\*\*\*[**Google Kubernetes Engine**](gke.md)**:**
+  * Recommended way to install Feast for production use.
+  * The guide has dependencies on BigQuery, and Google Cloud Storage.
+
+
+
diff --git a/docs/installing-feast/troubleshooting.md b/docs/installing-feast/troubleshooting.md
new file mode 100644
index 000000000..bcd4c9bdf
--- /dev/null
+++ b/docs/installing-feast/troubleshooting.md
@@ -0,0 +1,168 @@
+# Troubleshooting
+
+If at any point in time you cannot resolve a problem, please see the [Getting Help](../getting-help.md) section for reaching out to the Feast community.
+
+## How can I verify that all services are operational?
+
+### Docker Compose
+
+The containers should be in an `up` state:
+
+```text
+docker ps
+```
+
+```text
+CONTAINER ID        IMAGE                                  COMMAND                  CREATED             STATUS              PORTS                                            NAMES
+d7447205bced        jupyter/datascience-notebook:latest    "tini -g -- start-no…"   2 minutes ago       Up 2 minutes        0.0.0.0:8888->8888/tcp                           feast_jupyter_1
+8e49dbe81b92        gcr.io/kf-feast/feast-serving:latest   "java -Xms1024m -Xmx…"   2 minutes ago       Up 5 seconds        0.0.0.0:6567->6567/tcp                           feast_batch-serving_1
+b859494bd33a        gcr.io/kf-feast/feast-serving:latest   "java -jar /opt/feas…"   2 minutes ago       Up About a minute   0.0.0.0:6566->6566/tcp                           feast_online-serving_1
+5c4962811767        gcr.io/kf-feast/feast-core:latest      "java -jar /opt/feas…"   2 minutes ago       Up 2 minutes        0.0.0.0:6565->6565/tcp                           feast_core_1
+1ba7239e0ae0        confluentinc/cp-kafka:5.2.1            "/etc/confluent/dock…"   2 minutes ago       Up 2 minutes        0.0.0.0:9092->9092/tcp, 0.0.0.0:9094->9094/tcp   feast_kafka_1
+e2779672735c        confluentinc/cp-zookeeper:5.2.1        "/etc/confluent/dock…"   2 minutes ago       Up 2 minutes        2181/tcp, 2888/tcp, 3888/tcp                     feast_zookeeper_1
+39ac26f5c709        postgres:12-alpine                     "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        5432/tcp                                         feast_db_1
+3c4ee8616096        redis:5-alpine                         "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        0.0.0.0:6379->6379/tcp                           feast_redis_1
+```
+
+### Google Kubernetes Engine
+
+All services should either be in a `running` state or `complete`state:
+
+```text
+kubectl get pods
+```
+
+```text
+NAME                                                READY   STATUS      RESTARTS   AGE
+feast-feast-core-5ff566f946-4wlbh                   1/1     Running     1          32m
+feast-feast-serving-batch-848d74587b-96hq6          1/1     Running     2          32m
+feast-feast-serving-online-df69755d5-fml8v          1/1     Running     2          32m
+feast-kafka-0                                       1/1     Running     1          32m
+feast-kafka-1                                       1/1     Running     0          30m
+feast-kafka-2                                       1/1     Running     0          29m
+feast-kafka-config-3e860262-zkzr8                   0/1     Completed   0          32m
+feast-postgresql-0                                  1/1     Running     0          32m
+feast-prometheus-statsd-exporter-554db85b8d-r4hb8   1/1     Running     0          32m
+feast-redis-master-0                                1/1     Running     0          32m
+feast-zookeeper-0                                   1/1     Running     0          32m
+feast-zookeeper-1                                   1/1     Running     0          32m
+feast-zookeeper-2                                   1/1     Running     0          31m
+```
+
+## How can I verify that I can connect to all services?
+
+First find the `IP:Port` combination of your services.
+
+### **Docker Compose \(from inside the docker cluster\)**
+
+You will probably need to connect using the hostnames of services and standard Feast ports:
+
+```bash
+export FEAST_CORE_URL=core:6565
+export FEAST_ONLINE_SERVING_URL=online-serving:6566
+export FEAST_BATCH_SERVING_URL=batch-serving:6567
+```
+
+### **Docker Compose \(from outside the docker cluster\)**
+
+You will probably need to connect using `localhost` and standard ports:
+
+```bash
+export FEAST_CORE_URL=localhost:6565
+export FEAST_ONLINE_SERVING_URL=localhost:6566
+export FEAST_BATCH_SERVING_URL=localhost:6567
+```
+
+### **Google Kubernetes Engine \(GKE\)**
+
+You will need to find the external IP of one of the nodes as well as the NodePorts. Please make sure that your firewall is open for these ports:
+
+```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_BATCH_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 Feast Core:
+
+```bash
+grpc_cli ls ${FEAST_CORE_URL} feast.core.CoreService
+```
+
+```text
+GetFeastCoreVersion
+GetFeatureSet
+ListFeatureSets
+ListStores
+ApplyFeatureSet
+UpdateStore
+CreateProject
+ArchiveProject
+ListProjects
+```
+
+### Testing Feast Batch Serving and Online Serving
+
+```bash
+grpc_cli ls ${FEAST_BATCH_SERVING_URL} feast.serving.ServingService
+```
+
+```text
+GetFeastServingInfo
+GetOnlineFeatures
+GetBatchFeatures
+GetJob
+```
+
+```bash
+grpc_cli ls ${FEAST_ONLINE_SERVING_URL} feast.serving.ServingService
+```
+
+```text
+GetFeastServingInfo
+GetOnlineFeatures
+GetBatchFeatures
+GetJob
+```
+
+## How can I print logs from the Feast Services?
+
+Feast will typically have three services that you need to monitor if something goes wrong.
+
+* Feast Core
+* Feast Serving \(Online\)
+* Feast Serving \(Batch\)
+
+In order to print the logs from these services, please run the commands below.
+
+### Docker Compose
+
+```text
+ docker logs -f feast_core_1
+```
+
+```text
+docker logs -f feast_batch-serving_1
+```
+
+```text
+docker logs -f feast_online-serving_1
+```
+
+### Google Kubernetes Engine
+
+```text
+kubectl logs $(kubectl get pods | grep feast-core | awk '{print $1}')
+```
+
+```text
+kubectl logs $(kubectl get pods | grep feast-serving-batch | awk '{print $1}')
+```
+
+```text
+kubectl logs $(kubectl get pods | grep feast-serving-online | awk '{print $1}')
+```
+
diff --git a/examples/basic/basic.ipynb b/examples/basic/basic.ipynb
index 49658b423..94fc82f2c 100644
--- a/examples/basic/basic.ipynb
+++ b/examples/basic/basic.ipynb
@@ -15,15 +15,15 @@
     "1. Create a synthetic customer feature dataset\n",
     "2. Register a feature set to represent these features in Feast\n",
     "3. Ingest these features into Feast\n",
-    "4. Create a feature query and retrieve historical feature data\n",
-    "5. Create a feature query and retrieve online feature data"
+    "4. Create a feature query and retrieve online feature data\n",
+    "5. Create a feature query and retrieve historical feature data"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 1. Clone Feast and install all dependencies"
+    "### 0. Configuration"
    ]
   },
   {
@@ -32,9 +32,79 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "!git clone https://github.com/gojek/feast.git \\\n",
-    "&& cd feast/sdk/python/ &&  pip install --upgrade --quiet -e . \\\n",
-    "&& pip install --quiet --upgrade pandas numpy protobuf"
+    "import os\n",
+    "\n",
+    "# Feast Core acts as the central feature registry\n",
+    "FEAST_CORE_URL = os.getenv('FEAST_CORE_URL', 'core:6565')\n",
+    "\n",
+    "# Feast Online Serving allows for the retrieval of real-time feature data\n",
+    "FEAST_ONLINE_SERVING_URL = os.getenv('FEAST_ONLINE_SERVING_URL', 'online-serving:6566')\n",
+    "\n",
+    "# Feast Batch Serving allows for the retrieval of historical feature data\n",
+    "FEAST_BATCH_SERVING_URL = os.getenv('FEAST_BATCH_SERVING_URL', 'batch-serving:6567')\n",
+    "\n",
+    "# PYTHON_REPOSITORY_PATH is the path to the Python SDK inside the Feast Git Repo\n",
+    "PYTHON_REPOSITORY_PATH = os.getenv('PYTHON_REPOSITORY_PATH', '../../')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 1. Install Feast SDK"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Install from PyPi"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!pip install --ignore-installed --upgrade feast"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "(Alternative) Install from local repository"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import sys\n",
+    "os.environ['PYTHON_SDK_PATH'] = os.path.join(PYTHON_REPOSITORY_PATH, 'sdk/python')\n",
+    "sys.path.append(os.environ['PYTHON_SDK_PATH'])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!echo $PYTHON_SDK_PATH"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!python -m pip install --ignore-installed --upgrade -e  ${PYTHON_SDK_PATH}"
    ]
   },
   {
@@ -66,7 +136,25 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 3. Configure Feast services and connect the Feast client"
+    "### 3. Configure Feast services and connect the Feast client\n",
+    "\n",
+    "Connect to Feast Core and Feast Online Serving"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "client = Client(core_url=FEAST_CORE_URL, serving_url=FEAST_ONLINE_SERVING_URL)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Create a project workspace"
    ]
   },
   {
@@ -75,9 +163,14 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "CORE_URL = 'localhost:6565'\n",
-    "ONLINE_SERVING_URL = 'localhost:6566'\n",
-    "BATCH_SERVING_URL = 'localhost:6567'"
+    "client.create_project('customer_project')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Set the active project"
    ]
   },
   {
@@ -86,8 +179,6 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "client = Client(core_url=CORE_URL, serving_url=BATCH_SERVING_URL) # Connect to Feast Core\n",
-    "client.create_project('customer_project')\n",
     "client.set_project('customer_project')"
    ]
   },
@@ -95,7 +186,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 4. Create synthetic customer features"
+    "### 4. Create customer features"
    ]
   },
   {
@@ -132,7 +223,7 @@
     "    }\n",
     ")\n",
     "\n",
-    "print(customer_features.head(10))"
+    "print(customer_features.head(500))"
    ]
   },
   {
@@ -147,9 +238,7 @@
    "metadata": {},
    "source": [
     "Now we will create a feature set for these features. Feature sets are essentially a schema that represent\n",
-    "feature values. Feature sets allow Feast to both identify feature values and their structure. \n",
-    "\n",
-    "In this case we need to define any entity columns as well as the maximum age. The entity column in this case is \"customer_id\". Max age is set to 1 day (defined in seconds). This means that for each feature query during retrieval, the serving API will only retrieve features up to a maximum of 1 day per provided timestamp and entity combination. "
+    "feature values. Feature sets allow Feast to both identify feature values and their structure. The following feature set contains no features yet."
    ]
   },
   {
@@ -160,8 +249,8 @@
    "source": [
     "customer_fs = FeatureSet(\n",
     "    \"customer_transactions\",\n",
-    "    max_age=Duration(seconds=86400),\n",
-    "    entities=[Entity(name='customer_id', dtype=ValueType.INT64)]\n",
+    "    entities=[Entity(name='customer_id', dtype=ValueType.INT64)],\n",
+    "    max_age=Duration(seconds=432000)    \n",
     ")"
    ]
   },
@@ -169,7 +258,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Here we are automatically inferring the schema from the provided dataset"
+    "Here we are automatically inferring the schema from the provided dataset. The two features from the dataset will be added to the feature set"
    ]
   },
   {
@@ -241,16 +330,21 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 8. Create a batch retrieval query"
+    "### 8. Retrieve online features"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "In order to retrieve historical feature data, the user must provide an entity_rows dataframe. This dataframe contains a combination of timestamps and entities. In this case, the user must provide both customer_ids and timestamps. \n",
-    "\n",
-    "We will randomly generate timestamps over the last 30 days, and assign customer_ids to them. When these entity rows are sent to the Feast Serving API to retrieve feature values, along with a list of feature ids, Feast is then able to attach the correct feature values to each entity row. The one exception is if the feature values fall outside of the maximum age window."
+    "The process of retrieving features from the online API is very similar to that of the batch API. The only major difference is that users do not have to provide timestamps (only the latest features are returned, as long as they are within the maximum age window)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The example below retrieves online features for a single customer: \"1001\". It is possible to retrieve any features from feast, even outside of the current project."
    ]
   },
   {
@@ -259,23 +353,51 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "event_timestamps = [datetime.utcnow().replace(tzinfo=utc) - timedelta(days=randrange(15), hours=randrange(24), minutes=randrange(60)) for day in range(30)]\n",
-    "\n",
-    "entity_rows = pd.DataFrame(\n",
-    "    {\n",
-    "        \"datetime\": event_timestamps,\n",
-    "        \"customer_id\": [customers[idx % len(customers)] for idx in range(len(event_timestamps))],\n",
-    "    }\n",
+    "online_features = client.get_online_features(\n",
+    "    feature_refs=[\n",
+    "        f\"daily_transactions\",\n",
+    "        f\"total_transactions\",\n",
+    "    ],\n",
+    "    entity_rows=[\n",
+    "        GetOnlineFeaturesRequest.EntityRow(\n",
+    "            fields={\n",
+    "                \"customer_id\": Value(\n",
+    "                    int64_val=1001)\n",
+    "            }\n",
+    "        )\n",
+    "    ],\n",
     ")\n",
-    "\n",
-    "print(entity_rows.head(10))"
+    "print(online_features)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 9. Retrieve historical/batch features"
+    " "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### The following section requires Google Cloud Platform (Google Cloud Storage and BigQuery)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 9. Create a batch retrieval query"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In order to retrieve historical feature data, the user must provide an entity_rows dataframe. This dataframe contains a combination of timestamps and entities. In this case, the user must provide both customer_ids and timestamps. \n",
+    "\n",
+    "We will randomly generate timestamps over the last 30 days, and assign customer_ids to them. When these entity rows are sent to the Feast Serving API to retrieve feature values, along with a list of feature ids, Feast is then able to attach the correct feature values to each entity row. "
    ]
   },
   {
@@ -284,29 +406,30 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "job = client.get_batch_features(\n",
-    "                            feature_refs=[\n",
-    "                                f\"daily_transactions\", \n",
-    "                                f\"total_transactions\", \n",
-    "                               ],\n",
-    "                            entity_rows=entity_rows\n",
-    "                         )\n",
-    "df = job.to_dataframe()\n",
-    "print(df.head(10))"
+    "event_timestamps = [datetime.utcnow().replace(tzinfo=utc) - timedelta(days=randrange(15), hours=randrange(24), minutes=randrange(60)) for day in range(30)]\n",
+    "\n",
+    "entity_rows = pd.DataFrame(\n",
+    "    {\n",
+    "        \"datetime\": event_timestamps,\n",
+    "        \"customer_id\": [customers[idx % len(customers)] for idx in range(len(event_timestamps))],\n",
+    "    }\n",
+    ")\n",
+    "\n",
+    "print(entity_rows.head(10))"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### 10. Retrieve online features"
+    "### 10. Retrieve historical/batch features"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The process of retrieving features from the online API is very similar to that of the batch API. The only major difference is that users do not have to provide timestamps (only the latest features are returned, as long as they are within the maximum age window)"
+    "Next we will create a new client object, but this time we will configure it to connect to the Batch Serving Service. This service will allow us to retrieve historical feature data."
    ]
   },
   {
@@ -315,37 +438,39 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "online_client = Client(core_url=CORE_URL, serving_url=ONLINE_SERVING_URL)\n",
-    "online_client.set_project(\"customer_project\")"
+    "batch_client = Client(core_url=FEAST_CORE_URL, serving_url=FEAST_BATCH_SERVING_URL)\n",
+    "batch_client.set_project(\"customer_project\")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The example below retrieves online features for a single customer: \"1001\""
+    "By calling the `get_batch_features` method we are able to retrieve a `job` object for the exporting of feature data. For every entity and timestamp combination in `entity_rows` we will be receiving a row with feature values joined to it."
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "scrolled": true
+   },
    "outputs": [],
    "source": [
-    "online_features = online_client.get_online_features(\n",
-    "    feature_refs=[\n",
-    "        f\"daily_transactions\",\n",
-    "        f\"total_transactions\",\n",
-    "    ],\n",
-    "    entity_rows=[\n",
-    "        GetOnlineFeaturesRequest.EntityRow(\n",
-    "            fields={\n",
-    "                \"customer_id\": Value(\n",
-    "                    int64_val=1001)\n",
-    "            }\n",
-    "        )\n",
-    "    ],\n",
-    ")"
+    "job = batch_client.get_batch_features(\n",
+    "                            feature_refs=[\n",
+    "                                f\"customer_project/daily_transactions\", \n",
+    "                                f\"customer_project/total_transactions\", \n",
+    "                               ],\n",
+    "                            entity_rows=entity_rows\n",
+    "                         )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Once the job is complete, it is possible to retrieve the exported data (from Google Cloud Storage) and load it into memory as a Pandas Dataframe."
    ]
   },
   {
@@ -354,7 +479,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "print(online_features)"
+    "df = job.to_dataframe()\n",
+    "print(df.head(10))"
    ]
   }
  ],
@@ -374,7 +500,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.3"
+   "version": "3.7.4"
   },
   "pycharm": {
    "stem_cell": {
diff --git a/infra/charts/feast/README.md b/infra/charts/feast/README.md
index 0463a9a3f..ab5321ca8 100644
--- a/infra/charts/feast/README.md
+++ b/infra/charts/feast/README.md
@@ -36,10 +36,10 @@ helm repo add feast-charts https://feast-charts.storage.googleapis.com
 helm repo update
 ```
 
-Install Feast release with minimal features, without batch serving and persistency:
+Install Feast release with minimal features, without batch serving and persistence:
 ```bash
 RELEASE_NAME=demo
-helm install feast-charts/feast --name $RELEASE_NAME --version 0.3.2 -f values-demo.yaml
+helm install feast-charts/feast --name $RELEASE_NAME -f values-demo.yaml
 ```
 
 Install Feast release for typical use cases, with batch and online serving:
@@ -60,7 +60,7 @@ PROJECT_ID=google-cloud-project-id
 DATASET_ID=bigquery-dataset-id
 
 # Install the Helm release using default values.yaml
-helm install feast-charts/feast --name feast --version 0.3.2 \
+helm install feast-charts/feast --name feast \
   --set feast-serving-batch."application\.yaml".feast.jobs.staging-location=$STAGING_LOCATION \
   --set feast-serving-batch."store\.yaml".bigquery_config.project_id=$PROJECT_ID \
   --set feast-serving-batch."store\.yaml".bigquery_config.dataset_id=$DATASET_ID
diff --git a/infra/charts/feast/charts/feast-core/templates/deployment.yaml b/infra/charts/feast/charts/feast-core/templates/deployment.yaml
index 02a533c26..0671d9574 100644
--- a/infra/charts/feast/charts/feast-core/templates/deployment.yaml
+++ b/infra/charts/feast/charts/feast-core/templates/deployment.yaml
@@ -40,7 +40,7 @@ spec:
 
       containers:
       - name: {{ .Chart.Name }}
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+        image: '{{ .Values.image.repository }}:{{ required "No .image.tag found. This must be provided as input."  .Values.image.tag }}'
         imagePullPolicy: {{ .Values.image.pullPolicy }}
         
         volumeMounts:
diff --git a/infra/charts/feast/charts/feast-core/values.yaml b/infra/charts/feast/charts/feast-core/values.yaml
index 321d71c84..f746bc96e 100644
--- a/infra/charts/feast/charts/feast-core/values.yaml
+++ b/infra/charts/feast/charts/feast-core/values.yaml
@@ -42,7 +42,6 @@ replicaCount: 1
 # image configures the Docker image for Feast Core
 image:
   repository: gcr.io/kf-feast/feast-core
-  tag: 0.3.2
   pullPolicy: IfNotPresent
 
 # application.yaml is the main configuration for Feast Core application.
diff --git a/infra/charts/feast/charts/feast-serving/templates/deployment.yaml b/infra/charts/feast/charts/feast-serving/templates/deployment.yaml
index 5be636df9..e6824a234 100644
--- a/infra/charts/feast/charts/feast-serving/templates/deployment.yaml
+++ b/infra/charts/feast/charts/feast-serving/templates/deployment.yaml
@@ -47,7 +47,7 @@ spec:
 
       containers:
       - name: {{ .Chart.Name }}
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+        image: '{{ .Values.image.repository }}:{{ required "No .image.tag found. This must be provided as input."  .Values.image.tag }}'
         imagePullPolicy: {{ .Values.image.pullPolicy }}
         
         volumeMounts:
diff --git a/infra/charts/feast/charts/feast-serving/values.yaml b/infra/charts/feast/charts/feast-serving/values.yaml
index d489a4874..d2b3c5994 100644
--- a/infra/charts/feast/charts/feast-serving/values.yaml
+++ b/infra/charts/feast/charts/feast-serving/values.yaml
@@ -29,7 +29,6 @@ replicaCount: 1
 # image configures the Docker image for Feast Serving
 image:
   repository: gcr.io/kf-feast/feast-serving
-  tag: 0.3.2
   pullPolicy: IfNotPresent
 
 # application.yaml is the main configuration for Feast Serving application.
diff --git a/infra/charts/feast/requirements.yaml b/infra/charts/feast/requirements.yaml
index ed280d64b..5416ded3f 100644
--- a/infra/charts/feast/requirements.yaml
+++ b/infra/charts/feast/requirements.yaml
@@ -1,12 +1,12 @@
 dependencies:
 - name: feast-core
-  version: 0.3.2
+  version: 0.4.4
   condition: feast-core.enabled
 - name: feast-serving
   alias: feast-serving-batch
-  version: 0.3.2
+  version: 0.4.4
   condition: feast-serving-batch.enabled
 - name: feast-serving
   alias: feast-serving-online
-  version: 0.3.2
+  version: 0.4.4
   condition: feast-serving-online.enabled
diff --git a/infra/charts/feast/values.yaml b/infra/charts/feast/values.yaml
index db3ec44f3..f9a0a76dc 100644
--- a/infra/charts/feast/values.yaml
+++ b/infra/charts/feast/values.yaml
@@ -12,10 +12,6 @@
 # of Feast Serving: online and batch will be deployed. Both described
 # using the same chart "feast-serving".
 #
-# The following are default values for typical Feast deployment, but not
-# for production setting. Refer to "values-production.yaml" for recommended
-# values in production environment.
-#
 # Note that the import job by default uses DirectRunner
 # https://beam.apache.org/documentation/runners/direct/
 # in this configuration since it allows Feast to run in more environments
@@ -45,16 +41,18 @@
 # ============================================================
 
 feast-core:
-  # enabled specifies whether to install Feast Core component.
+  # If enabled specifies whether to install Feast Core component.
   #
   # Normally, this is set to "false" when Feast users need access to low latency
   # Feast Serving, by deploying multiple instances of Feast Serving closest
   # to the client. These instances of Feast Serving however can still use
   # the same shared Feast Core.
   enabled: true
-  # Specify what image tag to use. Keep this consistent for all components
+
+  # Specify which image tag to use. Keep this consistent for all components
   image:
     tag: "0.4.4"
+
   # jvmOptions are options that will be passed to the Java Virtual Machine (JVM)
   # running Feast Core.
   #
@@ -63,6 +61,7 @@ feast-core:
   jvmOptions:
   - -Xms1024m
   - -Xmx1024m
+
   # resources that should be allocated to Feast Core.
   resources:
     requests:
@@ -70,6 +69,7 @@ feast-core:
       memory: 1024Mi
     limits:
       memory: 2048Mi
+
   # gcpServiceAccount is the Google service account that Feast Core will use.
   gcpServiceAccount:
     # useExistingSecret specifies Feast to use an existing secret containing
diff --git a/infra/docker-compose/.env.sample b/infra/docker-compose/.env.sample
index e14bde277..c8652e8fe 100644
--- a/infra/docker-compose/.env.sample
+++ b/infra/docker-compose/.env.sample
@@ -1,19 +1,21 @@
+# General
 COMPOSE_PROJECT_NAME=feast
-
 FEAST_VERSION=latest
 
+# Feast Core
 FEAST_CORE_IMAGE=gcr.io/kf-feast/feast-core
-FEAST_CORE_CONFIG=direct-runner
-FEAST_CORE_GCP_SERVICE_ACCOUNT_KEY=placeholder
+FEAST_CORE_CONFIG=direct-runner.yml
+FEAST_CORE_GCP_SERVICE_ACCOUNT_KEY=placeholder.json
 
+# Feast Serving
 FEAST_SERVING_IMAGE=gcr.io/kf-feast/feast-serving
-FEAST_ONLINE_SERVING_CONFIG=online-serving
-FEAST_ONLINE_STORE_CONFIG=redis-store
-FEAST_BATCH_SERVING_CONFIG=batch-serving
-FEAST_BATCH_STORE_CONFIG=bq-store
-FEAST_BATCH_SERVING_GCP_SERVICE_ACCOUNT_KEY=placeholder
-FEAST_JOB_STAGING_LOCATION=gs://your-gcp-project/bucket
+FEAST_ONLINE_SERVING_CONFIG=online-serving.yml
+FEAST_ONLINE_STORE_CONFIG=redis-store.yml
+FEAST_BATCH_SERVING_CONFIG=batch-serving.yml
+FEAST_BATCH_STORE_CONFIG=bq-store.yml
+FEAST_BATCH_SERVING_GCP_SERVICE_ACCOUNT_KEY=placeholder.json
+FEAST_JOB_STAGING_LOCATION=gs://your-gcs-bucket/staging
 
-FEAST_JUPYTER_IMAGE=gcr.io/kf-feast/feast-jupyter
-FEAST_JUPYTER_GCP_SERVICE_ACCOUNT_KEY=placeholder
+# Jupyter
+FEAST_JUPYTER_GCP_SERVICE_ACCOUNT_KEY=placeholder.json
 
diff --git a/infra/docker-compose/docker-compose.batch.yml b/infra/docker-compose/docker-compose.batch.yml
deleted file mode 100644
index c00ac9475..000000000
--- a/infra/docker-compose/docker-compose.batch.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-version: "3.7"
-
-services:
-  batch-serving:
-    image: ${FEAST_SERVING_IMAGE}:${FEAST_VERSION}
-    volumes:
-      - ./serving/${FEAST_BATCH_SERVING_CONFIG}.yml:/etc/feast/application.yml
-      - ./serving/${FEAST_BATCH_STORE_CONFIG}.yml:/etc/feast/store.yml
-      - ./gcp-service-accounts/${FEAST_BATCH_SERVING_GCP_SERVICE_ACCOUNT_KEY}.json:/etc/gcloud/service-accounts/key.json
-    depends_on:
-      - core
-      - redis
-    ports:
-      - 6567:6567
-    restart: on-failure
-    environment:
-      GOOGLE_APPLICATION_CREDENTIALS: /etc/gcloud/service-accounts/key.json
-      FEAST_JOB_STAGING_LOCATION: ${FEAST_JOB_STAGING_LOCATION}
-    command:
-      - "java"
-      - "-Xms1024m"
-      - "-Xmx1024m"
-      - "-jar"
-      - "/opt/feast/feast-serving.jar"
-      - "--spring.config.location=classpath:/application.yml,file:/etc/feast/application.yml"
\ No newline at end of file
diff --git a/infra/docker-compose/docker-compose.yml b/infra/docker-compose/docker-compose.yml
index 44750650c..27d82efc3 100644
--- a/infra/docker-compose/docker-compose.yml
+++ b/infra/docker-compose/docker-compose.yml
@@ -4,8 +4,8 @@ services:
   core:
     image: ${FEAST_CORE_IMAGE}:${FEAST_VERSION}
     volumes:
-      - ./core/${FEAST_CORE_CONFIG}.yml:/etc/feast/application.yml
-      - ./gcp-service-accounts/${FEAST_CORE_GCP_SERVICE_ACCOUNT_KEY}.json:/etc/gcloud/service-accounts/key.json
+      - ./core/${FEAST_CORE_CONFIG}:/etc/feast/application.yml
+      - ./gcp-service-accounts/${FEAST_CORE_GCP_SERVICE_ACCOUNT_KEY}:/etc/gcloud/service-accounts/key.json
     environment:
       DB_HOST: db
       GOOGLE_APPLICATION_CREDENTIALS: /etc/gcloud/service-accounts/key.json
@@ -24,8 +24,8 @@ services:
   online-serving:
     image: ${FEAST_SERVING_IMAGE}:${FEAST_VERSION}
     volumes:
-      - ./serving/${FEAST_ONLINE_SERVING_CONFIG}.yml:/etc/feast/application.yml
-      - ./serving/${FEAST_ONLINE_STORE_CONFIG}.yml:/etc/feast/store.yml
+      - ./serving/${FEAST_ONLINE_SERVING_CONFIG}:/etc/feast/application.yml
+      - ./serving/${FEAST_ONLINE_STORE_CONFIG}:/etc/feast/store.yml
     depends_on:
       - core
       - redis
@@ -38,12 +38,34 @@ services:
       - /opt/feast/feast-serving.jar
       - --spring.config.location=classpath:/application.yml,file:/etc/feast/application.yml
 
+  batch-serving:
+    image: ${FEAST_SERVING_IMAGE}:${FEAST_VERSION}
+    volumes:
+      - ./serving/${FEAST_BATCH_SERVING_CONFIG}:/etc/feast/application.yml
+      - ./serving/${FEAST_BATCH_STORE_CONFIG}:/etc/feast/store.yml
+      - ./gcp-service-accounts/${FEAST_BATCH_SERVING_GCP_SERVICE_ACCOUNT_KEY}:/etc/gcloud/service-accounts/key.json
+    depends_on:
+      - core
+      - redis
+    ports:
+      - 6567:6567
+    restart: on-failure
+    environment:
+      GOOGLE_APPLICATION_CREDENTIALS: /etc/gcloud/service-accounts/key.json
+      FEAST_JOB_STAGING_LOCATION: ${FEAST_JOB_STAGING_LOCATION}
+    command:
+      - "java"
+      - "-Xms1024m"
+      - "-Xmx1024m"
+      - "-jar"
+      - "/opt/feast/feast-serving.jar"
+      - "--spring.config.location=classpath:/application.yml,file:/etc/feast/application.yml"
+
   jupyter:
-    image: ${FEAST_JUPYTER_IMAGE}:${FEAST_VERSION}
+    image: jupyter/datascience-notebook:latest
     volumes:
-      - ./jupyter/notebooks:/home/jovyan/feast-notebooks
-      - ./jupyter/features:/home/jovyan/features
-      - ./gcp-service-accounts/${FEAST_JUPYTER_GCP_SERVICE_ACCOUNT_KEY}.json:/etc/gcloud/service-accounts/key.json
+      - ../../:/home/jovyan/feast
+      - ./gcp-service-accounts/${FEAST_JUPYTER_GCP_SERVICE_ACCOUNT_KEY}:/etc/gcloud/service-accounts/key.json
     depends_on:
       - core
       - online-serving
diff --git a/infra/docker-compose/jupyter/features/cust_trans_fs.yaml b/infra/docker-compose/jupyter/features/cust_trans_fs.yaml
deleted file mode 100644
index eb21ce9b3..000000000
--- a/infra/docker-compose/jupyter/features/cust_trans_fs.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-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
\ No newline at end of file
diff --git a/infra/docker-compose/jupyter/features/cust_trans_fs_updated.yaml b/infra/docker-compose/jupyter/features/cust_trans_fs_updated.yaml
deleted file mode 100644
index 8293d04b8..000000000
--- a/infra/docker-compose/jupyter/features/cust_trans_fs_updated.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: customer_transactions
-kind: feature_set
-entities:
-- name: customer_id
-  valueType: INT64
-features:
-- name: daily_transactions
-  valueType: FLOAT
-- name: total_transactions
-  valueType: FLOAT
-- name: discounts
-  valueType: FLOAT
-maxAge: 3600s
\ No newline at end of file
diff --git a/infra/docker-compose/jupyter/notebooks/feast-batch-serving.ipynb b/infra/docker-compose/jupyter/notebooks/feast-batch-serving.ipynb
deleted file mode 100644
index c288093f0..000000000
--- a/infra/docker-compose/jupyter/notebooks/feast-batch-serving.ipynb
+++ /dev/null
@@ -1,504 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Feast Batch Serving\n",
-    "This is an extension to `feast-quickstart` notebook to demonstrate the batch serving capability of Feast.\n",
-    "\n",
-    "## Prerequisite\n",
-    "- A running Feast Serving service with store configuration that supports batch retrieval. (eg. BigQuery store)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Data Preparation\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import feast\n",
-    "import numpy as np\n",
-    "import pandas as pd\n",
-    "from datetime import datetime, timedelta\n",
-    "from feast.serving.ServingService_pb2 import GetOnlineFeaturesRequest\n",
-    "from feast.types.Value_pb2 import Value as Value\n",
-    "from feast.client import Client\n",
-    "from feast.feature_set import FeatureSet"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "client = feast.Client(core_url=\"core:6565\", serving_url=\"batch-serving:6567\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "cust_trans_fs = FeatureSet.from_yaml(\"../features/cust_trans_fs.yaml\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Feature set updated/created: \"customer_transactions:1\".\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.apply(cust_trans_fs)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>datetime</th>\n",
-       "      <th>customer_id</th>\n",
-       "      <th>daily_transactions</th>\n",
-       "      <th>total_transactions</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>2019-12-06 02:17:46.899904</td>\n",
-       "      <td>10000</td>\n",
-       "      <td>2.797627</td>\n",
-       "      <td>175.978266</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>2019-12-06 02:17:46.899915</td>\n",
-       "      <td>10001</td>\n",
-       "      <td>4.931632</td>\n",
-       "      <td>153.871975</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>2019-12-06 02:17:46.899922</td>\n",
-       "      <td>10002</td>\n",
-       "      <td>0.206628</td>\n",
-       "      <td>108.558844</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>2019-12-06 02:17:46.899929</td>\n",
-       "      <td>10003</td>\n",
-       "      <td>2.354937</td>\n",
-       "      <td>119.549455</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>4</th>\n",
-       "      <td>2019-12-06 02:17:46.899937</td>\n",
-       "      <td>10004</td>\n",
-       "      <td>7.171423</td>\n",
-       "      <td>115.345183</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "                    datetime  customer_id  daily_transactions  \\\n",
-       "0 2019-12-06 02:17:46.899904        10000            2.797627   \n",
-       "1 2019-12-06 02:17:46.899915        10001            4.931632   \n",
-       "2 2019-12-06 02:17:46.899922        10002            0.206628   \n",
-       "3 2019-12-06 02:17:46.899929        10003            2.354937   \n",
-       "4 2019-12-06 02:17:46.899937        10004            7.171423   \n",
-       "\n",
-       "   total_transactions  \n",
-       "0          175.978266  \n",
-       "1          153.871975  \n",
-       "2          108.558844  \n",
-       "3          119.549455  \n",
-       "4          115.345183  "
-      ]
-     },
-     "execution_count": 5,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "offset = 10000\n",
-    "nr_of_customers = 5\n",
-    "customer_df = pd.DataFrame(\n",
-    "    {\n",
-    "        \"datetime\": [datetime.utcnow() for _ in range(nr_of_customers)],\n",
-    "        \"customer_id\": [offset + inc for inc in range(nr_of_customers)],\n",
-    "        \"daily_transactions\": [np.random.uniform(0, 10) for _ in range(nr_of_customers)],\n",
-    "        \"total_transactions\": [np.random.uniform(100, 200) for _ in range(nr_of_customers)],\n",
-    "    }\n",
-    ")\n",
-    "customer_df"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "100%|██████████| 5/5 [00:00<00:00,  7.24rows/s]"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Ingested 5 rows into customer_transactions:1\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.ingest(cust_trans_fs, dataframe=customer_df)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {
-    "pycharm": {
-     "name": "#%% md\n"
-    }
-   },
-   "source": [
-    "## Batch Retrieval\n",
-    "Batch retrieval takes a dataframe containing the entities column and event timestamp as an input. The result would be the outer join of the input and the features. The input dataframe needs to have a column named `datetime` as event timestamp. No results will be returned if the difference between the feature ingestion timestamp and the `event_timestamp` is greater than the `maxAge` parameter specified in the feature set."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "metadata": {
-    "pycharm": {
-     "name": "#%%\n"
-    }
-   },
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>customer_transactions_v1_feature_timestamp</th>\n",
-       "      <th>customer_id</th>\n",
-       "      <th>event_timestamp</th>\n",
-       "      <th>customer_transactions_v1_daily_transactions</th>\n",
-       "      <th>customer_transactions_v1_total_transactions</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>2019-12-06 02:17:46+00:00</td>\n",
-       "      <td>10001</td>\n",
-       "      <td>2019-12-06 02:17:55.612449+00:00</td>\n",
-       "      <td>4.931632</td>\n",
-       "      <td>153.871980</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>2019-12-06 02:17:46+00:00</td>\n",
-       "      <td>10004</td>\n",
-       "      <td>2019-12-06 02:17:55.612449+00:00</td>\n",
-       "      <td>7.171423</td>\n",
-       "      <td>115.345184</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>2019-12-06 02:17:46+00:00</td>\n",
-       "      <td>10000</td>\n",
-       "      <td>2019-12-06 02:17:55.612449+00:00</td>\n",
-       "      <td>2.797627</td>\n",
-       "      <td>175.978270</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>2019-12-06 02:17:46+00:00</td>\n",
-       "      <td>10002</td>\n",
-       "      <td>2019-12-06 02:17:55.612449+00:00</td>\n",
-       "      <td>0.206628</td>\n",
-       "      <td>108.558846</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>4</th>\n",
-       "      <td>2019-12-06 02:17:46+00:00</td>\n",
-       "      <td>10003</td>\n",
-       "      <td>2019-12-06 02:17:55.612449+00:00</td>\n",
-       "      <td>2.354937</td>\n",
-       "      <td>119.549450</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "  customer_transactions_v1_feature_timestamp  customer_id  \\\n",
-       "0                  2019-12-06 02:17:46+00:00        10001   \n",
-       "1                  2019-12-06 02:17:46+00:00        10004   \n",
-       "2                  2019-12-06 02:17:46+00:00        10000   \n",
-       "3                  2019-12-06 02:17:46+00:00        10002   \n",
-       "4                  2019-12-06 02:17:46+00:00        10003   \n",
-       "\n",
-       "                   event_timestamp  \\\n",
-       "0 2019-12-06 02:17:55.612449+00:00   \n",
-       "1 2019-12-06 02:17:55.612449+00:00   \n",
-       "2 2019-12-06 02:17:55.612449+00:00   \n",
-       "3 2019-12-06 02:17:55.612449+00:00   \n",
-       "4 2019-12-06 02:17:55.612449+00:00   \n",
-       "\n",
-       "   customer_transactions_v1_daily_transactions  \\\n",
-       "0                                     4.931632   \n",
-       "1                                     7.171423   \n",
-       "2                                     2.797627   \n",
-       "3                                     0.206628   \n",
-       "4                                     2.354937   \n",
-       "\n",
-       "   customer_transactions_v1_total_transactions  \n",
-       "0                                   153.871980  \n",
-       "1                                   115.345184  \n",
-       "2                                   175.978270  \n",
-       "3                                   108.558846  \n",
-       "4                                   119.549450  "
-      ]
-     },
-     "execution_count": 7,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "entity_df = customer_df[[\"customer_id\"]].assign(datetime=datetime.utcnow())\n",
-    "feature_ids=[\n",
-    "    \"customer_transactions:1:daily_transactions\",\n",
-    "    \"customer_transactions:1:total_transactions\",\n",
-    "]\n",
-    "batch_job = client.get_batch_features(feature_ids, entity_df)\n",
-    "batch_job.to_dataframe()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>customer_transactions_v1_feature_timestamp</th>\n",
-       "      <th>customer_id</th>\n",
-       "      <th>event_timestamp</th>\n",
-       "      <th>customer_transactions_v1_daily_transactions</th>\n",
-       "      <th>customer_transactions_v1_total_transactions</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>None</td>\n",
-       "      <td>10000</td>\n",
-       "      <td>2020-01-05 02:18:43.900732+00:00</td>\n",
-       "      <td>None</td>\n",
-       "      <td>None</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>None</td>\n",
-       "      <td>10001</td>\n",
-       "      <td>2020-01-05 02:18:43.900732+00:00</td>\n",
-       "      <td>None</td>\n",
-       "      <td>None</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>None</td>\n",
-       "      <td>10002</td>\n",
-       "      <td>2020-01-05 02:18:43.900732+00:00</td>\n",
-       "      <td>None</td>\n",
-       "      <td>None</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>None</td>\n",
-       "      <td>10003</td>\n",
-       "      <td>2020-01-05 02:18:43.900732+00:00</td>\n",
-       "      <td>None</td>\n",
-       "      <td>None</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>4</th>\n",
-       "      <td>None</td>\n",
-       "      <td>10004</td>\n",
-       "      <td>2020-01-05 02:18:43.900732+00:00</td>\n",
-       "      <td>None</td>\n",
-       "      <td>None</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "  customer_transactions_v1_feature_timestamp  customer_id  \\\n",
-       "0                                       None        10000   \n",
-       "1                                       None        10001   \n",
-       "2                                       None        10002   \n",
-       "3                                       None        10003   \n",
-       "4                                       None        10004   \n",
-       "\n",
-       "                   event_timestamp  \\\n",
-       "0 2020-01-05 02:18:43.900732+00:00   \n",
-       "1 2020-01-05 02:18:43.900732+00:00   \n",
-       "2 2020-01-05 02:18:43.900732+00:00   \n",
-       "3 2020-01-05 02:18:43.900732+00:00   \n",
-       "4 2020-01-05 02:18:43.900732+00:00   \n",
-       "\n",
-       "  customer_transactions_v1_daily_transactions  \\\n",
-       "0                                        None   \n",
-       "1                                        None   \n",
-       "2                                        None   \n",
-       "3                                        None   \n",
-       "4                                        None   \n",
-       "\n",
-       "  customer_transactions_v1_total_transactions  \n",
-       "0                                        None  \n",
-       "1                                        None  \n",
-       "2                                        None  \n",
-       "3                                        None  \n",
-       "4                                        None  "
-      ]
-     },
-     "execution_count": 8,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "stale_entity_df = customer_df[[\"customer_id\"]].assign(datetime=datetime.utcnow() + timedelta(days=30))\n",
-    "feature_ids=[\n",
-    "    \"customer_transactions:1:daily_transactions\",\n",
-    "    \"customer_transactions:1:total_transactions\",\n",
-    "]\n",
-    "batch_job = client.get_batch_features(feature_ids, stale_entity_df)\n",
-    "batch_job.to_dataframe()"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.7.3"
-  },
-  "pycharm": {
-   "stem_cell": {
-    "cell_type": "raw",
-    "metadata": {
-     "collapsed": false
-    },
-    "source": []
-   }
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
diff --git a/infra/docker-compose/jupyter/notebooks/feast-quickstart.ipynb b/infra/docker-compose/jupyter/notebooks/feast-quickstart.ipynb
deleted file mode 100644
index b89e59b1e..000000000
--- a/infra/docker-compose/jupyter/notebooks/feast-quickstart.ipynb
+++ /dev/null
@@ -1,569 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Feast Quick Start\n",
-    "This is a quick example to demonstrate:\n",
-    "- Register a feature set on Feast\n",
-    "- Ingest features into Feast\n",
-    "- Retrieve the ingested features from Feast\n",
-    "- Update a feature"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import feast\n",
-    "import numpy as np\n",
-    "import pandas as pd\n",
-    "from datetime import datetime\n",
-    "from feast.serving.ServingService_pb2 import GetOnlineFeaturesRequest\n",
-    "from feast.types.Value_pb2 import Value as Value\n",
-    "from feast.client import Client\n",
-    "from feast.feature_set import FeatureSet"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "First, instantiate the client.\n",
-    "Feast endpoints can be set via the following environmental variables: `FEAST_CORE_URL`, `FEAST_SERVING_URL`.\n",
-    "Alternatively, they can also be passed in explicitly as follows:\n",
-    "    \n",
-    "`client = feast.Client(core_url=core:6565, serving_url=online-serving:6566)`"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "client = feast.Client()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Register a feature set\n",
-    "\n",
-    "Let's create and register our first feature set. Below is an example of a basic customer transactions feature set that has been exported to YAML:\n",
-    "```\n",
-    "name: customer_transactions\n",
-    "kind: feature_set\n",
-    "entities:\n",
-    "- name: customer_id\n",
-    "  valueType: INT64\n",
-    "features:\n",
-    "- name: daily_transactions\n",
-    "  valueType: FLOAT\n",
-    "- name: total_transactions\n",
-    "  valueType: FLOAT\n",
-    "maxAge: 3600s    \n",
-    "```"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "cust_trans_fs = FeatureSet.from_yaml(\"../features/cust_trans_fs.yaml\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Feature set updated/created: \"customer_transactions:1\".\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.apply(cust_trans_fs)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Ingest features into Feast\n",
-    "The dataframe below contains the features and entities of the above feature set."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>datetime</th>\n",
-       "      <th>customer_id</th>\n",
-       "      <th>daily_transactions</th>\n",
-       "      <th>total_transactions</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>2019-11-26 12:03:47.320634</td>\n",
-       "      <td>10000</td>\n",
-       "      <td>5.178112</td>\n",
-       "      <td>110.670651</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>2019-11-26 12:03:47.320644</td>\n",
-       "      <td>10001</td>\n",
-       "      <td>0.268114</td>\n",
-       "      <td>195.393913</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>2019-11-26 12:03:47.320651</td>\n",
-       "      <td>10002</td>\n",
-       "      <td>1.486614</td>\n",
-       "      <td>136.929052</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>2019-11-26 12:03:47.320658</td>\n",
-       "      <td>10003</td>\n",
-       "      <td>9.676433</td>\n",
-       "      <td>166.022999</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>4</th>\n",
-       "      <td>2019-11-26 12:03:47.320665</td>\n",
-       "      <td>10004</td>\n",
-       "      <td>5.928573</td>\n",
-       "      <td>165.687951</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "                    datetime  customer_id  daily_transactions  \\\n",
-       "0 2019-11-26 12:03:47.320634        10000            5.178112   \n",
-       "1 2019-11-26 12:03:47.320644        10001            0.268114   \n",
-       "2 2019-11-26 12:03:47.320651        10002            1.486614   \n",
-       "3 2019-11-26 12:03:47.320658        10003            9.676433   \n",
-       "4 2019-11-26 12:03:47.320665        10004            5.928573   \n",
-       "\n",
-       "   total_transactions  \n",
-       "0          110.670651  \n",
-       "1          195.393913  \n",
-       "2          136.929052  \n",
-       "3          166.022999  \n",
-       "4          165.687951  "
-      ]
-     },
-     "execution_count": 5,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "offset = 10000\n",
-    "nr_of_customers = 5\n",
-    "customer_df = pd.DataFrame(\n",
-    "    {\n",
-    "        \"datetime\": [datetime.utcnow() for _ in range(nr_of_customers)],\n",
-    "        \"customer_id\": [offset + inc for inc in range(nr_of_customers)],\n",
-    "        \"daily_transactions\": [np.random.uniform(0, 10) for _ in range(nr_of_customers)],\n",
-    "        \"total_transactions\": [np.random.uniform(100, 200) for _ in range(nr_of_customers)],\n",
-    "    }\n",
-    ")\n",
-    "customer_df"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "  0%|          | 0/5 [00:00<?, ?rows/s]8rows/s]"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Ingested 5 rows into customer_transactions:1\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.ingest(cust_trans_fs, dataframe=customer_df)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Retrieve the ingested features from Feast\n",
-    "Once the features are ingested, they can be retrieved from the Feast store for model training or prediction purposes."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "field_values {\n",
-       "  fields {\n",
-       "    key: \"customer_id\"\n",
-       "    value {\n",
-       "      int64_val: 10001\n",
-       "    }\n",
-       "  }\n",
-       "  fields {\n",
-       "    key: \"customer_transactions:1:daily_transactions\"\n",
-       "    value {\n",
-       "      float_val: 0.2681143283843994\n",
-       "    }\n",
-       "  }\n",
-       "  fields {\n",
-       "    key: \"customer_transactions:1:total_transactions\"\n",
-       "    value {\n",
-       "      float_val: 195.39390563964844\n",
-       "    }\n",
-       "  }\n",
-       "}"
-      ]
-     },
-     "execution_count": 7,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "client.get_online_features(\n",
-    "    entity_rows=[\n",
-    "        GetOnlineFeaturesRequest.EntityRow(\n",
-    "            fields={\n",
-    "                \"customer_id\": Value(\n",
-    "                    int64_val=10001\n",
-    "                )\n",
-    "            }\n",
-    "        )\n",
-    "    ],\n",
-    "    feature_ids=[\n",
-    "        \"customer_transactions:1:daily_transactions\",\n",
-    "        \"customer_transactions:1:total_transactions\",\n",
-    "    ],\n",
-    ")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Update a feature\n",
-    "We can also update feature set which has been ingested."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "cust_trans_fs_updated = FeatureSet.from_yaml(\"../features/cust_trans_fs_updated.yaml\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Feature set updated/created: \"customer_transactions:2\".\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.apply(cust_trans_fs_updated)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>datetime</th>\n",
-       "      <th>customer_id</th>\n",
-       "      <th>daily_transactions</th>\n",
-       "      <th>total_transactions</th>\n",
-       "      <th>discounts</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>2019-11-26 12:03:47.320634</td>\n",
-       "      <td>10000</td>\n",
-       "      <td>5.178112</td>\n",
-       "      <td>110.670651</td>\n",
-       "      <td>8.389938</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>2019-11-26 12:03:47.320644</td>\n",
-       "      <td>10001</td>\n",
-       "      <td>0.268114</td>\n",
-       "      <td>195.393913</td>\n",
-       "      <td>0.430047</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>2019-11-26 12:03:47.320651</td>\n",
-       "      <td>10002</td>\n",
-       "      <td>1.486614</td>\n",
-       "      <td>136.929052</td>\n",
-       "      <td>7.408917</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>2019-11-26 12:03:47.320658</td>\n",
-       "      <td>10003</td>\n",
-       "      <td>9.676433</td>\n",
-       "      <td>166.022999</td>\n",
-       "      <td>1.192721</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>4</th>\n",
-       "      <td>2019-11-26 12:03:47.320665</td>\n",
-       "      <td>10004</td>\n",
-       "      <td>5.928573</td>\n",
-       "      <td>165.687951</td>\n",
-       "      <td>2.051037</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "                    datetime  customer_id  daily_transactions  \\\n",
-       "0 2019-11-26 12:03:47.320634        10000            5.178112   \n",
-       "1 2019-11-26 12:03:47.320644        10001            0.268114   \n",
-       "2 2019-11-26 12:03:47.320651        10002            1.486614   \n",
-       "3 2019-11-26 12:03:47.320658        10003            9.676433   \n",
-       "4 2019-11-26 12:03:47.320665        10004            5.928573   \n",
-       "\n",
-       "   total_transactions  discounts  \n",
-       "0          110.670651   8.389938  \n",
-       "1          195.393913   0.430047  \n",
-       "2          136.929052   7.408917  \n",
-       "3          166.022999   1.192721  \n",
-       "4          165.687951   2.051037  "
-      ]
-     },
-     "execution_count": 10,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "discounts = [np.random.uniform(0, 10) for _ in range(nr_of_customers)]\n",
-    "customer_df_updated = customer_df.assign(discounts=discounts)\n",
-    "customer_df_updated"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "  0%|          | 0/5 [00:00<?, ?rows/s]6rows/s]"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Ingested 5 rows into customer_transactions:2\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "client.ingest(cust_trans_fs_updated, dataframe=customer_df_updated)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "field_values {\n",
-       "  fields {\n",
-       "    key: \"customer_id\"\n",
-       "    value {\n",
-       "      int64_val: 10001\n",
-       "    }\n",
-       "  }\n",
-       "  fields {\n",
-       "    key: \"customer_transactions:2:daily_transactions\"\n",
-       "    value {\n",
-       "      float_val: 0.2681143283843994\n",
-       "    }\n",
-       "  }\n",
-       "  fields {\n",
-       "    key: \"customer_transactions:2:discounts\"\n",
-       "    value {\n",
-       "      float_val: 0.4300469756126404\n",
-       "    }\n",
-       "  }\n",
-       "  fields {\n",
-       "    key: \"customer_transactions:2:total_transactions\"\n",
-       "    value {\n",
-       "      float_val: 195.39390563964844\n",
-       "    }\n",
-       "  }\n",
-       "}"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "client.get_online_features(\n",
-    "    entity_rows=[\n",
-    "        GetOnlineFeaturesRequest.EntityRow(\n",
-    "            fields={\n",
-    "                \"customer_id\": Value(\n",
-    "                    int64_val=10001\n",
-    "                )\n",
-    "            }\n",
-    "        )\n",
-    "    ],\n",
-    "    feature_ids=[\n",
-    "        \"customer_transactions:2:daily_transactions\",\n",
-    "        \"customer_transactions:2:total_transactions\",\n",
-    "        \"customer_transactions:2:discounts\",\n",
-    "    ],\n",
-    ")"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.7.3"
-  },
-  "pycharm": {
-   "stem_cell": {
-    "cell_type": "raw",
-    "source": [],
-    "metadata": {
-     "collapsed": false
-    }
-   }
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
\ No newline at end of file
diff --git a/infra/docker/core/Dockerfile b/infra/docker/core/Dockerfile
index c4cfe34b7..8f82e69f6 100644
--- a/infra/docker/core/Dockerfile
+++ b/infra/docker/core/Dockerfile
@@ -2,12 +2,12 @@
 # Build stage 1: Builder
 # ============================================================
 
-FROM maven:3.6-jdk-8-slim as builder
+FROM maven:3.6-jdk-11-slim as builder
 ARG REVISION=dev
 COPY  . /build
 WORKDIR /build
 #
-# Setting Maven repository .m2 directory relative to /build folder gives the 
+# Setting Maven repository .m2 directory relative to /build folder gives the
 # user to optionally use cached repository when building the image by copying
 # the existing .m2 directory to $FEAST_REPO_ROOT/.m2
 #
@@ -29,7 +29,7 @@ RUN apt-get -qq update && apt-get -y install unar && \
 # Build stage 2: Production
 # ============================================================
 
-FROM openjdk:8-jre as production
+FROM openjdk:11-jre as production
 ARG REVISION=dev
 COPY --from=builder /build/core/target/feast-core-$REVISION.jar /opt/feast/feast-core.jar
 COPY --from=builder /build/core/target/feast-core-$REVISION /opt/feast/feast-core
diff --git a/infra/docker/core/Dockerfile.dev b/infra/docker/core/Dockerfile.dev
index 34fb63f1b..3903badd7 100644
--- a/infra/docker/core/Dockerfile.dev
+++ b/infra/docker/core/Dockerfile.dev
@@ -1,4 +1,4 @@
-FROM openjdk:8-jre
+FROM openjdk:11-jre
 ARG REVISION=dev
 ADD $PWD/core/target/feast-core-$REVISION.jar /opt/feast/feast-core.jar
 CMD ["java",\
diff --git a/infra/docker/jupyter/Dockerfile b/infra/docker/jupyter/Dockerfile
deleted file mode 100644
index d0302761c..000000000
--- a/infra/docker/jupyter/Dockerfile
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM jupyter/datascience-notebook:latest
-ARG VERSION=0.3.2
-RUN pip install feast==${VERSION}
\ No newline at end of file
diff --git a/infra/docker/jupyter/Dockerfile.dev b/infra/docker/jupyter/Dockerfile.dev
deleted file mode 100644
index 41fb431fb..000000000
--- a/infra/docker/jupyter/Dockerfile.dev
+++ /dev/null
@@ -1,8 +0,0 @@
-FROM python:3.6-alpine as builder
-WORKDIR /build/feast-sdk
-ADD . ./
-RUN python setup.py sdist bdist_wheel
-
-FROM jupyter/datascience-notebook:latest
-COPY --from=builder /build/feast-sdk/dist /usr/local/lib/feast
-RUN pip install -f /usr/local/lib/feast feast
\ No newline at end of file
diff --git a/infra/docker/serving/Dockerfile b/infra/docker/serving/Dockerfile
index 3517183d7..48ca18462 100644
--- a/infra/docker/serving/Dockerfile
+++ b/infra/docker/serving/Dockerfile
@@ -2,12 +2,12 @@
 # Build stage 1: Builder
 # ============================================================
 
-FROM maven:3.6-jdk-8-slim as builder
+FROM maven:3.6-jdk-11-slim as builder
 ARG REVISION=dev
 COPY  . /build
 WORKDIR /build
 #
-# Setting Maven repository .m2 directory relative to /build folder gives the 
+# Setting Maven repository .m2 directory relative to /build folder gives the
 # user to optionally use cached repository when building the image by copying
 # the existing .m2 directory to $FEAST_REPO_ROOT/.m2
 #
@@ -19,10 +19,10 @@ RUN mvn --also-make --projects serving -Drevision=$REVISION \
 # Build stage 2: Production
 # ============================================================
 
-FROM openjdk:8-jre-alpine as production
+FROM openjdk:11-jre-alpine as production
 ARG REVISION=dev
 #
-# Download grpc_health_probe to run health check for Feast Serving 
+# Download grpc_health_probe to run health check for Feast Serving
 # https://kubernetes.io/blog/2018/10/01/health-checking-grpc-servers-on-kubernetes/
 #
 RUN wget -q https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.3.1/grpc_health_probe-linux-amd64 \
diff --git a/infra/docker/serving/Dockerfile.dev b/infra/docker/serving/Dockerfile.dev
index 74b7d0ba9..2075061f9 100644
--- a/infra/docker/serving/Dockerfile.dev
+++ b/infra/docker/serving/Dockerfile.dev
@@ -1,7 +1,7 @@
-FROM openjdk:8-jre-alpine as production
+FROM openjdk:11-jre-alpine as production
 ARG REVISION=dev
 #
-# Download grpc_health_probe to run health check for Feast Serving 
+# Download grpc_health_probe to run health check for Feast Serving
 # https://kubernetes.io/blog/2018/10/01/health-checking-grpc-servers-on-kubernetes/
 #
 RUN wget -q https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/v0.3.1/grpc_health_probe-linux-amd64 \
diff --git a/pom.xml b/pom.xml
index 821d3b723..8e4ed7d45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -298,6 +298,18 @@
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
+
+            <dependency>
+                <groupId>javax.xml.bind</groupId>
+                <artifactId>jaxb-api</artifactId>
+                <version>2.3.1</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.annotation</groupId>
+                <artifactId>javax.annotation-api</artifactId>
+                <version>1.3.2</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 
@@ -378,8 +390,7 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.8.1</version>
                 <configuration>
-                    <source>1.8</source>
-                    <target>1.8</target>
+                    <release>11</release>
                     <compilerArgs>
                         <arg>-Xlint:all</arg>
                           <!-- -Xdoclint:all breaks on a false positive in JDK < 9 -->
@@ -392,6 +403,13 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-enforcer-plugin</artifactId>
                 <version>3.0.0-M2</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>extra-enforcer-rules</artifactId>
+                        <version>1.2</version>
+                    </dependency>
+                </dependencies>
                 <executions>
                     <execution>
                         <id>valid-build-environment</id>
@@ -401,10 +419,10 @@
                         <configuration>
                             <rules>
                                 <requireMavenVersion>
-                                    <version>[3.5,4.0)</version>
+                                    <version>[3.6,4.0)</version>
                                 </requireMavenVersion>
                                 <requireJavaVersion>
-                                    <version>[1.8,1.9)</version>
+                                    <version>[1.8,11.1)</version>
                                 </requireJavaVersion>
                                 <reactorModuleConvergence />
                             </rules>
diff --git a/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java b/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java
index 075c570c4..874196e92 100644
--- a/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java
+++ b/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java
@@ -61,7 +61,7 @@ public class RequestUtil {
                   "Feature id '%s' contains invalid version. Expected format: <project>/<feature-name>:<feature-version>.",
                   featureRefString));
         }
-      } else if (projectSplit.length == 1) {
+      } else if (featureSplit.length == 1) {
         name = featureSplit[0];
       } else {
         throw new IllegalArgumentException(
diff --git a/sdk/java/src/main/java/com/gojek/feast/Row.java b/sdk/java/src/main/java/com/gojek/feast/Row.java
index 9366fe1bb..ceef139aa 100644
--- a/sdk/java/src/main/java/com/gojek/feast/Row.java
+++ b/sdk/java/src/main/java/com/gojek/feast/Row.java
@@ -31,7 +31,7 @@ import java.util.Optional;
 @SuppressWarnings("UnusedReturnValue")
 public class Row {
   private Timestamp entity_timestamp;
-  private Map<String, Value> fields;
+  private HashMap<String, Value> fields;
 
   public static Row create() {
     Row row = new Row();
diff --git a/sdk/python/setup.py b/sdk/python/setup.py
index 1617f8385..d0b37ad94 100644
--- a/sdk/python/setup.py
+++ b/sdk/python/setup.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import os
+import subprocess
 
 from setuptools import find_packages, setup
 
@@ -48,7 +49,13 @@ REQUIRED = [
 ]
 
 # README file from Feast repo root directory
-README_FILE = os.path.join(os.path.dirname(__file__), "..", "..", "README.md")
+repo_root = (
+    subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE)
+    .communicate()[0]
+    .rstrip()
+    .decode("utf-8")
+)
+README_FILE = os.path.join(repo_root, "README.md")
 with open(os.path.join(README_FILE), "r") as f:
     LONG_DESCRIPTION = f.read()
 
diff --git a/serving/src/main/java/feast/serving/specs/CachedSpecService.java b/serving/src/main/java/feast/serving/specs/CachedSpecService.java
index 040a870ff..1184f6da9 100644
--- a/serving/src/main/java/feast/serving/specs/CachedSpecService.java
+++ b/serving/src/main/java/feast/serving/specs/CachedSpecService.java
@@ -49,7 +49,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.commons.lang3.tuple.Triple;
 import org.slf4j.Logger;
 
 /** In-memory cache of specs. */
@@ -199,8 +198,8 @@ public class CachedSpecService {
         .collect(
             groupingBy(
                 featureSet ->
-                    Triple.of(
-                        featureSet.getProject(), featureSet.getName(), featureSet.getVersion())))
+                    Pair.of(
+                        featureSet.getProject(), featureSet.getName())))
         .forEach(
             (group, groupedFeatureSets) -> {
               groupedFeatureSets =


Test patch:
diff --git a/.prow/scripts/test-end-to-end-batch.sh b/.prow/scripts/test-end-to-end-batch.sh
index 2fb0bdb70..268bd248c 100755
--- a/.prow/scripts/test-end-to-end-batch.sh
+++ b/.prow/scripts/test-end-to-end-batch.sh
@@ -3,12 +3,6 @@
 set -e
 set -o pipefail
 
-if ! cat /etc/*release | grep -q stretch; then
-    echo ${BASH_SOURCE} only supports Debian stretch.
-    echo Please change your operating system to use this script.
-    exit 1
-fi
-
 test -z ${GOOGLE_APPLICATION_CREDENTIALS} && GOOGLE_APPLICATION_CREDENTIALS="/etc/service-account/service-account.json"
 test -z ${SKIP_BUILD_JARS} && SKIP_BUILD_JARS="false"
 test -z ${GOOGLE_CLOUD_PROJECT} && GOOGLE_CLOUD_PROJECT="kf-feast"
diff --git a/.prow/scripts/test-end-to-end.sh b/.prow/scripts/test-end-to-end.sh
index 7e785fbfa..c436d2f69 100755
--- a/.prow/scripts/test-end-to-end.sh
+++ b/.prow/scripts/test-end-to-end.sh
@@ -3,12 +3,6 @@
 set -e
 set -o pipefail
 
-if ! cat /etc/*release | grep -q stretch; then
-    echo ${BASH_SOURCE} only supports Debian stretch.
-    echo Please change your operating system to use this script.
-    exit 1
-fi
-
 test -z ${GOOGLE_APPLICATION_CREDENTIALS} && GOOGLE_APPLICATION_CREDENTIALS="/etc/service-account/service-account.json"
 test -z ${SKIP_BUILD_JARS} && SKIP_BUILD_JARS="false"
 test -z ${GOOGLE_CLOUD_PROJECT} && GOOGLE_CLOUD_PROJECT="kf-feast"
diff --git a/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java b/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java
index 1c58e9435..3b9429ad8 100644
--- a/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java
+++ b/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java
@@ -60,7 +60,7 @@ class RequestUtilTest {
             Arrays.asList(
                 "driver_project/driver_id:1",
                 "driver_project/driver_name:1",
-                "booking_project/driver_name:1"),
+                "booking_project/driver_name"),
             Arrays.asList(
                 FeatureReference.newBuilder()
                     .setProject("driver_project")
@@ -74,7 +74,6 @@ class RequestUtilTest {
                     .build(),
                 FeatureReference.newBuilder()
                     .setProject("booking_project")
-                    .setVersion(1)
                     .setName("driver_name")
                     .build())));
   }