Skip to content

Commit

Permalink
change Full Stack to Feature Experimentation (#368) (#369)
Browse files Browse the repository at this point in the history
Co-authored-by: Griffin Cox <[email protected]>
  • Loading branch information
1 parent e1c6ac9 commit 4aa4657
Show file tree
Hide file tree
Showing 29 changed files with 570 additions and 450 deletions.
297 changes: 168 additions & 129 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions api/openapi-spec/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
openapi: 3.0.0
info:
title: Optimizely Agent API
description: Optimizely Agent is a stand-alone, open-source microservice that provides major benefits over using Optimizely SDKs in certain use cases. Its REST API offers consolidated and simplified endpoints for accessing all the functionality of Optimizely Full Stack SDKs. Use this API the control experiments (such as a feature tests). For more info, see https://docs.developers.optimizely.com/full-stack/docs/optimizely-agent.
termsOfService: http://optimizely.com/terms/
description: Optimizely Agent is a stand-alone, open-source microservice that provides major benefits over using Optimizely SDKs in certain use cases. Its REST API offers consolidated and simplified endpoints for accessing all the functionality of Optimizely Feature Experimentation SDKs. Use this API the control experiments (such as a feature tests). For more info, see https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/optimizely-agent
termsOfService: https://www.optimizely.com/legal/terms/
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
Expand Down Expand Up @@ -168,7 +168,7 @@ paths:
post:
summary: Activate selected features and experiments for the given user.
operationId: activate
description: Returns Optimizely's decision about which features and experiments a given user is exposed to. Optionally sends an impression event to the Optimizely analytics backend for any decision made for an experiment. This endpoint consolidates key functionality from the Full Stack SDKs into one convenient call.
description: Returns Optimizely's decision about which features and experiments a given user is exposed to. Optionally sends an impression event to the Optimizely analytics backend for any decision made for an experiment. This endpoint consolidates key functionality from the Feature Experimentation SDKs into one convenient call.
responses:
'200':
description: Valid response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,88 @@ title: "Optimizely Agent"
excerpt: ""
slug: "optimizely-agent"
hidden: false
metadata:
title: "Optimizely Agent microservice - Optimizely Full Stack"
metadata:
title: "Optimizely Agent microservice - Optimizely Feature Experimentation"
createdAt: "2020-02-21T20:35:58.387Z"
updatedAt: "2020-07-14T20:51:52.458Z"
---
Optimizely Agent is a standalone, open-source, and highly available microservice that provides major benefits over using Optimizely SDKs in certain use cases. The [Agent REST API](https://library.optimizely.com/docs/api/agent/v1/index.html) offers consolidated and simplified endpoints for accessing all the functionality of Optimizely Full Stack SDKs.

Optimizely Agent is a standalone, open-source, and highly available microservice that provides major benefits over using Optimizely SDKs in certain use cases. The [Agent REST API](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/reference/getconfig) offers consolidated and simplified endpoints for accessing all the functionality of Optimizely Feature Experimentation SDKs.

A typical production installation of Optimizely Agent is to run two or more services behind a load balancer or proxy. The service itself can be run via a Docker container or installed from source. See [Setup Optimizely Agent](doc:setup-optimizely-agent) for instructions on how to run Optimizely Agent.

### Example Implementation

![example implementation](https://raw.githubusercontent.com/optimizely/agent/master/docs/images/agent-example-implementation.png)

# Should I Use Optimizely Agent?

Here are some of the top reasons to consider using Optimizely Agent:

## 1. Service Oriented Architecture (SOA)
If you already separate some of your logic into services that might need to access the Optimizely decision APIs, we recommend using Optimizely Agent.

The images below compare implementation styles in a service-oriented architecture, first *without* using Optimizely Agent, which shows six SDK embedded instances:
If you already separate some of your logic into services that might need to access the Optimizely decision APIs, we recommend using Optimizely Agent.

The images below compare implementation styles in a service-oriented architecture, first _without_ using Optimizely Agent, which shows six SDK embedded instances:

!["A diagram showing the use of SDKs installed on each service in a service oriented architecture \n(Click to Enlarge)"](https://raw.githubusercontent.com/optimizely/agent/master/docs/images/agent-service-oriented-architecture.png)

Now *with* Agent, instead of installing the SDK six times, you create just one Optimizely instance: an HTTP API that every service can access as needed.
Now _with_ Agent, instead of installing the SDK six times, you create just one Optimizely instance: an HTTP API that every service can access as needed.

!["A diagram showing the use of Optimizely Agent in a single service \n(Click to Enlarge)"](https://raw.githubusercontent.com/optimizely/agent/master/docs/images/agent-single-service.png)

## 2. Standardize Access Across Teams
If you want to deploy Optimizely Full Stack once, then roll out the single implementation across a large number of teams, we recommend using Optimizely Agent.

If you want to deploy Optimizely Feature Experimentation once, then roll out the single implementation across a large number of teams, we recommend using Optimizely Agent.

By standardizing your teams' access to the Optimizely service, you can better enforce processes and implement governance around feature management and experimentation as a practice.

!["A diagram showing the central and standardized access to the Optimizely Agent service across an arbitrary number of teams.\n(Click to Enlarge)"](https://raw.githubusercontent.com/optimizely/agent/master/docs/images/agent-standardized-access.png)

## 3. Networking Centralization
You don’t want many SDK instances connecting to Optimizely's cloud service from every node in your application. Optimizely Agent centralizes your network connection. Only one cluster of agent instances connects to Optimizely for tasks like update [datafiles](doc:get-the-datafile) and dispatch [events](doc:track-events).

You don’t want many SDK instances connecting to Optimizely's cloud service from every node in your application. Optimizely Agent centralizes your network connection. Only one cluster of agent instances connects to Optimizely for tasks like update [datafiles](doc:get-the-datafile) and dispatch [events](doc:track-events).

## 4. Languages
You’re using a language that isn’t supported by a native SDK (i.e. Elixir, Scala, Perl). While its possible to create your own service using an Optimizely SDK of your choice, you could also customize the open-source Optimizely Agent to your needs without building the service layer on your own.

You’re using a language that isn’t supported by a native SDK (i.e. Elixir, Scala, Perl). While its possible to create your own service using an Optimizely SDK of your choice, you could also customize the open-source Optimizely Agent to your needs without building the service layer on your own.

# Reasons to _not_ use Optimizely Agent

# Reasons to *not* use Optimizely Agent
If your use case wouldn't benefit greatly from Optimizely Agent, you should consider the below reasons to *not* use Optimizely Agent and review Optimizely's many [open-source SDKs](doc:sdk-reference-guides) instead.
If your use case wouldn't benefit greatly from Optimizely Agent, you should consider the below reasons to _not_ use Optimizely Agent and review Optimizely's many [open-source SDKs](doc:sdk-reference-guides) instead.

## 1. Latency
If time to provide bucketing decisions is a primary concern for you, you may want to use an embedded Full Stack SDK rather than Optimizely Agent.

If time to provide bucketing decisions is a primary concern for you, you may want to use an embedded Feature Experimentation SDK rather than Optimizely Agent.

| Implementation Option | Decision Latency |
|-----------------------|------------------|
| --------------------- | ---------------- |
| Embedded SDK | microseconds |
| Optimizely Agent | milliseconds |

## 2. Monolith
If your app is constructed as a monolith, embedded SDKs might be easier to install and might be a more natural fit for your application and development practices.

If your app is constructed as a monolith, embedded SDKs might be easier to install and might be a more natural fit for your application and development practices.

## 3. Velocity
If you’re looking for the fastest way to get a single team up and running with deploying feature management and experimentation, embedding an SDK is the best option for you at first. You can always start using Optimizely Agent later, and it can even be used alongside Optimizely Full Stack SDKs running in another part of your stack.

If you’re looking for the fastest way to get a single team up and running with deploying feature management and experimentation, embedding an SDK is the best option for you at first. You can always start using Optimizely Agent later, and it can even be used alongside Optimizely Feature Experimentation SDKs running in another part of your stack.

# Best Practices
While every implementation is different, you can review this section of best practices for tips on these commonly discussed topics.

While every implementation is different, you can review this section of best practices for tips on these commonly discussed topics.

## How many Agent instances should I deploy?

Agent can scale to large decision / event tracking volumes with relatively low CPU / memory specs. For example, at Optimizely, we scaled our deployment to 740 clients with a cluster of 12 agent instances, which in total use 6 vCPUs and 12GB RAM. You will likely need to focus more on network bandwidth than compute power.

## Using a load balancer

Any standard load balancer should let you route traffic across your agent cluster. At Optimizely, we used an AWS Elastic Load Balancer (ELB) for our internal deployment. This allows us to transparently scale our agent cluster as internal demands increase.

## Synchronizing datafiles across Agent instances

Agent offers eventual rather than strong consistency across datafiles.
In detail, today, each agent instance maintains a dedicated, separate cache. Each agent instance persists an SDK instance for each SDK key your team uses. Agent instances automatically keep datafiles up to date for each SDK key instance so that you will have eventual consistency across the cluster. The rate of the datafile update can be [set as the configuration](doc:configure-optimizely-agent) value ```OPTIMIZELY_CLIENT_POLLINGINTERVAL``` (the default is 1 minute).
Because SDKs are generally stateless today, they shouldn’t need to share data. We plan to add a common backing data store, so we invite you to share your feedback.
In detail, today, each agent instance maintains a dedicated, separate cache. Each agent instance persists an SDK instance for each SDK key your team uses. Agent instances automatically keep datafiles up to date for each SDK key instance so that you will have eventual consistency across the cluster. The rate of the datafile update can be [set as the configuration](doc:configure-optimizely-agent) value `OPTIMIZELY_CLIENT_POLLINGINTERVAL` (the default is 1 minute).
Because SDKs are generally stateless today, they shouldn’t need to share data. We plan to add a common backing data store, so we invite you to share your feedback.
If you require strong consistency across datafiles, then we recommend an active / passive deployment where all requests are made to a single vertically scaled host, with a passive, standby cluster available for high availability.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ title: "Quickstart for Agent"
excerpt: ""
slug: "quickstart-for-agent"
hidden: false
metadata:
title: "Quickstart for Agent - Optimizely Full Stack"
metadata:
title: "Quickstart for Agent - Optimizely Feature Experimentation"
createdAt: "2020-05-21T20:35:58.387Z"
updatedAt: "2020-08-17T20:51:52.458Z"
---
Expand All @@ -15,11 +15,10 @@ This brief quickstart describes how to run Agent, using two examples:

- To get started using example Node microservices, see the following video link.



## Running locally via Node
| Resource | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |

| Resource | Description |
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| [Implementing feature flags across microservices with Optimizely Agent](https://www.youtube.com/watch?v=kwNVdSXMGX8&t=20s) | 4-minute video on implementing Optimizely Agent with example microservices |

## Running locally via Docker
Expand All @@ -38,8 +37,9 @@ Then start the service in the foreground with the following command:
```bash
docker run -p 8080:8080 --env OPTIMIZELY_LOG_PRETTY=true optimizely/agent
```

Note that we're enabling "pretty" logs which provide colorized and human readable formatting.
The default log output format is structured JSON.
The default log output format is structured JSON.

## Evaluating REST APIs

Expand Down Expand Up @@ -85,4 +85,4 @@ resp = s.post(url = 'http://localhost:8080/v1/activate', params=params, json=pay
print(resp.json())
```

The activate API is a POST to signal to the caller that there are side-effects. Namely, activation results in a "decision" event sent to Optimizely analytics for the purpose of analyzing Feature Test results. A "decision" will NOT be sent if the feature is simply part of a rollout.
The activate API is a POST to signal to the caller that there are side-effects. Namely, activation results in a "decision" event sent to Optimizely analytics for the purpose of analyzing Feature Test results. A "decision" will NOT be sent if the feature is simply part of a rollout.
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@ title: "Install Optimizely Agent"
excerpt: ""
slug: "setup-optimizely-agent"
hidden: false
metadata:
title: "Install Agent - Optimizely Full Stack"
metadata:
title: "Install Agent - Optimizely Feature Experimentation"
createdAt: "2020-02-21T17:44:27.363Z"
updatedAt: "2020-03-31T23:54:17.841Z"
---

## Running Agent from source (Linux / OSX)

To develop and compile Optimizely Agent from source:

1. Install [Golang](https://golang.org/dl/) version 1.20+ .
2. Clone the [Optimizely Agent repo](https://github.com/optimizely/agent).
1. Install [Golang](https://golang.org/dl/) version 1.20+ .
2. Clone the [Optimizely Agent repo](https://github.com/optimizely/agent).
3. From the repo directory, open a terminal and start Optimizely Agent:

```bash
make setup
```

Then

```bash
make run
```
Expand All @@ -31,7 +34,7 @@ This starts the Optimizely Agent with the default configuration in the foregroun
You can use a [helper script](https://github.com/optimizely/agent/blob/master/scripts/build.ps1) to install prerequisites (Golang, Git) and compile agent in a Windows environment. Take these steps:

1. Clone the [Optimizely Agent repo](https://github.com/optimizely/agent)
2. From the repo directory, open a Powershell terminal and run
2. From the repo directory, open a Powershell terminal and run

```bash
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
Expand All @@ -50,6 +53,7 @@ If you have Docker installed, you can start Optimizely Agent as a container. Tak
```bash
docker pull optimizely/agent
```

By default this will pull the "latest" tag. You can also specify a specific version of Agent by providing the version as a tag to the docker command:

```bash
Expand All @@ -61,15 +65,18 @@ docker pull optimizely/agent:X.Y.Z
```bash
docker run -p 8080:8080 optimizely/agent
```

This will start Agent in the foreground and expose the container API port 8080 to the host.

3. (Optional) You can alter the configuration by passing in environment variables to the preceding command, without having to create a config.yaml file. See [configure optimizely agent](doc:configure-optimizely-agent) for more options.

Versioning:
When a new version is released, 2 images are pushed to dockerhub. They are distinguished by their tags:

- :latest (same as :X.Y.Z)
- :alpine (same as :X.Y.Z-alpine)

The difference between latest and alpine is that latest is built `FROM scratch` while alpine is `FROM alpine`.

- [latest Dockerfile](https://github.com/optimizely/agent/blob/master/scripts/dockerfiles/Dockerfile.static)
- [alpine Dockerfile](https://github.com/optimizely/agent/blob/master/scripts/dockerfiles/Dockerfile.alpine)
- [alpine Dockerfile](https://github.com/optimizely/agent/blob/master/scripts/dockerfiles/Dockerfile.alpine)
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@ title: "Evaluate REST APIs"
excerpt: ""
slug: "evaluate-rest-apis"
hidden: false
metadata:
title: "Evaluate REST APIs - Optimizely Full Stack"
metadata:
title: "Evaluate REST APIs - Optimizely Feature Experimentation"
createdAt: "2020-02-21T17:44:53.019Z"
updatedAt: "2020-04-13T23:02:34.056Z"
---

Below is an example demonstrating the APIs capabilities. For brevity, we've chosen to illustrate the API usage with Python. Note that the API documentation is defined via an OpenAPI (Swagger) spec and can be viewed [here](https://library.optimizely.com/docs/api/agent/v1/index.html).

## Start an http session
Each request made into Optimizely Agent is in the context of an Optimizely SDK Key. SDK Keys map API requests to a specific Optimizely Project and Environment. We can setup a global request header by using the `requests.Session` object.

Each request made into Optimizely Agent is in the context of an Optimizely SDK Key. SDK Keys map API requests to a specific Optimizely Project and Environment. We can setup a global request header by using the `requests.Session` object.

```python
import requests

s = requests.Session()
s.headers.update({'X-Optimizely-SDK-Key': 'YOUR-SDK-KEY'})
```

The following examples will assume this session is being maintained.

## Get current environment configuration

The `/v1/config` endpoint returns a manifest of the current working environment.

```python
Expand All @@ -34,11 +37,11 @@ for key in env['featuresMap']:
```

## Run flag rules

The `POST /v1/decide?keys={flagKey}` endpoint activates the feature for a given user. In Optimizely, activation is in the context of a given user to make the relative bucketing decision. In this case we'll provide a `userId` via the request body. The `userId` will be used to determine how the feature will be evaluated. Features can either be part of a Feature Test in which variations of feature variables are being measured against one another or a feature rollout, which progressively make the feature available to the selected audience.

From an API standpoint the presence of a Feature Test or Rollout is abstracted away from the response and only the resulting variation or enabled feature is returned.


```python
# single feature activate
params = { "featureKey": "my-feature" }
Expand All @@ -56,4 +59,5 @@ params = {
resp2 = s.post(url = 'http://localhost:8080/v1/activate', params=params, json=payload)
print(json.dumps(resp.json(), indent=4, sort_keys=True))
```

The activate API is a POST to signal to the caller that there are side-effects. Namely, activation results in a "decision" event sent to Optimizely analytics for the purpose of analyzing Feature Test results. A "decision" will NOT be sent if the feature is simply part of a rollout.
Loading

0 comments on commit 4aa4657

Please sign in to comment.