Skip to content

Commit 323f10b

Browse files
committed
Update docs
1 parent 5d8193e commit 323f10b

2 files changed

Lines changed: 50 additions & 54 deletions

File tree

lib/gen_stage.ex

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,6 @@ defmodule GenStage do
4343
producers to send data using different "strategies". See
4444
`GenStage.Dispatcher` for more information.
4545
46-
Many developers tend to create layers of stages, such as A, B and
47-
C, for achieving concurrency. If all you want is concurrency, starting
48-
multiple instances of the same stage is enough. Layers in GenStage must
49-
be created when there is a need for back-pressure or to route the data
50-
in different ways.
51-
52-
For example, if you need the data to go over multiple steps but
53-
without a need for back-pressure or without a need to break the
54-
data apart, do not design it as such:
55-
56-
[Producer] -> [Step 1] -> [Step 2] -> [Step 3]
57-
58-
Instead it is better to design it as:
59-
60-
[Consumer]
61-
/
62-
[Producer]-<-[Consumer]
63-
\
64-
[Consumer]
65-
66-
where "Consumer" are multiple processes running the same code that
67-
subscribe to the same "Producer".
68-
6946
## Example
7047
7148
Let's define the simple pipeline below:
@@ -167,6 +144,47 @@ defmodule GenStage do
167144
you subscribe all of them, demand will start flowing upstream and
168145
events downstream.
169146
147+
## Usage guidelines
148+
149+
As you get familiar with GenStage, developers may tend to create layers
150+
of stages, such as A, B and C, for achieving concurrency. For example,
151+
stage A does step 1 in your company workflow, stage B does step 2 and
152+
so forth. That's an anti-pattern.
153+
154+
The same guideline that applies to processes also applies to GenStage:
155+
use processes/stages to model runtime properties, such as concurrency and
156+
data-transfer, and not for code organization or domain design purposes.
157+
For the latter, you should use modules and functions.
158+
159+
If your domain has to process the data in multiple steps, you should write
160+
that logic in separate modules and not directly in a `GenStage`. You only add
161+
stages according to runtime needs, typically when you need to provide back-
162+
pressure or leverage concurrency. This way you are free to experiment with
163+
different `GenStage` pipelines without touching your business rules.
164+
165+
In particular, if your logic has three distinct steps, instead of starting
166+
three different stages for each step, it may be best to start multiple
167+
instances of a single stage that executes all steps. Instead of this:
168+
169+
[Producer Stage] -> [Stage Step 1] -> [Stage Step 2] -> [Stage Step 3]
170+
171+
You should rather have this:
172+
173+
[Consumer Step 1 + Step 2 + Step 3]
174+
/
175+
[Producer]->-[Consumer Step 1 + Step 2 + Step 3]
176+
\
177+
[Consumer Step 1 + Step 2 + Step 3]
178+
179+
The benefit of this approach is that you can scale the code based on the machine
180+
resources and runtime needs rather than the number of steps during development.
181+
182+
Finally, if you don't need back-pressure at all and you just need to process
183+
data that is already in-memory in parallel, a simpler solution is available
184+
directly in Elixir via `Task.async_stream/2`. This function consumes a stream
185+
of data, with each entry running in a separate task. The maximum number of tasks
186+
is configurable via the `:max_concurrency` option.
187+
170188
## Demand
171189
172190
When implementing consumers, we often set the `:max_demand` and
@@ -202,7 +220,7 @@ defmodule GenStage do
202220
50 seconds to be consumed by C, which will then request another
203221
batch of 50 items.
204222
205-
## `init` and `:subscribe_to`
223+
### `init` and `:subscribe_to`
206224
207225
In the example above, we have started the processes A, B, and C
208226
independently and subscribed them later on. But most often it is
@@ -305,30 +323,6 @@ defmodule GenStage do
305323
concurrently running in a `ConsumerSupervisor` is at most `max_demand` and
306324
the average amount of children is `(max_demand + min_demand) / 2`.
307325
308-
## Usage guidelines
309-
310-
As you get familiar with GenStage, you may want to organize your stages
311-
according to your business domain. For example, stage A does step 1 in
312-
your company workflow, stage B does step 2 and so forth. That's an anti-
313-
pattern.
314-
315-
The same guideline that applies to processes also applies to GenStage:
316-
use processes/stages to model runtime properties, such as concurrency and
317-
data-transfer, and not for code organization or domain design purposes.
318-
For the latter, you should use modules and functions.
319-
320-
If your domain has to process the data in multiple steps, you should write
321-
that logic in separate modules and not directly in a `GenStage`. You only add
322-
stages according to the runtime needs, typically when you need to provide back-
323-
pressure or leverage concurrency. This way you are free to experiment with
324-
different `GenStage` pipelines without touching your business rules.
325-
326-
Finally, if you don't need back-pressure at all and you just need to process
327-
data that is already in-memory in parallel, a simpler solution is available
328-
directly in Elixir via `Task.async_stream/2`. This function consumes a stream
329-
of data, with each entry running in a separate task. The maximum number of tasks
330-
is configurable via the `:max_concurrency` option.
331-
332326
## Buffering
333327
334328
In many situations, mismatches might happen between how many events can be produced
@@ -946,6 +940,8 @@ defmodule GenStage do
946940
947941
## Examples
948942
943+
### Demand with buffering
944+
949945
In the following example, the producer emits enough events to at least
950946
satisfy the demand, letting GenStage buffer any excess events:
951947

mix.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
%{
2-
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
3-
"ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"},
4-
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
5-
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
6-
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
7-
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
2+
"earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
3+
"ex_doc": {:hex, :ex_doc, "0.37.0", "970f92b39e62c460aa8a367508e938f5e4da6e2ff3eaed3f8530b25870f45471", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "b0ee7f17373948e0cf471e59c3a0ee42f3bd1171c67d91eb3626456ef9c6202c"},
4+
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
5+
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
6+
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
7+
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
88
}

0 commit comments

Comments
 (0)