You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: guides/useful_concepts/crash_groups.md
+62-7Lines changed: 62 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ The most fundamental functionality of crash groups is to separate the crash of a
10
10
11
11
## Defining Crash Groups
12
12
13
-
Crash groups are defined in the `spec` within your pipeline or bin. You assign a crash group ID to a set of children.
13
+
Crash groups are defined in the `spec` within your pipeline or bin. To create a crash group, you have to define a group name and set `crash_group_mode` to `:temporary`, like in the example below:
In the case above, the crash group ID is `:audio_processing`.
32
-
33
31
## Behavior
34
32
35
33
When an element belonging to a crash group crashes:
@@ -45,14 +43,71 @@ To react to a crash group failure, implement the `handle_crash_group_down/3` cal
45
43
@impltrue
46
44
defhandle_crash_group_down(crash_group_id, context, state) do
47
45
# Logic to restart the group or handle the error
48
-
{[], state}
49
46
end
50
47
```
51
48
52
-
`context` passed to `handle_crash_group_down/3` callback contiains 3 additional fields, that usually don't occur in contexts of other callbacks:
53
-
-`context.crash_initiator` - name or reference of the child that crashed, what caused crash group to explode.
49
+
## Flow of all callbacks reated to a crash within a crash group.
50
+
51
+
Let's assume, that in `:filter` spawned in `MyPipeline` raised with message `"internal error"`.
52
+
53
+
### Handling termination of the crash itiator
54
+
55
+
The first callback, that will be executed in `MyPipeline`, is
56
+
57
+
```elixir
58
+
@impltrue
59
+
defhandle_child_terminated(:filter, context, state) do
60
+
# ...
61
+
end
62
+
```
63
+
64
+
`context` passed to this callback will contain few extra fileds:
65
+
*`context.exit_reason` - in this case equals `{%RuntimeError{message: "internal error"}, _stacktrace}`.
66
+
*`context.group_name` - because `:filter` was spawned inside `:my_group` group, it equals `:my_group`. If a child is spawned beyond any crash group and is terminated gracefully, value of this field is `nil`.
67
+
*`context.crash_initiator` - the same as child's reference, that is `:filter`.
68
+
69
+
`:filter` won't be present in `context.children`, so you could respawn it here, however it is suggested to do it in `handle_crash_group_down/3` later.
70
+
71
+
### Terminating other children within the crash group that explodes
72
+
73
+
Because one of children from crash group `:my_group` ungracefully crashed, the rest of children from this group will be terminated as well.
74
+
75
+
Therefore, Membrane will terminate `:source` and `:sink` in random order. After each termination, `MyPipeline` will execute
76
+
77
+
```elixir
78
+
@impltrue
79
+
defhandle_child_terminated(child, context, state) do
80
+
# ...
81
+
end
82
+
```
83
+
84
+
callback. Each time, `context` will contain following extra fields:
85
+
*`context.exit_reason` - for these terminations, equals `{:shutdown, :membrane_crash_group_kill}`.
86
+
*`context.group_name` - equals `:my_group`.
87
+
*`context.crash_initiator` - TODO: continue
88
+
89
+
90
+
Note, that `context.children` map always contains only children that are still alive. E.g. if `:source` is terminated first, `hanlde_child_terminated(:source, context, state)` will contain only `:sink` in `context.children` map and for `hanlde_child_terminated(:sink, context, state)``context.children` will be empty.
91
+
92
+
Of course, `MyPipeline` could possibly spawn another children beyond `:source`, `:filter` and `:sink`, inside different crash groups or beyond any crash group. In such a case, `context.children` would contain all of them normally and these children wouldn't be interrupted by the crash of `:my_group` members. The main goal of crash groups is to limit the consequences of the child's crash only to children with the same crash group.
93
+
94
+
### Handling the crash group down
95
+
96
+
Then, when all members of a crash group are terminated, `MyPipeline` will execute
97
+
98
+
```elixir
99
+
@impltrue
100
+
defhandle_crash_group_down(:my_group, context, state) do
101
+
# ...
102
+
end
103
+
```
104
+
105
+
`context` passed as a third argument to `handle_crash_group_down/3` callback contains 3 additional fields, that usually don't occur in contexts of other callbacks:
106
+
-`context.crash_initiator` - name or reference of the child that crashed first and caused the crash group to explode.
54
107
-`context.crash_reason` - the reason with which `context.crash_initiator` crashed.
55
108
-`context.members` - names/references of all children that were in the crash group.
0 commit comments