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: basic_pipeline/06_OrderingBuffer.md
+62-52Lines changed: 62 additions & 52 deletions
Original file line number
Diff line number
Diff line change
@@ -4,84 +4,88 @@ In this chapter we will deal with the next [element](../glossary/glossary.md#ele
4
4
As stated in the [chapter about the system architecture](02_SystemArchitecture.md), this element is responsible for ordering the incoming [packets](../glossary/glossary.md#packet), based on their sequence id.
5
5
Because Ordering Buffer is a filtering element, we need to specify both the input and the output [pads](../glossary/glossary.md#pad):
Note the caps specification definition there - we expect `Basic.Formats.Packet` of type `:custom_packets` to be sent on the input pad, and the same type of packets to be sent through the output pad.
26
+
Note the format specification definition there - we expect `Basic.Formats.Packet` of type `:custom_packets` to be sent on the input pad, and the same type of packets to be sent through the output pad.
22
27
In the next step let's specify how we want the state of our element to look like:
23
28
24
-
**_`lib/elements/OrderingBuffer.ex`_**
29
+
**_`lib/elements/ordering_buffer.ex`_**
25
30
26
31
```elixir
27
32
defmoduleBasic.Elements.OrderingBufferdo
28
-
...
33
+
...
29
34
@impltrue
30
-
defhandle_init(_options) do
31
-
{:ok,
32
-
%{
33
-
ordered_packets: [],
34
-
last_sent_seq_id:0
35
-
}
36
-
}
35
+
defhandle_init(_context, _options) do
36
+
{[],
37
+
%{
38
+
ordered_packets: [],
39
+
last_sent_seq_id:0
40
+
}}
37
41
end
38
-
...
42
+
...
39
43
end
40
44
```
41
45
42
46
If you don't remember what is the purpose of the Ordering Buffer, please refer to the [2nd chapter](02_SystemArchitecture.md).
43
47
We will need to hold a list of ordered packets, as well as a sequence id of the packet, which most recently was sent through the output pad (we need to know if there are some packets missing between the last sent packet and the first packet in our ordered list).
44
48
45
-
Handling demand is quite straightforward:
49
+
Handling demands is quite straightforward:
46
50
47
-
**_`lib/elements/OrderingBuffer.ex`_**
51
+
**_`lib/elements/ordering_buffer.ex`_**
48
52
49
53
```elixir
50
54
defmoduleBasic.Elements.OrderingBufferdo
51
55
...
52
-
@impltrue
53
-
defhandle_demand(_ref, size, _unit, _ctx, state) do
54
-
{ {:ok, demand: {Pad.ref(:input), size} }, state}
55
-
end
56
+
@impltrue
57
+
defhandle_demand(:output, size, _unit, _context, state) do
58
+
{[demand: {:input, size}], state}
59
+
end
56
60
...
57
61
end
58
62
```
59
63
60
64
We simply send the `:demand` on the `:input` pad once we receive a demand on the `:output` pad. One packet on input corresponds to one packet on output so for each 1 unit of demand we send 1 unit of demand to the `:input` pad.
61
65
62
-
Now we can go to the main part of the Ordering Buffer implementation - the `handle_process/4` callback.
66
+
Now we can go to the main part of the Ordering Buffer implementation - the `handle_buffer/4` callback.
63
67
The purpose of this callback is to process the incoming buffer. It gets called once a new buffer is available and waiting to be processed.
64
68
65
-
**_`lib/elements/OrderingBuffer.ex`_**
69
+
**_`lib/elements/ordering_buffer.ex`_**
66
70
67
71
```elixir
68
72
defmoduleBasic.Elements.OrderingBufferdo
69
-
...
73
+
...
70
74
@impltrue
71
-
defhandle_process(:input, buffer, _context, state) do
75
+
defhandle_buffer(:input, buffer, _context, state) do
Once we unzip the header of the packet in the `handle_process/4` callback, we can put the incoming packet in the `ordered_packets` list and sort that list. Due to the fact, that elements of this list are tuples, whose first element is a sequence id (a value that is unique), the list will be sorted based on the sequence id.
125
+
Once we unzip the header of the packet in the `handle_buffer/4` callback, we can put the incoming packet in the `ordered_packets` list and sort that list. Due to the fact, that elements of this list are tuples, whose first element is a sequence id (a value that is unique), the list will be sorted based on the sequence id.
122
126
We also get the sequence id of the first element in the updated `ordered_packets` list.
123
127
124
-
Here comes the rest of the `handle_process/4` definition:
128
+
Here comes the rest of the `handle_buffer/4` definition:
125
129
126
-
**_`lib/elements/OrderingBuffer.ex`_**
130
+
**_`lib/elements/ordering_buffer.ex`_**
127
131
128
132
```elixir
129
133
defmoduleBasic.Elements.OrderingBufferdo
130
-
...
131
-
defhandle_process(:input, buffer, _context, state) do
132
-
...
134
+
...
135
+
defhandle_buffer(:input, buffer, _context, state) do
We need to distinguish between two situations: the currently processed packet can have a sequence id which is subsequent to the sequence id of the last sent packet or there might be some packets not yet delivered to us, with sequence ids in between the last sent sequence id and the sequence id of a currently processed packet. In the second case, we should store the packet and wait for the next packets to arrive. We will accomplish that using [`redemands` mechanism](../glossary/glossary.md#redemands), which will be explained in detail in the next chapter.
154
161
However, in the first situation, we need to get the ready packet's sequence - that means, a consistent batch of packets from the `:ordered_packets`. This can be done in the following way:
155
162
156
-
**_`lib/elements/OrderingBuffer.ex`_**
163
+
**_`lib/elements/ordering_buffer.ex`_**
157
164
158
165
```elixir
159
166
defmoduleBasic.Elements.OrderingBufferdo
160
-
...
161
-
defpget_ready_packets_sequence([], acc) do
162
-
{acc, []}
167
+
...
168
+
defpget_ready_packets_sequence([first_packet | ordered_rest], []) do
defpget_ready_packets_sequence([first_seq | rest], acc) do
172
-
{[first_seq | acc], rest}
173
-
end
180
+
defpget_ready_packets_sequence(ordered_packets, ready_sequence) do
181
+
{Enum.reverse(ready_sequence), ordered_packets}
182
+
end
183
+
...
174
184
end
175
185
```
176
186
177
187
Note the order of the definitions, since we are taking advantage of the pattern matching mechanism!
178
-
The algorithm implemented in the snippet above is really simple - we are recursively taking the next packet out of the `:ordered_packets` buffer until it becomes empty or there is a missing packet (`first_id + 1 == second_id`) between the last taken packet and the next packet in the buffer.
188
+
The algorithm implemented in the snippet above is really simple - we are recursively taking the next packet out of the `:ordered_packets` buffer until it becomes empty or there is a missing packet (`next_id == last_id + 1`) between the last taken packet and the next packet in the buffer.
179
189
Once we have a consistent batch of packets, we can update the state (both the`:ordered_packets` and the `:last_sent_seq_id` need to be updated) and output the ready packets by defining the `:buffer` action.
0 commit comments