Skip to content

Commit 74bf0e3

Browse files
committed
Update docs
1 parent 5d9c586 commit 74bf0e3

5 files changed

Lines changed: 132 additions & 11 deletions

File tree

README.md

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,88 @@
11
# Rollout
22

3-
**TODO: Add description**
3+
Rollout allows you to flip features quickly and easily. It relies on
4+
distributed erlang and uses LWW-Register and Hybrid-logical clocks
5+
to provide maximum availability. Rollout has no dependency on an external
6+
service such as redis which means rollout feature flags can be used in the
7+
critical path of a request with minimal latency increase.
48

5-
## Installation
9+
* [Docs](https://hexdocs.pm/rollout).
610

7-
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
8-
by adding `rollout` to your list of dependencies in `mix.exs`:
11+
## Installation
912

1013
```elixir
1114
def deps do
1215
[
13-
{:rollout, "~> 0.1.0"}
16+
{:rollout, "~> 0.1"}
1417
]
1518
end
1619
```
1720

18-
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
19-
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
20-
be found at [https://hexdocs.pm/rollout](https://hexdocs.pm/rollout).
21+
## Usage
22+
23+
Rollout provides a simple api for enabling and disabling feature flags across
24+
your cluster. A feature flag can be any term.
25+
26+
```elixir
27+
# Check if a feature is active
28+
Rollout.active?(:blog_post_comments)
29+
# => false
30+
31+
# Activate the feature
32+
Rollout.activate(:blog_post_comments)
33+
34+
# De-activate the feature
35+
Rollout.deactivate(:blog_post_comments)
36+
```
37+
38+
You can also activate a feature a certain percentage of the time.
39+
40+
```elixir
41+
Rollout.activate_percentage(:blog_post_comments, 20)
42+
```
43+
44+
You can run this function on one node in your cluster and the updates will
45+
be propogated across the system. This means that updates to feature flags may
46+
not be instantaneous across the cluster but under normal conditions should propogate
47+
quickly. This is a tradeoff I've made in order to maintain the low latency when
48+
checking if a flag is enabled.
49+
50+
## How does Rollout work?
51+
52+
Rollout maintains a LWW Register for each flag that has been activated or
53+
deactivated. These Registers use hybrid logical clocks (HLC) for causality
54+
tracking. When a flag is activated or deactivated we update the HLC for that
55+
register and propogate that change across the cluster. When merging registers
56+
across the cluster we always take register with the latest HLC. After merging is
57+
done we store the values for each register into an ets table for fast lookups.
58+
59+
## Caveats
60+
61+
Rollout relies on your nodes being connected through distributed erlang. If you
62+
are running your application on more than one node and you are not clustering than
63+
your changes won't propogate. You will need to run the command on all nodes but
64+
in practice you'll probably just want to look for an alternative solution.
65+
66+
Flags are *not* maintained in between node restarts. New nodes added to your cluster
67+
will be caught up on the current state. But if you bring up an entirely new cluster
68+
your flags will revert to their default states. You can mitigate this problem
69+
by setting defaults for each flag in your `Application.start/2` callback.
70+
71+
Because we're using CRDTs to propogate changes its possible that a change made
72+
on one node will take time to propogate to the other nodes. Its a safe operation
73+
to run the same operation on multiple nodes. When feature flags are merged we
74+
also default to the latest HLC.
75+
76+
## Should I use this?
77+
78+
For now I'd say "No". The functionality works but this repo currently has 0
79+
tests and I'm sure there are edge cases. You should also not cluster your
80+
application for the sole purpose of using this library. If you need this
81+
you'll know it. Otherwise you're better off looking at an alternative solution.
82+
83+
## Future work
84+
85+
I'd like to implement an alternative storage engine for use in non-clustered
86+
environments, preferably using a fast storage engine such as redis. If anyone
87+
wants to submit that PR I'd love to take a look at it.
2188

lib/rollout.ex

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,48 @@
11
defmodule Rollout do
22
@moduledoc """
3-
Documentation for Rollout.
3+
Rollout allows you to flip features quickly and easily. It relies on
4+
distributed erlang and uses LWW-register and Hybrid-logical clocks
5+
to provide maximum availability. Rollout has no dependency on an external
6+
service such as redis which means rollout feature flags can be used in the
7+
critical path of a request with minimal latency increase.
8+
9+
## Usage
10+
11+
Rollout provides a simple api for enabling and disabling feature flags across
12+
your cluster. A feature flag can be any term.
13+
14+
```elixir
15+
# Check if a feature is active
16+
Rollout.active?(:blog_post_comments)
17+
# => false
18+
19+
# Activate the feature
20+
Rollout.activate(:blog_post_comments)
21+
22+
# De-activate the feature
23+
Rollout.deactivate(:blog_post_comments)
24+
```
25+
26+
You can also activate a feature a certain percentage of the time.
27+
28+
```elixir
29+
Rollout.activate_percentage(:blog_post_comments, 20)
30+
```
31+
32+
You can run this function on one node in your cluster and the updates will
33+
be propogated across the system. This means that updates to feature flags may
34+
not be instantaneous across the cluster but under normal conditions should propogate
35+
quickly. This is a tradeoff I've made in order to maintain the low latency when
36+
checking if a flag is enabled.
37+
38+
## How does Rollout work?
39+
40+
Rollout maintains a LWW Register for each flag that has been activated or
41+
deactivated. These Registers use hybrid logical clocks (HLC) for causality
42+
tracking. When a flag is activated or deactivated we update the HLC for that
43+
register and propogate the register across the cluster. When merging registers
44+
we always take register with the latest HLC. After merging is
45+
done we store the values for each register into an ets table for fast lookups.
446
"""
547

648
alias Rollout.Storage

lib/rollout/storage.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
defmodule Rollout.Storage do
22
@moduledoc false
3+
# This module provides a server for maintaining flags. It monitors node
4+
# connects in order to propogate existing flag information. Currently it
5+
# manages the ets table we're using for holding the value for each flag.
6+
# This isn't a great design because if we crash we lose the ets table.
7+
# We should make that ets table public and move its creation to a supervisor
8+
# or application start.
39

410
use GenServer
511

mix.exs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ defmodule Rollout.MixProject do
3131
defp deps do
3232
[
3333
{:hlclock, "~> 1.0"},
34+
{:ex_doc, ">= 0.0.0", only: :dev}
3435
]
3536
end
3637

3738
def description do
3839
"""
3940
Rollout allows you to flip features quickly and easily. It relies on
40-
distributed erlang and uses LWW-register CRDTs and Hybrid-logical clocks
41-
to provide maximum availability.
41+
distributed erlang and uses LWW-Registers and Hybrid-logical clocks
42+
to provide maximum availability and low latency.
4243
"""
4344
end
4445

mix.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
%{
2+
"earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm"},
3+
"ex_doc": {:hex, :ex_doc, "0.21.1", "5ac36660846967cd869255f4426467a11672fec3d8db602c429425ce5b613b90", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
24
"hlclock": {:hex, :hlclock, "1.0.0", "7a72fc7a20a9382499216227edf97a8b118e21fc3fcad0e81b8d10c616ce1431", [:mix], [], "hexpm"},
5+
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
6+
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
7+
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
38
}

0 commit comments

Comments
 (0)