Skip to content

Commit

Permalink
chore: docs rework
Browse files Browse the repository at this point in the history
Signed-off-by: Todd Baert <[email protected]>
  • Loading branch information
toddbaert committed Sep 27, 2023
1 parent 9065cba commit 09012e5
Show file tree
Hide file tree
Showing 73 changed files with 1,649 additions and 1,696 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ on:
paths-ignore:
- "README.md"
- "docs/**"
- "web-docs/**"
pull_request:
branches:
- main
paths-ignore:
- "README.md"
- "docs/**"
- "web-docs/**"

env:
GO_VERSION: '1.20'
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ go.work.sum
bin/

# built documentation
site
site
.cache/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "spec"]
path = spec
url = https://github.com/open-feature/spec.git
[submodule "schemas"]
path = schemas
url = https://github.com/open-feature/schemas.git
5 changes: 5 additions & 0 deletions .markdownlint-cli2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ config:
- br
github-admonition: true
max-one-sentence-per-line: true
code-block-style: false # not compatible with mkdocs "details" panes

ignores:
- "**/CHANGELOG.md"
- "docs/specification"
- "node_modules"
- "tmp"
- "**/protos.md" # auto-generated
- "schemas" # submodule
- "spec" # submodule
- "test-harness" # submodule
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ ZD_CLIENT_IMG ?= zd-client:latest
FLAGD_PROXY_IMG ?= flagd-proxy:latest
FLAGD_PROXY_IMG_ZD ?= flagd-proxy:zd

DOCS_DIR ?= docs

workspace-init: workspace-clean
go work init
$(foreach module, $(ALL_GO_MOD_DIRS), go work use $(module);)
Expand Down Expand Up @@ -105,4 +107,19 @@ markdownlint:
$(MDL_CMD) davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md"

markdownlint-fix:
$(MDL_CMD) --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md"
$(MDL_CMD) --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2-rules:$(MDL_DOCKER_VERSION) "**/*.md"

.PHONY: pull-schemas-submodule
pull-schemas-submodule:
git submodule update schemas

.PHONY: generate-proto-docs
generate-proto-docs: pull-schemas-submodule
docker run --rm -v ${PWD}/$(DOCS_DIR)/reference/specifications:/out -v ${PWD}/schemas/protobuf:/protos pseudomuto/protoc-gen-doc --doc_opt=markdown,protos-with-toc.md schema/v1/schema.proto sync/v1/sync_service.proto \
&& echo '<!-- WARNING: THIS DOC IS AUTO-GENERATED. DO NOT EDIT! -->' > ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \
&& sed '/^## Table of Contents/,/#top/d' ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md >> ${PWD}/$(DOCS_DIR)/reference/specifications/protos.md \
&& rm -f ${PWD}/$(DOCS_DIR)/reference/specifications/protos-with-toc.md

.PHONY: run-web-docs
run-web-docs: generate-docs generate-proto-docs
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

## What's flagd?

Flagd is a feature flag daemon with a Unix philosophy. Think of it as a ready-made, open source, OpenFeature compliant feature flag backend system.
Flagd is a feature flag daemon with a Unix philosophy. Think of it as a ready-made, open source, OpenFeature-compliant feature flag backend system.

## Features

Expand Down
76 changes: 0 additions & 76 deletions docs/README.md

This file was deleted.

45 changes: 45 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Architecture

flagd architectures fall into two broad categories: those where the evaluation engine is deployed in a standalone process to which the client application connects ([RPC](#rpc-evaluation)), and those where the evaluation engine is embedded into the client application ([in-process](#in-process-evaluation)).

## RPC vs In-Process Evaluation

### RPC evaluation

In RPC-based deployments one or more flagd instances deployed and exposed to client applications in your infrastructure.
flagd RPC providers use HTTP or gRPC to request flag evaluations from flagd.
The request payload contains the [flag key](https://openfeature.dev/specification/glossary#flag-key) identifying the flag to be evaluated, as well as the relevant [evaluation context](https://openfeature.dev/specification/glossary#evaluation-context).
The flagd instance is configured to watch one or more [syncs](./concepts/syncs.md), and merges them to build its set of flags (see [here](./concepts/syncs.md#merging) for more details on flag definition merging).
When sync sources are updated, flagd will send notifications to clients that flags have changed, enabling applications to react to changes by re-evaluating flags.

This architecture is can be leveraged by very simple clients, since no in-process engine is needed; in fact, you can evaluate flags directly from a terminal console using the `cURL` utility.
One disadvantage of this pattern is the latency involved in the remote request (though flagd typically takes <10ms for an evaluation, and can evaluate thousands of flags per second).

```mermaid
---
title: RPC Evaluation
---
erDiagram
flagd ||--o{ "sync (file)" : watches
flagd ||--o{ "sync (http)" : polls
flagd ||--o{ "sync (grpc)" : "sync.proto (gRPC/stream)"
flagd ||--o{ "sync (kubernetes)" : watches
"client app (+ flagd RPC provider)" ||--|| flagd : "evaluation.proto (gRPC/stream) / HTTP"
```

### In-Process evaluation

In-process deployments embed the flagd evaluation engine directly into the client application through the use of an [in-process provider](./deployment.md#in-process).
The in-process provider is connected via the sync protocol to an implementing [gRPC service](./concepts/syncs.md#grpc-sync) that provides the flag definitions.
This pattern requires an in-process implementation of the flagd evaluation engine, but has the benefit of no I/O overhead, since no inter-process communication is required.

```mermaid
---
title: In-Process Evaluation
---
erDiagram
"client app (+ flagd in-process provider)" ||--|| "sync (grpc)" : "sync.proto (gRPC/stream)"
```

<!-- TODO: add link to sync protocol reference entry -->
<!-- TODO: we might want a dedicated Kubernets section here eventually to talk about the specifics of the K8s implementation -->
22 changes: 22 additions & 0 deletions docs/assets/demo.flagd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"flags": {
"show-welcome-banner": {
"state": "ENABLED",
"variants": {
"on": true,
"off": false
},
"defaultVariant": "off"
},
"background-color": {
"state": "ENABLED",
"variants": {
"red": "#FF0000",
"blue": "#0000FF",
"green": "#00FF00",
"yellow": "#FFFF00"
},
"defaultVariant": "red"
}
}
}
10 changes: 10 additions & 0 deletions docs/assets/logo-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/concepts/feature-flagging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Feature Flagging

Feature flags are a software development technique that allows teams to enable, disable or change the behavior of certain features or code paths in a product or service, without modifying the source code.

## OpenFeature Compliance

[OpenFeature](https://openfeature.dev/) is an open standard that provides a vendor-agnostic, community-driven API for feature flagging.
The flagd project is fully OpenFeature-compliant.
In fact, flagd was initially conceived as a reference implementation for an OpenFeature backend, but has become a powerful tool in its own right.
For this reason, you'll find flagd's concepts and terminology align with that of the OpenFeature project.
Within the context of an OpenFeature-compliant feature flag solution, flagd artifacts and libraries comprise the [flag management system](https://openfeature.dev/specification/glossary#flag-management-system) and [providers](https://openfeature.dev/specification/glossary#provider).
These artifacts and libraries alone won't allow you to evaluate flags in your application - you'll also need the [OpenFeature SDK](https://openfeature.dev/specification/glossary#feature-flag-sdk) for your language as well, which provides the evaluation API for application developers to use.

## Supported Feature Flagging Use-Cases

Below is a non-exhaustive table of common feature flag use-cases, and how flagd supports them:

| Use case | flagd Feature |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| flag evaluation | Returns the value of a particular feature flag, if the flag is enabled. Supports flags of various types including boolean, numeric, string, and JSON. |
| dynamic configuration | Flag definitions from any sync source are monitored for changes, with some syncs supporting near real time updates. |
| dynamic (context-sensitive) evaluation | flagd evaluations are context sensitive. Rules can use arbitrary context attributes as inputs for flag evaluation logic. |
| fractional evaluation / random assignment | flagd's [fractional](../reference/custom-operations/fractional-operation.md) custom operation supports pseudorandom assignment of flag values. |
| progressive roll-outs | Progressive roll-outs of new features can be accomplished by leveraging the [fractional](../reference/custom-operations/fractional-operation.md) custom operation as well as automation in your build pipeline, SCM, or infrastructure which updates the distribution over time. |
114 changes: 114 additions & 0 deletions docs/concepts/syncs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Syncs

Syncs are a core part of flagd; they are the abstraction that enables different sources for feature flag definitions.
flagd can connect to one or more sync sources to

## Available syncs

### Filepath sync

The file path sync provider reads and watch the source file for updates(ex: changes and deletions).

```shell
flagd start --uri file:etc/featureflags.json
```

In this example, `etc/featureflags.json` is a valid feature flag definition file accessible by the flagd process.
See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details.

---

### HTTP sync

The HTTP sync provider fetch flags from a remote source and periodically poll the source for flag definition updates.

```shell
flagd start --uri https://my-flag-source.json
```

In this example, `https://my-flag-source.json` is a remote endpoint responding valid feature flag definition when
invoked with **HTTP GET** request.
The polling interval, port, TLS settings, and authentication information can be configured.
See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details.

---

### gRPC sync

The gRPC sync provider streams flag definition from a gRPC sync provider implementation. This stream connection is ruled
by
the [sync service protobuf definition](https://github.com/open-feature/schemas/blob/main/protobuf/sync/v1/sync_service.proto).

```shell
flagd start --uri grpc://grpc-sync-source
```

In this example, `grpc-sync-source` is a grpc target implementing [sync.proto](../reference/flag-sync-protocol.md) definition.
See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details.

---

### Kubernetes sync

The Kubernetes sync provider allows flagd to connect to a Kubernetes cluster and evaluate flags against a specified
FeatureFlagConfiguration resource as defined within
the [open-feature-operator](https://github.com/open-feature/open-feature-operator/blob/main/apis/core/v1alpha1/featureflagconfiguration_types.go)
spec.
This configuration is best used in conjunction with the [OpenFeature Operator](https://github.com/open-feature/open-feature-operator).

To use an existing FeatureFlagConfiguration custom resource, start flagD with the following command:

```shell
flagd start --uri core.openfeature.dev/default/my_example
```

In this example, `default/my_example` expected to be a valid FeatureFlagConfiguration resource, where `default` is the
namespace and `my_example` being the resource name.
See [sync source](../reference/sync-configuration.md#source-configuration) configuration for details.

## Merging

Flagd can be configured to read from multiple sources at once, when this is the case flagd will merge all flag definition into a single
merged state.

For example:

![flag merge 1](../images/flag-merge-1.svg)

In this example, `source-A` and `source-B` provide a single flag definition, the `foo` flag and the `bar` flag respectively.
The merge logic for this definition is simple, both flag definition are added to the `store`.

In most scenarios, these flag sources will be supplying `n` number of definition, using a unique flag key for each definition.

However, as multiple sources are being used, there is the opportunity for keys to be duplicated, intentionally or not, between flag sources.
In these situations `flagd` uses a merge priority order to ensure that its behavior is consistent.

Merge order is dictated by the order that `sync-providers` and `uris` are defined, with the latest defined source taking precedence over those defined before it, as an example:

```sh
./bin/flagd start --uri file:source-A.json --uri file:source-B.json --uri file:source-C.json
```

When `flagd` is started with the command defined above, `source-B` takes priority over `source-A`, whilst `source-C` takes priority over both `source-B` and `source-A`.

Using the above example, if a flag key is duplicated across all 3 sources, then the definition from `source-C` would be the only one stored in the merged state.

![flag merge 2](../images/flag-merge-2.svg)

### State Resync Events

Given the above example, the `source-A` and `source-B` 'versions' of flag definition the `foo` have been discarded, so if a delete event in `source-C` results in the removal of the `foo`flag, there will no longer be any reference of `foo` in flagd's store.

As a result of this flagd will return `FLAG_NOT_FOUND` errors, and the OpenFeature SDK will always return the default value.

To prevent flagd falling out of sync with its flag sources during delete events, resync events are used.
When a delete event results in a flag definition being removed from the merged state, the full set of definition is requested from all flag sources, and the merged state is rebuilt.
As a result, the value of the `foo` flag from `source-B` will be stored in the merged state, preventing flagd from returning `FLAG_NOT_FOUND` errors.

![flag merge 3](../images/flag-merge-3.svg)

In the example above, a delete event results in a resync event being fired, as `source-C` has deleted its 'version' of the `foo`, this results in a new merge state being formed from the remaining definition.

![flag merge 4](../images/flag-merge-4.svg)

Resync events may lead to further resync events if the returned flag definition result in further delete events, however the state will eventually be resolved correctly.
Loading

0 comments on commit 09012e5

Please sign in to comment.